using System; using System.Collections.Generic; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media.Imaging; using InABox.Clients; using InABox.Core; using InABox.DynamicGrid; using InABox.WPF; using javax.sql.rowset; namespace PRSDesktop { public class GlobalTokenGrid : DynamicGrid { private List? _descriptors; private static readonly BitmapImage defaultdisabled = PRSDesktop.Resources.disabled.Fade(0.25F).AsBitmapImage(); private static readonly BitmapImage defaulttick = PRSDesktop.Resources.tick.Fade(0.25F).AsBitmapImage(); private static readonly BitmapImage disabled = PRSDesktop.Resources.disabled.AsBitmapImage(); private static readonly BitmapImage tick = PRSDesktop.Resources.tick.AsBitmapImage(); public GlobalTokenGrid() { GroupNames = new Dictionary(); UserNames = new Dictionary(); UserGroups = new Dictionary(); GroupID = Guid.Empty; Items = new List(); HiddenColumns.Add(x => x.Descriptor); HiddenColumns.Add(x => x.Default); HeaderHeight = 125; } protected override void Init() { } protected override void DoReconfigure(FluentList options) { options.AddRange(DynamicGridOption.FilterRows); } public Dictionary GroupNames { get; } public Dictionary UserNames { get; } public Dictionary UserGroups { get; } public List Items { get; } public Guid GroupID { get; set; } private const string ENABLED_TOKENS = "Enabled"; private const string DISABLED_TOKENS = "Disabled"; private const string OVERRIDDEN_TOKENS = "Overridden"; private const string DEFAULT_TOKENS = "Default"; private static readonly string[] ENABLED_FILTERS = new[] { ENABLED_TOKENS, DISABLED_TOKENS }; private static readonly string[] OVERRIDDEN_FILTERS = new[] { OVERRIDDEN_TOKENS, DEFAULT_TOKENS }; private static readonly string[] ALL_FILTERS = new[] { ENABLED_TOKENS, DISABLED_TOKENS, OVERRIDDEN_TOKENS, DEFAULT_TOKENS }; protected override DynamicGridColumns LoadColumns() { var columns = new DynamicGridColumns { new DynamicGridColumn { ColumnName = "Group", Caption = "Group", Width = 150, Alignment = Alignment.MiddleCenter }, new DynamicGridColumn { ColumnName = "Description", Caption = "Description", Width = 0, Alignment = Alignment.MiddleLeft }, new DynamicGridColumn { ColumnName = "Category", Caption = "Applies To", Width = 200, Alignment = Alignment.MiddleCenter } }; ActionColumns.Clear(); if (GroupID == Guid.Empty) { ActionColumns.Add( new DynamicImageColumn( GlobalImage, r => r != null ? GlobalAction(new CoreRow[] { r }, TokenAction.Toggle) : CreateGlobalMenu() ) { HeaderText = "Default", Filters = ALL_FILTERS, FilterRecord = (r,f) => GlobalFilter(r,f) }); foreach (var groupid in GroupNames.Keys) ActionColumns.Add( new DynamicImageColumn( r => GroupImage(r, groupid), r => r != null ? GroupAction(new CoreRow[] { r }, groupid, TokenAction.Toggle) : CreateGroupMenu(groupid) ) { HeaderText = GroupNames[groupid], Filters = ALL_FILTERS, FilterRecord = (r,f) => GroupFilter(r,f,groupid) } ); } else { ActionColumns.Add( new DynamicImageColumn( r => GroupImage(r, GroupID), r => r != null ? GroupAction(new CoreRow[] { r }, GroupID, TokenAction.Toggle) : CreateGroupMenu(GroupID) ) { HeaderText = GroupNames[GroupID], Filters = ALL_FILTERS, FilterRecord = (r,f) => GroupFilter(r,f, GroupID) } ); foreach (var userid in UserNames.Keys) if (UserGroups[userid] == GroupID) ActionColumns.Add( new DynamicImageColumn( r => UserImage(r, GroupID, userid), r => r != null ? UserAction(new CoreRow[] { r }, GroupID, userid, TokenAction.Toggle) : CreateUserMenu(GroupID, userid) ) { HeaderText = UserNames[userid], Filters = ALL_FILTERS, FilterRecord = (r,f) => UserFilter(r,f, GroupID, userid) } ); } return columns; } private static bool MatchFilter(string[] filter, string[] test) { if ((filter == null) && (test == null)) return true; if ((filter == null) || (test == null)) return false; if (filter.Length != test.Length) return false; if (filter.Except(test).Any()) return false; if (test.Except(filter).Any()) return false; return true; } private bool GlobalFilter(CoreRow row, string[] filter) { if (MatchFilter(filter, ALL_FILTERS)) return true; var descriptor = row.Get(c => c.Descriptor); var globaldefault = row.Get(c => c.Default); if (!MatchFilter(filter, ENABLED_FILTERS)) { bool isenabled = GetGlobalOrDefault(descriptor, globaldefault); var check = (filter.Contains(ENABLED_TOKENS) && isenabled) || (filter.Contains(DISABLED_TOKENS) && !isenabled); if (!check) return false; } if (!MatchFilter(filter, OVERRIDDEN_FILTERS)) { bool isoverridden = Items.Any(x => String.Equals(x.Descriptor, descriptor) && (x.Type == SecurityTokenType.Global)); var check = (filter.Contains(OVERRIDDEN_TOKENS) && isoverridden) || (filter.Contains(DEFAULT_TOKENS) && !isoverridden); if (!check) return false; } return true; } private bool GroupFilter(CoreRow row, string[] filter, Guid groupid) { if (MatchFilter(filter, ALL_FILTERS)) return true; string descriptor = row.Get(c => c.Descriptor); bool globaldefault = row.Get(c => c.Default); if (!MatchFilter(filter, ENABLED_FILTERS)) { bool isenabled = GetGroupOrDefault(descriptor, groupid, globaldefault); var check = (filter.Contains(ENABLED_TOKENS) && isenabled) || (filter.Contains(DISABLED_TOKENS) && !isenabled); if (!check) return false; } if (!MatchFilter(filter, OVERRIDDEN_FILTERS)) { bool isoverridden = Items.Any(x => String.Equals(x.Descriptor, descriptor) && (x.Type == SecurityTokenType.Group)); var check = (filter.Contains(OVERRIDDEN_TOKENS) && isoverridden) || (filter.Contains(DEFAULT_TOKENS) && !isoverridden); if (!check) return false; } return true; } private bool UserFilter(CoreRow row, string[] filter, Guid groupid, Guid userid) { if (MatchFilter(filter, ALL_FILTERS)) return true; var descriptor = row.Get(c => c.Descriptor); var globaldefault = row.Get(c => c.Default); if (!MatchFilter(filter, ENABLED_FILTERS)) { bool isenabled = GetUserOrDefault(descriptor, userid, groupid, globaldefault); var check = (filter.Contains(ENABLED_TOKENS) && isenabled) || (filter.Contains(DISABLED_TOKENS) && !isenabled); if (!check) return false; } if (!MatchFilter(filter, OVERRIDDEN_FILTERS)) { bool isoverridden = Items.Any(x => string.Equals(x.Descriptor, descriptor) && (x.Type == SecurityTokenType.User)); var check = (filter.Contains(OVERRIDDEN_TOKENS) && isoverridden) || (filter.Contains(DEFAULT_TOKENS) && !isoverridden); if (!check) return false; } return true; } public override SecurityDescriptor LoadItem(CoreRow row) { if(_descriptors is null) { throw new Exception("LoadItem() called before Reload()"); } return _descriptors[row.Index]; } private CoreTable? _table = null; protected override void Reload(Filters criteria, Columns columns, ref SortOrder? sort, Action action) { if (_table == null) { Progress.ShowModal("Scanning Tokens..", (progress) => { _table = new CoreTable(); foreach (var column in columns.Items) _table.Columns.Add(new CoreColumn { ColumnName = column.ToString() }); if (_descriptors == null) { _descriptors = new List(); var list = Security.Descriptors.Where(x => x.Visible).ToArray(); foreach (var descriptor in list) { progress.Report($"Loading Tokens ({(double)(_descriptors.Count+1) * 100.0D / (double)list.Length:F2}% complete)"); var _descriptor = new SecurityDescriptor { Category = descriptor.Category, Group = descriptor.Type, Descriptor = descriptor.Code, Description = descriptor.Description, Default = descriptor.Value, IsGlobal = descriptor.HasScope(SecurityDescriptorScope.Global), IsGroup = descriptor.HasScope(SecurityDescriptorScope.Group), IsUser = descriptor.HasScope(SecurityDescriptorScope.User) }; _descriptors.Add(_descriptor); var row = _table.NewRow(); _table.LoadRow(row, _descriptor); _table.Rows.Add(row); } } }); } action.Invoke(_table, null); } 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; } 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); } 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); } private BitmapImage? GlobalImage(CoreRow? row) { if (row == null) return null; var code = row.Get(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; return row.Get(c => c.Default) ? defaulttick : defaultdisabled; } private BitmapImage? GroupImage(CoreRow? row, Guid groupid) { if (row == null) return null; var code = row.Get(c => c.Descriptor); 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 ? tick : disabled; return GetGlobalOrDefault(code, row.Get(c => c.Default)) ? defaulttick : defaultdisabled; } private BitmapImage? UserImage(CoreRow? row, Guid groupid, Guid userid) { if (row == null) return null; var code = row.Get(c => c.Descriptor); 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 ? tick : disabled; return GetGroupOrDefault(code, groupid, row.Get(c => c.Default)) ? defaulttick : defaultdisabled; } private void ResetGlobals(IEnumerable codes) { var globals = Items.Where(x => codes.Contains(x.Descriptor) && x.Type.Equals(SecurityTokenType.Global)); if (!globals.Any()) return; var globalupdates = globals.Select(x => new GlobalSecurityToken { ID = x.RecordID }).ToArray(); new Client().Delete(globalupdates, "", (o, e) => { }); Items.RemoveAll(x => globals.Contains(x)); } private void ResetGroups(Guid groupid, IEnumerable 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 groupupdates = groups.Select(x => new SecurityToken { ID = x.RecordID }).ToArray(); new Client().Delete(groupupdates, "", (o, e) => { }); Items.RemoveAll(x => groups.Contains(x)); } private void ResetUsers(Guid groupid, IEnumerable 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().Delete(userupdates, "", (o, e) => { }); Items.RemoveAll(x => users.Contains(x)); } private void ResetUser(Guid userid, IEnumerable 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().Delete(userupdates, "", (o, e) => { }); Items.RemoveAll(x => users.Contains(x)); } private enum TokenAction { Enable, Disable, Toggle, Reset } private readonly Dictionary> _tokennames = new() { { TokenAction.Enable, new("Enable", "Enabling") }, { TokenAction.Disable, new("Disable", "Enabling") }, { TokenAction.Toggle, new("Toggle", "Enabling") }, { TokenAction.Reset, new("Reset", "Enabling") }, }; private bool CreateGlobalMenu() { var menu = new ContextMenu(); menu.Items.Add(new MenuItem() { Header = "Enable All Tokens", Command = new Command((o) => GlobalAction(GetVisibleRows(), TokenAction.Enable)) }); menu.Items.Add(new MenuItem() { Header = "Disable All Tokens", Command = new Command((o) => GlobalAction(GetVisibleRows(), TokenAction.Disable)) }); menu.Items.Add(new Separator()); menu.Items.Add(new MenuItem() { Header = "Reset All Tokens", Command = new Command((o) => GlobalAction(GetVisibleRows(), TokenAction.Reset)) }); menu.IsOpen = true; return false; } private bool CreateGroupMenu(Guid groupid) { var menu = new ContextMenu(); menu.Items.Add(new MenuItem() { Header = "Enable All Tokens", Command = new Command((o) => GroupAction(GetVisibleRows(), groupid, TokenAction.Enable)) }); menu.Items.Add(new MenuItem() { Header = "Disable All Tokens", Command = new Command((o) => GroupAction(GetVisibleRows(), groupid, TokenAction.Disable)) }); menu.Items.Add(new Separator()); menu.Items.Add(new MenuItem() { Header = "Reset All Tokens", Command = new Command((o) => GroupAction(GetVisibleRows(), groupid, TokenAction.Reset)) }); menu.IsOpen = true; return false; } private bool CreateUserMenu(Guid groupid, Guid userid) { var menu = new ContextMenu(); menu.Items.Add(new MenuItem() { Header = "Enable All Tokens", Command = new Command((o) => UserAction(GetVisibleRows(), groupid, userid, TokenAction.Enable)) }); menu.Items.Add(new MenuItem() { Header = "Disable All Tokens", Command = new Command((o) => UserAction(GetVisibleRows(), groupid, userid, TokenAction.Disable)) }); menu.Items.Add(new Separator()); menu.Items.Add(new MenuItem() { Header = "Reset All Tokens", Command = new Command((o) => UserAction(GetVisibleRows(), groupid, userid, TokenAction.Reset)) }); menu.IsOpen = true; return false; } private bool GlobalAction(IList rows, TokenAction action) { if (!rows.Any()) return false; var descriptors = rows.Select(r =>r.Get(c => c.Descriptor)).ToArray(); var resetchildren = Items.Where(x => descriptors.Contains(x.Descriptor) && !x.Type.Equals(SecurityTokenType.Global)).Any(); if (resetchildren) { var confirm = MessageBox.Show($"{_tokennames[action].Item1} Group and User Tokens as well?", $"{_tokennames[action].Item1} All", MessageBoxButton.YesNoCancel); if (confirm == MessageBoxResult.Cancel) return false; resetchildren = confirm == MessageBoxResult.Yes; } var resetusers = new List(); var resetgroups = new List(); var resetglobals = new List(); var updates = new List(); Progress.ShowModal($"{_tokennames[action].Item2} Tokens", (progress) => { int i = 1; foreach (var row in rows) { progress.Report($"{_tokennames[action].Item2} Tokens ({(double)(i) * 100.0D / (double)rows.Count:F2}% complete)"); i++; string descriptor = row.Get(c => c.Descriptor); bool defaultvalue = row.Get(c => c.Default); bool desiredvalue = action switch { TokenAction.Enable => true, TokenAction.Disable => false, TokenAction.Reset => defaultvalue, _ => !GetGlobalOrDefault(descriptor, defaultvalue) }; var currentvalue = GetGlobalOrDefault(descriptor, row.Get(c => c.Default)); if (currentvalue != defaultvalue) resetglobals.Add(descriptor); if (resetchildren) { resetgroups.Add(descriptor); resetusers.Add(descriptor); } if (desiredvalue != defaultvalue) { // ResetGlobals(new[] { descriptor }); // // if (resetchildren) // { // ResetGroups(Guid.Empty, new[] { descriptor }); // ResetUsers(Guid.Empty, new[] { descriptor }); // } if (currentvalue == defaultvalue) { var token = new GlobalSecurityToken { Descriptor = descriptor, Enabled = !currentvalue }; updates.Add(token); // new Client().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()) ResetUsers(Guid.Empty,resetusers); if (resetgroups.Any()) ResetGroups(Guid.Empty, resetgroups); if (resetglobals.Any()) ResetGlobals(resetglobals); progress.Report("Creating new Tokens..."); if (updates.Any()) { new Client().Save(updates, ""); Items.AddRange(updates.Select(x => new SecurityTokenItem() { Type = SecurityTokenType.Global, Descriptor = x.Descriptor, ID = Guid.Empty, RecordID = x.ID, Enabled = x.Enabled })); } }); Refresh(false, true); return false; } private bool GroupAction(IList rows, Guid groupid, TokenAction action) { if (!rows.Any()) return false; var descriptors = rows.Select(r =>r.Get(c => c.Descriptor)).ToArray(); var resetchildren = Items.Where(x => descriptors.Contains(x.Descriptor) && x.Type.Equals(SecurityTokenType.User) && UserGroups[x.ID].Equals(groupid)).Any(); if (resetchildren) { var confirm = MessageBox.Show($"{_tokennames[action].Item1} User Tokens as well?", $"{_tokennames[action].Item1} All", MessageBoxButton.YesNoCancel); if (confirm == MessageBoxResult.Cancel) return false; resetchildren = confirm == MessageBoxResult.Yes; } var resetusers = new List(); var resetgroups = new List(); var updates = new List(); Progress.ShowModal($"{_tokennames[action].Item2} Tokens", (progress) => { int i = 1; foreach (var row in rows) { progress.Report($"{_tokennames[action].Item2} Tokens ({(double)(i) * 100.0D / (double)rows.Count:F2}% complete)"); i++; string descriptor = row.Get(c => c.Descriptor); bool globaldefault = row.Get(c => c.Default); bool defaultvalue = GetGlobalOrDefault(descriptor, globaldefault); var currentvalue = GetGroupOrDefault(descriptor, groupid, globaldefault); bool desiredvalue = action switch { TokenAction.Enable => true, TokenAction.Disable => false, TokenAction.Reset => GetGlobalOrDefault(descriptor, defaultvalue), _ => !GetGroupOrDefault(descriptor, groupid, defaultvalue) }; if (currentvalue != defaultvalue) resetgroups.Add(descriptor); if (resetchildren) resetusers.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()) ResetGroups(groupid, resetgroups); progress.Report("Creating new Tokens..."); if (updates.Any()) { new Client().Save(updates, ""); Items.AddRange(updates.Select(x => new SecurityTokenItem() { Type = SecurityTokenType.Group, Descriptor = x.Descriptor, ID = groupid, RecordID = x.ID, Enabled = x.Enabled })); } }); Refresh(false, true); return false; } private bool UserAction(IList rows, Guid groupid, Guid userid, TokenAction action) { if (!rows.Any()) return false; Progress.ShowModal($"{_tokennames[action].Item2} Tokens", (progress) => { var resets = new List(); var updates = new List(); int i = 1; foreach (var row in rows) { progress.Report($"{_tokennames[action].Item2} Tokens ({(double)(i) * 100.0D / (double)rows.Count:F2}% complete)"); i++; var descriptor = row.Get(c => c.Descriptor); bool globaldefault = row.Get(c => c.Default); bool defaultvalue = GetGroupOrDefault(descriptor, groupid, globaldefault); var currentvalue = GetUserOrDefault(descriptor, userid, groupid, defaultvalue); bool desiredvalue = action switch { TokenAction.Enable => true, TokenAction.Disable => false, TokenAction.Toggle => !currentvalue, _ => GetGroupOrDefault(descriptor, groupid, globaldefault) }; if (currentvalue != defaultvalue) resets.Add(descriptor); if (desiredvalue != defaultvalue) { var token = new UserSecurityToken { Descriptor = descriptor, Enabled = desiredvalue }; token.User.ID = userid; updates.Add(token); } } progress.Report("Clearing Old Tokens..."); if (resets.Any()) ResetUser(userid, resets); progress.Report("Creating new Tokens..."); if (updates.Any()) { new Client().Save(updates, ""); Items.AddRange(updates.Select(x => new SecurityTokenItem() { Type = SecurityTokenType.User, Descriptor = x.Descriptor, ID = userid, RecordID = x.ID, Enabled = x.Enabled })); } }); Refresh(false, true); return false; } public override void DeleteItems(params CoreRow[] row) { // Not required or implemented } public override void SaveItem(SecurityDescriptor item) { // Not required or implemented } } }