]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
add support for per-id access on AccessReaderComponent (#13659)
authorNemanja <98561806+EmoGarbage404@users.noreply.github.com>
Tue, 28 Feb 2023 16:03:55 +0000 (11:03 -0500)
committerGitHub <noreply@github.com>
Tue, 28 Feb 2023 16:03:55 +0000 (08:03 -0800)
* add support for per-id access on AccessReaderComponent

* comments!!!

* oh yeah we predicting baby

* foobar

* sloth review

* weh

12 files changed:
Content.IntegrationTests/Tests/Access/AccessReaderTest.cs
Content.Server/Access/Systems/IdCardConsoleSystem.cs
Content.Server/CrewManifest/CrewManifestSystem.cs
Content.Server/Mind/Commands/RenameCommand.cs
Content.Server/StationRecords/Components/StationRecordKeyStorageComponent.cs [deleted file]
Content.Server/StationRecords/Components/StationRecordsComponent.cs
Content.Server/StationRecords/Systems/StationRecordsSystem.cs
Content.Shared/Access/Components/AccessReaderComponent.cs
Content.Shared/Access/Systems/AccessReaderSystem.cs
Content.Shared/Emag/Systems/EmagSystem.cs
Content.Shared/StationRecords/StationRecordKeyStorageComponent.cs [new file with mode: 0644]
Content.Shared/StationRecords/StationRecordKeyStorageSystem.cs [moved from Content.Server/StationRecords/Systems/StationRecordKeyStorageSystem.cs with 63% similarity]

index ae8da040ec52d996b7f5c714bd1f2505b22acc1d..6b4df318b4f942b680b7cd7b2b048a94c97414aa 100644 (file)
@@ -17,59 +17,60 @@ namespace Content.IntegrationTests.Tests.Access
             await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true});
             var server = pairTracker.Pair.Server;
 
+
             await server.WaitAssertion(() =>
             {
                 var system = EntitySystem.Get<AccessReaderSystem>();
 
                 // test empty
                 var reader = new AccessReaderComponent();
-                Assert.That(system.IsAllowed(new[] { "Foo" }, reader), Is.True);
-                Assert.That(system.IsAllowed(new[] { "Bar" }, reader), Is.True);
-                Assert.That(system.IsAllowed(new string[] { }, reader), Is.True);
+                Assert.That(system.AreAccessTagsAllowed(new[] { "Foo" }, reader), Is.True);
+                Assert.That(system.AreAccessTagsAllowed(new[] { "Bar" }, reader), Is.True);
+                Assert.That(system.AreAccessTagsAllowed(new string[] { }, reader), Is.True);
 
                 // test deny
                 reader = new AccessReaderComponent();
                 reader.DenyTags.Add("A");
-                Assert.That(system.IsAllowed(new[] { "Foo" }, reader), Is.True);
-                Assert.That(system.IsAllowed(new[] { "A" }, reader), Is.False);
-                Assert.That(system.IsAllowed(new[] { "A", "Foo" }, reader), Is.False);
-                Assert.That(system.IsAllowed(new string[] { }, reader), Is.True);
+                Assert.That(system.AreAccessTagsAllowed(new[] { "Foo" }, reader), Is.True);
+                Assert.That(system.AreAccessTagsAllowed(new[] { "A" }, reader), Is.False);
+                Assert.That(system.AreAccessTagsAllowed(new[] { "A", "Foo" }, reader), Is.False);
+                Assert.That(system.AreAccessTagsAllowed(new string[] { }, reader), Is.True);
 
                 // test one list
                 reader = new AccessReaderComponent();
                 reader.AccessLists.Add(new HashSet<string> { "A" });
-                Assert.That(system.IsAllowed(new[] { "A" }, reader), Is.True);
-                Assert.That(system.IsAllowed(new[] { "B" }, reader), Is.False);
-                Assert.That(system.IsAllowed(new[] { "A", "B" }, reader), Is.True);
-                Assert.That(system.IsAllowed(new string[] { }, reader), Is.False);
+                Assert.That(system.AreAccessTagsAllowed(new[] { "A" }, reader), Is.True);
+                Assert.That(system.AreAccessTagsAllowed(new[] { "B" }, reader), Is.False);
+                Assert.That(system.AreAccessTagsAllowed(new[] { "A", "B" }, reader), Is.True);
+                Assert.That(system.AreAccessTagsAllowed(new string[] { }, reader), Is.False);
 
                 // test one list - two items
                 reader = new AccessReaderComponent();
                 reader.AccessLists.Add(new HashSet<string> { "A", "B" });
-                Assert.That(system.IsAllowed(new[] { "A" }, reader), Is.False);
-                Assert.That(system.IsAllowed(new[] { "B" }, reader), Is.False);
-                Assert.That(system.IsAllowed(new[] { "A", "B" }, reader), Is.True);
-                Assert.That(system.IsAllowed(new string[] { }, reader), Is.False);
+                Assert.That(system.AreAccessTagsAllowed(new[] { "A" }, reader), Is.False);
+                Assert.That(system.AreAccessTagsAllowed(new[] { "B" }, reader), Is.False);
+                Assert.That(system.AreAccessTagsAllowed(new[] { "A", "B" }, reader), Is.True);
+                Assert.That(system.AreAccessTagsAllowed(new string[] { }, reader), Is.False);
 
                 // test two list
                 reader = new AccessReaderComponent();
                 reader.AccessLists.Add(new HashSet<string> { "A" });
                 reader.AccessLists.Add(new HashSet<string> { "B", "C" });
-                Assert.That(system.IsAllowed(new[] { "A" }, reader), Is.True);
-                Assert.That(system.IsAllowed(new[] { "B" }, reader), Is.False);
-                Assert.That(system.IsAllowed(new[] { "A", "B" }, reader), Is.True);
-                Assert.That(system.IsAllowed(new[] { "C", "B" }, reader), Is.True);
-                Assert.That(system.IsAllowed(new[] { "C", "B", "A" }, reader), Is.True);
-                Assert.That(system.IsAllowed(new string[] { }, reader), Is.False);
+                Assert.That(system.AreAccessTagsAllowed(new[] { "A" }, reader), Is.True);
+                Assert.That(system.AreAccessTagsAllowed(new[] { "B" }, reader), Is.False);
+                Assert.That(system.AreAccessTagsAllowed(new[] { "A", "B" }, reader), Is.True);
+                Assert.That(system.AreAccessTagsAllowed(new[] { "C", "B" }, reader), Is.True);
+                Assert.That(system.AreAccessTagsAllowed(new[] { "C", "B", "A" }, reader), Is.True);
+                Assert.That(system.AreAccessTagsAllowed(new string[] { }, reader), Is.False);
 
                 // test deny list
                 reader = new AccessReaderComponent();
                 reader.AccessLists.Add(new HashSet<string> { "A" });
                 reader.DenyTags.Add("B");
-                Assert.That(system.IsAllowed(new[] { "A" }, reader), Is.True);
-                Assert.That(system.IsAllowed(new[] { "B" }, reader), Is.False);
-                Assert.That(system.IsAllowed(new[] { "A", "B" }, reader), Is.False);
-                Assert.That(system.IsAllowed(new string[] { }, reader), Is.False);
+                Assert.That(system.AreAccessTagsAllowed(new[] { "A" }, reader), Is.True);
+                Assert.That(system.AreAccessTagsAllowed(new[] { "B" }, reader), Is.False);
+                Assert.That(system.AreAccessTagsAllowed(new[] { "A", "B" }, reader), Is.False);
+                Assert.That(system.AreAccessTagsAllowed(new string[] { }, reader), Is.False);
             });
             await pairTracker.CleanReturnAsync();
         }
index 15578a0b9815b46ce18784327efd6f2698f9fa1c..5804e709d43d65c6174104e786df054b19b4d522 100644 (file)
@@ -1,6 +1,6 @@
 using System.Linq;
 using Content.Server.Station.Systems;
-using Content.Server.StationRecords;
+using Content.Server.StationRecords.Systems;
 using Content.Shared.Access.Components;
 using Content.Shared.Access.Systems;
 using Content.Shared.Administration.Logs;
index 1bd61d872b5a5e00408b743d52e861dbbc538ad1..fe037edcc37ee551367166238c0a552da3093cbb 100644 (file)
@@ -1,22 +1,17 @@
 using System.Linq;
 using Content.Server.Administration;
 using Content.Server.EUI;
-using Content.Server.GameTicking;
 using Content.Server.Station.Systems;
 using Content.Server.StationRecords;
+using Content.Server.StationRecords.Systems;
 using Content.Shared.Administration;
 using Content.Shared.CCVar;
 using Content.Shared.CrewManifest;
 using Content.Shared.GameTicking;
-using Content.Shared.Roles;
 using Content.Shared.StationRecords;
-using Robust.Server.GameObjects;
 using Robust.Server.Player;
 using Robust.Shared.Configuration;
 using Robust.Shared.Console;
-using Robust.Shared.Player;
-using Robust.Shared.Players;
-using Robust.Shared.Prototypes;
 
 namespace Content.Server.CrewManifest;
 
index 2986f578e1096ddd75fd3563286a764dcd11231a..7b3cb92c1696cca74a16947bfc042889859907a1 100644 (file)
@@ -1,10 +1,9 @@
 using Content.Server.Access.Systems;
 using Content.Server.Administration;
 using Content.Server.Administration.Systems;
-using Content.Server.Cloning;
 using Content.Server.Mind.Components;
 using Content.Server.PDA;
-using Content.Server.StationRecords;
+using Content.Server.StationRecords.Systems;
 using Content.Shared.Access.Components;
 using Content.Shared.Administration;
 using Content.Shared.PDA;
diff --git a/Content.Server/StationRecords/Components/StationRecordKeyStorageComponent.cs b/Content.Server/StationRecords/Components/StationRecordKeyStorageComponent.cs
deleted file mode 100644 (file)
index a5e118c..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-using Content.Shared.StationRecords;
-
-namespace Content.Server.StationRecords;
-
-[RegisterComponent]
-public sealed class StationRecordKeyStorageComponent : Component
-{
-    /// <summary>
-    ///     The key stored in this component.
-    /// </summary>
-    [ViewVariables]
-    public StationRecordKey? Key;
-}
index 4cd295dc12305d8c3d1b1446779bf636bcba3c69..447d06385a593095c5323b5869b7438b79a2239e 100644 (file)
@@ -1,3 +1,5 @@
+using Content.Server.StationRecords.Systems;
+
 namespace Content.Server.StationRecords;
 
 [Access(typeof(StationRecordsSystem))]
index e801cad73ed2c2e7c77fbcffa92827a6aadf30ab..50f62d323ba30e0612069ce01ab20936544c551c 100644 (file)
@@ -1,9 +1,7 @@
 using System.Diagnostics.CodeAnalysis;
-using Content.Server.Access.Systems;
+using System.Linq;
 using Content.Server.GameTicking;
 using Content.Server.Station.Systems;
-using Content.Server.StationRecords;
-using Content.Server.StationRecords.Systems;
 using Content.Shared.Access.Components;
 using Content.Shared.Inventory;
 using Content.Shared.PDA;
@@ -13,6 +11,8 @@ using Content.Shared.StationRecords;
 using Robust.Shared.Enums;
 using Robust.Shared.Prototypes;
 
+namespace Content.Server.StationRecords.Systems;
+
 /// <summary>
 ///     Station records.
 ///
index 97fe78f7e69861401301072383868fe4f7f4e780..a365ecae845f3c4a3126b8b0ab78aba7b2791903 100644 (file)
@@ -1,22 +1,52 @@
-namespace Content.Shared.Access.Components
+using Content.Shared.Access.Systems;
+using Content.Shared.StationRecords;
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
+
+namespace Content.Shared.Access.Components;
+
+/// <summary>
+///     Stores access levels necessary to "use" an entity
+///     and allows checking if something or somebody is authorized with these access levels.
+/// </summary>
+[RegisterComponent, NetworkedComponent]
+public sealed class AccessReaderComponent : Component
 {
     /// <summary>
-    ///     Stores access levels necessary to "use" an entity
-    ///     and allows checking if something or somebody is authorized with these access levels.
+    ///     The set of tags that will automatically deny an allowed check, if any of them are present.
+    /// </summary>
+    [DataField("denyTags", customTypeSerializer: typeof(PrototypeIdHashSetSerializer<AccessLevelPrototype>))]
+    public HashSet<string> DenyTags = new();
+
+    /// <summary>
+    ///     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.
+    /// </summary>
+    [DataField("access")]
+    public List<HashSet<string>> AccessLists = new();
+
+    /// <summary>
+    /// A list of valid stationrecordkeys
     /// </summary>
-    [RegisterComponent]
-    public sealed class AccessReaderComponent : Component
+    [DataField("accessKeys")]
+    public HashSet<StationRecordKey> AccessKeys = new();
+}
+
+[Serializable, NetSerializable]
+public sealed class AccessReaderComponentState : ComponentState
+{
+    public HashSet<string> DenyTags;
+
+    public List<HashSet<string>> AccessLists;
+
+    public HashSet<StationRecordKey> AccessKeys;
+
+    public AccessReaderComponentState(HashSet<string> denyTags, List<HashSet<string>> accessLists, HashSet<StationRecordKey> accessKeys)
     {
-        /// <summary>
-        ///     The set of tags that will automatically deny an allowed check, if any of them are present.
-        /// </summary>
-        public HashSet<string> DenyTags = new();
-
-        /// <summary>
-        ///     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.
-        /// </summary>
-        [DataField("access")]
-        public List<HashSet<string>> AccessLists = new();
+        DenyTags = denyTags;
+        AccessLists = accessLists;
+        AccessKeys = accessKeys;
     }
 }
+
index 4562d05858fca23c648754386f82b477105cef0b..b486d6dd9caa1fa20c18d467401105539f6a989d 100644 (file)
@@ -8,6 +8,8 @@ using Content.Shared.Access.Components;
 using Robust.Shared.Prototypes;
 using Content.Shared.Hands.EntitySystems;
 using Content.Shared.MachineLinking.Events;
+using Content.Shared.StationRecords;
+using Robust.Shared.GameStates;
 
 namespace Content.Shared.Access.Systems
 {
@@ -23,6 +25,24 @@ namespace Content.Shared.Access.Systems
             SubscribeLocalEvent<AccessReaderComponent, ComponentInit>(OnInit);
             SubscribeLocalEvent<AccessReaderComponent, GotEmaggedEvent>(OnEmagged);
             SubscribeLocalEvent<AccessReaderComponent, LinkAttemptEvent>(OnLinkAttempt);
+
+            SubscribeLocalEvent<AccessReaderComponent, ComponentGetState>(OnGetState);
+            SubscribeLocalEvent<AccessReaderComponent, ComponentHandleState>(OnHandleState);
+        }
+
+        private void OnGetState(EntityUid uid, AccessReaderComponent component, ref ComponentGetState args)
+        {
+            args.State = new AccessReaderComponentState(component.DenyTags, component.AccessLists,
+                component.AccessKeys);
+        }
+
+        private void OnHandleState(EntityUid uid, AccessReaderComponent component, ref ComponentHandleState args)
+        {
+            if (args.Current is not AccessReaderComponentState state)
+                return;
+            component.AccessKeys = new (state.AccessKeys);
+            component.AccessLists = new (state.AccessLists);
+            component.DenyTags = new (state.DenyTags);
         }
 
         private void OnLinkAttempt(EntityUid uid, AccessReaderComponent component, LinkAttemptEvent args)
@@ -62,8 +82,7 @@ namespace Content.Shared.Access.Systems
         {
             if (!Resolve(target, ref reader, false))
                 return true;
-            var tags = FindAccessTags(source);
-            return IsAllowed(tags, reader);
+            return IsAllowed(source, reader);
         }
 
         /// <summary>
@@ -74,8 +93,15 @@ namespace Content.Shared.Access.Systems
         /// <param name="reader">A reader from a different entity</param>
         public bool IsAllowed(EntityUid entity, AccessReaderComponent reader)
         {
-            var tags = FindAccessTags(entity);
-            return IsAllowed(tags, reader);
+            var allEnts = FindPotentialAccessItems(entity);
+
+            if (AreAccessTagsAllowed(FindAccessTags(entity, allEnts), reader))
+                return true;
+
+            if (AreStationRecordKeysAllowed(FindStationRecordKeys(entity, allEnts), reader))
+                return true;
+
+            return false;
         }
 
         /// <summary>
@@ -83,7 +109,7 @@ namespace Content.Shared.Access.Systems
         /// </summary>
         /// <param name="accessTags">A list of access tags</param>
         /// <param name="reader">An access reader to check against</param>
-        public bool IsAllowed(ICollection<string> accessTags, AccessReaderComponent reader)
+        public bool AreAccessTagsAllowed(ICollection<string> accessTags, AccessReaderComponent reader)
         {
             if (HasComp<EmaggedComponent>(reader.Owner))
             {
@@ -105,17 +131,18 @@ namespace Content.Shared.Access.Systems
         }
 
         /// <summary>
-        /// Finds the access tags on the given entity
+        /// Compares the given stationrecordkeys with the accessreader to see if it is allowed.
         /// </summary>
-        /// <param name="uid">The entity that is being searched.</param>
-        public ICollection<string> FindAccessTags(EntityUid uid)
+        public bool AreStationRecordKeysAllowed(ICollection<StationRecordKey> keys, AccessReaderComponent reader)
         {
-            HashSet<string>? tags = null;
-            var owned = false;
-
-            // check entity itself
-            FindAccessTagsItem(uid, ref tags, ref owned);
+            return keys.Any() && reader.AccessKeys.Any(keys.Contains);
+        }
 
+        /// <summary>
+        /// Finds all the items that could potentially give access to a given entity
+        /// </summary>
+        public HashSet<EntityUid> FindPotentialAccessItems(EntityUid uid)
+        {
             FindAccessItemsInventory(uid, out var items);
 
             var ev = new GetAdditionalAccessEvent
@@ -123,7 +150,23 @@ namespace Content.Shared.Access.Systems
                 Entities = items
             };
             RaiseLocalEvent(uid, ref ev);
-            foreach (var ent in ev.Entities)
+            items.Add(uid);
+            return items;
+        }
+
+        /// <summary>
+        /// Finds the access tags on the given entity
+        /// </summary>
+        /// <param name="uid">The entity that is being searched.</param>
+        /// <param name="items">All of the items to search for access. If none are passed in, <see cref="FindPotentialAccessItems"/> will be used.</param>
+        public ICollection<string> FindAccessTags(EntityUid uid, HashSet<EntityUid>? items = null)
+        {
+            HashSet<string>? tags = null;
+            var owned = false;
+
+            items ??= FindPotentialAccessItems(uid);
+
+            foreach (var ent in items)
             {
                 FindAccessTagsItem(ent, ref tags, ref owned);
             }
@@ -131,6 +174,26 @@ namespace Content.Shared.Access.Systems
             return (ICollection<string>?) tags ?? Array.Empty<string>();
         }
 
+        /// <summary>
+        /// Finds the access tags on the given entity
+        /// </summary>
+        /// <param name="uid">The entity that is being searched.</param>
+        /// <param name="items">All of the items to search for access. If none are passed in, <see cref="FindPotentialAccessItems"/> will be used.</param>
+        public ICollection<StationRecordKey> FindStationRecordKeys(EntityUid uid, HashSet<EntityUid>? items = null)
+        {
+            HashSet<StationRecordKey> keys = new();
+
+            items ??= FindPotentialAccessItems(uid);
+
+            foreach (var ent in items)
+            {
+                if (FindStationRecordKeyItem(ent, out var key))
+                    keys.Add(key.Value);
+            }
+
+            return keys;
+        }
+
         /// <summary>
         ///     Try to find <see cref="AccessComponent"/> on this item
         ///     or inside this item (if it's pda)
@@ -203,5 +266,31 @@ namespace Content.Shared.Access.Systems
             tags = null;
             return false;
         }
+
+        /// <summary>
+        ///     Try to find <see cref="StationRecordKeyStorageComponent"/> on this item
+        ///     or inside this item (if it's pda)
+        /// </summary>
+        private bool FindStationRecordKeyItem(EntityUid uid, [NotNullWhen(true)] out StationRecordKey? key)
+        {
+            if (EntityManager.TryGetComponent(uid, out StationRecordKeyStorageComponent? storage) && storage.Key != null)
+            {
+                key = storage.Key;
+                return true;
+            }
+
+            if (TryComp<PDAComponent>(uid, out var pda) &&
+                pda.ContainedID?.Owner is {Valid: true} id)
+            {
+                if (TryComp<StationRecordKeyStorageComponent>(id, out var pdastorage) && pdastorage.Key != null)
+                {
+                    key = pdastorage.Key;
+                    return true;
+                }
+            }
+
+            key = null;
+            return false;
+        }
     }
 }
index d5db09e1e242b5d5212e3de7a1eba9d43af129fa..360cfa31419bd916a29d6d585489ee393749d82a 100644 (file)
@@ -130,7 +130,8 @@ namespace Content.Shared.Emag.Systems
 
             if (component.Charges <= 0)
             {
-                _popupSystem.PopupEntity(Loc.GetString("emag-no-charges"), user, user);
+                if (_net.IsServer)
+                    _popupSystem.PopupEntity(Loc.GetString("emag-no-charges"), user, user);
                 return false;
             }
 
diff --git a/Content.Shared/StationRecords/StationRecordKeyStorageComponent.cs b/Content.Shared/StationRecords/StationRecordKeyStorageComponent.cs
new file mode 100644 (file)
index 0000000..87f7893
--- /dev/null
@@ -0,0 +1,25 @@
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.StationRecords;
+
+[RegisterComponent, NetworkedComponent]
+public sealed class StationRecordKeyStorageComponent : Component
+{
+    /// <summary>
+    ///     The key stored in this component.
+    /// </summary>
+    [ViewVariables]
+    public StationRecordKey? Key;
+}
+
+[Serializable, NetSerializable]
+public sealed class StationRecordKeyStorageComponentState : ComponentState
+{
+    public StationRecordKey? Key;
+
+    public StationRecordKeyStorageComponentState(StationRecordKey? key)
+    {
+        Key = key;
+    }
+}
similarity index 63%
rename from Content.Server/StationRecords/Systems/StationRecordKeyStorageSystem.cs
rename to Content.Shared/StationRecords/StationRecordKeyStorageSystem.cs
index d19444b644baca2c1589dbbf8d2a87420d0d9076..94eda02c5fb966ef50a436377c64af031d41fa70 100644 (file)
@@ -1,9 +1,29 @@
-using Content.Shared.StationRecords;
+using Robust.Shared.GameStates;
 
-namespace Content.Server.StationRecords.Systems;
+namespace Content.Shared.StationRecords;
 
 public sealed class StationRecordKeyStorageSystem : EntitySystem
 {
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<StationRecordKeyStorageComponent, ComponentGetState>(OnGetState);
+        SubscribeLocalEvent<StationRecordKeyStorageComponent, ComponentHandleState>(OnHandleState);
+    }
+
+    private void OnGetState(EntityUid uid, StationRecordKeyStorageComponent component, ref ComponentGetState args)
+    {
+        args.State = new StationRecordKeyStorageComponentState(component.Key);
+    }
+
+    private void OnHandleState(EntityUid uid, StationRecordKeyStorageComponent component, ref ComponentHandleState args)
+    {
+        if (args.Current is not StationRecordKeyStorageComponentState state)
+            return;
+        component.Key = state.Key;
+    }
+
     /// <summary>
     ///     Assigns a station record key to an entity.
     /// </summary>
@@ -18,6 +38,7 @@ public sealed class StationRecordKeyStorageSystem : EntitySystem
         }
 
         keyStorage.Key = key;
+        Dirty(keyStorage);
     }
 
     /// <summary>
@@ -35,6 +56,7 @@ public sealed class StationRecordKeyStorageSystem : EntitySystem
 
         var key = keyStorage.Key;
         keyStorage.Key = null;
+        Dirty(keyStorage);
 
         return key;
     }