@@ -44,9 +44,10 @@ public static UserEntity? AnonymousUser
44
44
public static IQueryable<UserEntity> Users(this RoleEntity r) =>
45
45
As.Expression(() => Database.Query<UserEntity>().Where(u => u.Role.Is(r)));
46
46
47
-
static ResetLazy<DirectedGraph<Lite<RoleEntity>>> roles = null!;
47
+
static ResetLazy<DirectedGraph<Lite<RoleEntity>>> rolesGraph = null!;
48
48
static ResetLazy<DirectedGraph<Lite<RoleEntity>>> rolesInverse = null!;
49
49
static ResetLazy<Dictionary<string, Lite<RoleEntity>>> rolesByName = null!;
50
+
static ResetLazy<Dictionary<Lite<RoleEntity>, RoleEntity>> rolesByLite = null!;
50
51
51
52
class RoleData
52
53
{
@@ -68,6 +69,8 @@ public static void Start(SchemaBuilder sb, string? systemUserName, string? anony
68
69
SystemUserName = systemUserName;
69
70
AnonymousUserName = anonymousUserName;
70
71
72
+
RoleEntity.RetrieveFromCache = r => rolesByLite.Value.GetOrThrow(r);
73
+
71
74
UserWithClaims.FillClaims += (userWithClaims, user)=>
72
75
{
73
76
userWithClaims.Claims["Role"] = ((UserEntity)user).Role;
@@ -110,14 +113,15 @@ public static void Start(SchemaBuilder sb, string? systemUserName, string? anony
110
113
r.Description,
111
114
});
112
115
113
-
roles = sb.GlobalLazy(CacheRoles, new InvalidateWith(typeof(RoleEntity)), AuthLogic.NotifyRulesChanged);
114
-
rolesInverse = sb.GlobalLazy(() => roles.Value.Inverse(), new InvalidateWith(typeof(RoleEntity)));
115
-
rolesByName = sb.GlobalLazy(() => roles.Value.ToDictionaryEx(a => a.ToString()!), new InvalidateWith(typeof(RoleEntity)));
116
+
rolesByLite = sb.GlobalLazy(() => Database.Query<RoleEntity>().ToDictionaryEx(a => a.ToLite()), new InvalidateWith(typeof(RoleEntity)), AuthLogic.NotifyRulesChanged);
117
+
rolesByName = sb.GlobalLazy(() => rolesByLite.Value.Keys.ToDictionaryEx(a => a.ToString()!), new InvalidateWith(typeof(RoleEntity)));
118
+
rolesGraph = sb.GlobalLazy(()=> CacheRoles(rolesByLite.Value), new InvalidateWith(typeof(RoleEntity)));
119
+
rolesInverse = sb.GlobalLazy(() => rolesGraph.Value.Inverse(), new InvalidateWith(typeof(RoleEntity)));
116
120
mergeStrategies = sb.GlobalLazy(() =>
117
121
{
118
122
var strategies = Database.Query<RoleEntity>().Select(r => KeyValuePair.Create(r.ToLite(), r.MergeStrategy)).ToDictionary();
119
123
120
-
var graph = roles.Value;
124
+
var graph = rolesGraph.Value;
121
125
122
126
Dictionary<Lite<RoleEntity>, RoleData> result = new Dictionary<Lite<RoleEntity>, RoleData>();
123
127
foreach (var r in graph.CompilationOrder())
@@ -134,7 +138,7 @@ public static void Start(SchemaBuilder sb, string? systemUserName, string? anony
134
138
}
135
139
136
140
return result;
137
-
}, new InvalidateWith(typeof(RoleEntity)), AuthLogic.NotifyRulesChanged);
141
+
}, new InvalidateWith(typeof(RoleEntity)));
138
142
139
143
sb.Schema.EntityEvents<RoleEntity>().Saving += Schema_Saving;
140
144
@@ -154,48 +158,126 @@ public static void Start(SchemaBuilder sb, string? systemUserName, string? anony
154
158
}
155
159
}
156
160
161
+
public static Lite<RoleEntity> GetOrCreateTrivialMergeRole(List<Lite<RoleEntity>> roles)
162
+
{
163
+
if (roles.Count == 1)
164
+
return roles.SingleEx();
165
+
166
+
var flatRoles = roles
167
+
.Select(a => rolesByLite.Value.GetOrThrow(a))
168
+
.ToList()
169
+
.SelectMany(a => a.IsTrivialMerge ? a.InheritsFrom.ToArray() : new[] { a.ToLite() })
170
+
.Distinct()
171
+
.ToList();
172
+
173
+
if (flatRoles.Count == 1)
174
+
return flatRoles.SingleEx();
175
+
176
+
var newName = RoleEntity.CalculateTrivialMergeName(flatRoles);
177
+
178
+
var db = rolesByName.Value.TryGetC(newName);
179
+
180
+
if (db != null)
181
+
return db;
182
+
183
+
using (AuthLogic.Disable())
184
+
{
185
+
var result = new RoleEntity
186
+
{
187
+
Name = newName,
188
+
MergeStrategy = MergeStrategy.Union,
189
+
Description = null,
190
+
IsTrivialMerge = true,
191
+
InheritsFrom = flatRoles.ToMList()
192
+
}.Execute(RoleOperation.Save);
193
+
194
+
return result.ToLite();
195
+
}
196
+
}
197
+
157
198
static void Schema_Saving(RoleEntity role)
158
199
{
159
-
if (!role.IsNew && role.InheritsFrom.IsGraphModified)
200
+
if (!role.IsNew)
160
201
{
161
202
using (new EntityCache(EntityCacheType.ForceNew))
162
203
{
163
204
EntityCache.AddFullGraph(role);
164
205
var allRoles = Database.RetrieveAll<RoleEntity>();
165
206
166
-
var roleGraph = DirectedGraph<RoleEntity>.Generate(allRoles, r => r.InheritsFrom.Select(sr => sr.RetrieveAndRemember()));
207
+
if (role.InheritsFrom.IsGraphModified)
208
+
{
209
+
var roleGraph = DirectedGraph<RoleEntity>.Generate(allRoles, r => r.InheritsFrom.Select(sr => sr.RetrieveAndRemember()));
210
+
211
+
var problems = roleGraph.FeedbackEdgeSet().Edges.ToList();
212
+
213
+
if (problems.Count > 0)
214
+
throw new ApplicationException(
215
+
AuthAdminMessage._0CyclesHaveBeenFoundInTheGraphOfRolesDueToTheRelationships.NiceToString(problems.Count) +
216
+
problems.ToString("\r\n"));
217
+
}
167
218
168
-
var problems = roleGraph.FeedbackEdgeSet().Edges.ToList();
219
+
var dic = allRoles.ToDictionary(a => a.ToLite());
169
220
170
-
if (problems.Count > 0)
221
+
var problems2 = allRoles.SelectMany(r => r.InheritsFrom.Where(inh => rolesByLite.Value.GetOrThrow(inh).IsTrivialMerge).Select(inh => new { r, inh })).ToList();
222
+
if (problems2.Any())
171
223
throw new ApplicationException(
172
-
AuthAdminMessage._0CyclesHaveBeenFoundInTheGraphOfRolesDueToTheRelationships.NiceToString().FormatWith(problems.Count) +
173
-
problems.ToString("\r\n"));
224
+
problems2.GroupBy(a => a.r, a => a.inh)
225
+
.Select(gr => AuthAdminMessage.Role0InheritsFromTrivialMergeRole1.NiceToString(gr.Key, gr.CommaAnd()))
226
+
.ToString("\r\n"));
227
+
}
228
+
229
+
if (!role.IsTrivialMerge)
230
+
{
231
+
var trivialDependant = rolesInverse.Value.IndirectlyRelatedTo(role.ToLite())
232
+
.Select(r => rolesByLite.Value.GetOrThrow(r))
233
+
.Where(a => a.IsTrivialMerge);
234
+
235
+
if (trivialDependant.Any())
236
+
{
237
+
if (role.Name != role.InDB(a => a.Name))
238
+
{
239
+
foreach (var item in trivialDependant)
240
+
{
241
+
var replaced = item.InheritsFrom.Select(r => r.Is(role) ? role.ToLite() : r);
242
+
243
+
var newName = RoleEntity.CalculateTrivialMergeName(replaced);
244
+
245
+
item.InDB().UnsafeUpdate(a => a.Name, a => newName);
246
+
}
247
+
}
248
+
}
249
+
}
250
+
}
251
+
else
252
+
{
253
+
if (role.InheritsFrom.Any())
254
+
{
255
+
using (new EntityCache(EntityCacheType.ForceNew))
256
+
{
257
+
var problems = role.InheritsFrom.Where(a => a.EntityOrNull?.IsTrivialMerge ?? a.InDB(a => a.IsTrivialMerge)).ToList();
258
+
259
+
if (problems.Any())
260
+
{
261
+
throw new ApplicationException(AuthAdminMessage.Role0InheritsFromTrivialMergeRole1.NiceToString(role, problems.CommaAnd()));
262
+
}
263
+
}
174
264
}
175
265
}
176
266
}
177
267
178
-
static DirectedGraph<Lite<RoleEntity>> CacheRoles()
268
+
static DirectedGraph<Lite<RoleEntity>> CacheRoles(Dictionary<Lite<RoleEntity>, RoleEntity> rolesLite)
179
269
{
180
-
using (AuthLogic.Disable())
181
-
{
182
-
DirectedGraph<Lite<RoleEntity>> newRoles = new DirectedGraph<Lite<RoleEntity>>();
270
+
var graph = DirectedGraph<Lite<RoleEntity>>.Generate(rolesLite.Keys, r => rolesLite.GetOrThrow(r).InheritsFrom);
183
271
184
-
using (new EntityCache(EntityCacheType.ForceNewSealed))
185
-
foreach (var role in Database.RetrieveAll<RoleEntity>())
186
-
{
187
-
newRoles.Expand(role.ToLite(), r => r.RetrieveAndRemember().InheritsFrom);
188
-
}
272
+
var problems = graph.FeedbackEdgeSet().Edges.ToList();
189
273
190
-
var problems = newRoles.FeedbackEdgeSet().Edges.ToList();
274
+
if (problems.Count > 0)
275
+
throw new ApplicationException(
276
+
AuthAdminMessage._0CyclesHaveBeenFoundInTheGraphOfRolesDueToTheRelationships.NiceToString().FormatWith(problems.Count) +
277
+
problems.ToString("\r\n"));
191
278
192
-
if (problems.Count > 0)
193
-
throw new ApplicationException(
194
-
AuthAdminMessage._0CyclesHaveBeenFoundInTheGraphOfRolesDueToTheRelationships.NiceToString().FormatWith(problems.Count) +
195
-
problems.ToString("\r\n"));
279
+
return graph;
196
280
197
-
return newRoles;
198
-
}
199
281
}
200
282
201
283
public static IDisposable UnsafeUserSession(string username)
@@ -223,14 +305,15 @@ public static IDisposable UnsafeUserSession(string username)
223
305
return result;
224
306
}
225
307
226
-
public static IEnumerable<Lite<RoleEntity>> RolesInOrder()
308
+
public static IEnumerable<Lite<RoleEntity>> RolesInOrder(bool includeTrivialMerge)
227
309
{
228
-
return roles.Value.CompilationOrderGroups().SelectMany(gr => gr.OrderBy(a => a.ToString()));
310
+
return rolesGraph.Value.CompilationOrderGroups().SelectMany(gr => gr.OrderBy(a => a.ToString()))
311
+
.Where(r => includeTrivialMerge || !rolesByLite.Value.GetOrCreate(r).IsTrivialMerge);
229
312
}
230
313
231
314
internal static DirectedGraph<Lite<RoleEntity>> RolesGraph()
232
315
{
233
-
return roles.Value;
316
+
return rolesGraph.Value;
234
317
}
235
318
236
319
public static Lite<RoleEntity> GetRole(string roleName)
@@ -240,7 +323,7 @@ public static Lite<RoleEntity> GetRole(string roleName)
240
323
241
324
public static IEnumerable<Lite<RoleEntity>> RelatedTo(Lite<RoleEntity> role)
242
325
{
243
-
return roles.Value.RelatedTo(role);
326
+
return rolesGraph.Value.RelatedTo(role);
244
327
}
245
328
246
329
public static MergeStrategy GetMergeStrategy(Lite<RoleEntity> role)
@@ -389,12 +472,12 @@ public static void StartAllModules(SchemaBuilder sb, bool activeDirectoryIntegra
389
472
390
473
public static HashSet<Lite<RoleEntity>> CurrentRoles()
391
474
{
392
-
return roles.Value.IndirectlyRelatedTo(RoleEntity.Current, true);
475
+
return rolesGraph.Value.IndirectlyRelatedTo(RoleEntity.Current, true);
393
476
}
394
477
395
478
public static HashSet<Lite<RoleEntity>> IndirectlyRelated(Lite<RoleEntity> role)
396
479
{
397
-
return roles.Value.IndirectlyRelatedTo(role, true);
480
+
return rolesGraph.Value.IndirectlyRelatedTo(role, true);
398
481
}
399
482
400
483
public static HashSet<Lite<RoleEntity>> InverseIndirectlyRelated(Lite<RoleEntity> role)
@@ -404,7 +487,7 @@ public static HashSet<Lite<RoleEntity>> InverseIndirectlyRelated(Lite<RoleEntity
404
487
405
488
internal static int Rank(Lite<RoleEntity> role)
406
489
{
407
-
return roles.Value.IndirectlyRelatedTo(role).Count;
490
+
return rolesGraph.Value.IndirectlyRelatedTo(role).Count;
408
491
}
409
492
410
493
public static event Func<bool, XElement>? ExportToXml;
@@ -420,10 +503,11 @@ public static XDocument ExportRules(bool exportAll = false)
420
503
new XDeclaration("1.0", "utf-8", "yes"),
421
504
new XElement("Auth",
422
505
new XElement("Roles",
423
-
RolesInOrder().Select(r => new XElement("Role",
506
+
RolesInOrder(includeTrivialMerge: true).Select(r => new XElement("Role",
424
507
new XAttribute("Name", r.ToString()!),
425
508
GetMergeStrategy(r) == MergeStrategy.Intersection ? new XAttribute("MergeStrategy", MergeStrategy.Intersection) : null!,
426
-
new XAttribute("Contains", roles.Value.RelatedTo(r).ToString(",")),
509
+
rolesByLite.Value.GetOrCreate(r).IsTrivialMerge ? new XAttribute("IsTrivialMerge", true) : null!,
510
+
new XAttribute("Contains", rolesGraph.Value.RelatedTo(r).ToString(",")),
427
511
rolesDic.TryGetC(r)?.Description?.Let(d => new XAttribute("Description", d))
428
512
))),
429
513
ExportToXml?.GetInvocationListTyped().Select(a => a(exportAll)).NotNull().OrderBy(a => a.Name.ToString())!));
@@ -433,7 +517,7 @@ public static XDocument ExportRules(bool exportAll = false)
433
517
{
434
518
Replacements replacements = new Replacements { Interactive = interactive };
435
519
436
-
Dictionary<string, Lite<RoleEntity>> rolesDic = roles.Value.ToDictionary(a => a.ToString()!);
520
+
Dictionary<string, Lite<RoleEntity>> rolesDic = rolesGraph.Value.ToDictionary(a => a.ToString()!);
437
521
Dictionary<string, XElement> rolesXml = doc.Root!.Element("Roles")!.Elements("Role").ToDictionary(x => x.Attribute("Name")!.Value);
438
522
439
523
replacements.AskForReplacements(rolesXml.Keys.ToHashSet(), rolesDic.Keys.ToHashSet(), "Roles");
@@ -450,14 +534,14 @@ public static XDocument ExportRules(bool exportAll = false)
450
534
{
451
535
var r = rolesDic.GetOrThrow(kvp.Key);
452
536
453
-
var current = GetMergeStrategy(r);
454
-
var should = kvp.Value.Attribute("MergeStrategy")?.Let(t => t.Value.ToEnum<MergeStrategy>()) ?? MergeStrategy.Union;
455
-
456
-
if (current != should)
457
-
throw new InvalidOperationException("Merge strategy of {0} is {1} in the database but is {2} in the file".FormatWith(r, current, should));
537
+
var currentMergeStrategy = GetMergeStrategy(r);
538
+
var shouldMergeStrategy = kvp.Value.Attribute("MergeStrategy")?.Let(t => t.Value.ToEnum<MergeStrategy>()) ?? MergeStrategy.Union;
539
+
540
+
if (currentMergeStrategy != shouldMergeStrategy)
541
+
throw new InvalidOperationException("Merge strategy of {0} is {1} in the database but is {2} in the file".FormatWith(r, currentMergeStrategy, shouldMergeStrategy));
458
542
459
543
EnumerableExtensions.JoinStrict(
460
-
roles.Value.RelatedTo(r),
544
+
rolesGraph.Value.RelatedTo(r),
461
545
kvp.Value.Attribute("Contains")!.Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries),
462
546
sr => sr.ToString()!,
463
547
s => rolesDic.GetOrThrow(s).ToString()!,
@@ -500,6 +584,7 @@ public static void LoadRoles(XDocument doc)
500
584
{
501
585
Name = x.Attribute("Name")!.Value,
502
586
MergeStrategy = x.Attribute("MergeStrategy")?.Let(ms => ms.Value.ToEnum<MergeStrategy>()) ?? MergeStrategy.Union,
587
+
IsTrivialMerge = x.Attribute("IsTrivialMerge")?.Let(t => t.Value.ToBool()) ?? false,
503
588
SubRoles = x.Attribute("Contains")!.Value.SplitNoEmpty(','),
504
589
Description = x.Attribute("Description")?.Value,
505
590
}).ToList();
@@ -713,10 +798,10 @@ public static bool IsLogged()
713
798
714
799
public static int Compare(Lite<RoleEntity> role1, Lite<RoleEntity> role2)
715
800
{
716
-
if (roles.Value.IndirectlyRelatedTo(role1).Contains(role2))
801
+
if (rolesGraph.Value.IndirectlyRelatedTo(role1).Contains(role2))
717
802
return 1;
718
803
719
-
if (roles.Value.IndirectlyRelatedTo(role2).Contains(role1))
804
+
if (rolesGraph.Value.IndirectlyRelatedTo(role2).Contains(role1))
720
805
return -1;
721
806
722
807
return 0;
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4