diff options
Diffstat (limited to 'Pages/Component')
| -rw-r--r-- | Pages/Component/AclDialog.razor | 114 | ||||
| -rw-r--r-- | Pages/Component/AclDialog.razor.css | 19 | ||||
| -rw-r--r-- | Pages/Component/MiniPrincipalSelect.razor | 44 | ||||
| -rw-r--r-- | Pages/Component/MiniPrincipalSelect.razor.css | 18 |
4 files changed, 124 insertions, 71 deletions
diff --git a/Pages/Component/AclDialog.razor b/Pages/Component/AclDialog.razor index 0d24530..691e984 100644 --- a/Pages/Component/AclDialog.razor +++ b/Pages/Component/AclDialog.razor @@ -1,5 +1,5 @@ @using System.Numerics; -@inject IDbContextFactory<HBContext> dbFactory; +@inject HBContext db @implements IDialog <Dialog HeightPixels=500 WidthPixels=900 Title="Edit permissions" @ref=dialog> @@ -7,19 +7,7 @@ <div class="hcontainer"> <div> @if(obj?.Acl is not null) { - <div class="principal-select"> - @if(editOwner is not null) { - <label>Owner</label> - <input type="text" autocomplete="off" @bind=editOwner/> - <button class="secondary" @onclick=@(() => editOwner = null)>Cancel</button> - <button @onclick=@(() => editOwner = null)>OK</button> - } else { - <label>Owner:</label> - <a href="javascript:;" @onclick=@(() => editOwner = obj.Owner.ToString())> - @obj.Owner.ToString() - </a> - } - </div> + <MiniPrincipalSelect Label="Owner"/> <table class="data-table"> <tr> <th>Action</th> @@ -28,7 +16,13 @@ </tr> @foreach(var rule in obj.Acl.Rules.OrderByDescending(r => r.Action)) { <tr> - <td><div><AclActionSwitch InitialValue=@(rule.Action == AclRuleAction.Allow)/></div></td> + <td> + <div> + <AclActionSwitch + InitialValue=@(rule.Action == AclRuleAction.Allow) + OnToggle=@((v) => rule.Action = v ? AclRuleAction.Allow : AclRuleAction.Deny)/> + </div> + </td> <td> @rule.Principal.ToString() </td> @@ -42,30 +36,12 @@ </table> <br/> <center><a href="javascript:;" @onclick=AddRule>Add new</a></center> - } else { - <p><i>This item does not have any permissions set!</i></p> } </div> <div> @if(ruleToEdit is not null && permissionCheckboxes is not null) { - <div class="principal-select"> - @if(editSubject is not null) { - <label>Subject</label> - <input type="text" autocomplete="off" @bind=editSubject/> - <button class="secondary" @onclick=@(() => editSubject = null)>Cancel</button> - <button @onclick=@(() => editSubject = null)>OK</button> - } else { - <label>Subject:</label> - <a href="javascript:;" @onclick=@(() => editSubject = ruleToEdit.Principal.ToString())> - @if(ruleToEdit.Principal == WellKnownSid.NullSid) { - <i>Select a user or group</i> - } else { - @ruleToEdit.Principal.ToString() - } - </a> - } - </div> - var permissions = Acl.GetPermissionDescriptions(obj!) + <MiniPrincipalSelect Label="Subject"/> + var permissions = Acl.GetPermissionDescriptions(obj) .OrderByDescending(kv => BitOperations.PopCount(kv.Value)) .ThenBy(kv => kv.Value); foreach(var perm in permissionCheckboxes) { @@ -84,7 +60,9 @@ <ButtonContainer> <button class="secondary" @onclick=Hide>Cancel</button> @if(obj?.Acl is not null) { - <button data-keyboard-shortcut="a" @onclick=Hide><u>A</u>pply</button> + <button data-keyboard-shortcut="a" @onclick=ApplyAcl disabled=@(ApplyDisabled)> + <u>A</u>pply + </button> } </ButtonContainer> </div> @@ -94,16 +72,22 @@ 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 HBObject obj; private AclRule? ruleToEdit; private PermissionCheckbox[]? permissionCheckboxes; + private int lastHashCode; + private bool addedAcl; + private string? editOwner; private string? editSubject; @@ -117,32 +101,42 @@ Show(); } - public HBObject? Object { + public HBObject Object { get => obj; set { editOwner = null; CancelEditRule(); - if(value is null) { - obj = null; - return; - } - - using var db = dbFactory.CreateDbContext(); - obj = db.Objects .Include(o => o.Acl) .First(o => o.ObjectId == value.ObjectId); - if(obj.Acl is not null) + 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; + } + + lastHashCode = GetAclHashCode(obj.Acl); } } + public bool ApplyDisabled => + #if DEBUG + false; + #else + GetAclHashCode(obj.Acl!) == lastHashCode || + obj.Acl!.Rules.Select(r => r.Principal).Contains(WellKnownSid.NullSid); + #endif + private string GetActivePermissions(AclRule rule) { - var perms = Acl.GetPermissionDescriptions(obj!) - .Where(kv => (rule.Permissions & kv.Value) == kv.Value) - .ToList(); + 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 @@ -164,6 +158,11 @@ .Select(kv => kv.Key)); } + private void ApplyAcl() { + db.SaveChanges(); + Hide(); + } + private void AddRule() { var rule = new AclRule() { Principal = WellKnownSid.NullSid, @@ -171,20 +170,20 @@ Permissions = 0 }; - obj!.Acl!.Rules.Add(rule); + obj.Acl!.Rules.Add(rule); EditRule(rule); } private void RemoveRule(AclRule rule) { if(rule == ruleToEdit) CancelEditRule(); - obj!.Acl!.Rules.Remove(rule); + obj.Acl!.Rules.Remove(rule); } private void EditRule(AclRule rule) { ruleToEdit = rule; editSubject = null; - permissionCheckboxes = Acl.GetPermissionDescriptions(obj!) + permissionCheckboxes = Acl.GetPermissionDescriptions(obj) .OrderByDescending(kv => BitOperations.PopCount(kv.Value)) .ThenBy(kv => kv.Value) .Select(kv => new PermissionCheckbox(rule, kv)) @@ -196,6 +195,17 @@ 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) => + !acl.Rules.Any() ? 0 : acl.Rules + .Select(r => ( + r.Action, + r.Permissions, + r.Principal.GetHashCode()).GetHashCode()) + .Aggregate((a, v) => HashCode.Combine(a, v)); + private class PermissionCheckbox { public string Description { get; private init; } diff --git a/Pages/Component/AclDialog.razor.css b/Pages/Component/AclDialog.razor.css index c9ac518..3cab1c6 100644 --- a/Pages/Component/AclDialog.razor.css +++ b/Pages/Component/AclDialog.razor.css @@ -30,25 +30,6 @@ div.hcontainer > div > p { text-align: center; } -div.principal-select { - align-items: center; - display: flex; - flex-direction: row; - margin-bottom: 16px; -} - -div.principal-select * { - margin: 0; -} - -div.principal-select :not(:last-child) { - margin-right: 5px; -} - -div.principal-select input[type="text"] { - flex-grow: 1; -} - div.hcontainer > div:last-child > label { font-family: 'Lucida Console'; } diff --git a/Pages/Component/MiniPrincipalSelect.razor b/Pages/Component/MiniPrincipalSelect.razor new file mode 100644 index 0000000..2202b95 --- /dev/null +++ b/Pages/Component/MiniPrincipalSelect.razor @@ -0,0 +1,44 @@ +@inject ISecurityService securityService + +<div> + @if(edit) { + <label>@Label</label> + <input type="text" autocomplete="off" @bind=name/> + <button class="secondary" @onclick=@(() => Edit(false))>Cancel</button> + <button @onclick=Submit>OK</button> + } else { + <label>@(Label):</label> + <a href="javascript:;" @onclick=@(() => Edit(true))> + @if(SecurityIdentifier is null || SecurityIdentifier == WellKnownSid.NullSid) { + <i>Please select a user or group</i> + } else { + @securityService.TranslateName(SecurityIdentifier) + } + </a> + } +</div> + +@code { + [Parameter] + public string Label { get; set; } + + [Parameter] + public EventCallback<SecurityIdentifier> OnChange { get; set; } + + private bool edit = false; + private string name; + + public SecurityIdentifier? SecurityIdentifier { get; set; } + + private void Edit(bool enableEdit) { + edit = enableEdit; + + if(enableEdit) + name = SecurityIdentifier is null ? "" : + securityService.TranslateName(SecurityIdentifier); + } + + private void Submit() { + Edit(false); + } +}
\ No newline at end of file diff --git a/Pages/Component/MiniPrincipalSelect.razor.css b/Pages/Component/MiniPrincipalSelect.razor.css new file mode 100644 index 0000000..4b7a217 --- /dev/null +++ b/Pages/Component/MiniPrincipalSelect.razor.css @@ -0,0 +1,18 @@ +div { + align-items: center; + display: flex; + flex-direction: row; + margin-bottom: 16px; +} + +div * { + margin: 0; +} + +div :not(:last-child) { + margin-right: 5px; +} + +div input[type="text"] { + flex-grow: 1; +} |
