1
0
mirror of https://github.com/chylex/Minecraft-Phantom-Panel.git synced 2025-05-05 03:34:05 +02:00

Fix web not checking permissions in events

This commit is contained in:
chylex 2022-10-26 04:37:44 +02:00
parent 8a87d7aff6
commit c618a8d045
Signed by: chylex
GPG Key ID: 4DE42C8F19A80548
9 changed files with 93 additions and 10 deletions

View File

@ -22,12 +22,12 @@ public sealed class PermissionManager {
return new IdentityPermissions(userPermissions.Union(rolePermissions));
}
private IdentityPermissions GetPermissionsForUserId(string? userId) {
private IdentityPermissions GetPermissionsForUserId(string? userId, bool refreshCache) {
if (userId == null) {
return IdentityPermissions.None;
}
if (userIdsToPermissionIds.TryGetValue(userId, out var userPermissions)) {
if (!refreshCache && userIdsToPermissionIds.TryGetValue(userId, out var userPermissions)) {
return userPermissions;
}
else {
@ -35,11 +35,11 @@ public sealed class PermissionManager {
}
}
public IdentityPermissions GetPermissions(ClaimsPrincipal user) {
return GetPermissionsForUserId(identityLookup.GetAuthenticatedUserId(user));
public IdentityPermissions GetPermissions(ClaimsPrincipal user, bool refreshCache = false) {
return GetPermissionsForUserId(identityLookup.GetAuthenticatedUserId(user), refreshCache);
}
public bool CheckPermission(ClaimsPrincipal user, Permission permission) {
return GetPermissions(user).Check(permission);
public bool CheckPermission(ClaimsPrincipal user, Permission permission, bool refreshCache = false) {
return GetPermissions(user, refreshCache).Check(permission);
}
}

View File

@ -0,0 +1,27 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Authorization;
using Phantom.Common.Logging;
using Phantom.Server.Web.Identity.Authorization;
using Phantom.Server.Web.Identity.Data;
using ILogger = Serilog.ILogger;
namespace Phantom.Server.Web.Base;
public abstract class PhantomComponent : ComponentBase {
private static readonly ILogger Logger = PhantomLogger.Create<PhantomComponent>();
[CascadingParameter]
public Task<AuthenticationState> AuthenticationStateTask { get; set; } = null!;
[Inject]
public PermissionManager PermissionManager { get; set; } = null!;
protected async Task<bool> CheckPermission(Permission permission) {
var authenticationState = await AuthenticationStateTask;
return PermissionManager.CheckPermission(authenticationState.User, permission, refreshCache: true);
}
protected void InvokeAsyncChecked(Func<Task> task) {
InvokeAsync(task).ContinueWith(static t => Logger.Error(t.Exception, "Caught exception in async task."), TaskContinuationOptions.OnlyOnFaulted);
}
}

View File

@ -1,6 +1,7 @@
@using Phantom.Common.Data.Replies
@using Phantom.Server.Services.Audit
@using Phantom.Server.Services.Instances
@inherits PhantomComponent
@inject InstanceManager InstanceManager
@inject AuditLog AuditLog
@ -33,6 +34,11 @@
private async Task ExecuteCommand(EditContext context) {
await form.SubmitModel.StartSubmitting();
if (!await CheckPermission(Permission.ViewInstances)) {
form.SubmitModel.StopSubmitting("You do not have permission to execute commands.");
return;
}
var result = await InstanceManager.SendCommand(InstanceGuid, form.Command);
if (result == SendCommandToInstanceResult.Success) {
await AuditLog.AddInstanceCommandExecutedEvent(InstanceGuid, form.Command);

View File

@ -1,6 +1,8 @@
@using Phantom.Server.Services.Instances
@inherits PhantomComponent
@using Phantom.Server.Services.Instances
@using Phantom.Utils.Collections
@using Phantom.Utils.Events
@using System.Diagnostics
@implements IDisposable
@inject IJSRuntime Js;
@inject InstanceLogManager InstanceLogManager
@ -11,28 +13,30 @@
}
</div>
@code {
[Parameter, EditorRequired]
public Guid InstanceGuid { get; set; }
private IJSObjectReference? PageJs { get; set; }
private EventSubscribers<RingBuffer<string>> instanceLogsSubs = null!;
private RingBuffer<string> instanceLogs = null!;
private readonly Stopwatch recheckPermissionsStopwatch = Stopwatch.StartNew();
protected override void OnInitialized() {
instanceLogsSubs = InstanceLogManager.GetSubs(InstanceGuid);
instanceLogsSubs.Subscribe(this, buffer => {
instanceLogs = buffer;
InvokeAsync(RefreshLog);
InvokeAsyncChecked(RefreshLog);
});
}
protected override async Task OnAfterRenderAsync(bool firstRender) {
if (firstRender) {
PageJs = await Js.InvokeAsync<IJSObjectReference>("import", "./Shared/InstanceLog.razor.js");
await RecheckPermissions();
StateHasChanged();
await PageJs.InvokeVoidAsync("initLog");
@ -40,6 +44,10 @@
}
private async Task RefreshLog() {
if (recheckPermissionsStopwatch.Elapsed > TimeSpan.FromSeconds(2)) {
await RecheckPermissions();
}
StateHasChanged();
if (PageJs != null) {
@ -47,7 +55,18 @@
}
}
private async Task RecheckPermissions() {
recheckPermissionsStopwatch.Restart();
if (!await CheckPermission(Permission.ViewInstances)) {
await Task.Yield();
Dispose();
instanceLogs = new RingBuffer<string>(0);
}
}
public void Dispose() {
instanceLogsSubs.Unsubscribe(this);
}
}

View File

@ -3,6 +3,7 @@
@using Phantom.Server.Services.Instances
@using System.ComponentModel.DataAnnotations
@using Phantom.Common.Data.Minecraft
@inherits PhantomComponent
@inject IJSRuntime Js;
@inject InstanceManager InstanceManager;
@inject AuditLog AuditLog
@ -50,6 +51,11 @@
private async Task StopInstance(EditContext context) {
await form.SubmitModel.StartSubmitting();
if (!await CheckPermission(Permission.ViewInstances)) {
form.SubmitModel.StopSubmitting("You do not have permission to stop instances.");
return;
}
var result = await InstanceManager.StopInstance(InstanceGuid, new MinecraftStopStrategy(form.StopInSeconds));
if (result == StopInstanceResult.StopInitiated) {
await AuditLog.AddInstanceStoppedEvent(InstanceGuid, form.StopInSeconds);

View File

@ -1,6 +1,7 @@
@using Microsoft.AspNetCore.Identity
@using Phantom.Server.Services.Audit
@using System.ComponentModel.DataAnnotations
@inherits PhantomComponent
@inject IJSRuntime Js;
@inject UserManager<IdentityUser> UserManager
@inject AuditLog AuditLog
@ -51,6 +52,11 @@
private async Task AddUser(EditContext context) {
await form.SubmitModel.StartSubmitting();
if (!await CheckPermission(Permission.EditUsers)) {
form.SubmitModel.StopSubmitting("You do not have permission to add users.");
return;
}
var user = new IdentityUser(form.Username);
var result = await UserManager.CreateAsync(user, form.Password);
if (result.Succeeded) {

View File

@ -1,5 +1,6 @@
@using Microsoft.AspNetCore.Identity
@using Phantom.Server.Services.Audit
@inherits PhantomComponent
@inject IJSRuntime Js;
@inject UserManager<IdentityUser> UserManager
@inject AuditLog AuditLog
@ -38,6 +39,11 @@
private async Task DeleteUser() {
await submitModel.StartSubmitting();
if (!await CheckPermission(Permission.EditUsers)) {
submitModel.StopSubmitting("You do not have permission to delete users.");
return;
}
if (userToDelete == null) {
submitModel.StopSubmitting("Invalid user.");
return;

View File

@ -7,6 +7,7 @@
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using Phantom.Server.Web
@using Phantom.Server.Web.Base
@using Phantom.Server.Web.Components.Dialogs
@using Phantom.Server.Web.Components.Forms
@using Phantom.Server.Web.Components.Graphics

View File

@ -6,6 +6,10 @@ public sealed class RingBuffer<T> {
private int writeIndex;
public RingBuffer(int capacity) {
if (capacity < 0) {
throw new ArgumentException("Capacity must not be negative.", nameof(capacity));
}
this.buffer = new T[capacity];
}
@ -19,6 +23,10 @@ public sealed class RingBuffer<T> {
}
public void Add(T item) {
if (Capacity == 0) {
throw new InvalidOperationException("Ring buffer has no capacity.");
}
buffer[writeIndex++] = item;
Count = Math.Max(writeIndex, Count);
writeIndex %= Capacity;
@ -30,6 +38,10 @@ public sealed class RingBuffer<T> {
}
public IEnumerable<T> EnumerateLast(uint maximumItems) {
if (Capacity == 0) {
yield break;
}
int totalItemsToReturn = (int) Math.Min(maximumItems, Count);
// Yield items until we hit the end of the buffer.