Security.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Collections.Generic;
  4. using System.ComponentModel;
  5. using System.Linq;
  6. using System.Reflection;
  7. using System.Security;
  8. using System.Threading.Tasks;
  9. using InABox.Clients;
  10. namespace InABox.Core
  11. {
  12. public static class Security
  13. {
  14. private static ISecurityDescriptor[]? _descriptors;
  15. private static GlobalSecurityToken[]? _globaltokens;
  16. private static Dictionary<Guid, SecurityToken[]> _grouptokens = new Dictionary<Guid, SecurityToken[]>();
  17. private static Dictionary<Guid, UserSecurityToken[]> _usertokens = new Dictionary<Guid, UserSecurityToken[]>();
  18. public static IEnumerable<ISecurityDescriptor> Descriptors
  19. {
  20. get
  21. {
  22. if (_descriptors == null)
  23. {
  24. ISecurityDescriptor[] GetTokens(params Task<ISecurityDescriptor[]>[] tasks)
  25. {
  26. Task.WaitAll(tasks);
  27. return CoreUtils.Concatenate(tasks.ToArray(x => x.Result));
  28. }
  29. var custom = Task.Run(() =>
  30. {
  31. return CoreUtils.Entities.Where(x => !x.IsGenericType && x.HasInterface<ISecurityDescriptor>())
  32. .Select(x => Activator.CreateInstance(x) as ISecurityDescriptor)
  33. .NotNull()
  34. .ToArray();
  35. });
  36. bool Overridden(Type @class, Func<EntitySecurityAttribute, Type?> getToken)
  37. {
  38. return @class.GetCustomAttribute<EntitySecurityAttribute>() is EntitySecurityAttribute attr && getToken(attr) != null;
  39. }
  40. var auto = Task.Run(() =>
  41. {
  42. var entities = CoreUtils.Entities.Where( x => !x.IsGenericType && x.IsSubclassOf(typeof(Entity))).ToArray();
  43. var view = Task.Run(() =>
  44. {
  45. return entities
  46. .Where(x => !Overridden(x, x => x.CanView))
  47. .Select(x => GetAutoToken(x, typeof(CanView<>)))
  48. .NotNull()
  49. .ToArray();
  50. });
  51. var edit = Task.Run(() =>
  52. {
  53. return entities
  54. .Where(x => !Overridden(x, x => x.CanEdit))
  55. .Select(x => GetAutoToken(x, typeof(CanEdit<>)))
  56. .NotNull()
  57. .ToArray();
  58. });
  59. var delete = Task.Run(() =>
  60. {
  61. return entities
  62. .Where(x => !Overridden(x, x => x.CanDelete))
  63. .Select(x => GetAutoToken(x, typeof(CanDelete<>)))
  64. .NotNull()
  65. .ToArray();
  66. });
  67. var issues = Task.Run(() =>
  68. {
  69. return entities.Where(x => x.GetInterfaces().Contains(typeof(IIssues)))
  70. .Select(x => GetAutoToken(x, typeof(CanManageIssues<>)))
  71. .NotNull()
  72. .ToArray();
  73. });
  74. var exports = Task.Run(() =>
  75. {
  76. return entities.Where(x => x.GetInterfaces().Contains(typeof(IExportable)))
  77. .Select(x => GetAutoToken(x, typeof(CanExport<>)))
  78. .NotNull()
  79. .ToArray();
  80. });
  81. var imports = Task.Run(() =>
  82. {
  83. return entities.Where(x => x.GetInterfaces().Contains(typeof(IImportable)))
  84. .Select(x => GetAutoToken(x, typeof(CanImport<>)))
  85. .NotNull()
  86. .ToArray();
  87. });
  88. var merges = Task.Run(() =>
  89. {
  90. return entities.Where(x => x.GetInterfaces().Contains(typeof(IMergeable)))
  91. .Select(x => GetAutoToken(x, typeof(CanMerge<>)))
  92. .NotNull()
  93. .ToArray();
  94. });
  95. var posts = Task.Run(() =>
  96. {
  97. return entities.Where(x => x.GetInterfaces().Contains(typeof(IPostable)))
  98. .Select(x => GetAutoToken(x, typeof(CanPost<>)))
  99. .NotNull()
  100. .ToArray();
  101. });
  102. var configPosts = Task.Run(() =>
  103. {
  104. return entities.Where(x => x.GetInterfaces().Contains(typeof(IPostable)))
  105. .Select(x => GetAutoToken(x, typeof(CanConfigurePost<>)))
  106. .NotNull()
  107. .ToArray();
  108. });
  109. return GetTokens(view, edit, delete, issues, exports, merges, posts, configPosts);
  110. });
  111. _descriptors = GetTokens(custom, auto);
  112. Array.Sort(_descriptors, CoreUtils.OrderBy((ISecurityDescriptor x) => x.Type).ThenBy(x => x.Code));
  113. }
  114. return _descriptors;
  115. }
  116. }
  117. public static void Reset()
  118. {
  119. _globaltokens = null;
  120. _grouptokens.Clear();
  121. _usertokens.Clear();
  122. _descriptors = null;
  123. }
  124. public static void CheckTokens(Guid userId, Guid securityID)
  125. {
  126. var userTask = !_usertokens.ContainsKey(userId)
  127. ? Client.QueryAsync(
  128. Filter<UserSecurityToken>.Where(x => x.User.ID).IsEqualTo(ClientFactory.UserGuid),
  129. Columns.None<UserSecurityToken>().Add(x => x.Descriptor).Add(x => x.Enabled))
  130. : null;
  131. var groupTask = !_grouptokens.ContainsKey(securityID)
  132. ? Client.QueryAsync(
  133. Filter<SecurityToken>.Where(x => x.Group.ID).IsEqualTo(ClientFactory.UserSecurityID),
  134. Columns.None<SecurityToken>().Add(x => x.Descriptor).Add(x => x.Enabled))
  135. : null;
  136. var globalTask = _globaltokens is null
  137. ? Client.QueryAsync(
  138. null,
  139. Columns.None<GlobalSecurityToken>().Add(x => x.Descriptor).Add(x => x.Enabled))
  140. : null;
  141. if (userTask is null && groupTask is null && globalTask is null) return;
  142. CoreUtils.WaitAllNotNull(userTask, groupTask, globalTask);
  143. if(userTask != null)
  144. {
  145. _usertokens.Add(userId, userTask.Result.ToArray<UserSecurityToken>());
  146. }
  147. if(groupTask != null)
  148. {
  149. _grouptokens.Add(securityID, groupTask.Result.ToArray<SecurityToken>());
  150. }
  151. if(globalTask != null)
  152. {
  153. _globaltokens = globalTask.Result.ToArray<GlobalSecurityToken>();
  154. }
  155. }
  156. private static ISecurityDescriptor? GetAutoToken(Type _class, Type type)
  157. {
  158. var basetype = typeof(AutoSecurityDescriptor<,>);
  159. var actiontype = type.MakeGenericType(_class);
  160. var descriptortype = basetype.MakeGenericType(_class, actiontype);
  161. var descriptor = (Activator.CreateInstance(descriptortype) as ISecurityDescriptor)!;
  162. return descriptor;
  163. // if (!_descriptors.Any(x => string.Equals(x.Code, descriptor.Code)))
  164. // _descriptors.Add(descriptor);
  165. }
  166. private static bool IsAllowedInternal(ISecurityDescriptor descriptor, Guid userGuid, Guid securityId)
  167. {
  168. // If you're not logged in, you can't do jack!
  169. if (userGuid == Guid.Empty)
  170. return false;
  171. CheckTokens(userGuid, securityId);
  172. // First Check for a matching User Token (override)
  173. var usertoken = _usertokens[userGuid].FirstOrDefault(x => x.Descriptor.Equals(descriptor.Code));
  174. if (usertoken != null)
  175. return usertoken.Enabled;
  176. // If not found, fall back to the Group Token
  177. var grouptoken = _grouptokens[securityId].FirstOrDefault(x => x.Descriptor.Equals(descriptor.Code));
  178. if (grouptoken != null)
  179. return grouptoken.Enabled;
  180. // Still not found? fall back to the Global Token
  181. var globaltoken = _globaltokens.FirstOrDefault(x => x.Descriptor.Equals(descriptor.Code));
  182. if (globaltoken != null)
  183. return globaltoken.Enabled;
  184. // Aaand finally, just return the default for the descriptor
  185. return descriptor.Value;
  186. }
  187. public static bool IsAllowed(Type T, Guid userGuid, Guid securityId)
  188. {
  189. var descriptor = (Activator.CreateInstance(T) as ISecurityDescriptor)!;
  190. try
  191. {
  192. if(IsAllowedInternal(descriptor, userGuid, securityId))
  193. {
  194. if(descriptor is IDependentSecurityDescriptor dependent)
  195. {
  196. return dependent.DependsOn.All(x => IsAllowed(x, userGuid, securityId));
  197. }
  198. else
  199. {
  200. return true;
  201. }
  202. }
  203. else
  204. {
  205. return false;
  206. }
  207. }
  208. catch (Exception e)
  209. {
  210. Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
  211. return false;
  212. }
  213. }
  214. public static bool IsAllowed<T>(Guid userGuid, Guid securityId) where T : ISecurityDescriptor, new()
  215. => IsAllowed(typeof(T), userGuid, securityId);
  216. public static bool IsAllowed<T>() where T : ISecurityDescriptor, new()
  217. => IsAllowed<T>(ClientFactory.UserGuid, ClientFactory.UserSecurityID);
  218. public static bool IsAllowed(Type T)
  219. => IsAllowed(T, ClientFactory.UserGuid, ClientFactory.UserSecurityID);
  220. private static Type CreateAutoDescriptor(Type TAction, Type TEntity)
  221. {
  222. return typeof(AutoSecurityDescriptor<,>).MakeGenericType(TEntity, TAction.MakeGenericType(TEntity));
  223. }
  224. #region CanView
  225. private static Type CanViewSecurityDescriptor(Type T)
  226. {
  227. var security = T.GetCustomAttribute<EntitySecurityAttribute>();
  228. return security?.CanView ?? CreateAutoDescriptor(typeof(CanView<>), T);
  229. }
  230. private static Type CanViewSecurityDescriptor<T>()
  231. where T : Entity, new()
  232. {
  233. var security = typeof(T).GetCustomAttribute<EntitySecurityAttribute>();
  234. return security?.CanView ?? typeof(AutoSecurityDescriptor<T, CanView<T>>);
  235. }
  236. public static bool CanView<TEntity>(Guid userGuid, Guid securityId) where TEntity : Entity, new()
  237. {
  238. return IsAllowed(CanViewSecurityDescriptor<TEntity>(), userGuid, securityId);
  239. }
  240. public static bool CanView(Type TEntity)
  241. {
  242. return IsAllowed(CanViewSecurityDescriptor(TEntity));
  243. }
  244. public static bool CanView<TEntity>() where TEntity : Entity, new()
  245. {
  246. return IsAllowed(CanViewSecurityDescriptor<TEntity>());
  247. }
  248. #endregion
  249. #region CanEdit
  250. private static Type CanEditSecurityDescriptor(Type T)
  251. {
  252. var security = T.GetCustomAttribute<EntitySecurityAttribute>();
  253. return security?.CanEdit ?? CreateAutoDescriptor(typeof(CanEdit<>), T);
  254. }
  255. private static Type CanEditSecurityDescriptor<T>()
  256. where T : Entity, new()
  257. {
  258. var security = typeof(T).GetCustomAttribute<EntitySecurityAttribute>();
  259. return security?.CanEdit ?? typeof(AutoSecurityDescriptor<T, CanEdit<T>>);
  260. }
  261. public static bool CanEdit(Type TEntity, Guid userGuid, Guid securityId)
  262. {
  263. return IsAllowed(CanEditSecurityDescriptor(TEntity), userGuid, securityId);
  264. }
  265. public static bool CanEdit<TEntity>(Guid userGuid, Guid securityId) where TEntity : Entity, new()
  266. {
  267. return IsAllowed(CanEditSecurityDescriptor<TEntity>(), userGuid, securityId);
  268. }
  269. public static bool CanEdit(Type TEntity)
  270. {
  271. return IsAllowed(CanEditSecurityDescriptor(TEntity));
  272. }
  273. public static bool CanEdit<TEntity>() where TEntity : Entity, new()
  274. {
  275. return IsAllowed(CanEditSecurityDescriptor<TEntity>());
  276. }
  277. #endregion
  278. public static bool CanImport<TEntity>() where TEntity : Entity, new()
  279. {
  280. return IsAllowed<AutoSecurityDescriptor<TEntity, CanImport<TEntity>>>();
  281. }
  282. public static bool CanExport<TEntity>() where TEntity : Entity, new()
  283. {
  284. return IsAllowed<AutoSecurityDescriptor<TEntity, CanExport<TEntity>>>();
  285. }
  286. public static bool CanMerge<TEntity>() where TEntity : Entity, new()
  287. {
  288. return IsAllowed<AutoSecurityDescriptor<TEntity, CanMerge<TEntity>>>();
  289. }
  290. public static bool CanPost<TEntity>() where TEntity : Entity, new()
  291. {
  292. return IsAllowed<AutoSecurityDescriptor<TEntity, CanPost<TEntity>>>();
  293. }
  294. public static bool CanConfigurePost<TEntity>() where TEntity : Entity, new()
  295. {
  296. return IsAllowed<AutoSecurityDescriptor<TEntity, CanConfigurePost<TEntity>>>();
  297. }
  298. #region CanDelete
  299. private static Type CanDeleteSecurityDescriptor(Type T)
  300. {
  301. var security = T.GetCustomAttribute<EntitySecurityAttribute>();
  302. return security?.CanDelete ?? CreateAutoDescriptor(typeof(CanDelete<>), T);
  303. }
  304. private static Type CanDeleteSecurityDescriptor<T>()
  305. where T : Entity, new()
  306. {
  307. var security = typeof(T).GetCustomAttribute<EntitySecurityAttribute>();
  308. return security?.CanDelete ?? typeof(AutoSecurityDescriptor<T, CanDelete<T>>);
  309. }
  310. public static bool CanDelete<TEntity>() where TEntity : Entity, new()
  311. {
  312. return IsAllowed(CanDeleteSecurityDescriptor<TEntity>());
  313. }
  314. public static bool CanDelete(Type TEntity)
  315. {
  316. return IsAllowed(CanDeleteSecurityDescriptor(TEntity));
  317. }
  318. #endregion
  319. public static bool CanManageIssues(Type TEntity)
  320. {
  321. return IsAllowed(typeof(AutoSecurityDescriptor<,>).MakeGenericType(TEntity, typeof(CanManageIssues<>).MakeGenericType(TEntity)));
  322. }
  323. public static bool CanManageIssues<TEntity>() where TEntity : Entity, IIssues, new()
  324. {
  325. return IsAllowed<AutoSecurityDescriptor<TEntity, CanManageIssues<TEntity>>>();
  326. }
  327. public static bool CanManageProblems(Type TEntity)
  328. {
  329. return IsAllowed(typeof(AutoSecurityDescriptor<,>).MakeGenericType(TEntity, typeof(CanManageProblems<>).MakeGenericType(TEntity)));
  330. }
  331. public static bool CanManageProblems<TEntity>() where TEntity : Entity, IProblems, new()
  332. {
  333. return IsAllowed<AutoSecurityDescriptor<TEntity, CanManageProblems<TEntity>>>();
  334. }
  335. }
  336. }