using HyperBooru.Util; using Microsoft.EntityFrameworkCore; using System.Data; namespace HyperBooru.Services; public class SecurityService { private IDbContextFactory dbFactory; private MemoryCache membershipCache; private MemoryCache aclCache; IPrincipalProvider principalProvider; public SecurityService( IDbContextFactory dbFactory, IPrincipalProvider principalProvider) { this.dbFactory = dbFactory; this.principalProvider = principalProvider; // TODO: preload the principal cache membershipCache = new() { MaxItems = 1000, MaxAge = TimeSpan.FromMinutes(10), DataSource = (SidStruct sid) => { } }; 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.AclId == id); } }; } public IEnumerable Filter( IEnumerable objects, IPrincipal principal, ulong permissions) { foreach(var obj in objects) { var perms = GetPermissions(obj.Acl, principal); if((perms & permissions) == permissions) yield return obj; } } public IEnumerable Filter( IEnumerable objects, IPrincipal principal, T permissions) where T : Enum => Filter(objects, principal, permissions); /// /// Resolve the specified ACL and return a bitmask representing /// all the permissions the specified principal has. /// /// /// ACL to resolve (returns a bitmask consisting of all 1's if this field is null) /// private ulong GetPermissions(Acl? acl, IPrincipal principal) { if(acl is null) return ulong.MaxValue; ulong permissions = 0; var principals = GetGroupMemberShip(principal) .Cast() .Concat(new[] { principal }) .Select(p => p.Sid) .ToArray(); var allowRules = acl.Rules.Where(r => r.Action == AclRuleAction.Allow); var denyRules = acl.Rules.Where(r => r.Action == AclRuleAction.Deny); foreach(var rule in allowRules) { if(!principals.Contains(rule.Principal)) continue; permissions |= rule.Permissions; } foreach(var rule in denyRules) { if(!principals.Contains(rule.Principal)) continue; permissions &= ~rule.Permissions; } return permissions; } }