summaryrefslogtreecommitdiff
path: root/Services
diff options
context:
space:
mode:
authorJake Mannens <jake@asger.xyz>2023-09-27 03:38:58 +1000
committerJake Mannens <jake@asger.xyz>2023-09-27 03:38:58 +1000
commitbc82b2dc2f7405c0fd4d179830412ea8209137b1 (patch)
tree26fdaa8635a1487e3ae9fd112336be8df0f723ec /Services
parent39eead0052215d7be4f49906e987fef7fb0c700b (diff)
Added MemoryCache class and implemented principal/ACL cache in the security service
Diffstat (limited to 'Services')
-rw-r--r--Services/SecurityService.cs61
1 files changed, 42 insertions, 19 deletions
diff --git a/Services/SecurityService.cs b/Services/SecurityService.cs
index 6e5ecb8..f0ebd70 100644
--- a/Services/SecurityService.cs
+++ b/Services/SecurityService.cs
@@ -1,5 +1,5 @@
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Caching.Memory;
+using HyperBooru.Util;
+using Microsoft.EntityFrameworkCore;
using System.Data;
namespace HyperBooru.Services;
@@ -7,25 +7,34 @@ namespace HyperBooru.Services;
public class SecurityService {
private IDbContextFactory<HBContext> dbFactory;
- private Group[] groups;
- private Acl[] acls;
+ private MemoryCache<int, HBPrincipal> principalCache;
+ private MemoryCache<int, Acl> aclCache;
public SecurityService(IDbContextFactory<HBContext> dbFactory) {
this.dbFactory = dbFactory;
- Reload();
- }
-
- public void Reload() {
- using var db = dbFactory.CreateDbContext();
- groups = db.Groups
- .Include(g => g.MemberOf)
- .ToArray();
+ // TODO: preload the principal cache
+ principalCache = new() {
+ MaxItems = 10_000,
+ MaxAge = TimeSpan.FromMinutes(10),
+ DataSource = (int id) => {
+ using var db = dbFactory.CreateDbContext();
+ return db.Principals
+ .Include(p => p.MemberOf)
+ .FirstOrDefault(p => p.ObjectId == id);
+ }
+ };
- acls = db.Acls
- .Include(a => a.Rules)
- .ThenInclude(r => r.Principal)
- .ToArray();
+ aclCache = new() {
+ MaxItems = 1000,
+ MaxAge = TimeSpan.FromMinutes(10),
+ DataSource = (int id) => {
+ using var db = dbFactory.CreateDbContext();
+ return db.Acls
+ .Include(a => a.Rules)
+ .FirstOrDefault(a => a.ObjectId == id);
+ }
+ };
}
public IEnumerable<HBObject> Filter(
@@ -46,6 +55,13 @@ public class SecurityService {
T permissions) where T : Enum =>
Filter(objects, principal, permissions);
+ /// <summary>
+ /// Resolve the specified ACL and return a bitmask representing
+ /// all the permissions the specified principal has.
+ /// </summary>
+ /// <param name="acl">
+ /// ACL to resolve (returns a bitmask consisting of all 1's if this field is null)
+ /// </param>
private ulong GetPermissions(Acl? acl, HBPrincipal principal) {
if(acl is null)
return ulong.MaxValue;
@@ -75,18 +91,25 @@ public class SecurityService {
return permissions;
}
+ /// <summary>
+ /// Recursively get all groups of which the specified principal
+ /// is a member, including implicit memberships.
+ /// </summary>
private List<Group> GetGroupMemberShip(HBPrincipal principal) {
var groups = principal.MemberOf.ToList();
while(true) {
- var toAdd = this.groups
- .Where(g => !groups.Contains(g))
+ var toAdd = groups
+ .SelectMany(g => g.MemberOf)
+ .Select(g => g.ObjectId)
+ .Where(id => !groups.Select(g => g.ObjectId).Contains(id))
.ToArray();
if(toAdd.Count() == 0)
break;
- groups.AddRange(toAdd);
+ foreach(var id in toAdd)
+ groups.Add((Group) principalCache[id]);
}
return groups;