using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Reflection; using System.Threading.Tasks; using InABox.Clients; namespace InABox.Core { public static class Security { private static ConcurrentBag? _descriptors; private static GlobalSecurityToken[]? _globaltokens; private static SecurityToken[]? _grouptokens; private static UserSecurityToken[]? _usertokens; public static IEnumerable Descriptors { get { if (_descriptors == null) { _descriptors = new ConcurrentBag(); var custom = Task.Run(() => { var tokens = CoreUtils.Entities.Where( x => !x.IsGenericType && x.HasInterface()); foreach (var _class in tokens) { var token = (Activator.CreateInstance(_class) as ISecurityDescriptor)!; _descriptors.Add(token); } }); var auto = Task.Run(() => { var tokens = CoreUtils.Entities.Where( x => !x.IsGenericType && x.IsSubclassOf(typeof(Entity))).ToArray(); var view = Task.Run(() => { foreach (var _class in tokens) CheckAutoToken(_class, typeof(CanView<>)); }); var edit = Task.Run(() => { foreach (var _class in tokens.Where(x => x.GetCustomAttribute() == null)) CheckAutoToken(_class, typeof(CanEdit<>)); }); var delete = Task.Run(() => { foreach (var _class in tokens.Where(x => x.GetCustomAttribute() == null)) CheckAutoToken(_class, typeof(CanDelete<>)); }); var issues = Task.Run(() => { foreach (var _class in tokens.Where(x => x.GetInterfaces().Contains(typeof(IIssues)))) CheckAutoToken(_class, typeof(CanManageIssues<>)); }); var exports = Task.Run(() => { foreach (var _class in tokens.Where(x => x.GetInterfaces().Contains(typeof(IExportable)))) CheckAutoToken(_class, typeof(CanExport<>)); }); var imports = Task.Run(() => { foreach (var _class in tokens.Where(x => x.GetInterfaces().Contains(typeof(IImportable)))) CheckAutoToken(_class, typeof(CanImport<>)); }); var merges = Task.Run(() => { foreach (var _class in tokens.Where(x => x.GetInterfaces().Contains(typeof(IMergeable)))) CheckAutoToken(_class, typeof(CanMerge<>)); }); var posts = Task.Run(() => { foreach (var _class in tokens.Where(x => x.GetInterfaces().Contains(typeof(IPostable)))) CheckAutoToken(_class, typeof(CanPost<>)); }); var configPosts = Task.Run(() => { foreach (var _class in tokens.Where(x => x.GetInterfaces().Contains(typeof(IPostable)))) CheckAutoToken(_class, typeof(CanConfigurePost<>)); }); Task.WaitAll(view, edit, delete, issues, exports, merges, posts, configPosts); }); Task.WaitAll(custom, auto); } return _descriptors.OrderBy(x => x.Type).ThenBy(x => x.Code); } } public static void Reset() { _globaltokens = null; _grouptokens = null; _usertokens = null; _descriptors = null; } public static void CheckTokens() { _usertokens ??= Client.Query( new Filter(x => x.User.ID).IsEqualTo(ClientFactory.UserGuid), Columns.None().Add(x => x.Descriptor).Add(x => x.Enabled) ).ToArray(); _grouptokens ??= Client.Query( new Filter(x => x.Group.ID).IsEqualTo(ClientFactory.UserSecurityID), Columns.None().Add(x => x.Descriptor).Add(x => x.Enabled) ).ToArray(); _globaltokens ??= Client.Query( null, Columns.None().Add(x => x.Descriptor).Add(x => x.Enabled)) .ToArray(); } private static void CheckAutoToken(Type _class, Type type) { var basetype = typeof(AutoSecurityDescriptor<,>); var actiontype = type.MakeGenericType(_class); var descriptortype = basetype.MakeGenericType(_class, actiontype); var descriptor = (Activator.CreateInstance(descriptortype) as ISecurityDescriptor)!; if (!_descriptors.Any(x => string.Equals(x.Code, descriptor.Code))) _descriptors.Add(descriptor); } public static bool IsAllowed(Type T, Guid userGuid, Guid securityId) { var descriptor = (Activator.CreateInstance(T) as ISecurityDescriptor)!; try { // If you're not logged in, you can't do jack! if (userGuid == Guid.Empty) return false; CheckTokens(); // First Check for a matching User Token (override) var usertoken = _usertokens.FirstOrDefault(x => x.Descriptor.Equals(descriptor.Code)); if (usertoken != null) return usertoken.Enabled; // If not found, fall back to the Group Token var grouptoken = _grouptokens.FirstOrDefault(x => x.Descriptor.Equals(descriptor.Code)); if (grouptoken != null) return grouptoken.Enabled; // Still not found? fall back to the Global Token var globaltoken = _globaltokens.FirstOrDefault(x => x.Descriptor.Equals(descriptor.Code)); if (globaltoken != null) return globaltoken.Enabled; } catch (Exception e) { Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace)); } // Aaand finally, just return the default for the descriptor return descriptor.Value; } public static bool IsAllowed(Guid userGuid, Guid securityId) where T : ISecurityDescriptor, new() => IsAllowed(typeof(T), userGuid, securityId); public static bool IsAllowed() where T : ISecurityDescriptor, new() => IsAllowed(ClientFactory.UserGuid, ClientFactory.UserSecurityID); public static bool IsAllowed(Type T) => IsAllowed(T, ClientFactory.UserGuid, ClientFactory.UserSecurityID); public static bool CanView(Guid userGuid, Guid securityId) where TEntity : Entity, new() { return ClientFactory.IsSupported() && IsAllowed>>(userGuid, securityId); } public static bool CanView(Type TEntity) { return ClientFactory.IsSupported(TEntity) && IsAllowed(typeof(AutoSecurityDescriptor<,>).MakeGenericType(TEntity, typeof(CanView<>).MakeGenericType(TEntity))); } public static bool CanView() where TEntity : Entity, new() { return ClientFactory.IsSupported() && IsAllowed>>(); } public static bool CanEdit(Type TEntity, Guid userGuid, Guid securityId) { return ClientFactory.IsSupported(TEntity) && IsAllowed(typeof(AutoSecurityDescriptor<,>).MakeGenericType(TEntity, typeof(CanEdit<>).MakeGenericType(TEntity)), userGuid, securityId); } public static bool CanEdit(Guid userGuid, Guid securityId) where TEntity : Entity, new() { return ClientFactory.IsSupported() && IsAllowed>>(userGuid, securityId); } public static bool CanEdit(Type TEntity) { return ClientFactory.IsSupported(TEntity) && IsAllowed(typeof(AutoSecurityDescriptor<,>).MakeGenericType(TEntity, typeof(CanEdit<>).MakeGenericType(TEntity))); } public static bool CanEdit() where TEntity : Entity, new() { return ClientFactory.IsSupported() && IsAllowed>>(); } public static bool CanImport() where TEntity : Entity, new() { return ClientFactory.IsSupported() && IsAllowed>>(); } public static bool CanExport() where TEntity : Entity, new() { return ClientFactory.IsSupported() && IsAllowed>>(); } public static bool CanMerge() where TEntity : Entity, new() { return ClientFactory.IsSupported() && IsAllowed>>(); } public static bool CanPost() where TEntity : Entity, new() { return ClientFactory.IsSupported() && IsAllowed>>(); } public static bool CanConfigurePost() where TEntity : Entity, new() { return ClientFactory.IsSupported() && IsAllowed>>(); } public static bool CanDelete() where TEntity : Entity, new() { return ClientFactory.IsSupported() && IsAllowed>>(); } public static bool CanDelete(Type TEntity) { return ClientFactory.IsSupported(TEntity) && IsAllowed(typeof(AutoSecurityDescriptor<,>).MakeGenericType(TEntity, typeof(CanDelete<>).MakeGenericType(TEntity))); } public static bool CanManageIssues(Type TEntity) { return ClientFactory.IsSupported(TEntity) && IsAllowed(typeof(AutoSecurityDescriptor<,>).MakeGenericType(TEntity, typeof(CanManageIssues<>).MakeGenericType(TEntity))); } public static bool CanManageIssues() where TEntity : Entity, IIssues, new() { return ClientFactory.IsSupported() && IsAllowed>>(); } public static bool CanManageProblems(Type TEntity) { return ClientFactory.IsSupported(TEntity) && IsAllowed(typeof(AutoSecurityDescriptor<,>).MakeGenericType(TEntity, typeof(CanManageProblems<>).MakeGenericType(TEntity))); } public static bool CanManageProblems() where TEntity : Entity, IProblems, new() { return ClientFactory.IsSupported() && IsAllowed>>(); } } }