Jelajahi Sumber

Improvements to Security

Kenric Nugteren 2 minggu lalu
induk
melakukan
f3e9a463c0

+ 224 - 135
prs.desktop/Panels/Security/Global/GlobalTokenGrid.cs

@@ -30,8 +30,6 @@ public class GlobalTokenGrid : DynamicGrid<SecurityDescriptor>
         UserGroups = new Dictionary<Guid, Guid>();
         GroupID = Guid.Empty;
 
-        Items = new List<SecurityTokenItem>();
-
         HiddenColumns.Add(x => x.Descriptor);
         HiddenColumns.Add(x => x.Default);
 
@@ -48,7 +46,11 @@ public class GlobalTokenGrid : DynamicGrid<SecurityDescriptor>
     public Dictionary<Guid, string> UserNames { get; }
     public Dictionary<Guid, Guid> UserGroups { get; }
 
-    public List<SecurityTokenItem> Items { get; }
+    private Dictionary<string, List<SecurityTokenItem>> _globalItems = new();
+    private Dictionary<string, List<SecurityTokenItem>> _groupItems = new();
+    private Dictionary<string, List<SecurityTokenItem>> _userItems = new();
+
+    // public List<SecurityTokenItem> Items { get; }
 
     public Guid GroupID { get; set; }
 
@@ -153,7 +155,7 @@ public class GlobalTokenGrid : DynamicGrid<SecurityDescriptor>
         
         if (!MatchFilter(filter, ENABLED_FILTERS))
         {
-            bool isenabled = GetGlobalOrDefault(descriptor, globaldefault);
+            var isenabled = GetGlobalOrDefault(descriptor, globaldefault);
             var check = (filter(ENABLED_TOKENS) && isenabled) || (filter(DISABLED_TOKENS) && !isenabled);
             if (!check)
                 return false;
@@ -161,7 +163,7 @@ public class GlobalTokenGrid : DynamicGrid<SecurityDescriptor>
 
         if (!MatchFilter(filter, OVERRIDDEN_FILTERS))
         {
-            bool isoverridden = Items.Any(x => String.Equals(x.Descriptor, descriptor) && (x.Type == SecurityTokenType.Global));
+            var isoverridden = _globalItems.TryGetValue(descriptor, out var items) && items.Count > 0;
             var check = (filter(OVERRIDDEN_TOKENS) && isoverridden) || (filter(DEFAULT_TOKENS) && !isoverridden);
             if (!check)
                 return false;
@@ -188,7 +190,8 @@ public class GlobalTokenGrid : DynamicGrid<SecurityDescriptor>
 
         if (!MatchFilter(filter, OVERRIDDEN_FILTERS))
         {
-            bool isoverridden = Items.Any(x => String.Equals(x.Descriptor, descriptor) && (x.Type == SecurityTokenType.Group) && x.ID == groupid);
+            var isoverridden =
+                _groupItems.TryGetValue(descriptor, out var items) && items.Any(x => x.ID == groupid);
             var check = (filter(OVERRIDDEN_TOKENS) && isoverridden) || (filter(DEFAULT_TOKENS) && !isoverridden);
             if (!check)
                 return false;
@@ -215,7 +218,8 @@ public class GlobalTokenGrid : DynamicGrid<SecurityDescriptor>
 
         if (!MatchFilter(filter, OVERRIDDEN_FILTERS))
         {
-            bool isoverridden = Items.Any(x => string.Equals(x.Descriptor, descriptor) && (x.Type == SecurityTokenType.User) && x.ID == userid);
+            var isoverridden =
+                _userItems.TryGetValue(descriptor, out var items) && items.Any(x => x.ID == userid);
             var check = (filter(OVERRIDDEN_TOKENS) && isoverridden) || (filter(DEFAULT_TOKENS) && !isoverridden);
             if (!check)
                 return false;
@@ -277,41 +281,67 @@ public class GlobalTokenGrid : DynamicGrid<SecurityDescriptor>
         action.Invoke(_table, null);
     }
 
+    public void AddItem(SecurityTokenItem item)
+    {
+        switch (item.Type)
+        {
+            case SecurityTokenType.Global:
+                _globalItems.GetValueOrAdd(item.Descriptor).Add(item);
+                break;
+            case SecurityTokenType.Group:
+                _groupItems.GetValueOrAdd(item.Descriptor).Add(item);
+                break;
+            case SecurityTokenType.User:
+                _userItems.GetValueOrAdd(item.Descriptor).Add(item);
+                break;
+        }
+    }
+    public void AddItems(IEnumerable<SecurityTokenItem> items)
+    {
+        foreach(var item in items)
+        {
+            AddItem(item);
+        }
+    }
+
+    private bool? GetGlobal(string code)
+    {
+        return _globalItems.GetValueOrDefault(code)?.FirstOrDefault()?.Enabled;
+    }
     private bool GetGlobalOrDefault(string code, bool globaldefault)
     {
-        var global = Items.FirstOrDefault(x => x.Type.Equals(SecurityTokenType.Global) && string.Equals(x.Descriptor, code));
-        if (global != null)
-            return global.Enabled;
-        return globaldefault;
+        return GetGlobal(code) ?? globaldefault;
     }
 
+    private bool? GetGroup(string code, Guid groupid)
+    {
+        return _groupItems.GetValueOrDefault(code)?.FirstOrDefault(x => x.ID == groupid)?.Enabled;
+    }
     private bool GetGroupOrDefault(string code, Guid groupid, bool globaldefault)
     {
-        var group = Items.FirstOrDefault(
-            x => string.Equals(x.Descriptor, code) && x.Type.Equals(SecurityTokenType.Group) && Equals(x.ID, groupid));
-        if (group != null)
-            return group.Enabled;
-        return GetGlobalOrDefault(code, globaldefault);
+        return GetGroup(code, groupid)
+            ?? GetGlobalOrDefault(code, globaldefault);
     }
 
+    private bool? GetUser(string code, Guid userid)
+    {
+        return _userItems.GetValueOrDefault(code)?.FirstOrDefault(x => x.ID == userid)?.Enabled;
+    }
     private bool GetUserOrDefault(string code, Guid userid, Guid groupid, bool globaldefault)
     {
-        var user = Items.FirstOrDefault(x => string.Equals(x.Descriptor, code) && x.Type.Equals(SecurityTokenType.User) && Equals(x.ID, userid));
-        if (user != null)
-            return user.Enabled;
-        return GetGroupOrDefault(code, groupid, globaldefault);
+        return GetUser(code, userid)
+            ?? GetGroupOrDefault(code, groupid, globaldefault);
     }
 
-
-
     private BitmapImage? GlobalImage(CoreRow? row)
     {
         if (row == null)
             return null;
         var code = row.Get<SecurityDescriptor, string>(c => c.Descriptor);
-        var item = Items.FirstOrDefault(x => string.Equals(x.Descriptor, code) && x.Type.Equals(SecurityTokenType.Global));
-        if (item != null)
-            return item.Enabled ? tick : disabled;
+
+        var global = GetGlobal(code);
+        if (global != null)
+            return global.Value ? tick : disabled;
         return row.Get<SecurityDescriptor, bool>(c => c.Default) ? defaulttick : defaultdisabled;
     }
 
@@ -320,10 +350,10 @@ public class GlobalTokenGrid : DynamicGrid<SecurityDescriptor>
         if (row == null)
             return null;
         var code = row.Get<SecurityDescriptor, string>(c => c.Descriptor);
-        var group = Items.FirstOrDefault(
-            x => string.Equals(x.Descriptor, code) && x.Type.Equals(SecurityTokenType.Group) && Equals(x.ID, groupid));
+
+        var group = GetGroup(code, groupid);
         if (group != null)
-            return group.Enabled ? tick : disabled;
+            return group.Value ? tick : disabled;
         return GetGlobalOrDefault(code, row.Get<SecurityDescriptor, bool>(c => c.Default))
             ? defaulttick
             : defaultdisabled;
@@ -334,9 +364,9 @@ public class GlobalTokenGrid : DynamicGrid<SecurityDescriptor>
         if (row == null)
             return null;
         var code = row.Get<SecurityDescriptor, string>(c => c.Descriptor);
-        var user = Items.FirstOrDefault(x => string.Equals(x.Descriptor, code) && x.Type.Equals(SecurityTokenType.User) && Equals(x.ID, userid));
+        var user = GetUser(code, userid);
         if (user != null)
-            return user.Enabled ? tick : disabled;
+            return user.Value ? tick : disabled;
         return GetGroupOrDefault(code, groupid, row.Get<SecurityDescriptor, bool>(c => c.Default))
             ? defaulttick
             : defaultdisabled;
@@ -344,43 +374,109 @@ public class GlobalTokenGrid : DynamicGrid<SecurityDescriptor>
 
     private void ResetGlobals(IEnumerable<string> codes)
     {
-        var globals = Items.Where(x => codes.Contains(x.Descriptor) && x.Type.Equals(SecurityTokenType.Global));
-        if (!globals.Any())
+        var globals = codes
+            .SelectMany(x => _globalItems.GetValueOrDefault(x) ?? Enumerable.Empty<SecurityTokenItem>())
+            .Select(x => new GlobalSecurityToken { ID = x.RecordID })
+            .ToArray();
+        if (globals.Length == 0)
             return;
-        var globalupdates = globals.Select(x => new GlobalSecurityToken { ID = x.RecordID }).ToArray();
-        new Client<GlobalSecurityToken>().Delete(globalupdates, "", (o, e) => { });
-        Items.RemoveAll(x => globals.Contains(x));
+        Client.DeleteAsync(globals, "").LogIfFail();
+        foreach(var code in codes)
+        {
+            _globalItems.Remove(code);
+        }
     }
 
     private void ResetGroups(Guid groupid, IEnumerable<string> codes)
     {
-        var groups = groupid == Guid.Empty
-            ? Items.Where(x => codes.Contains(x.Descriptor) && x.Type.Equals(SecurityTokenType.Group))
-            : Items.Where(x => codes.Contains(x.Descriptor) && x.Type.Equals(SecurityTokenType.Group) && Equals(groupid, x.ID));
+        var groups = codes.SelectMany(x => _groupItems.GetValueOrDefault(x) ?? Enumerable.Empty<SecurityTokenItem>());
+        if(groupid != Guid.Empty)
+        {
+            groups = groups.Where(x => x.ID == groupid);
+        }
+
         var groupupdates = groups.Select(x => new SecurityToken { ID = x.RecordID }).ToArray();
-        new Client<SecurityToken>().Delete(groupupdates, "", (o, e) => { });
-        Items.RemoveAll(x => groups.Contains(x));
+        Client.DeleteAsync(groupupdates, "").LogIfFail();
+
+        if(groupid == Guid.Empty)
+        {
+            foreach(var code in codes)
+            {
+                _groupItems.Remove(code);
+            }
+        }
+        else
+        {
+            foreach (var code in codes)
+            {
+                if (_groupItems.TryGetValue(code, out var items))
+                {
+                    items.RemoveAll(x => x.ID == groupid);
+                    if(items.Count == 0)
+                    {
+                        _groupItems.Remove(code);
+                    }
+                }
+            }
+        }
     }
 
     private void ResetUsers(Guid groupid, IEnumerable<string> codes)
     {
-        var users = groupid == Guid.Empty
-            ? Items.Where(x => codes.Contains(x.Descriptor) && x.Type.Equals(SecurityTokenType.User))
-            : Items.Where(x => codes.Contains(x.Descriptor) && x.Type.Equals(SecurityTokenType.User) && UserGroups[x.ID].Equals(groupid));
-        var userupdates = users.Select(x => new UserSecurityToken { ID = x.RecordID }).ToArray();
-        new Client<UserSecurityToken>().Delete(userupdates, "", (o, e) => { });
-        Items.RemoveAll(x => users.Contains(x));
+        var users = codes.SelectMany(x => _userItems.GetValueOrDefault(x) ?? Enumerable.Empty<SecurityTokenItem>());
+        if(groupid != Guid.Empty)
+        {
+            users = users.Where(x => UserGroups[x.ID] == groupid);
+        }
+
+        var userUpdates = users.Select(x => new UserSecurityToken { ID = x.RecordID }).ToArray();
+        Client.DeleteAsync(userUpdates, "").LogIfFail();
+
+        if(groupid == Guid.Empty)
+        {
+            foreach(var code in codes)
+            {
+                _userItems.Remove(code);
+            }
+        }
+        else
+        {
+            foreach (var code in codes)
+            {
+                if (_userItems.TryGetValue(code, out var items))
+                {
+                    items.RemoveAll(x => UserGroups[x.ID] == groupid);
+                    if(items.Count == 0)
+                    {
+                        _userItems.Remove(code);
+                    }
+                }
+            }
+        }
     }
 
     private void ResetUser(Guid userid, IEnumerable<string> codes)
     {
-        var users = Items.Where(x => codes.Contains(x.Descriptor) && x.Type.Equals(SecurityTokenType.User) && Equals(userid, x.ID));
-        var userupdates = users.Select(x => new UserSecurityToken { ID = x.RecordID }).ToArray();
-        new Client<UserSecurityToken>().Delete(userupdates, "", (o, e) => { });
-        Items.RemoveAll(x => users.Contains(x));
+        var users = codes.SelectMany(x => _userItems.GetValueOrDefault(x) ?? Enumerable.Empty<SecurityTokenItem>())
+            .Where(x => x.ID == userid)
+            .Select(x => new UserSecurityToken { ID = x.RecordID })
+            .ToArray();
+
+        Client.DeleteAsync(users, "").LogIfFail();
+
+        foreach (var code in codes)
+        {
+            if (_userItems.TryGetValue(code, out var items))
+            {
+                items.RemoveAll(x => x.ID == userid);
+                if(items.Count == 0)
+                {
+                    _userItems.Remove(code);
+                }
+            }
+        }
     }
     
-    
     private enum TokenAction
     {
         Enable,
@@ -437,7 +533,11 @@ public class GlobalTokenGrid : DynamicGrid<SecurityDescriptor>
             return false;
         
         var descriptors = rows.Select(r =>r.Get<SecurityDescriptor, string>(c => c.Descriptor)).ToArray();
-        var resetchildren = Items.Where(x => descriptors.Contains(x.Descriptor) && !x.Type.Equals(SecurityTokenType.Global)).Any();
+        var resetchildren = descriptors.Any(x =>
+        {
+            return _groupItems.TryGetValue(x, out var group) && group.Count != 0
+                || _userItems.TryGetValue(x, out var user) && user.Count != 0;
+        });
         if (resetchildren)
         {
             var confirm = MessageBox.Show($"{_tokennames[action].Item1} Group and User Tokens as well?", $"{_tokennames[action].Item1} All",
@@ -462,71 +562,53 @@ public class GlobalTokenGrid : DynamicGrid<SecurityDescriptor>
                 progress.Report($"{_tokennames[action].Item2} Tokens ({(double)(i) * 100.0D / (double)rows.Count:F2}% complete)");
                 i++;
                 
-                string descriptor = row.Get<SecurityToken, String>(c => c.Descriptor);
-                bool defaultvalue = row.Get<SecurityDescriptor, bool>(c => c.Default);
-                bool desiredvalue = action switch
+                var descriptor = row.Get<SecurityToken, String>(c => c.Descriptor);
+                var defaultvalue = row.Get<SecurityDescriptor, bool>(c => c.Default);
+                var currentvalue = GetGlobal(descriptor);
+                var desiredvalue = action switch
                 {
                     TokenAction.Enable => true,
                     TokenAction.Disable => false,
                     TokenAction.Reset => defaultvalue,
-                    _ => !GetGlobalOrDefault(descriptor, defaultvalue)
+                    _ => !(currentvalue ?? defaultvalue)
                 };
-                var currentvalue = GetGlobalOrDefault(descriptor, row.Get<SecurityDescriptor, bool>(c => c.Default));
 
-                if (currentvalue != defaultvalue)
-                    resetglobals.Add(descriptor);
-                
                 if (resetchildren)
                 {
                     resetgroups.Add(descriptor);
                     resetusers.Add(descriptor);
                 }
-                
-                if (desiredvalue != defaultvalue)
+
+                if (desiredvalue != (currentvalue ?? defaultvalue))
                 {
-                    // ResetGlobals(new[] { descriptor });
-                    //
-                    // if (resetchildren)
-                    // {
-                    //     ResetGroups(Guid.Empty, new[] { descriptor });
-                    //     ResetUsers(Guid.Empty, new[] { descriptor });
-                    // }
-
-                    if (currentvalue == defaultvalue)
+                    if(currentvalue is not null)
+                    {
+                        resetglobals.Add(descriptor);
+                    }
+                    if(desiredvalue != defaultvalue)
                     {
                         var token = new GlobalSecurityToken
                         {
                             Descriptor = descriptor,
-                            Enabled = !currentvalue
+                            Enabled = desiredvalue
                         };
                         updates.Add(token);
-                        // new Client<GlobalSecurityToken>().Save(token, "");
-                        // var item = new SecurityTokenItem
-                        // {
-                        //     Type = SecurityTokenType.Global,
-                        //     Descriptor = descriptor,
-                        //     ID = Guid.Empty,
-                        //     RecordID = token.ID,
-                        //     Enabled = token.Enabled
-                        // };
-                        // Items.Add(item);
-
                     }
                 }
             }
             
             progress.Report("Clearing Old Tokens...");
-            if (resetusers.Any())
+            if (resetusers.Count != 0)
                 ResetUsers(Guid.Empty,resetusers);
-            if (resetgroups.Any())
+            if (resetgroups.Count != 0)
                 ResetGroups(Guid.Empty, resetgroups);
-            if (resetglobals.Any())
+            if (resetglobals.Count != 0)
                 ResetGlobals(resetglobals);
             progress.Report("Creating new Tokens...");
-            if (updates.Any())
+            if (updates.Count != 0)
             {
-                new Client<GlobalSecurityToken>().Save(updates, "");
-                Items.AddRange(updates.Select(x => new SecurityTokenItem()
+                Client.Save(updates, "");
+                AddItems(updates.Select(x => new SecurityTokenItem()
                 {
                     Type = SecurityTokenType.Global,
                     Descriptor = x.Descriptor,
@@ -542,7 +624,6 @@ public class GlobalTokenGrid : DynamicGrid<SecurityDescriptor>
         Refresh(false, true);
         return false;
     }
-
     
     private bool GroupAction(IList<CoreRow> rows, Guid groupid, TokenAction action)
     {
@@ -552,8 +633,7 @@ public class GlobalTokenGrid : DynamicGrid<SecurityDescriptor>
 
         
         var descriptors = rows.Select(r =>r.Get<SecurityDescriptor, string>(c => c.Descriptor)).ToArray();
-        var resetchildren = Items.Where(x => descriptors.Contains(x.Descriptor) && x.Type.Equals(SecurityTokenType.User) && UserGroups[x.ID].Equals(groupid)).Any();
-        
+        var resetchildren = descriptors.Any(x => _userItems.TryGetValue(x, out var users) && users.Any(x => UserGroups[x.ID] == groupid));
         if (resetchildren)
         {
             var confirm = MessageBox.Show($"{_tokennames[action].Item1} User Tokens as well?", $"{_tokennames[action].Item1} All",
@@ -576,46 +656,51 @@ public class GlobalTokenGrid : DynamicGrid<SecurityDescriptor>
                 progress.Report($"{_tokennames[action].Item2} Tokens ({(double)(i) * 100.0D / (double)rows.Count:F2}% complete)");
                 i++;
                 
-                string descriptor = row.Get<SecurityDescriptor, String>(c => c.Descriptor);
-                bool globaldefault = row.Get<SecurityDescriptor, bool>(c => c.Default);
-                bool defaultvalue = GetGlobalOrDefault(descriptor, globaldefault);
-                var currentvalue = GetGroupOrDefault(descriptor, groupid, globaldefault);
+                var descriptor = row.Get<SecurityDescriptor, String>(c => c.Descriptor);
+                var globaldefault = row.Get<SecurityDescriptor, bool>(c => c.Default);
+                var defaultvalue = GetGlobalOrDefault(descriptor, globaldefault);
+                var currentvalue = GetGroup(descriptor, groupid);
                 
-                bool desiredvalue = action switch
+                var desiredvalue = action switch
                 {
                     TokenAction.Enable => true,
                     TokenAction.Disable => false,
-                    TokenAction.Reset => GetGlobalOrDefault(descriptor, defaultvalue),
-                    _ => !GetGroupOrDefault(descriptor, groupid, defaultvalue)
+                    TokenAction.Reset => defaultvalue,
+                    _ => !(currentvalue ?? defaultvalue)
                 };
-                
-                if (currentvalue != defaultvalue)
-                    resetgroups.Add(descriptor);
+
                 if (resetchildren)
                     resetusers.Add(descriptor);
                 
-                if (desiredvalue != defaultvalue)
+                if (desiredvalue != (currentvalue ?? defaultvalue))
                 { 
-                    var token = new SecurityToken
+                    if(currentvalue is not null)
                     {
-                        Descriptor = descriptor,
-                        Enabled = desiredvalue
-                    };
-                    token.Group.ID = groupid;
-                    updates.Add(token);
+                        resetgroups.Add(descriptor);
+                    }
+                    if(desiredvalue != defaultvalue)
+                    {
+                        var token = new SecurityToken
+                        {
+                            Descriptor = descriptor,
+                            Enabled = desiredvalue
+                        };
+                        token.Group.ID = groupid;
+                        updates.Add(token);
+                    }
                 }
             }
             
             progress.Report("Clearing Old Tokens...");
-            if (resetusers.Any())
-                ResetUsers(groupid,resetusers);
-            if (resetgroups.Any())
+            if (resetusers.Count != 0)
+                ResetUsers(groupid, resetusers);
+            if (resetgroups.Count != 0)
                 ResetGroups(groupid, resetgroups);
             progress.Report("Creating new Tokens...");
-            if (updates.Any())
+            if (updates.Count != 0)
             {
-                new Client<SecurityToken>().Save(updates, "");
-                Items.AddRange(updates.Select(x => new SecurityTokenItem()
+                Client.Save(updates, "");
+                AddItems(updates.Select(x => new SecurityTokenItem()
                 {
                     Type = SecurityTokenType.Group,
                     Descriptor = x.Descriptor,
@@ -648,42 +733,46 @@ public class GlobalTokenGrid : DynamicGrid<SecurityDescriptor>
                 i++;
                 
                 var descriptor = row.Get<SecurityDescriptor, string>(c => c.Descriptor);
-                bool globaldefault = row.Get<SecurityDescriptor, bool>(c => c.Default);
-                bool defaultvalue = GetGroupOrDefault(descriptor, groupid, globaldefault);
-                var currentvalue = GetUserOrDefault(descriptor, userid, groupid, defaultvalue);
+                var globaldefault = row.Get<SecurityDescriptor, bool>(c => c.Default);
+                var defaultvalue = GetGroupOrDefault(descriptor, groupid, globaldefault);
+                var currentvalue = GetUser(descriptor, userid); // null if no user token
 
-                bool desiredvalue = action switch
+                var desiredvalue = action switch
                 {
                     TokenAction.Enable => true,
                     TokenAction.Disable => false,
-                    TokenAction.Toggle => !currentvalue,
-                    _ => GetGroupOrDefault(descriptor, groupid, globaldefault)
+                    TokenAction.Toggle => !(currentvalue ?? defaultvalue),
+                    _ => defaultvalue
                 };
 
-                if (currentvalue != defaultvalue)
-                    resets.Add(descriptor);
-
-                if (desiredvalue != defaultvalue)
+                if (desiredvalue != (currentvalue ?? defaultvalue))
                 {
-                    var token = new UserSecurityToken
+                    if(currentvalue is not null)
+                    {
+                        resets.Add(descriptor);
+                    }
+                    if(desiredvalue != defaultvalue)
                     {
-                        Descriptor = descriptor,
-                        Enabled = desiredvalue
-                    };
-                    token.User.ID = userid;
-                    updates.Add(token);
+                        var token = new UserSecurityToken
+                        {
+                            Descriptor = descriptor,
+                            Enabled = desiredvalue
+                        };
+                        token.User.ID = userid;
+                        updates.Add(token);
+                    }
                 }
 
             }
 
             progress.Report("Clearing Old Tokens...");
-            if (resets.Any())
+            if (resets.Count != 0)
                 ResetUser(userid, resets);
             progress.Report("Creating new Tokens...");
-            if (updates.Any())
+            if (updates.Count != 0)
             {
-                new Client<UserSecurityToken>().Save(updates, "");
-                Items.AddRange(updates.Select(x => new SecurityTokenItem()
+                Client.Save(updates, "");
+                AddItems(updates.Select(x => new SecurityTokenItem()
                 {
                     Type = SecurityTokenType.User,
                     Descriptor = x.Descriptor,

+ 3 - 3
prs.desktop/Panels/Security/Global/GlobalTokenWindow.xaml.cs

@@ -108,7 +108,7 @@ namespace PRSDesktop
                 }
 
                 foreach (var row in query.Get<GlobalSecurityToken>().Rows)
-                    Tokens.Items.Add(new SecurityTokenItem
+                    Tokens.AddItem(new SecurityTokenItem
                     {
                         Descriptor = row.Get<GlobalSecurityToken, string>(c => c.Descriptor).Split('.').Last(),
                         Type = SecurityTokenType.Global,
@@ -118,7 +118,7 @@ namespace PRSDesktop
                     });
 
                 foreach (var row in query.Get<SecurityToken>().Rows)
-                    Tokens.Items.Add(new SecurityTokenItem
+                    Tokens.AddItem(new SecurityTokenItem
                     {
                         Descriptor = row.Get<SecurityToken, string>(c => c.Descriptor).Split('.').Last(),
                         Type = SecurityTokenType.Group,
@@ -132,7 +132,7 @@ namespace PRSDesktop
                     var userid = row.Get<UserSecurityToken, Guid>(c => c.User.ID);
                     if (Tokens.UserGroups.ContainsKey(userid))
                     {
-                        Tokens.Items.Add(new SecurityTokenItem
+                        Tokens.AddItem(new SecurityTokenItem
                         {
                             Descriptor = row.Get<UserSecurityToken, string>(c => c.Descriptor).Split('.').Last(),
                             Type = SecurityTokenType.User,