@using System.Numerics; @inject HBContext db @inject ISecurityService securityService @implements IDialog
obj.Owner = sid) @ref=ownerSelect/> @if(obj?.Acl is not null) { @foreach(var rule in obj.Acl.Rules) { } }
Action Subject Permissions
rule.Action = v ? AclRuleAction.Allow : AclRuleAction.Deny)/>
@if(rule.Principal != WellKnownSid.NullSid) { @(securityService.TranslateName(rule.Principal)) } else { Select a user/group! } @if(rule.Permissions == 0) { None } else { @GetActivePermissions(rule); } EditRule(rule))>🖉 RemoveRule(rule))>✖

Add new
@if(ruleToEdit is not null && permissionCheckboxes is not null) { ruleToEdit.Principal = sid) @ref=subjectSelect/> var permissions = Acl.GetPermissionDescriptions(obj!) .OrderByDescending(kv => BitOperations.PopCount(kv.Value)) .ThenBy(kv => kv.Value); foreach(var perm in permissionCheckboxes) { } } else {

Click 'Edit' next to a rule to edit it's permissions

}
@if(obj?.Acl is not null) { }
@code { public bool Visible { get => dialog.Visible; set { if(value && obj is null) throw new ArgumentException("Object must not be null!", "Object"); dialog.Visible = value; if(value) StateHasChanged(); } } private HBObject obj; private AclRule? ruleToEdit; private PermissionCheckbox[]? permissionCheckboxes; private int lastHashCode; private bool addedAcl; private string? editOwner; private string? editSubject; private Dialog dialog; private MiniPrincipalSelect ownerSelect; private MiniPrincipalSelect? subjectSelect; public void Show() => Visible = true; public void Hide() => Visible = false; public void Show(HBObject obj) { Object = obj; Show(); } public HBObject Object { get => obj; set { editOwner = null; CancelEditRule(); db.ChangeTracker.Clear(); obj = db.Objects .Include(o => o.Acl) .First(o => o.ObjectId == value.ObjectId); if (obj.Acl is not null) { db.Entry(obj.Acl).Collection(a => a.Rules).Load(); addedAcl = false; } else { obj.Acl = new() { Rules = new() }; addedAcl = true; } ownerSelect.SecurityIdentifier = obj.Owner; lastHashCode = GetAclHashCode(obj.Acl); } } public bool ApplyDisabled => GetAclHashCode(obj.Acl!) == lastHashCode || obj.Acl!.Rules.Select(r => r.Principal).Contains(WellKnownSid.NullSid); protected override void OnAfterRender(bool firstRender) { if(subjectSelect is null || ruleToEdit is null) return; if(subjectSelect.SecurityIdentifier is not null) return; subjectSelect.SecurityIdentifier = ruleToEdit.Principal; StateHasChanged(); } private string GetActivePermissions(AclRule rule) { var perms = Acl.GetPermissionDescriptions(obj) .Where(kv => (rule.Permissions & kv.Value) == kv.Value) .ToList(); // Filter the list of matching permissions to include the // most relevant encapsulation permissions only. E.g. if // 'Full access' includes 'Read' and 'Write', then only // show 'Full access'. List toRemove = new(); for(int i = 0; i < perms.Count(); i++) for(int j = 0; j < perms.Count(); j++) if(i != j) if((perms[i].Value & perms[j].Value) == perms[i].Value) toRemove.Add(i); toRemove = toRemove.Order().Distinct().ToList(); for(int i = toRemove.Count() - 1; i >= 0; i--) perms.RemoveAt(toRemove[i]); return string.Join(", ", perms .OrderByDescending(kv => BitOperations.PopCount(kv.Value)) .ThenBy(kv => kv.Value) .Select(kv => kv.Key)); } private void ApplyAcl() { if(obj.Acl!.Rules.Count() == 0) { if(!addedAcl) db.Remove(obj.Acl!); obj.Acl = null; } db.SaveChanges(); Hide(); } private void AddRule() { var rule = new AclRule() { Principal = WellKnownSid.NullSid, Action = AclRuleAction.Allow, Permissions = 0 }; obj.Acl!.Rules.Add(rule); EditRule(rule); } private void RemoveRule(AclRule rule) { if(rule == ruleToEdit) CancelEditRule(); obj.Acl!.Rules.Remove(rule); } private void EditRule(AclRule rule) { ruleToEdit = rule; editSubject = null; permissionCheckboxes = Acl.GetPermissionDescriptions(obj) .OrderByDescending(kv => BitOperations.PopCount(kv.Value)) .ThenBy(kv => kv.Value) .Select(kv => new PermissionCheckbox(rule, kv)) .ToArray(); } private void CancelEditRule() { ruleToEdit = null; permissionCheckboxes = null; } // Special hash function to identify only the elements of // the ACL that may have been changed by the user via this // dialog. private int GetAclHashCode(Acl acl) { var aclHash = !acl.Rules.Any() ? 0 : acl.Rules .Select(r => ( r.Action, r.Permissions, r.Principal.GetHashCode()).GetHashCode()) .Aggregate((a, v) => HashCode.Combine(a, v)); return HashCode.Combine(aclHash, obj.Owner.GetHashCode()); } private class PermissionCheckbox { public string Description { get; private init; } private AclRule rule; private ulong mask; public PermissionCheckbox(AclRule rule, KeyValuePair kv) { this.rule = rule; this.mask = kv.Value; Description = kv.Key; } public bool Value { get => (rule.Permissions & mask) == mask; set { if(value) rule.Permissions |= mask; else rule.Permissions &= ~mask; } } } }