mirror of
https://github.com/chylex/Minecraft-Phantom-Panel.git
synced 2025-06-06 14:34:04 +02:00
Fix web not checking permissions in events
This commit is contained in:
parent
8a87d7aff6
commit
c618a8d045
Server
Phantom.Server.Web.Identity/Authorization
Phantom.Server.Web
Utils/Phantom.Utils.Collections
@ -22,12 +22,12 @@ public sealed class PermissionManager {
|
|||||||
return new IdentityPermissions(userPermissions.Union(rolePermissions));
|
return new IdentityPermissions(userPermissions.Union(rolePermissions));
|
||||||
}
|
}
|
||||||
|
|
||||||
private IdentityPermissions GetPermissionsForUserId(string? userId) {
|
private IdentityPermissions GetPermissionsForUserId(string? userId, bool refreshCache) {
|
||||||
if (userId == null) {
|
if (userId == null) {
|
||||||
return IdentityPermissions.None;
|
return IdentityPermissions.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userIdsToPermissionIds.TryGetValue(userId, out var userPermissions)) {
|
if (!refreshCache && userIdsToPermissionIds.TryGetValue(userId, out var userPermissions)) {
|
||||||
return userPermissions;
|
return userPermissions;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -35,11 +35,11 @@ public sealed class PermissionManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IdentityPermissions GetPermissions(ClaimsPrincipal user) {
|
public IdentityPermissions GetPermissions(ClaimsPrincipal user, bool refreshCache = false) {
|
||||||
return GetPermissionsForUserId(identityLookup.GetAuthenticatedUserId(user));
|
return GetPermissionsForUserId(identityLookup.GetAuthenticatedUserId(user), refreshCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CheckPermission(ClaimsPrincipal user, Permission permission) {
|
public bool CheckPermission(ClaimsPrincipal user, Permission permission, bool refreshCache = false) {
|
||||||
return GetPermissions(user).Check(permission);
|
return GetPermissions(user, refreshCache).Check(permission);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
27
Server/Phantom.Server.Web/Base/PhantomComponent.cs
Normal file
27
Server/Phantom.Server.Web/Base/PhantomComponent.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
@using Phantom.Common.Data.Replies
|
@using Phantom.Common.Data.Replies
|
||||||
@using Phantom.Server.Services.Audit
|
@using Phantom.Server.Services.Audit
|
||||||
@using Phantom.Server.Services.Instances
|
@using Phantom.Server.Services.Instances
|
||||||
|
@inherits PhantomComponent
|
||||||
@inject InstanceManager InstanceManager
|
@inject InstanceManager InstanceManager
|
||||||
@inject AuditLog AuditLog
|
@inject AuditLog AuditLog
|
||||||
|
|
||||||
@ -33,6 +34,11 @@
|
|||||||
private async Task ExecuteCommand(EditContext context) {
|
private async Task ExecuteCommand(EditContext context) {
|
||||||
await form.SubmitModel.StartSubmitting();
|
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);
|
var result = await InstanceManager.SendCommand(InstanceGuid, form.Command);
|
||||||
if (result == SendCommandToInstanceResult.Success) {
|
if (result == SendCommandToInstanceResult.Success) {
|
||||||
await AuditLog.AddInstanceCommandExecutedEvent(InstanceGuid, form.Command);
|
await AuditLog.AddInstanceCommandExecutedEvent(InstanceGuid, form.Command);
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
@using Phantom.Server.Services.Instances
|
@inherits PhantomComponent
|
||||||
|
@using Phantom.Server.Services.Instances
|
||||||
@using Phantom.Utils.Collections
|
@using Phantom.Utils.Collections
|
||||||
@using Phantom.Utils.Events
|
@using Phantom.Utils.Events
|
||||||
|
@using System.Diagnostics
|
||||||
@implements IDisposable
|
@implements IDisposable
|
||||||
@inject IJSRuntime Js;
|
@inject IJSRuntime Js;
|
||||||
@inject InstanceLogManager InstanceLogManager
|
@inject InstanceLogManager InstanceLogManager
|
||||||
@ -11,7 +13,6 @@
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
|
||||||
[Parameter, EditorRequired]
|
[Parameter, EditorRequired]
|
||||||
@ -22,17 +23,20 @@
|
|||||||
private EventSubscribers<RingBuffer<string>> instanceLogsSubs = null!;
|
private EventSubscribers<RingBuffer<string>> instanceLogsSubs = null!;
|
||||||
private RingBuffer<string> instanceLogs = null!;
|
private RingBuffer<string> instanceLogs = null!;
|
||||||
|
|
||||||
|
private readonly Stopwatch recheckPermissionsStopwatch = Stopwatch.StartNew();
|
||||||
|
|
||||||
protected override void OnInitialized() {
|
protected override void OnInitialized() {
|
||||||
instanceLogsSubs = InstanceLogManager.GetSubs(InstanceGuid);
|
instanceLogsSubs = InstanceLogManager.GetSubs(InstanceGuid);
|
||||||
instanceLogsSubs.Subscribe(this, buffer => {
|
instanceLogsSubs.Subscribe(this, buffer => {
|
||||||
instanceLogs = buffer;
|
instanceLogs = buffer;
|
||||||
InvokeAsync(RefreshLog);
|
InvokeAsyncChecked(RefreshLog);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender) {
|
protected override async Task OnAfterRenderAsync(bool firstRender) {
|
||||||
if (firstRender) {
|
if (firstRender) {
|
||||||
PageJs = await Js.InvokeAsync<IJSObjectReference>("import", "./Shared/InstanceLog.razor.js");
|
PageJs = await Js.InvokeAsync<IJSObjectReference>("import", "./Shared/InstanceLog.razor.js");
|
||||||
|
await RecheckPermissions();
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
|
|
||||||
await PageJs.InvokeVoidAsync("initLog");
|
await PageJs.InvokeVoidAsync("initLog");
|
||||||
@ -40,6 +44,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async Task RefreshLog() {
|
private async Task RefreshLog() {
|
||||||
|
if (recheckPermissionsStopwatch.Elapsed > TimeSpan.FromSeconds(2)) {
|
||||||
|
await RecheckPermissions();
|
||||||
|
}
|
||||||
|
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
|
|
||||||
if (PageJs != null) {
|
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() {
|
public void Dispose() {
|
||||||
instanceLogsSubs.Unsubscribe(this);
|
instanceLogsSubs.Unsubscribe(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
@using Phantom.Server.Services.Instances
|
@using Phantom.Server.Services.Instances
|
||||||
@using System.ComponentModel.DataAnnotations
|
@using System.ComponentModel.DataAnnotations
|
||||||
@using Phantom.Common.Data.Minecraft
|
@using Phantom.Common.Data.Minecraft
|
||||||
|
@inherits PhantomComponent
|
||||||
@inject IJSRuntime Js;
|
@inject IJSRuntime Js;
|
||||||
@inject InstanceManager InstanceManager;
|
@inject InstanceManager InstanceManager;
|
||||||
@inject AuditLog AuditLog
|
@inject AuditLog AuditLog
|
||||||
@ -50,6 +51,11 @@
|
|||||||
private async Task StopInstance(EditContext context) {
|
private async Task StopInstance(EditContext context) {
|
||||||
await form.SubmitModel.StartSubmitting();
|
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));
|
var result = await InstanceManager.StopInstance(InstanceGuid, new MinecraftStopStrategy(form.StopInSeconds));
|
||||||
if (result == StopInstanceResult.StopInitiated) {
|
if (result == StopInstanceResult.StopInitiated) {
|
||||||
await AuditLog.AddInstanceStoppedEvent(InstanceGuid, form.StopInSeconds);
|
await AuditLog.AddInstanceStoppedEvent(InstanceGuid, form.StopInSeconds);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
@using Microsoft.AspNetCore.Identity
|
@using Microsoft.AspNetCore.Identity
|
||||||
@using Phantom.Server.Services.Audit
|
@using Phantom.Server.Services.Audit
|
||||||
@using System.ComponentModel.DataAnnotations
|
@using System.ComponentModel.DataAnnotations
|
||||||
|
@inherits PhantomComponent
|
||||||
@inject IJSRuntime Js;
|
@inject IJSRuntime Js;
|
||||||
@inject UserManager<IdentityUser> UserManager
|
@inject UserManager<IdentityUser> UserManager
|
||||||
@inject AuditLog AuditLog
|
@inject AuditLog AuditLog
|
||||||
@ -51,6 +52,11 @@
|
|||||||
private async Task AddUser(EditContext context) {
|
private async Task AddUser(EditContext context) {
|
||||||
await form.SubmitModel.StartSubmitting();
|
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 user = new IdentityUser(form.Username);
|
||||||
var result = await UserManager.CreateAsync(user, form.Password);
|
var result = await UserManager.CreateAsync(user, form.Password);
|
||||||
if (result.Succeeded) {
|
if (result.Succeeded) {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
@using Microsoft.AspNetCore.Identity
|
@using Microsoft.AspNetCore.Identity
|
||||||
@using Phantom.Server.Services.Audit
|
@using Phantom.Server.Services.Audit
|
||||||
|
@inherits PhantomComponent
|
||||||
@inject IJSRuntime Js;
|
@inject IJSRuntime Js;
|
||||||
@inject UserManager<IdentityUser> UserManager
|
@inject UserManager<IdentityUser> UserManager
|
||||||
@inject AuditLog AuditLog
|
@inject AuditLog AuditLog
|
||||||
@ -38,6 +39,11 @@
|
|||||||
private async Task DeleteUser() {
|
private async Task DeleteUser() {
|
||||||
await submitModel.StartSubmitting();
|
await submitModel.StartSubmitting();
|
||||||
|
|
||||||
|
if (!await CheckPermission(Permission.EditUsers)) {
|
||||||
|
submitModel.StopSubmitting("You do not have permission to delete users.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (userToDelete == null) {
|
if (userToDelete == null) {
|
||||||
submitModel.StopSubmitting("Invalid user.");
|
submitModel.StopSubmitting("Invalid user.");
|
||||||
return;
|
return;
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||||
@using Microsoft.JSInterop
|
@using Microsoft.JSInterop
|
||||||
@using Phantom.Server.Web
|
@using Phantom.Server.Web
|
||||||
|
@using Phantom.Server.Web.Base
|
||||||
@using Phantom.Server.Web.Components.Dialogs
|
@using Phantom.Server.Web.Components.Dialogs
|
||||||
@using Phantom.Server.Web.Components.Forms
|
@using Phantom.Server.Web.Components.Forms
|
||||||
@using Phantom.Server.Web.Components.Graphics
|
@using Phantom.Server.Web.Components.Graphics
|
||||||
|
@ -6,6 +6,10 @@ public sealed class RingBuffer<T> {
|
|||||||
private int writeIndex;
|
private int writeIndex;
|
||||||
|
|
||||||
public RingBuffer(int capacity) {
|
public RingBuffer(int capacity) {
|
||||||
|
if (capacity < 0) {
|
||||||
|
throw new ArgumentException("Capacity must not be negative.", nameof(capacity));
|
||||||
|
}
|
||||||
|
|
||||||
this.buffer = new T[capacity];
|
this.buffer = new T[capacity];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -19,6 +23,10 @@ public sealed class RingBuffer<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void Add(T item) {
|
public void Add(T item) {
|
||||||
|
if (Capacity == 0) {
|
||||||
|
throw new InvalidOperationException("Ring buffer has no capacity.");
|
||||||
|
}
|
||||||
|
|
||||||
buffer[writeIndex++] = item;
|
buffer[writeIndex++] = item;
|
||||||
Count = Math.Max(writeIndex, Count);
|
Count = Math.Max(writeIndex, Count);
|
||||||
writeIndex %= Capacity;
|
writeIndex %= Capacity;
|
||||||
@ -30,6 +38,10 @@ public sealed class RingBuffer<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<T> EnumerateLast(uint maximumItems) {
|
public IEnumerable<T> EnumerateLast(uint maximumItems) {
|
||||||
|
if (Capacity == 0) {
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
int totalItemsToReturn = (int) Math.Min(maximumItems, Count);
|
int totalItemsToReturn = (int) Math.Min(maximumItems, Count);
|
||||||
|
|
||||||
// Yield items until we hit the end of the buffer.
|
// Yield items until we hit the end of the buffer.
|
||||||
|
Loading…
Reference in New Issue
Block a user