mirror of
https://github.com/chylex/Minecraft-Phantom-Panel.git
synced 2024-11-25 16:42:54 +01:00
Compare commits
2 Commits
1ce0c4cfa9
...
0a29b6f21e
Author | SHA1 | Date | |
---|---|---|---|
0a29b6f21e | |||
8bee9bc763 |
24
.run/Web.run.xml
Normal file
24
.run/Web.run.xml
Normal file
@ -0,0 +1,24 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Web" type="DotNetProject" factoryName=".NET Project">
|
||||
<option name="EXE_PATH" value="$PROJECT_DIR$/.artifacts/bin/Phantom.Web/debug/Phantom.Web.exe" />
|
||||
<option name="PROGRAM_PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/.workdir/Web" />
|
||||
<option name="PASS_PARENT_ENVS" value="1" />
|
||||
<envs>
|
||||
<env name="ASPNETCORE_ENVIRONMENT" value="Development" />
|
||||
<env name="WEB_SERVER_HOST" value="localhost" />
|
||||
</envs>
|
||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||
<option name="USE_MONO" value="0" />
|
||||
<option name="RUNTIME_ARGUMENTS" value="" />
|
||||
<option name="PROJECT_PATH" value="$PROJECT_DIR$/Web/Phantom.Web/Phantom.Web.csproj" />
|
||||
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
|
||||
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
||||
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="0" />
|
||||
<option name="PROJECT_KIND" value="DotNetCore" />
|
||||
<option name="PROJECT_TFM" value="net8.0" />
|
||||
<method v="2">
|
||||
<option name="Build" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
@ -4,7 +4,7 @@ using Phantom.Controller.Database.Entities;
|
||||
|
||||
namespace Phantom.Controller.Services.Users;
|
||||
|
||||
internal static class UserPasswords {
|
||||
static class UserPasswords {
|
||||
private static PasswordHasher<UserEntity> Hasher { get; } = new ();
|
||||
|
||||
private const int MinimumLength = 16;
|
||||
|
@ -17,6 +17,34 @@ public static class Files {
|
||||
await stream.WriteAsync(bytes);
|
||||
}
|
||||
|
||||
public static async Task ReadExactlyBytesAsync(string path, Memory<byte> bytes) {
|
||||
var options = new FileStreamOptions {
|
||||
Mode = FileMode.Open,
|
||||
Access = FileAccess.Read,
|
||||
Options = FileOptions.Asynchronous,
|
||||
Share = FileShare.Read
|
||||
};
|
||||
|
||||
await using var stream = new FileStream(path, options);
|
||||
|
||||
bool wrongLength = false;
|
||||
|
||||
if (stream.Length == bytes.Length) {
|
||||
try {
|
||||
await stream.ReadExactlyAsync(bytes);
|
||||
} catch (EndOfStreamException) {
|
||||
wrongLength = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
wrongLength = true;
|
||||
}
|
||||
|
||||
if (wrongLength) {
|
||||
throw new IOException("Expected file size to be exactly " + bytes.Length + " B, actual size is " + stream.Length + " B.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void RequireMaximumFileSize(string path, long maximumBytes) {
|
||||
var actualBytes = new FileInfo(path).Length;
|
||||
if (actualBytes > maximumBytes) {
|
||||
|
@ -2,7 +2,6 @@
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Phantom.Common.Logging;
|
||||
using Phantom.Controller.Services.Users;
|
||||
using Phantom.Utils.Cryptography;
|
||||
using Phantom.Web.Identity.Interfaces;
|
||||
using ILogger = Serilog.ILogger;
|
||||
|
@ -1,5 +1,4 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Phantom.Web.Identity.Data;
|
||||
|
||||
namespace Phantom.Web.Identity.Authorization;
|
||||
|
||||
|
@ -1,6 +1,4 @@
|
||||
using Phantom.Controller.Database.Entities;
|
||||
|
||||
namespace Phantom.Web.Identity.Interfaces;
|
||||
namespace Phantom.Web.Identity.Interfaces;
|
||||
|
||||
public interface ILoginEvents {
|
||||
void UserLoggedIn(UserEntity user);
|
||||
|
@ -1,6 +1,4 @@
|
||||
using Phantom.Controller.Database.Entities;
|
||||
using Phantom.Controller.Services.Audit;
|
||||
using Phantom.Web.Identity.Interfaces;
|
||||
using Phantom.Web.Identity.Interfaces;
|
||||
|
||||
namespace Phantom.Web.Base;
|
||||
|
||||
|
@ -1,8 +1,6 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using Phantom.Common.Logging;
|
||||
using Phantom.Web.Identity.Authorization;
|
||||
using Phantom.Web.Identity.Data;
|
||||
using ILogger = Serilog.ILogger;
|
||||
|
||||
namespace Phantom.Web.Base;
|
||||
|
@ -1,5 +1,4 @@
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Phantom.Controller.Services;
|
||||
using Phantom.Utils.Tasks;
|
||||
using Phantom.Web.Base;
|
||||
using Phantom.Web.Identity;
|
||||
|
@ -1,5 +1,4 @@
|
||||
@using Phantom.Controller.Services
|
||||
@inject ServiceConfiguration Configuration
|
||||
@inject ServiceConfiguration Configuration
|
||||
@inject PermissionManager PermissionManager
|
||||
|
||||
<div class="navbar navbar-dark">
|
||||
|
@ -1,5 +1,4 @@
|
||||
@page "/agents"
|
||||
@using Phantom.Controller.Services.Agents
|
||||
@using Phantom.Utils.Collections
|
||||
@implements IDisposable
|
||||
@inject AgentManager AgentManager
|
||||
|
@ -1,10 +1,6 @@
|
||||
@page "/audit"
|
||||
@attribute [Authorize(Permission.ViewAuditPolicy)]
|
||||
@using Phantom.Controller.Services.Audit
|
||||
@using Phantom.Controller.Services.Instances
|
||||
@using Phantom.Controller.Services.Users
|
||||
@using System.Collections.Immutable
|
||||
@using Phantom.Controller.Database.Enums
|
||||
@implements IDisposable
|
||||
@inject AuditLog AuditLog
|
||||
@inject InstanceManager InstanceManager
|
||||
|
@ -1,10 +1,6 @@
|
||||
@page "/events"
|
||||
@attribute [Authorize(Permission.ViewEventsPolicy)]
|
||||
@using System.Collections.Immutable
|
||||
@using Phantom.Controller.Services.Events
|
||||
@using Phantom.Controller.Services.Instances
|
||||
@using Phantom.Controller.Database.Enums
|
||||
@using Phantom.Controller.Services.Agents
|
||||
@implements IDisposable
|
||||
@inject AgentManager AgentManager
|
||||
@inject EventLog EventLog
|
||||
|
@ -1,10 +1,6 @@
|
||||
@page "/instances/{InstanceGuid:guid}"
|
||||
@attribute [Authorize(Permission.ViewInstancesPolicy)]
|
||||
@inherits PhantomComponent
|
||||
@using Phantom.Common.Data.Instance
|
||||
@using Phantom.Common.Data.Replies
|
||||
@using Phantom.Controller.Services.Audit
|
||||
@using Phantom.Controller.Services.Instances
|
||||
@implements IDisposable
|
||||
@inject InstanceManager InstanceManager
|
||||
@inject AuditLog AuditLog
|
||||
|
@ -1,7 +1,5 @@
|
||||
@page "/instances/{InstanceGuid:guid}/edit"
|
||||
@attribute [Authorize(Permission.CreateInstancesPolicy)]
|
||||
@using Phantom.Common.Data.Instance
|
||||
@using Phantom.Controller.Services.Instances
|
||||
@inherits PhantomComponent
|
||||
@inject InstanceManager InstanceManager
|
||||
|
||||
|
@ -1,8 +1,6 @@
|
||||
@page "/instances"
|
||||
@attribute [Authorize(Permission.ViewInstancesPolicy)]
|
||||
@using System.Collections.Immutable
|
||||
@using Phantom.Controller.Services.Instances
|
||||
@using Phantom.Controller.Services.Agents
|
||||
@implements IDisposable
|
||||
@inject AgentManager AgentManager
|
||||
@inject InstanceManager InstanceManager
|
||||
|
@ -1,12 +1,8 @@
|
||||
@page "/setup"
|
||||
@using Phantom.Controller.Services.Users
|
||||
@using Phantom.Utils.Cryptography
|
||||
@using Phantom.Utils.Tasks
|
||||
@using Phantom.Controller.Database.Entities
|
||||
@using Phantom.Controller.Services
|
||||
@using Phantom.Controller.Services.Audit
|
||||
@using Phantom.Web.Identity.Authentication
|
||||
@using System.ComponentModel.DataAnnotations
|
||||
@using Phantom.Utils.Cryptography
|
||||
@using System.Security.Cryptography
|
||||
@attribute [AllowAnonymous]
|
||||
@inject ServiceConfiguration ServiceConfiguration
|
||||
|
@ -1,6 +1,4 @@
|
||||
@page "/users"
|
||||
@using Phantom.Controller.Database.Entities
|
||||
@using Phantom.Controller.Services.Users
|
||||
@using System.Collections.Immutable
|
||||
@attribute [Authorize(Permission.ViewUsersPolicy)]
|
||||
@inject UserManager UserManager
|
||||
|
68
Web/Phantom.Web/Program.cs
Normal file
68
Web/Phantom.Web/Program.cs
Normal file
@ -0,0 +1,68 @@
|
||||
using System.Reflection;
|
||||
using Phantom.Common.Logging;
|
||||
using Phantom.Utils.Cryptography;
|
||||
using Phantom.Utils.IO;
|
||||
using Phantom.Utils.Runtime;
|
||||
using Phantom.Utils.Tasks;
|
||||
using Phantom.Web;
|
||||
|
||||
var cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
PosixSignals.RegisterCancellation(cancellationTokenSource, static () => {
|
||||
PhantomLogger.Root.InformationHeading("Stopping Phantom Panel web...");
|
||||
});
|
||||
|
||||
static void CreateFolderOrStop(string path, UnixFileMode chmod) {
|
||||
if (!Directory.Exists(path)) {
|
||||
try {
|
||||
Directories.Create(path, chmod);
|
||||
} catch (Exception e) {
|
||||
PhantomLogger.Root.Fatal(e, "Error creating folder: {FolderName}", path);
|
||||
throw StopProcedureException.Instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
var fullVersion = AssemblyAttributes.GetFullVersion(Assembly.GetExecutingAssembly());
|
||||
|
||||
PhantomLogger.Root.InformationHeading("Initializing Phantom Panel web...");
|
||||
PhantomLogger.Root.Information("Web version: {Version}", fullVersion);
|
||||
|
||||
var (controllerHost, controllerPort, webKeyToken, webKeyFilePath, webServerHost, webServerPort, webBasePath) = Variables.LoadOrStop();
|
||||
|
||||
string webKeysPath = Path.GetFullPath("./keys");
|
||||
CreateFolderOrStop(webKeysPath, Chmod.URWX);
|
||||
|
||||
PhantomLogger.Root.InformationHeading("Launching Phantom Panel web...");
|
||||
|
||||
var taskManager = new TaskManager(PhantomLogger.Create<TaskManager>("Web"));
|
||||
try {
|
||||
var configuration = new Configuration(PhantomLogger.Create("Web"), webServerHost, webServerPort, webBasePath, webKeysPath, cancellationTokenSource.Token);
|
||||
|
||||
var administratorToken = TokenGenerator.Create(60);
|
||||
PhantomLogger.Root.Information("Your administrator token is: {AdministratorToken}", administratorToken);
|
||||
PhantomLogger.Root.Information("For administrator setup, visit: {HttpUrl}{SetupPath}", configuration.HttpUrl, configuration.BasePath + "setup");
|
||||
|
||||
var serviceConfiguration = new ServiceConfiguration(fullVersion, TokenGenerator.GetBytesOrThrow(administratorToken), cancellationTokenSource.Token);
|
||||
var webApplication = Launcher.CreateApplication(configuration, serviceConfiguration, taskManager);
|
||||
|
||||
await Launcher.Launch(configuration, webApplication);
|
||||
} finally {
|
||||
cancellationTokenSource.Cancel();
|
||||
await taskManager.Stop();
|
||||
}
|
||||
|
||||
return 0;
|
||||
} catch (OperationCanceledException) {
|
||||
return 0;
|
||||
} catch (StopProcedureException) {
|
||||
return 1;
|
||||
} catch (Exception e) {
|
||||
PhantomLogger.Root.Fatal(e, "Caught exception in entry point.");
|
||||
return 1;
|
||||
} finally {
|
||||
cancellationTokenSource.Dispose();
|
||||
PhantomLogger.Root.Information("Bye!");
|
||||
PhantomLogger.Dispose();
|
||||
}
|
7
Web/Phantom.Web/ServiceConfiguration.cs
Normal file
7
Web/Phantom.Web/ServiceConfiguration.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace Phantom.Web;
|
||||
|
||||
public sealed record ServiceConfiguration(
|
||||
string Version,
|
||||
byte[] AdministratorToken,
|
||||
CancellationToken CancellationToken
|
||||
);
|
@ -1,9 +1,7 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.JSInterop;
|
||||
using Phantom.Controller.Database.Entities;
|
||||
using Phantom.Web.Base;
|
||||
using Phantom.Web.Components.Forms;
|
||||
using Phantom.Web.Identity.Data;
|
||||
|
||||
namespace Phantom.Web.Shared;
|
||||
|
||||
|
37
Web/Phantom.Web/Variables.cs
Normal file
37
Web/Phantom.Web/Variables.cs
Normal file
@ -0,0 +1,37 @@
|
||||
using Phantom.Common.Logging;
|
||||
using Phantom.Utils.Runtime;
|
||||
|
||||
namespace Phantom.Web;
|
||||
|
||||
sealed record Variables(
|
||||
string ControllerHost,
|
||||
ushort ControllerPort,
|
||||
string? WebKeyToken,
|
||||
string? WebKeyFilePath,
|
||||
string WebServerHost,
|
||||
ushort WebServerPort,
|
||||
string WebBasePath
|
||||
) {
|
||||
private static Variables LoadOrThrow() {
|
||||
var (webKeyToken, webKeyFilePath) = EnvironmentVariables.GetEitherString("WEB_KEY", "WEB_KEY_FILE").Require;
|
||||
|
||||
return new Variables(
|
||||
EnvironmentVariables.GetString("CONTROLLER_HOST").Require,
|
||||
EnvironmentVariables.GetPortNumber("CONTROLLER_PORT").WithDefault(9402),
|
||||
webKeyToken,
|
||||
webKeyFilePath,
|
||||
EnvironmentVariables.GetString("WEB_SERVER_HOST").WithDefault("0.0.0.0"),
|
||||
EnvironmentVariables.GetPortNumber("WEB_SERVER_PORT").WithDefault(9400),
|
||||
EnvironmentVariables.GetString("WEB_BASE_PATH").Validate(static value => value.StartsWith('/') && value.EndsWith('/'), "Environment variable must begin and end with '/'").WithDefault("/")
|
||||
);
|
||||
}
|
||||
|
||||
public static Variables LoadOrStop() {
|
||||
try {
|
||||
return LoadOrThrow();
|
||||
} catch (Exception e) {
|
||||
PhantomLogger.Root.Fatal(e.Message);
|
||||
throw StopProcedureException.Instance;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user