summaryrefslogtreecommitdiff
path: root/Pages/Component/AclDialog.razor
diff options
context:
space:
mode:
authorJake Mannens <jake@asger.xyz>2023-10-09 17:50:12 +1100
committerJake Mannens <jake@asger.xyz>2023-10-09 17:50:12 +1100
commitea89ec0c1b05ac246f2ffd5907daace27564100b (patch)
tree39d870b5167e0cab112a85dadcd8d8e67785b446 /Pages/Component/AclDialog.razor
parent36757ad31901cce4bb55f4911a28bb63ee1dee7a (diff)
parent299e58db28202be8706dee1c570c382e1489213b (diff)
Merge branch 'security' of gitlab.com:plasmicplexus/HyperBooru-Server into security
Diffstat (limited to 'Pages/Component/AclDialog.razor')
-rw-r--r--Pages/Component/AclDialog.razor114
1 files changed, 62 insertions, 52 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; }