mirror of
https://github.com/chylex/Minecraft-Phantom-Panel.git
synced 2024-11-25 07:42:58 +01:00
Compare commits
3 Commits
8c623171f3
...
21c90cb7c2
Author | SHA1 | Date | |
---|---|---|---|
21c90cb7c2 | |||
7b68b5d50d | |||
2d70d2b7d1 |
@ -3,7 +3,7 @@
|
|||||||
"isRoot": true,
|
"isRoot": true,
|
||||||
"tools": {
|
"tools": {
|
||||||
"dotnet-ef": {
|
"dotnet-ef": {
|
||||||
"version": "7.0.0-rc.1.22426.7",
|
"version": "8.0.3",
|
||||||
"commands": [
|
"commands": [
|
||||||
"dotnet-ef"
|
"dotnet-ef"
|
||||||
]
|
]
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using MemoryPack;
|
using System.Collections.Immutable;
|
||||||
|
using MemoryPack;
|
||||||
|
|
||||||
namespace Phantom.Common.Data.Web.Users;
|
namespace Phantom.Common.Data.Web.Users;
|
||||||
|
|
||||||
@ -6,5 +7,18 @@ namespace Phantom.Common.Data.Web.Users;
|
|||||||
public sealed partial record AuthenticatedUserInfo(
|
public sealed partial record AuthenticatedUserInfo(
|
||||||
[property: MemoryPackOrder(0)] Guid Guid,
|
[property: MemoryPackOrder(0)] Guid Guid,
|
||||||
[property: MemoryPackOrder(1)] string Name,
|
[property: MemoryPackOrder(1)] string Name,
|
||||||
[property: MemoryPackOrder(2)] PermissionSet Permissions
|
[property: MemoryPackOrder(2)] PermissionSet Permissions,
|
||||||
);
|
[property: MemoryPackOrder(3)] ImmutableHashSet<Guid> ManagedAgentGuids
|
||||||
|
) {
|
||||||
|
public bool CheckPermission(Permission permission) {
|
||||||
|
return Permissions.Check(permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasAccessToAgent(Guid agentGuid) {
|
||||||
|
return ManagedAgentGuids.Contains(agentGuid) || Permissions.Check(Permission.ManageAllAgents);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImmutableHashSet<Guid> FilterAccessibleAgentGuids(ImmutableHashSet<Guid> agentGuids) {
|
||||||
|
return Permissions.Check(Permission.ManageAllAgents) ? agentGuids : agentGuids.Intersect(ManagedAgentGuids);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -14,6 +14,9 @@ public sealed record Permission(string Id, Permission? Parent) {
|
|||||||
return Register(id, this);
|
return Register(id, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public const string ManageAllAgentsPolicy = "Agents.ManageAll";
|
||||||
|
public static readonly Permission ManageAllAgents = Register(ManageAllAgentsPolicy);
|
||||||
|
|
||||||
public const string ViewInstancesPolicy = "Instances.View";
|
public const string ViewInstancesPolicy = "Instances.View";
|
||||||
public static readonly Permission ViewInstances = Register(ViewInstancesPolicy);
|
public static readonly Permission ViewInstances = Register(ViewInstancesPolicy);
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
|
using Phantom.Common.Data;
|
||||||
using Phantom.Common.Data.Web.Users;
|
using Phantom.Common.Data.Web.Users;
|
||||||
using Phantom.Utils.Actor;
|
using Phantom.Utils.Actor;
|
||||||
|
|
||||||
@ -8,4 +9,4 @@ namespace Phantom.Common.Messages.Web.ToController;
|
|||||||
public sealed partial record LogInMessage(
|
public sealed partial record LogInMessage(
|
||||||
[property: MemoryPackOrder(0)] string Username,
|
[property: MemoryPackOrder(0)] string Username,
|
||||||
[property: MemoryPackOrder(1)] string Password
|
[property: MemoryPackOrder(1)] string Password
|
||||||
) : IMessageToController, ICanReply<LogInSuccess?>;
|
) : IMessageToController, ICanReply<Optional<LogInSuccess>>;
|
||||||
|
@ -24,7 +24,7 @@ public static class WebMessageRegistries {
|
|||||||
static WebMessageRegistries() {
|
static WebMessageRegistries() {
|
||||||
ToController.Add<RegisterWebMessage>(0);
|
ToController.Add<RegisterWebMessage>(0);
|
||||||
ToController.Add<UnregisterWebMessage>(1);
|
ToController.Add<UnregisterWebMessage>(1);
|
||||||
ToController.Add<LogInMessage, LogInSuccess?>(2);
|
ToController.Add<LogInMessage, Optional<LogInSuccess>>(2);
|
||||||
ToController.Add<LogOutMessage>(3);
|
ToController.Add<LogOutMessage>(3);
|
||||||
ToController.Add<GetAuthenticatedUser, Optional<AuthenticatedUserInfo>>(4);
|
ToController.Add<GetAuthenticatedUser, Optional<AuthenticatedUserInfo>>(4);
|
||||||
ToController.Add<CreateOrUpdateAdministratorUserMessage, CreateOrUpdateAdministratorUserResult>(5);
|
ToController.Add<CreateOrUpdateAdministratorUserMessage, CreateOrUpdateAdministratorUserResult>(5);
|
||||||
|
353
Controller/Phantom.Controller.Database.Postgres/Migrations/20240407211636_UserAgentAccess.Designer.cs
generated
Normal file
353
Controller/Phantom.Controller.Database.Postgres/Migrations/20240407211636_UserAgentAccess.Designer.cs
generated
Normal file
@ -0,0 +1,353 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using System.Text.Json;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
using Phantom.Controller.Database;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Phantom.Controller.Database.Postgres.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(ApplicationDbContext))]
|
||||||
|
[Migration("20240407211636_UserAgentAccess")]
|
||||||
|
partial class UserAgentAccess
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "8.0.0")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Phantom.Controller.Database.Entities.AgentEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("AgentGuid")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("BuildVersion")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<int>("MaxInstances")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<ushort>("MaxMemory")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<int>("ProtocolVersion")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.HasKey("AgentGuid");
|
||||||
|
|
||||||
|
b.ToTable("Agents", "agents");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Phantom.Controller.Database.Entities.AuditLogEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<JsonDocument>("Data")
|
||||||
|
.HasColumnType("jsonb");
|
||||||
|
|
||||||
|
b.Property<string>("EventType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("SubjectId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("SubjectType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<Guid?>("UserGuid")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UtcTime")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserGuid");
|
||||||
|
|
||||||
|
b.ToTable("AuditLog", "system");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Phantom.Controller.Database.Entities.EventLogEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("EventGuid")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<Guid?>("AgentGuid")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<JsonDocument>("Data")
|
||||||
|
.HasColumnType("jsonb");
|
||||||
|
|
||||||
|
b.Property<string>("EventType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("SubjectId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("SubjectType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UtcTime")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.HasKey("EventGuid");
|
||||||
|
|
||||||
|
b.ToTable("EventLog", "system");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Phantom.Controller.Database.Entities.InstanceEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("InstanceGuid")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<Guid>("AgentGuid")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("InstanceName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<Guid>("JavaRuntimeGuid")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("JvmArguments")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<bool>("LaunchAutomatically")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<ushort>("MemoryAllocation")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("MinecraftServerKind")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("MinecraftVersion")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<int>("RconPort")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<int>("ServerPort")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.HasKey("InstanceGuid");
|
||||||
|
|
||||||
|
b.ToTable("Instances", "agents");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Phantom.Controller.Database.Entities.PermissionEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Permissions", "identity");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Phantom.Controller.Database.Entities.RoleEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("RoleGuid")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("RoleGuid");
|
||||||
|
|
||||||
|
b.ToTable("Roles", "identity");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Phantom.Controller.Database.Entities.RolePermissionEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("RoleGuid")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("PermissionId")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("RoleGuid", "PermissionId");
|
||||||
|
|
||||||
|
b.HasIndex("PermissionId");
|
||||||
|
|
||||||
|
b.ToTable("RolePermissions", "identity");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Phantom.Controller.Database.Entities.UserAgentAccessEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("UserGuid")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<Guid>("AgentGuid")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.HasKey("UserGuid", "AgentGuid");
|
||||||
|
|
||||||
|
b.HasIndex("AgentGuid");
|
||||||
|
|
||||||
|
b.ToTable("UserAgentAccess", "identity");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Phantom.Controller.Database.Entities.UserEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("UserGuid")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("UserGuid");
|
||||||
|
|
||||||
|
b.HasIndex("Name")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Users", "identity");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Phantom.Controller.Database.Entities.UserPermissionEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("UserGuid")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("PermissionId")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("UserGuid", "PermissionId");
|
||||||
|
|
||||||
|
b.HasIndex("PermissionId");
|
||||||
|
|
||||||
|
b.ToTable("UserPermissions", "identity");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Phantom.Controller.Database.Entities.UserRoleEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("UserGuid")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<Guid>("RoleGuid")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.HasKey("UserGuid", "RoleGuid");
|
||||||
|
|
||||||
|
b.HasIndex("RoleGuid");
|
||||||
|
|
||||||
|
b.ToTable("UserRoles", "identity");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Phantom.Controller.Database.Entities.AuditLogEntity", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Phantom.Controller.Database.Entities.UserEntity", "User")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserGuid")
|
||||||
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Phantom.Controller.Database.Entities.RolePermissionEntity", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Phantom.Controller.Database.Entities.PermissionEntity", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("PermissionId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Phantom.Controller.Database.Entities.RoleEntity", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleGuid")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Phantom.Controller.Database.Entities.UserAgentAccessEntity", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Phantom.Controller.Database.Entities.AgentEntity", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("AgentGuid")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Phantom.Controller.Database.Entities.UserEntity", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserGuid")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Phantom.Controller.Database.Entities.UserPermissionEntity", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Phantom.Controller.Database.Entities.PermissionEntity", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("PermissionId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Phantom.Controller.Database.Entities.UserEntity", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserGuid")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Phantom.Controller.Database.Entities.UserRoleEntity", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Phantom.Controller.Database.Entities.RoleEntity", "Role")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleGuid")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Phantom.Controller.Database.Entities.UserEntity", "User")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserGuid")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Role");
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Phantom.Controller.Database.Postgres.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class UserAgentAccess : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "UserAgentAccess",
|
||||||
|
schema: "identity",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
UserGuid = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
AgentGuid = table.Column<Guid>(type: "uuid", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_UserAgentAccess", x => new { x.UserGuid, x.AgentGuid });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_UserAgentAccess_Agents_AgentGuid",
|
||||||
|
column: x => x.AgentGuid,
|
||||||
|
principalSchema: "agents",
|
||||||
|
principalTable: "Agents",
|
||||||
|
principalColumn: "AgentGuid",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_UserAgentAccess_Users_UserGuid",
|
||||||
|
column: x => x.UserGuid,
|
||||||
|
principalSchema: "identity",
|
||||||
|
principalTable: "Users",
|
||||||
|
principalColumn: "UserGuid",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_UserAgentAccess_AgentGuid",
|
||||||
|
schema: "identity",
|
||||||
|
table: "UserAgentAccess",
|
||||||
|
column: "AgentGuid");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "UserAgentAccess",
|
||||||
|
schema: "identity");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -18,7 +18,7 @@ namespace Phantom.Controller.Database.Postgres.Migrations
|
|||||||
{
|
{
|
||||||
#pragma warning disable 612, 618
|
#pragma warning disable 612, 618
|
||||||
modelBuilder
|
modelBuilder
|
||||||
.HasAnnotation("ProductVersion", "7.0.11")
|
.HasAnnotation("ProductVersion", "8.0.0")
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
@ -204,6 +204,21 @@ namespace Phantom.Controller.Database.Postgres.Migrations
|
|||||||
b.ToTable("RolePermissions", "identity");
|
b.ToTable("RolePermissions", "identity");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Phantom.Controller.Database.Entities.UserAgentAccessEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("UserGuid")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<Guid>("AgentGuid")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.HasKey("UserGuid", "AgentGuid");
|
||||||
|
|
||||||
|
b.HasIndex("AgentGuid");
|
||||||
|
|
||||||
|
b.ToTable("UserAgentAccess", "identity");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Phantom.Controller.Database.Entities.UserEntity", b =>
|
modelBuilder.Entity("Phantom.Controller.Database.Entities.UserEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("UserGuid")
|
b.Property<Guid>("UserGuid")
|
||||||
@ -281,6 +296,21 @@ namespace Phantom.Controller.Database.Postgres.Migrations
|
|||||||
.IsRequired();
|
.IsRequired();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Phantom.Controller.Database.Entities.UserAgentAccessEntity", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Phantom.Controller.Database.Entities.AgentEntity", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("AgentGuid")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Phantom.Controller.Database.Entities.UserEntity", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserGuid")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Phantom.Controller.Database.Entities.UserPermissionEntity", b =>
|
modelBuilder.Entity("Phantom.Controller.Database.Entities.UserPermissionEntity", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Phantom.Controller.Database.Entities.PermissionEntity", null)
|
b.HasOne("Phantom.Controller.Database.Entities.PermissionEntity", null)
|
||||||
|
@ -20,6 +20,7 @@ public class ApplicationDbContext : DbContext {
|
|||||||
public DbSet<UserRoleEntity> UserRoles { get; init; } = null!;
|
public DbSet<UserRoleEntity> UserRoles { get; init; } = null!;
|
||||||
public DbSet<UserPermissionEntity> UserPermissions { get; init; } = null!;
|
public DbSet<UserPermissionEntity> UserPermissions { get; init; } = null!;
|
||||||
public DbSet<RolePermissionEntity> RolePermissions { get; init; } = null!;
|
public DbSet<RolePermissionEntity> RolePermissions { get; init; } = null!;
|
||||||
|
public DbSet<UserAgentAccessEntity> UserAgentAccess { get; init; } = null!;
|
||||||
|
|
||||||
public DbSet<AgentEntity> Agents { get; init; } = null!;
|
public DbSet<AgentEntity> Agents { get; init; } = null!;
|
||||||
public DbSet<InstanceEntity> Instances { get; init; } = null!;
|
public DbSet<InstanceEntity> Instances { get; init; } = null!;
|
||||||
@ -62,6 +63,12 @@ public class ApplicationDbContext : DbContext {
|
|||||||
b.HasOne<RoleEntity>().WithMany().HasForeignKey(static e => e.RoleGuid).IsRequired().OnDelete(DeleteBehavior.Cascade);
|
b.HasOne<RoleEntity>().WithMany().HasForeignKey(static e => e.RoleGuid).IsRequired().OnDelete(DeleteBehavior.Cascade);
|
||||||
b.HasOne<PermissionEntity>().WithMany().HasForeignKey(static e => e.PermissionId).IsRequired().OnDelete(DeleteBehavior.Cascade);
|
b.HasOne<PermissionEntity>().WithMany().HasForeignKey(static e => e.PermissionId).IsRequired().OnDelete(DeleteBehavior.Cascade);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
builder.Entity<UserAgentAccessEntity>(static b => {
|
||||||
|
b.HasKey(static e => new { UserId = e.UserGuid, AgentId = e.AgentGuid });
|
||||||
|
b.HasOne<UserEntity>().WithMany().HasForeignKey(static e => e.UserGuid).IsRequired().OnDelete(DeleteBehavior.Cascade);
|
||||||
|
b.HasOne<AgentEntity>().WithMany().HasForeignKey(static e => e.AgentGuid).IsRequired().OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ConfigureConventions(ModelConfigurationBuilder builder) {
|
protected override void ConfigureConventions(ModelConfigurationBuilder builder) {
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Phantom.Controller.Database.Entities;
|
||||||
|
|
||||||
|
[Table("UserAgentAccess", Schema = "identity")]
|
||||||
|
public sealed class UserAgentAccessEntity {
|
||||||
|
public Guid UserGuid { get; init; }
|
||||||
|
public Guid AgentGuid { get; init; }
|
||||||
|
|
||||||
|
public UserAgentAccessEntity(Guid userGuid, Guid agentGuid) {
|
||||||
|
UserGuid = userGuid;
|
||||||
|
AgentGuid = agentGuid;
|
||||||
|
}
|
||||||
|
}
|
@ -17,11 +17,12 @@ public sealed class EventLogRepository {
|
|||||||
db.Ctx.EventLog.Add(new EventLogEntity(eventGuid, utcTime, agentGuid, eventType, subjectId, extra));
|
db.Ctx.EventLog.Add(new EventLogEntity(eventGuid, utcTime, agentGuid, eventType, subjectId, extra));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<ImmutableArray<EventLogItem>> GetMostRecentItems(int count, CancellationToken cancellationToken) {
|
public Task<ImmutableArray<EventLogItem>> GetMostRecentItems(ImmutableHashSet<Guid> agentGuids, int count, CancellationToken cancellationToken) {
|
||||||
return db.Ctx
|
return db.Ctx
|
||||||
.EventLog
|
.EventLog
|
||||||
.AsQueryable()
|
.AsQueryable()
|
||||||
.OrderByDescending(static entity => entity.UtcTime)
|
.OrderByDescending(static entity => entity.UtcTime)
|
||||||
|
.Where(entity => entity.AgentGuid == null || agentGuids.Contains(entity.AgentGuid.Value))
|
||||||
.Take(count)
|
.Take(count)
|
||||||
.AsAsyncEnumerable()
|
.AsAsyncEnumerable()
|
||||||
.Select(static entity => new EventLogItem(entity.UtcTime, entity.AgentGuid, entity.EventType, entity.SubjectType, entity.SubjectId, entity.Data?.RootElement.ToString()))
|
.Select(static entity => new EventLogItem(entity.UtcTime, entity.AgentGuid, entity.EventType, entity.SubjectType, entity.SubjectId, entity.Data?.RootElement.ToString()))
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using System.Collections.Immutable;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Phantom.Common.Data.Web.Users;
|
using Phantom.Common.Data.Web.Users;
|
||||||
using Phantom.Controller.Database.Entities;
|
using Phantom.Controller.Database.Entities;
|
||||||
using Phantom.Utils.Collections;
|
using Phantom.Utils.Collections;
|
||||||
@ -23,4 +24,12 @@ public sealed class PermissionRepository {
|
|||||||
|
|
||||||
return new PermissionSet(await userPermissions.Union(rolePermissions).AsAsyncEnumerable().ToImmutableSetAsync());
|
return new PermissionSet(await userPermissions.Union(rolePermissions).AsAsyncEnumerable().ToImmutableSetAsync());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task<ImmutableHashSet<Guid>> GetManagedAgentGuids(UserEntity user) {
|
||||||
|
return db.Ctx.UserAgentAccess
|
||||||
|
.Where(ua => ua.UserGuid == user.UserGuid)
|
||||||
|
.Select(static ua => ua.AgentGuid)
|
||||||
|
.AsAsyncEnumerable()
|
||||||
|
.ToImmutableSetAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,14 +10,12 @@ using Phantom.Common.Data.Replies;
|
|||||||
using Phantom.Common.Data.Web.Agent;
|
using Phantom.Common.Data.Web.Agent;
|
||||||
using Phantom.Common.Data.Web.Instance;
|
using Phantom.Common.Data.Web.Instance;
|
||||||
using Phantom.Common.Data.Web.Minecraft;
|
using Phantom.Common.Data.Web.Minecraft;
|
||||||
using Phantom.Common.Data.Web.Users;
|
|
||||||
using Phantom.Common.Messages.Agent;
|
using Phantom.Common.Messages.Agent;
|
||||||
using Phantom.Common.Messages.Agent.ToAgent;
|
using Phantom.Common.Messages.Agent.ToAgent;
|
||||||
using Phantom.Controller.Database;
|
using Phantom.Controller.Database;
|
||||||
using Phantom.Controller.Database.Entities;
|
using Phantom.Controller.Database.Entities;
|
||||||
using Phantom.Controller.Minecraft;
|
using Phantom.Controller.Minecraft;
|
||||||
using Phantom.Controller.Services.Instances;
|
using Phantom.Controller.Services.Instances;
|
||||||
using Phantom.Controller.Services.Users.Sessions;
|
|
||||||
using Phantom.Utils.Actor;
|
using Phantom.Utils.Actor;
|
||||||
using Phantom.Utils.Actor.Mailbox;
|
using Phantom.Utils.Actor.Mailbox;
|
||||||
using Phantom.Utils.Actor.Tasks;
|
using Phantom.Utils.Actor.Tasks;
|
||||||
@ -34,7 +32,7 @@ sealed class AgentActor : ReceiveActor<AgentActor.ICommand> {
|
|||||||
private static readonly TimeSpan DisconnectionRecheckInterval = TimeSpan.FromSeconds(5);
|
private static readonly TimeSpan DisconnectionRecheckInterval = TimeSpan.FromSeconds(5);
|
||||||
private static readonly TimeSpan DisconnectionThreshold = TimeSpan.FromSeconds(12);
|
private static readonly TimeSpan DisconnectionThreshold = TimeSpan.FromSeconds(12);
|
||||||
|
|
||||||
public readonly record struct Init(Guid AgentGuid, AgentConfiguration AgentConfiguration, ControllerState ControllerState, MinecraftVersions MinecraftVersions, UserLoginManager UserLoginManager, IDbContextProvider DbProvider, CancellationToken CancellationToken);
|
public readonly record struct Init(Guid AgentGuid, AgentConfiguration AgentConfiguration, ControllerState ControllerState, MinecraftVersions MinecraftVersions, IDbContextProvider DbProvider, CancellationToken CancellationToken);
|
||||||
|
|
||||||
public static Props<ICommand> Factory(Init init) {
|
public static Props<ICommand> Factory(Init init) {
|
||||||
return Props<ICommand>.Create(() => new AgentActor(init), new ActorConfiguration { SupervisorStrategy = SupervisorStrategies.Resume, MailboxType = UnboundedJumpAheadMailbox.Name });
|
return Props<ICommand>.Create(() => new AgentActor(init), new ActorConfiguration { SupervisorStrategy = SupervisorStrategies.Resume, MailboxType = UnboundedJumpAheadMailbox.Name });
|
||||||
@ -42,7 +40,6 @@ sealed class AgentActor : ReceiveActor<AgentActor.ICommand> {
|
|||||||
|
|
||||||
private readonly ControllerState controllerState;
|
private readonly ControllerState controllerState;
|
||||||
private readonly MinecraftVersions minecraftVersions;
|
private readonly MinecraftVersions minecraftVersions;
|
||||||
private readonly UserLoginManager userLoginManager;
|
|
||||||
private readonly IDbContextProvider dbProvider;
|
private readonly IDbContextProvider dbProvider;
|
||||||
private readonly CancellationToken cancellationToken;
|
private readonly CancellationToken cancellationToken;
|
||||||
|
|
||||||
@ -79,7 +76,6 @@ sealed class AgentActor : ReceiveActor<AgentActor.ICommand> {
|
|||||||
private AgentActor(Init init) {
|
private AgentActor(Init init) {
|
||||||
this.controllerState = init.ControllerState;
|
this.controllerState = init.ControllerState;
|
||||||
this.minecraftVersions = init.MinecraftVersions;
|
this.minecraftVersions = init.MinecraftVersions;
|
||||||
this.userLoginManager = init.UserLoginManager;
|
|
||||||
this.dbProvider = init.DbProvider;
|
this.dbProvider = init.DbProvider;
|
||||||
this.cancellationToken = init.CancellationToken;
|
this.cancellationToken = init.CancellationToken;
|
||||||
|
|
||||||
@ -98,11 +94,11 @@ sealed class AgentActor : ReceiveActor<AgentActor.ICommand> {
|
|||||||
Receive<NotifyIsAliveCommand>(NotifyIsAlive);
|
Receive<NotifyIsAliveCommand>(NotifyIsAlive);
|
||||||
Receive<UpdateStatsCommand>(UpdateStats);
|
Receive<UpdateStatsCommand>(UpdateStats);
|
||||||
Receive<UpdateJavaRuntimesCommand>(UpdateJavaRuntimes);
|
Receive<UpdateJavaRuntimesCommand>(UpdateJavaRuntimes);
|
||||||
ReceiveAndReplyLater<CreateOrUpdateInstanceCommand, Result<CreateOrUpdateInstanceResult, UserInstanceActionFailure>>(CreateOrUpdateInstance);
|
ReceiveAndReplyLater<CreateOrUpdateInstanceCommand, Result<CreateOrUpdateInstanceResult, InstanceActionFailure>>(CreateOrUpdateInstance);
|
||||||
Receive<UpdateInstanceStatusCommand>(UpdateInstanceStatus);
|
Receive<UpdateInstanceStatusCommand>(UpdateInstanceStatus);
|
||||||
ReceiveAndReplyLater<LaunchInstanceCommand, Result<LaunchInstanceResult, UserInstanceActionFailure>>(LaunchInstance);
|
ReceiveAndReplyLater<LaunchInstanceCommand, Result<LaunchInstanceResult, InstanceActionFailure>>(LaunchInstance);
|
||||||
ReceiveAndReplyLater<StopInstanceCommand, Result<StopInstanceResult, UserInstanceActionFailure>>(StopInstance);
|
ReceiveAndReplyLater<StopInstanceCommand, Result<StopInstanceResult, InstanceActionFailure>>(StopInstance);
|
||||||
ReceiveAndReplyLater<SendCommandToInstanceCommand, Result<SendCommandToInstanceResult, UserInstanceActionFailure>>(SendMinecraftCommand);
|
ReceiveAndReplyLater<SendCommandToInstanceCommand, Result<SendCommandToInstanceResult, InstanceActionFailure>>(SendMinecraftCommand);
|
||||||
Receive<ReceiveInstanceDataCommand>(ReceiveInstanceData);
|
Receive<ReceiveInstanceDataCommand>(ReceiveInstanceData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,21 +146,13 @@ sealed class AgentActor : ReceiveActor<AgentActor.ICommand> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Result<TReply, UserInstanceActionFailure>> RequestInstance<TCommand, TReply>(ImmutableArray<byte> authToken, Guid instanceGuid, Func<Guid, TCommand> commandFactoryFromLoggedInUserGuid) where TCommand : InstanceActor.ICommand, ICanReply<Result<TReply, InstanceActionFailure>> {
|
private async Task<Result<TReply, InstanceActionFailure>> RequestInstance<TCommand, TReply>(Guid instanceGuid, TCommand command) where TCommand : InstanceActor.ICommand, ICanReply<Result<TReply, InstanceActionFailure>> {
|
||||||
var loggedInUser = userLoginManager.GetLoggedInUser(authToken);
|
|
||||||
if (!loggedInUser.CheckPermission(Permission.ControlInstances)) {
|
|
||||||
return (UserInstanceActionFailure) UserActionFailure.NotAuthorized;
|
|
||||||
}
|
|
||||||
|
|
||||||
var command = commandFactoryFromLoggedInUserGuid(loggedInUser.Guid!.Value);
|
|
||||||
|
|
||||||
if (instanceActorByGuid.TryGetValue(instanceGuid, out var instance)) {
|
if (instanceActorByGuid.TryGetValue(instanceGuid, out var instance)) {
|
||||||
var result = await instance.Request(command, cancellationToken);
|
return await instance.Request(command, cancellationToken);
|
||||||
return result.MapError(static error => (UserInstanceActionFailure) error);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Logger.Warning("Could not deliver command {CommandType} to instance {InstanceGuid}, instance not found.", command.GetType().Name, instanceGuid);
|
Logger.Warning("Could not deliver command {CommandType} to instance {InstanceGuid}, instance not found.", command.GetType().Name, instanceGuid);
|
||||||
return (UserInstanceActionFailure) InstanceActionFailure.InstanceDoesNotExist;
|
return InstanceActionFailure.InstanceDoesNotExist;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,15 +183,15 @@ sealed class AgentActor : ReceiveActor<AgentActor.ICommand> {
|
|||||||
|
|
||||||
public sealed record UpdateJavaRuntimesCommand(ImmutableArray<TaggedJavaRuntime> JavaRuntimes) : ICommand;
|
public sealed record UpdateJavaRuntimesCommand(ImmutableArray<TaggedJavaRuntime> JavaRuntimes) : ICommand;
|
||||||
|
|
||||||
public sealed record CreateOrUpdateInstanceCommand(ImmutableArray<byte> AuthToken, Guid InstanceGuid, InstanceConfiguration Configuration) : ICommand, ICanReply<Result<CreateOrUpdateInstanceResult, UserInstanceActionFailure>>;
|
public sealed record CreateOrUpdateInstanceCommand(Guid LoggedInUserGuid, Guid InstanceGuid, InstanceConfiguration Configuration) : ICommand, ICanReply<Result<CreateOrUpdateInstanceResult, InstanceActionFailure>>;
|
||||||
|
|
||||||
public sealed record UpdateInstanceStatusCommand(Guid InstanceGuid, IInstanceStatus Status) : ICommand;
|
public sealed record UpdateInstanceStatusCommand(Guid InstanceGuid, IInstanceStatus Status) : ICommand;
|
||||||
|
|
||||||
public sealed record LaunchInstanceCommand(ImmutableArray<byte> AuthToken, Guid InstanceGuid) : ICommand, ICanReply<Result<LaunchInstanceResult, UserInstanceActionFailure>>;
|
public sealed record LaunchInstanceCommand(Guid LoggedInUserGuid, Guid InstanceGuid) : ICommand, ICanReply<Result<LaunchInstanceResult, InstanceActionFailure>>;
|
||||||
|
|
||||||
public sealed record StopInstanceCommand(ImmutableArray<byte> AuthToken, Guid InstanceGuid, MinecraftStopStrategy StopStrategy) : ICommand, ICanReply<Result<StopInstanceResult, UserInstanceActionFailure>>;
|
public sealed record StopInstanceCommand(Guid LoggedInUserGuid, Guid InstanceGuid, MinecraftStopStrategy StopStrategy) : ICommand, ICanReply<Result<StopInstanceResult, InstanceActionFailure>>;
|
||||||
|
|
||||||
public sealed record SendCommandToInstanceCommand(ImmutableArray<byte> AuthToken, Guid InstanceGuid, string Command) : ICommand, ICanReply<Result<SendCommandToInstanceResult, UserInstanceActionFailure>>;
|
public sealed record SendCommandToInstanceCommand(Guid LoggedInUserGuid, Guid InstanceGuid, string Command) : ICommand, ICanReply<Result<SendCommandToInstanceResult, InstanceActionFailure>>;
|
||||||
|
|
||||||
public sealed record ReceiveInstanceDataCommand(Instance Instance) : ICommand, IJumpAhead;
|
public sealed record ReceiveInstanceDataCommand(Instance Instance) : ICommand, IJumpAhead;
|
||||||
|
|
||||||
@ -292,30 +280,25 @@ sealed class AgentActor : ReceiveActor<AgentActor.ICommand> {
|
|||||||
controllerState.UpdateAgentJavaRuntimes(agentGuid, javaRuntimes);
|
controllerState.UpdateAgentJavaRuntimes(agentGuid, javaRuntimes);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task<Result<CreateOrUpdateInstanceResult, UserInstanceActionFailure>> CreateOrUpdateInstance(CreateOrUpdateInstanceCommand command) {
|
private Task<Result<CreateOrUpdateInstanceResult, InstanceActionFailure>> CreateOrUpdateInstance(CreateOrUpdateInstanceCommand command) {
|
||||||
var loggedInUser = userLoginManager.GetLoggedInUser(command.AuthToken);
|
|
||||||
if (!loggedInUser.CheckPermission(Permission.CreateInstances)) {
|
|
||||||
return Task.FromResult<Result<CreateOrUpdateInstanceResult, UserInstanceActionFailure>>((UserInstanceActionFailure) UserActionFailure.NotAuthorized);
|
|
||||||
}
|
|
||||||
|
|
||||||
var instanceConfiguration = command.Configuration;
|
var instanceConfiguration = command.Configuration;
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(instanceConfiguration.InstanceName)) {
|
if (string.IsNullOrWhiteSpace(instanceConfiguration.InstanceName)) {
|
||||||
return Task.FromResult<Result<CreateOrUpdateInstanceResult, UserInstanceActionFailure>>(CreateOrUpdateInstanceResult.InstanceNameMustNotBeEmpty);
|
return Task.FromResult<Result<CreateOrUpdateInstanceResult, InstanceActionFailure>>(CreateOrUpdateInstanceResult.InstanceNameMustNotBeEmpty);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (instanceConfiguration.MemoryAllocation <= RamAllocationUnits.Zero) {
|
if (instanceConfiguration.MemoryAllocation <= RamAllocationUnits.Zero) {
|
||||||
return Task.FromResult<Result<CreateOrUpdateInstanceResult, UserInstanceActionFailure>>(CreateOrUpdateInstanceResult.InstanceMemoryMustNotBeZero);
|
return Task.FromResult<Result<CreateOrUpdateInstanceResult, InstanceActionFailure>>(CreateOrUpdateInstanceResult.InstanceMemoryMustNotBeZero);
|
||||||
}
|
}
|
||||||
|
|
||||||
return minecraftVersions.GetServerExecutableInfo(instanceConfiguration.MinecraftVersion, cancellationToken)
|
return minecraftVersions.GetServerExecutableInfo(instanceConfiguration.MinecraftVersion, cancellationToken)
|
||||||
.ContinueOnActor(CreateOrUpdateInstance1, loggedInUser.Guid!.Value, command)
|
.ContinueOnActor(CreateOrUpdateInstance1, command)
|
||||||
.Unwrap();
|
.Unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task<Result<CreateOrUpdateInstanceResult, UserInstanceActionFailure>> CreateOrUpdateInstance1(FileDownloadInfo? serverExecutableInfo, Guid loggedInUserGuid, CreateOrUpdateInstanceCommand command) {
|
private Task<Result<CreateOrUpdateInstanceResult, InstanceActionFailure>> CreateOrUpdateInstance1(FileDownloadInfo? serverExecutableInfo, CreateOrUpdateInstanceCommand command) {
|
||||||
if (serverExecutableInfo == null) {
|
if (serverExecutableInfo == null) {
|
||||||
return Task.FromResult<Result<CreateOrUpdateInstanceResult, UserInstanceActionFailure>>(CreateOrUpdateInstanceResult.MinecraftVersionDownloadInfoNotFound);
|
return Task.FromResult<Result<CreateOrUpdateInstanceResult, InstanceActionFailure>>(CreateOrUpdateInstanceResult.MinecraftVersionDownloadInfoNotFound);
|
||||||
}
|
}
|
||||||
|
|
||||||
var instanceConfiguration = command.Configuration;
|
var instanceConfiguration = command.Configuration;
|
||||||
@ -325,13 +308,13 @@ sealed class AgentActor : ReceiveActor<AgentActor.ICommand> {
|
|||||||
instanceActorRef = CreateNewInstance(Instance.Offline(command.InstanceGuid, instanceConfiguration));
|
instanceActorRef = CreateNewInstance(Instance.Offline(command.InstanceGuid, instanceConfiguration));
|
||||||
}
|
}
|
||||||
|
|
||||||
var configureInstanceCommand = new InstanceActor.ConfigureInstanceCommand(loggedInUserGuid, command.InstanceGuid, instanceConfiguration, new InstanceLaunchProperties(serverExecutableInfo), isCreatingInstance);
|
var configureInstanceCommand = new InstanceActor.ConfigureInstanceCommand(command.LoggedInUserGuid, command.InstanceGuid, instanceConfiguration, new InstanceLaunchProperties(serverExecutableInfo), isCreatingInstance);
|
||||||
|
|
||||||
return instanceActorRef.Request(configureInstanceCommand, cancellationToken)
|
return instanceActorRef.Request(configureInstanceCommand, cancellationToken)
|
||||||
.ContinueOnActor(CreateOrUpdateInstance2, configureInstanceCommand);
|
.ContinueOnActor(CreateOrUpdateInstance2, configureInstanceCommand);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result<CreateOrUpdateInstanceResult, UserInstanceActionFailure> CreateOrUpdateInstance2(Result<ConfigureInstanceResult, InstanceActionFailure> result, InstanceActor.ConfigureInstanceCommand command) {
|
private Result<CreateOrUpdateInstanceResult, InstanceActionFailure> CreateOrUpdateInstance2(Result<ConfigureInstanceResult, InstanceActionFailure> result, InstanceActor.ConfigureInstanceCommand command) {
|
||||||
var instanceGuid = command.InstanceGuid;
|
var instanceGuid = command.InstanceGuid;
|
||||||
var instanceName = command.Configuration.InstanceName;
|
var instanceName = command.Configuration.InstanceName;
|
||||||
var isCreating = command.IsCreatingInstance;
|
var isCreating = command.IsCreatingInstance;
|
||||||
@ -359,16 +342,16 @@ sealed class AgentActor : ReceiveActor<AgentActor.ICommand> {
|
|||||||
TellInstance(command.InstanceGuid, new InstanceActor.SetStatusCommand(command.Status));
|
TellInstance(command.InstanceGuid, new InstanceActor.SetStatusCommand(command.Status));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task<Result<LaunchInstanceResult, UserInstanceActionFailure>> LaunchInstance(LaunchInstanceCommand command) {
|
private Task<Result<LaunchInstanceResult, InstanceActionFailure>> LaunchInstance(LaunchInstanceCommand command) {
|
||||||
return RequestInstance<InstanceActor.LaunchInstanceCommand, LaunchInstanceResult>(command.AuthToken, command.InstanceGuid, static loggedInUserGuid => new InstanceActor.LaunchInstanceCommand(loggedInUserGuid));
|
return RequestInstance<InstanceActor.LaunchInstanceCommand, LaunchInstanceResult>(command.InstanceGuid, new InstanceActor.LaunchInstanceCommand(command.LoggedInUserGuid));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task<Result<StopInstanceResult, UserInstanceActionFailure>> StopInstance(StopInstanceCommand command) {
|
private Task<Result<StopInstanceResult, InstanceActionFailure>> StopInstance(StopInstanceCommand command) {
|
||||||
return RequestInstance<InstanceActor.StopInstanceCommand, StopInstanceResult>(command.AuthToken, command.InstanceGuid, loggedInUserGuid => new InstanceActor.StopInstanceCommand(loggedInUserGuid, command.StopStrategy));
|
return RequestInstance<InstanceActor.StopInstanceCommand, StopInstanceResult>(command.InstanceGuid, new InstanceActor.StopInstanceCommand(command.LoggedInUserGuid, command.StopStrategy));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task<Result<SendCommandToInstanceResult, UserInstanceActionFailure>> SendMinecraftCommand(SendCommandToInstanceCommand command) {
|
private Task<Result<SendCommandToInstanceResult, InstanceActionFailure>> SendMinecraftCommand(SendCommandToInstanceCommand command) {
|
||||||
return RequestInstance<InstanceActor.SendCommandToInstanceCommand, SendCommandToInstanceResult>(command.AuthToken, command.InstanceGuid, loggedInUserGuid => new InstanceActor.SendCommandToInstanceCommand(loggedInUserGuid, command.Command));
|
return RequestInstance<InstanceActor.SendCommandToInstanceCommand, SendCommandToInstanceResult>(command.InstanceGuid, new InstanceActor.SendCommandToInstanceCommand(command.LoggedInUserGuid, command.Command));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ReceiveInstanceData(ReceiveInstanceDataCommand command) {
|
private void ReceiveInstanceData(ReceiveInstanceDataCommand command) {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Immutable;
|
||||||
using Akka.Actor;
|
using Akka.Actor;
|
||||||
using Phantom.Common.Data;
|
using Phantom.Common.Data;
|
||||||
using Phantom.Common.Data.Agent;
|
using Phantom.Common.Data.Agent;
|
||||||
@ -44,7 +45,7 @@ sealed class AgentManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ActorRef<AgentActor.ICommand> CreateAgentActor(Guid agentGuid, AgentConfiguration agentConfiguration) {
|
private ActorRef<AgentActor.ICommand> CreateAgentActor(Guid agentGuid, AgentConfiguration agentConfiguration) {
|
||||||
var init = new AgentActor.Init(agentGuid, agentConfiguration, controllerState, minecraftVersions, userLoginManager, dbProvider, cancellationToken);
|
var init = new AgentActor.Init(agentGuid, agentConfiguration, controllerState, minecraftVersions, dbProvider, cancellationToken);
|
||||||
var name = "Agent:" + agentGuid;
|
var name = "Agent:" + agentGuid;
|
||||||
return actorSystem.ActorOf(AgentActor.Factory(init), name);
|
return actorSystem.ActorOf(AgentActor.Factory(init), name);
|
||||||
}
|
}
|
||||||
@ -87,7 +88,18 @@ sealed class AgentManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Result<TReply, UserInstanceActionFailure>> DoInstanceAction<TCommand, TReply>(Guid agentGuid, TCommand command) where TCommand : class, AgentActor.ICommand, ICanReply<Result<TReply, UserInstanceActionFailure>> {
|
public async Task<Result<TReply, UserInstanceActionFailure>> DoInstanceAction<TCommand, TReply>(Permission requiredPermission, ImmutableArray<byte> authToken, Guid agentGuid, Func<Guid, TCommand> commandFactoryFromLoggedInUserGuid) where TCommand : class, AgentActor.ICommand, ICanReply<Result<TReply, InstanceActionFailure>> {
|
||||||
return agentsByGuid.TryGetValue(agentGuid, out var agent) ? await agent.Request(command, cancellationToken) : (UserInstanceActionFailure) InstanceActionFailure.AgentDoesNotExist;
|
var loggedInUser = userLoginManager.GetLoggedInUser(authToken);
|
||||||
|
if (!loggedInUser.HasAccessToAgent(agentGuid) || !loggedInUser.CheckPermission(requiredPermission)) {
|
||||||
|
return (UserInstanceActionFailure) UserActionFailure.NotAuthorized;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!agentsByGuid.TryGetValue(agentGuid, out var agent)) {
|
||||||
|
return (UserInstanceActionFailure) InstanceActionFailure.AgentDoesNotExist;
|
||||||
|
}
|
||||||
|
|
||||||
|
var command = commandFactoryFromLoggedInUserGuid(loggedInUser.Guid!.Value);
|
||||||
|
var result = await agent.Request(command, cancellationToken);
|
||||||
|
return result.MapError(static error => (UserInstanceActionFailure) error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,7 @@ public sealed class ControllerServices : IDisposable {
|
|||||||
this.InstanceLogManager = new InstanceLogManager();
|
this.InstanceLogManager = new InstanceLogManager();
|
||||||
|
|
||||||
this.AuditLogManager = new AuditLogManager(dbProvider);
|
this.AuditLogManager = new AuditLogManager(dbProvider);
|
||||||
this.EventLogManager = new EventLogManager(ActorSystem, dbProvider, shutdownCancellationToken);
|
this.EventLogManager = new EventLogManager(ControllerState, ActorSystem, dbProvider, shutdownCancellationToken);
|
||||||
|
|
||||||
this.AgentRegistrationHandler = new AgentRegistrationHandler(AgentManager, InstanceLogManager, EventLogManager);
|
this.AgentRegistrationHandler = new AgentRegistrationHandler(AgentManager, InstanceLogManager, EventLogManager);
|
||||||
this.WebRegistrationHandler = new WebRegistrationHandler(webAuthToken, ControllerState, InstanceLogManager, UserManager, RoleManager, UserRoleManager, UserLoginManager, AuditLogManager, AgentManager, MinecraftVersions, EventLogManager);
|
this.WebRegistrationHandler = new WebRegistrationHandler(webAuthToken, ControllerState, InstanceLogManager, UserManager, RoleManager, UserRoleManager, UserLoginManager, AuditLogManager, AgentManager, MinecraftVersions, EventLogManager);
|
||||||
|
@ -11,11 +11,13 @@ using Phantom.Utils.Actor;
|
|||||||
namespace Phantom.Controller.Services.Events;
|
namespace Phantom.Controller.Services.Events;
|
||||||
|
|
||||||
sealed partial class EventLogManager {
|
sealed partial class EventLogManager {
|
||||||
|
private readonly ControllerState controllerState;
|
||||||
private readonly ActorRef<EventLogDatabaseStorageActor.ICommand> databaseStorageActor;
|
private readonly ActorRef<EventLogDatabaseStorageActor.ICommand> databaseStorageActor;
|
||||||
private readonly IDbContextProvider dbProvider;
|
private readonly IDbContextProvider dbProvider;
|
||||||
private readonly CancellationToken cancellationToken;
|
private readonly CancellationToken cancellationToken;
|
||||||
|
|
||||||
public EventLogManager(IActorRefFactory actorSystem, IDbContextProvider dbProvider, CancellationToken cancellationToken) {
|
public EventLogManager(ControllerState controllerState, IActorRefFactory actorSystem, IDbContextProvider dbProvider, CancellationToken cancellationToken) {
|
||||||
|
this.controllerState = controllerState;
|
||||||
this.databaseStorageActor = actorSystem.ActorOf(EventLogDatabaseStorageActor.Factory(new EventLogDatabaseStorageActor.Init(dbProvider, cancellationToken)), "EventLogDatabaseStorage");
|
this.databaseStorageActor = actorSystem.ActorOf(EventLogDatabaseStorageActor.Factory(new EventLogDatabaseStorageActor.Init(dbProvider, cancellationToken)), "EventLogDatabaseStorage");
|
||||||
this.dbProvider = dbProvider;
|
this.dbProvider = dbProvider;
|
||||||
this.cancellationToken = cancellationToken;
|
this.cancellationToken = cancellationToken;
|
||||||
@ -30,7 +32,9 @@ sealed partial class EventLogManager {
|
|||||||
return UserActionFailure.NotAuthorized;
|
return UserActionFailure.NotAuthorized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var accessibleAgentGuids = loggedInUser.FilterAccessibleAgentGuids(controllerState.AgentsByGuid.Keys.ToImmutableHashSet());
|
||||||
|
|
||||||
await using var db = dbProvider.Lazy();
|
await using var db = dbProvider.Lazy();
|
||||||
return await new EventLogRepository(db).GetMostRecentItems(count, cancellationToken);
|
return await new EventLogRepository(db).GetMostRecentItems(accessibleAgentGuids, count, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,7 @@ sealed class WebMessageHandlerActor : ReceiveActor<IMessageToController> {
|
|||||||
|
|
||||||
ReceiveAsync<RegisterWebMessage>(HandleRegisterWeb);
|
ReceiveAsync<RegisterWebMessage>(HandleRegisterWeb);
|
||||||
Receive<UnregisterWebMessage>(HandleUnregisterWeb);
|
Receive<UnregisterWebMessage>(HandleUnregisterWeb);
|
||||||
ReceiveAndReplyLater<LogInMessage, LogInSuccess?>(HandleLogIn);
|
ReceiveAndReplyLater<LogInMessage, Optional<LogInSuccess>>(HandleLogIn);
|
||||||
Receive<LogOutMessage>(HandleLogOut);
|
Receive<LogOutMessage>(HandleLogOut);
|
||||||
ReceiveAndReply<GetAuthenticatedUser, Optional<AuthenticatedUserInfo>>(GetAuthenticatedUser);
|
ReceiveAndReply<GetAuthenticatedUser, Optional<AuthenticatedUserInfo>>(GetAuthenticatedUser);
|
||||||
ReceiveAndReplyLater<CreateOrUpdateAdministratorUserMessage, CreateOrUpdateAdministratorUserResult>(HandleCreateOrUpdateAdministratorUser);
|
ReceiveAndReplyLater<CreateOrUpdateAdministratorUserMessage, CreateOrUpdateAdministratorUserResult>(HandleCreateOrUpdateAdministratorUser);
|
||||||
@ -100,7 +100,7 @@ sealed class WebMessageHandlerActor : ReceiveActor<IMessageToController> {
|
|||||||
connection.Close();
|
connection.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task<LogInSuccess?> HandleLogIn(LogInMessage message) {
|
private Task<Optional<LogInSuccess>> HandleLogIn(LogInMessage message) {
|
||||||
return userLoginManager.LogIn(message.Username, message.Password);
|
return userLoginManager.LogIn(message.Username, message.Password);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,19 +141,39 @@ sealed class WebMessageHandlerActor : ReceiveActor<IMessageToController> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Task<Result<CreateOrUpdateInstanceResult, UserInstanceActionFailure>> HandleCreateOrUpdateInstance(CreateOrUpdateInstanceMessage message) {
|
private Task<Result<CreateOrUpdateInstanceResult, UserInstanceActionFailure>> HandleCreateOrUpdateInstance(CreateOrUpdateInstanceMessage message) {
|
||||||
return agentManager.DoInstanceAction<AgentActor.CreateOrUpdateInstanceCommand, CreateOrUpdateInstanceResult>(message.Configuration.AgentGuid, new AgentActor.CreateOrUpdateInstanceCommand(message.AuthToken, message.InstanceGuid, message.Configuration));
|
return agentManager.DoInstanceAction<AgentActor.CreateOrUpdateInstanceCommand, CreateOrUpdateInstanceResult>(
|
||||||
|
Permission.CreateInstances,
|
||||||
|
message.AuthToken,
|
||||||
|
message.Configuration.AgentGuid,
|
||||||
|
loggedInUserGuid => new AgentActor.CreateOrUpdateInstanceCommand(loggedInUserGuid, message.InstanceGuid, message.Configuration)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task<Result<LaunchInstanceResult, UserInstanceActionFailure>> HandleLaunchInstance(LaunchInstanceMessage message) {
|
private Task<Result<LaunchInstanceResult, UserInstanceActionFailure>> HandleLaunchInstance(LaunchInstanceMessage message) {
|
||||||
return agentManager.DoInstanceAction<AgentActor.LaunchInstanceCommand, LaunchInstanceResult>(message.AgentGuid, new AgentActor.LaunchInstanceCommand(message.AuthToken, message.InstanceGuid));
|
return agentManager.DoInstanceAction<AgentActor.LaunchInstanceCommand, LaunchInstanceResult>(
|
||||||
|
Permission.ControlInstances,
|
||||||
|
message.AuthToken,
|
||||||
|
message.AgentGuid,
|
||||||
|
loggedInUserGuid => new AgentActor.LaunchInstanceCommand(loggedInUserGuid, message.InstanceGuid)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task<Result<StopInstanceResult, UserInstanceActionFailure>> HandleStopInstance(StopInstanceMessage message) {
|
private Task<Result<StopInstanceResult, UserInstanceActionFailure>> HandleStopInstance(StopInstanceMessage message) {
|
||||||
return agentManager.DoInstanceAction<AgentActor.StopInstanceCommand, StopInstanceResult>(message.AgentGuid, new AgentActor.StopInstanceCommand(message.AuthToken, message.InstanceGuid, message.StopStrategy));
|
return agentManager.DoInstanceAction<AgentActor.StopInstanceCommand, StopInstanceResult>(
|
||||||
|
Permission.ControlInstances,
|
||||||
|
message.AuthToken,
|
||||||
|
message.AgentGuid,
|
||||||
|
loggedInUserGuid => new AgentActor.StopInstanceCommand(loggedInUserGuid, message.InstanceGuid, message.StopStrategy)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task<Result<SendCommandToInstanceResult, UserInstanceActionFailure>> HandleSendCommandToInstance(SendCommandToInstanceMessage message) {
|
private Task<Result<SendCommandToInstanceResult, UserInstanceActionFailure>> HandleSendCommandToInstance(SendCommandToInstanceMessage message) {
|
||||||
return agentManager.DoInstanceAction<AgentActor.SendCommandToInstanceCommand, SendCommandToInstanceResult>(message.AgentGuid, new AgentActor.SendCommandToInstanceCommand(message.AuthToken, message.InstanceGuid, message.Command));
|
return agentManager.DoInstanceAction<AgentActor.SendCommandToInstanceCommand, SendCommandToInstanceResult>(
|
||||||
|
Permission.ControlInstances,
|
||||||
|
message.AuthToken,
|
||||||
|
message.AgentGuid,
|
||||||
|
loggedInUserGuid => new AgentActor.SendCommandToInstanceCommand(loggedInUserGuid, message.InstanceGuid, message.Command)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task<ImmutableArray<MinecraftVersion>> HandleGetMinecraftVersions(GetMinecraftVersionsMessage message) {
|
private Task<ImmutableArray<MinecraftVersion>> HandleGetMinecraftVersions(GetMinecraftVersionsMessage message) {
|
||||||
|
@ -14,9 +14,12 @@ sealed class AuthenticatedUserCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task<AuthenticatedUserInfo?> Update(UserEntity user, ILazyDbContext db) {
|
public async Task<AuthenticatedUserInfo?> Update(UserEntity user, ILazyDbContext db) {
|
||||||
|
var permissionRepository = new PermissionRepository(db);
|
||||||
|
var userPermissions = await permissionRepository.GetAllUserPermissions(user);
|
||||||
|
var userManagedAgentGuids = await permissionRepository.GetManagedAgentGuids(user);
|
||||||
|
|
||||||
var userGuid = user.UserGuid;
|
var userGuid = user.UserGuid;
|
||||||
var userPermissions = await new PermissionRepository(db).GetAllUserPermissions(user);
|
var userInfo = new AuthenticatedUserInfo(userGuid, user.Name, userPermissions, userManagedAgentGuids);
|
||||||
var userInfo = new AuthenticatedUserInfo(userGuid, user.Name, userPermissions);
|
|
||||||
return authenticatedUsersByGuid[userGuid] = userInfo;
|
return authenticatedUsersByGuid[userGuid] = userInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Phantom.Common.Data.Web.Users;
|
using System.Collections.Immutable;
|
||||||
|
using Phantom.Common.Data.Web.Users;
|
||||||
|
|
||||||
namespace Phantom.Controller.Services.Users.Sessions;
|
namespace Phantom.Controller.Services.Users.Sessions;
|
||||||
|
|
||||||
@ -6,6 +7,14 @@ readonly record struct LoggedInUser(AuthenticatedUserInfo? AuthenticatedUserInfo
|
|||||||
public Guid? Guid => AuthenticatedUserInfo?.Guid;
|
public Guid? Guid => AuthenticatedUserInfo?.Guid;
|
||||||
|
|
||||||
public bool CheckPermission(Permission permission) {
|
public bool CheckPermission(Permission permission) {
|
||||||
return AuthenticatedUserInfo != null && AuthenticatedUserInfo.Permissions.Check(permission);
|
return AuthenticatedUserInfo is {} info && info.CheckPermission(permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasAccessToAgent(Guid agentGuid) {
|
||||||
|
return AuthenticatedUserInfo is {} info && info.HasAccessToAgent(agentGuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImmutableHashSet<Guid> FilterAccessibleAgentGuids(ImmutableHashSet<Guid> agentGuids) {
|
||||||
|
return AuthenticatedUserInfo is {} info ? info.FilterAccessibleAgentGuids(agentGuids) : ImmutableHashSet<Guid>.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
using Phantom.Common.Data;
|
||||||
using Phantom.Common.Data.Web.Users;
|
using Phantom.Common.Data.Web.Users;
|
||||||
using Phantom.Controller.Database;
|
using Phantom.Controller.Database;
|
||||||
using Phantom.Controller.Database.Repositories;
|
using Phantom.Controller.Database.Repositories;
|
||||||
@ -29,7 +30,7 @@ sealed class UserLoginManager {
|
|||||||
return sessionBuckets[token[0]];
|
return sessionBuckets[token[0]];
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<LogInSuccess?> LogIn(string username, string password) {
|
public async Task<Optional<LogInSuccess>> LogIn(string username, string password) {
|
||||||
Guid userGuid;
|
Guid userGuid;
|
||||||
AuthenticatedUserInfo? authenticatedUserInfo;
|
AuthenticatedUserInfo? authenticatedUserInfo;
|
||||||
|
|
||||||
@ -38,12 +39,12 @@ sealed class UserLoginManager {
|
|||||||
|
|
||||||
var user = await userRepository.GetByName(username);
|
var user = await userRepository.GetByName(username);
|
||||||
if (user == null || !UserPasswords.Verify(password, user.PasswordHash)) {
|
if (user == null || !UserPasswords.Verify(password, user.PasswordHash)) {
|
||||||
return null;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
authenticatedUserInfo = await authenticatedUserCache.Update(user, db);
|
authenticatedUserInfo = await authenticatedUserCache.Update(user, db);
|
||||||
if (authenticatedUserInfo == null) {
|
if (authenticatedUserInfo == null) {
|
||||||
return null;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
userGuid = user.UserGuid;
|
userGuid = user.UserGuid;
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using Phantom.Common.Data.Web.Agent;
|
using Phantom.Common.Data.Web.Agent;
|
||||||
using Phantom.Utils.Events;
|
using Phantom.Utils.Events;
|
||||||
using Phantom.Utils.Logging;
|
using Phantom.Utils.Logging;
|
||||||
|
using Phantom.Web.Services.Authentication;
|
||||||
|
|
||||||
namespace Phantom.Web.Services.Agents;
|
namespace Phantom.Web.Services.Agents;
|
||||||
|
|
||||||
@ -18,7 +19,13 @@ public sealed class AgentManager {
|
|||||||
return agents.Value;
|
return agents.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImmutableDictionary<Guid, Agent> ToDictionaryByGuid() {
|
public ImmutableDictionary<Guid, Agent> ToDictionaryByGuid(AuthenticatedUser? authenticatedUser) {
|
||||||
return agents.Value.ToImmutableDictionary(static agent => agent.AgentGuid);
|
if (authenticatedUser == null) {
|
||||||
|
return ImmutableDictionary<Guid, Agent>.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
return agents.Value
|
||||||
|
.Where(agent => authenticatedUser.Info.HasAccessToAgent(agent.AgentGuid))
|
||||||
|
.ToImmutableDictionary(static agent => agent.AgentGuid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,4 @@ using Phantom.Common.Data.Web.Users;
|
|||||||
|
|
||||||
namespace Phantom.Web.Services.Authentication;
|
namespace Phantom.Web.Services.Authentication;
|
||||||
|
|
||||||
public sealed record AuthenticatedUser(AuthenticatedUserInfo Info, ImmutableArray<byte> Token) {
|
public sealed record AuthenticatedUser(AuthenticatedUserInfo Info, ImmutableArray<byte> Token);
|
||||||
public bool CheckPermission(Permission permission) {
|
|
||||||
return Info.Permissions.Check(permission);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Phantom.Common.Data.Web.Users;
|
using Phantom.Common.Data;
|
||||||
|
using Phantom.Common.Data.Web.Users;
|
||||||
using Phantom.Common.Messages.Web.ToController;
|
using Phantom.Common.Messages.Web.ToController;
|
||||||
using Phantom.Utils.Logging;
|
using Phantom.Utils.Logging;
|
||||||
using Phantom.Web.Services.Rpc;
|
using Phantom.Web.Services.Rpc;
|
||||||
@ -22,23 +23,20 @@ public sealed class UserLoginManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> LogIn(string username, string password, string? returnUrl = null) {
|
public async Task<bool> LogIn(string username, string password, string? returnUrl = null) {
|
||||||
LogInSuccess? success;
|
Optional<LogInSuccess> result;
|
||||||
try {
|
try {
|
||||||
success = await controllerConnection.Send<LogInMessage, LogInSuccess?>(new LogInMessage(username, password), TimeSpan.FromSeconds(30));
|
result = await controllerConnection.Send<LogInMessage, Optional<LogInSuccess>>(new LogInMessage(username, password), TimeSpan.FromSeconds(30));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Logger.Error(e, "Could not log in {Username}.", username);
|
Logger.Error(e, "Could not log in {Username}.", username);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (success == null) {
|
if (result.Value is not var (userInfo, authToken)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.Information("Successfully logged in {Username}.", username);
|
Logger.Information("Successfully logged in {Username}.", username);
|
||||||
|
|
||||||
var userInfo = success.UserInfo;
|
|
||||||
var authToken = success.AuthToken;
|
|
||||||
|
|
||||||
authenticationStateProvider.SetUnloadedSession();
|
authenticationStateProvider.SetUnloadedSession();
|
||||||
await sessionBrowserStorage.Store(userInfo.Guid, authToken);
|
await sessionBrowserStorage.Store(userInfo.Guid, authToken);
|
||||||
await authenticationStateProvider.GetAuthenticationStateAsync();
|
await authenticationStateProvider.GetAuthenticationStateAsync();
|
||||||
|
@ -16,7 +16,7 @@ public sealed class EventLogManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Result<ImmutableArray<EventLogItem>, UserActionFailure>> GetMostRecentItems(AuthenticatedUser? authenticatedUser, int count, CancellationToken cancellationToken) {
|
public async Task<Result<ImmutableArray<EventLogItem>, UserActionFailure>> GetMostRecentItems(AuthenticatedUser? authenticatedUser, int count, CancellationToken cancellationToken) {
|
||||||
if (authenticatedUser != null && authenticatedUser.CheckPermission(Permission.ViewEvents)) {
|
if (authenticatedUser != null && authenticatedUser.Info.CheckPermission(Permission.ViewEvents)) {
|
||||||
var message = new GetEventLogMessage(authenticatedUser.Token, count);
|
var message = new GetEventLogMessage(authenticatedUser.Token, count);
|
||||||
return await controllerConnection.Send<GetEventLogMessage, Result<ImmutableArray<EventLogItem>, UserActionFailure>>(message, cancellationToken);
|
return await controllerConnection.Send<GetEventLogMessage, Result<ImmutableArray<EventLogItem>, UserActionFailure>>(message, cancellationToken);
|
||||||
}
|
}
|
||||||
|
@ -33,12 +33,17 @@ public sealed class InstanceManager {
|
|||||||
return instances.Value;
|
return instances.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Instance? GetByGuid(Guid instanceGuid) {
|
public Instance? GetByGuid(AuthenticatedUser? authenticatedUser, Guid instanceGuid) {
|
||||||
return instances.Value.GetValueOrDefault(instanceGuid);
|
if (authenticatedUser == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var instance = instances.Value.GetValueOrDefault(instanceGuid);
|
||||||
|
return instance != null && authenticatedUser.Info.HasAccessToAgent(instance.Configuration.AgentGuid) ? instance : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Result<CreateOrUpdateInstanceResult, UserInstanceActionFailure>> CreateOrUpdateInstance(AuthenticatedUser? authenticatedUser, Guid instanceGuid, InstanceConfiguration configuration, CancellationToken cancellationToken) {
|
public async Task<Result<CreateOrUpdateInstanceResult, UserInstanceActionFailure>> CreateOrUpdateInstance(AuthenticatedUser? authenticatedUser, Guid instanceGuid, InstanceConfiguration configuration, CancellationToken cancellationToken) {
|
||||||
if (authenticatedUser != null && authenticatedUser.CheckPermission(Permission.CreateInstances)) {
|
if (authenticatedUser != null && authenticatedUser.Info.CheckPermission(Permission.CreateInstances)) {
|
||||||
var message = new CreateOrUpdateInstanceMessage(authenticatedUser.Token, instanceGuid, configuration);
|
var message = new CreateOrUpdateInstanceMessage(authenticatedUser.Token, instanceGuid, configuration);
|
||||||
return await controllerConnection.Send<CreateOrUpdateInstanceMessage, Result<CreateOrUpdateInstanceResult, UserInstanceActionFailure>>(message, cancellationToken);
|
return await controllerConnection.Send<CreateOrUpdateInstanceMessage, Result<CreateOrUpdateInstanceResult, UserInstanceActionFailure>>(message, cancellationToken);
|
||||||
}
|
}
|
||||||
@ -48,7 +53,7 @@ public sealed class InstanceManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Result<LaunchInstanceResult, UserInstanceActionFailure>> LaunchInstance(AuthenticatedUser? authenticatedUser, Guid agentGuid, Guid instanceGuid, CancellationToken cancellationToken) {
|
public async Task<Result<LaunchInstanceResult, UserInstanceActionFailure>> LaunchInstance(AuthenticatedUser? authenticatedUser, Guid agentGuid, Guid instanceGuid, CancellationToken cancellationToken) {
|
||||||
if (authenticatedUser != null && authenticatedUser.CheckPermission(Permission.ControlInstances)) {
|
if (authenticatedUser != null && authenticatedUser.Info.CheckPermission(Permission.ControlInstances)) {
|
||||||
var message = new LaunchInstanceMessage(authenticatedUser.Token, agentGuid, instanceGuid);
|
var message = new LaunchInstanceMessage(authenticatedUser.Token, agentGuid, instanceGuid);
|
||||||
return await controllerConnection.Send<LaunchInstanceMessage, Result<LaunchInstanceResult, UserInstanceActionFailure>>(message, cancellationToken);
|
return await controllerConnection.Send<LaunchInstanceMessage, Result<LaunchInstanceResult, UserInstanceActionFailure>>(message, cancellationToken);
|
||||||
}
|
}
|
||||||
@ -58,7 +63,7 @@ public sealed class InstanceManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Result<StopInstanceResult, UserInstanceActionFailure>> StopInstance(AuthenticatedUser? authenticatedUser, Guid agentGuid, Guid instanceGuid, MinecraftStopStrategy stopStrategy, CancellationToken cancellationToken) {
|
public async Task<Result<StopInstanceResult, UserInstanceActionFailure>> StopInstance(AuthenticatedUser? authenticatedUser, Guid agentGuid, Guid instanceGuid, MinecraftStopStrategy stopStrategy, CancellationToken cancellationToken) {
|
||||||
if (authenticatedUser != null && authenticatedUser.CheckPermission(Permission.ControlInstances)) {
|
if (authenticatedUser != null && authenticatedUser.Info.CheckPermission(Permission.ControlInstances)) {
|
||||||
var message = new StopInstanceMessage(authenticatedUser.Token, agentGuid, instanceGuid, stopStrategy);
|
var message = new StopInstanceMessage(authenticatedUser.Token, agentGuid, instanceGuid, stopStrategy);
|
||||||
return await controllerConnection.Send<StopInstanceMessage, Result<StopInstanceResult, UserInstanceActionFailure>>(message, cancellationToken);
|
return await controllerConnection.Send<StopInstanceMessage, Result<StopInstanceResult, UserInstanceActionFailure>>(message, cancellationToken);
|
||||||
}
|
}
|
||||||
@ -68,7 +73,7 @@ public sealed class InstanceManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Result<SendCommandToInstanceResult, UserInstanceActionFailure>> SendCommandToInstance(AuthenticatedUser? authenticatedUser, Guid agentGuid, Guid instanceGuid, string command, CancellationToken cancellationToken) {
|
public async Task<Result<SendCommandToInstanceResult, UserInstanceActionFailure>> SendCommandToInstance(AuthenticatedUser? authenticatedUser, Guid agentGuid, Guid instanceGuid, string command, CancellationToken cancellationToken) {
|
||||||
if (authenticatedUser != null && authenticatedUser.CheckPermission(Permission.ControlInstances)) {
|
if (authenticatedUser != null && authenticatedUser.Info.CheckPermission(Permission.ControlInstances)) {
|
||||||
var message = new SendCommandToInstanceMessage(authenticatedUser.Token, agentGuid, instanceGuid, command);
|
var message = new SendCommandToInstanceMessage(authenticatedUser.Token, agentGuid, instanceGuid, command);
|
||||||
return await controllerConnection.Send<SendCommandToInstanceMessage, Result<SendCommandToInstanceResult, UserInstanceActionFailure>>(message, cancellationToken);
|
return await controllerConnection.Send<SendCommandToInstanceMessage, Result<SendCommandToInstanceResult, UserInstanceActionFailure>>(message, cancellationToken);
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ public sealed class AuditLogManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Result<ImmutableArray<AuditLogItem>, UserActionFailure>> GetMostRecentItems(AuthenticatedUser? authenticatedUser, int count, CancellationToken cancellationToken) {
|
public async Task<Result<ImmutableArray<AuditLogItem>, UserActionFailure>> GetMostRecentItems(AuthenticatedUser? authenticatedUser, int count, CancellationToken cancellationToken) {
|
||||||
if (authenticatedUser != null && authenticatedUser.CheckPermission(Permission.ViewAudit)) {
|
if (authenticatedUser != null && authenticatedUser.Info.CheckPermission(Permission.ViewAudit)) {
|
||||||
var message = new GetAuditLogMessage(authenticatedUser.Token, count);
|
var message = new GetAuditLogMessage(authenticatedUser.Token, count);
|
||||||
return await controllerConnection.Send<GetAuditLogMessage, Result<ImmutableArray<AuditLogItem>, UserActionFailure>>(message, cancellationToken);
|
return await controllerConnection.Send<GetAuditLogMessage, Result<ImmutableArray<AuditLogItem>, UserActionFailure>>(message, cancellationToken);
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ public sealed class UserManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Result<CreateUserResult, UserActionFailure>> Create(AuthenticatedUser? authenticatedUser, string username, string password, CancellationToken cancellationToken) {
|
public async Task<Result<CreateUserResult, UserActionFailure>> Create(AuthenticatedUser? authenticatedUser, string username, string password, CancellationToken cancellationToken) {
|
||||||
if (authenticatedUser != null && authenticatedUser.CheckPermission(Permission.EditUsers)) {
|
if (authenticatedUser != null && authenticatedUser.Info.CheckPermission(Permission.EditUsers)) {
|
||||||
return await controllerConnection.Send<CreateUserMessage, Result<CreateUserResult, UserActionFailure>>(new CreateUserMessage(authenticatedUser.Token, username, password), cancellationToken);
|
return await controllerConnection.Send<CreateUserMessage, Result<CreateUserResult, UserActionFailure>>(new CreateUserMessage(authenticatedUser.Token, username, password), cancellationToken);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -28,7 +28,7 @@ public sealed class UserManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Result<DeleteUserResult, UserActionFailure>> DeleteByGuid(AuthenticatedUser? authenticatedUser, Guid userGuid, CancellationToken cancellationToken) {
|
public async Task<Result<DeleteUserResult, UserActionFailure>> DeleteByGuid(AuthenticatedUser? authenticatedUser, Guid userGuid, CancellationToken cancellationToken) {
|
||||||
if (authenticatedUser != null && authenticatedUser.CheckPermission(Permission.EditUsers)) {
|
if (authenticatedUser != null && authenticatedUser.Info.CheckPermission(Permission.EditUsers)) {
|
||||||
return await controllerConnection.Send<DeleteUserMessage, Result<DeleteUserResult, UserActionFailure>>(new DeleteUserMessage(authenticatedUser.Token, userGuid), cancellationToken);
|
return await controllerConnection.Send<DeleteUserMessage, Result<DeleteUserResult, UserActionFailure>>(new DeleteUserMessage(authenticatedUser.Token, userGuid), cancellationToken);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -23,7 +23,7 @@ public sealed class UserRoleManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Result<ChangeUserRolesResult, UserActionFailure>> ChangeUserRoles(AuthenticatedUser? authenticatedUser, Guid subjectUserGuid, ImmutableHashSet<Guid> addToRoleGuids, ImmutableHashSet<Guid> removeFromRoleGuids, CancellationToken cancellationToken) {
|
public async Task<Result<ChangeUserRolesResult, UserActionFailure>> ChangeUserRoles(AuthenticatedUser? authenticatedUser, Guid subjectUserGuid, ImmutableHashSet<Guid> addToRoleGuids, ImmutableHashSet<Guid> removeFromRoleGuids, CancellationToken cancellationToken) {
|
||||||
if (authenticatedUser != null && authenticatedUser.CheckPermission(Permission.EditUsers)) {
|
if (authenticatedUser != null && authenticatedUser.Info.CheckPermission(Permission.EditUsers)) {
|
||||||
return await controllerConnection.Send<ChangeUserRolesMessage, Result<ChangeUserRolesResult, UserActionFailure>>(new ChangeUserRolesMessage(authenticatedUser.Token, subjectUserGuid, addToRoleGuids, removeFromRoleGuids), cancellationToken);
|
return await controllerConnection.Send<ChangeUserRolesMessage, Result<ChangeUserRolesResult, UserActionFailure>>(new ChangeUserRolesMessage(authenticatedUser.Token, subjectUserGuid, addToRoleGuids, removeFromRoleGuids), cancellationToken);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
@page "/agents"
|
@page "/agents"
|
||||||
|
@using System.Collections.Immutable
|
||||||
@using Phantom.Common.Data.Web.Agent
|
@using Phantom.Common.Data.Web.Agent
|
||||||
@using Phantom.Utils.Collections
|
@using Phantom.Utils.Collections
|
||||||
@using Phantom.Web.Services.Agents
|
@using Phantom.Web.Services.Agents
|
||||||
@ -46,33 +47,45 @@
|
|||||||
<Cell class="fw-semibold text-center text-success">Online</Cell>
|
<Cell class="fw-semibold text-center text-success">Online</Cell>
|
||||||
<Cell class="text-end">-</Cell>
|
<Cell class="text-end">-</Cell>
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AgentIsOffline:
|
case AgentIsOffline:
|
||||||
<Cell class="fw-semibold text-center">Offline</Cell>
|
<Cell class="fw-semibold text-center">Offline</Cell>
|
||||||
<Cell class="text-end">N/A</Cell>
|
<Cell class="text-end">N/A</Cell>
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AgentIsDisconnected status:
|
case AgentIsDisconnected status:
|
||||||
<Cell class="fw-semibold text-center">Offline</Cell>
|
<Cell class="fw-semibold text-center">Offline</Cell>
|
||||||
<Cell class="text-end">
|
<Cell class="text-end">
|
||||||
<TimeWithOffset Time="status.LastPingTime" />
|
<TimeWithOffset Time="status.LastPingTime" />
|
||||||
</Cell>
|
</Cell>
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
<Cell class="fw-semibold text-center">N/A</Cell>
|
<Cell class="fw-semibold text-center">N/A</Cell>
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
</ItemRow>
|
</ItemRow>
|
||||||
<NoItemsRow>
|
<NoItemsRow>
|
||||||
No agents registered.
|
No agents found.
|
||||||
</NoItemsRow>
|
</NoItemsRow>
|
||||||
</Table>
|
</Table>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
|
||||||
private readonly TableData<Agent, Guid> agentTable = new();
|
private TableData<Agent, Guid>? agentTable;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync() {
|
||||||
|
var authenticatedUser = await GetAuthenticatedUser();
|
||||||
|
if (authenticatedUser == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnInitialized() {
|
|
||||||
AgentManager.AgentsChanged.Subscribe(this, agents => {
|
AgentManager.AgentsChanged.Subscribe(this, agents => {
|
||||||
var sortedAgents = agents.Sort(static (a1, a2) => a1.Configuration.AgentName.CompareTo(a2.Configuration.AgentName));
|
var sortedAgents = agents.Where(agent => authenticatedUser.Info.HasAccessToAgent(agent.AgentGuid))
|
||||||
|
.OrderBy(static agent => agent.Configuration.AgentName)
|
||||||
|
.ToImmutableArray();
|
||||||
|
|
||||||
|
agentTable ??= new TableData<Agent, Guid>();
|
||||||
agentTable.UpdateFrom(sortedAgents, static agent => agent.AgentGuid, static agent => agent, static (agent, _) => agent);
|
agentTable.UpdateFrom(sortedAgents, static agent => agent.AgentGuid, static agent => agent, static (agent, _) => agent);
|
||||||
InvokeAsync(StateHasChanged);
|
InvokeAsync(StateHasChanged);
|
||||||
});
|
});
|
||||||
|
@ -7,9 +7,15 @@
|
|||||||
@using Phantom.Common.Data.Instance
|
@using Phantom.Common.Data.Instance
|
||||||
@using Phantom.Web.Services.Instances
|
@using Phantom.Web.Services.Instances
|
||||||
@using Phantom.Web.Services.Authorization
|
@using Phantom.Web.Services.Authorization
|
||||||
@inherits Phantom.Web.Components.PhantomComponent
|
@inherits PhantomComponent
|
||||||
@inject InstanceManager InstanceManager
|
@inject InstanceManager InstanceManager
|
||||||
|
|
||||||
|
@if (isLoading) {
|
||||||
|
<h1>Instance</h1>
|
||||||
|
<p>Loading...</p>
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
@if (Instance == null) {
|
@if (Instance == null) {
|
||||||
<h1>Instance Not Found</h1>
|
<h1>Instance Not Found</h1>
|
||||||
<p>Return to <a href="instances">all instances</a>.</p>
|
<p>Return to <a href="instances">all instances</a>.</p>
|
||||||
@ -54,19 +60,25 @@
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public Guid InstanceGuid { get; init; }
|
public Guid InstanceGuid { get; init; }
|
||||||
|
|
||||||
|
private Instance? Instance { get; set; }
|
||||||
|
private bool isLoading = true;
|
||||||
|
|
||||||
private string? lastError = null;
|
private string? lastError = null;
|
||||||
private bool isLaunchingInstance = false;
|
private bool isLaunchingInstance = false;
|
||||||
|
|
||||||
private Instance? Instance { get; set; }
|
protected override async Task OnInitializedAsync() {
|
||||||
|
Instance = InstanceManager.GetByGuid(await GetAuthenticatedUser(), InstanceGuid);
|
||||||
|
isLoading = false;
|
||||||
|
|
||||||
protected override void OnInitialized() {
|
if (Instance != null) {
|
||||||
InstanceManager.InstancesChanged.Subscribe(this, instances => {
|
InstanceManager.InstancesChanged.Subscribe(this, instances => {
|
||||||
var newInstance = instances.TryGetValue(InstanceGuid, out var instance) ? instance : null;
|
var newInstance = instances.TryGetValue(InstanceGuid, out var instance) ? instance : null;
|
||||||
if (newInstance != Instance) {
|
if (newInstance != Instance) {
|
||||||
Instance = newInstance;
|
Instance = newInstance;
|
||||||
InvokeAsync(StateHasChanged);
|
InvokeAsync(StateHasChanged);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task LaunchInstance() {
|
private async Task LaunchInstance() {
|
||||||
|
@ -6,14 +6,20 @@
|
|||||||
@inherits PhantomComponent
|
@inherits PhantomComponent
|
||||||
@inject InstanceManager InstanceManager
|
@inject InstanceManager InstanceManager
|
||||||
|
|
||||||
|
@if (isLoading) {
|
||||||
|
<h1>Edit Instance</h1>
|
||||||
|
<p>Loading...</p>
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
@if (Instance == null) {
|
@if (Instance == null) {
|
||||||
<h1>Instance Not Found</h1>
|
<h1>Instance Not Found</h1>
|
||||||
<p>Return to <a href="instances">all instances</a>.</p>
|
<p>Return to <a href="instances">all instances</a>.</p>
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
<h1>Edit Instance: @Instance.Configuration.InstanceName</h1>
|
<h1>Edit Instance: @Instance.Configuration.InstanceName</h1>
|
||||||
<InstanceAddOrEditForm EditedInstance="Instance" />
|
<InstanceAddOrEditForm EditedInstance="Instance" />
|
||||||
}
|
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
|
||||||
@ -21,9 +27,11 @@ else {
|
|||||||
public Guid InstanceGuid { get; init; }
|
public Guid InstanceGuid { get; init; }
|
||||||
|
|
||||||
private Instance? Instance { get; set; }
|
private Instance? Instance { get; set; }
|
||||||
|
private bool isLoading = true;
|
||||||
|
|
||||||
protected override void OnInitialized() {
|
protected override async Task OnInitializedAsync() {
|
||||||
Instance = InstanceManager.GetByGuid(InstanceGuid);
|
Instance = InstanceManager.GetByGuid(await GetAuthenticatedUser(), InstanceGuid);
|
||||||
|
isLoading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -69,9 +69,18 @@
|
|||||||
this.agentNamesByGuid = agents.ToImmutableDictionary(static agent => agent.AgentGuid, static agent => agent.Configuration.AgentName);
|
this.agentNamesByGuid = agents.ToImmutableDictionary(static agent => agent.AgentGuid, static agent => agent.Configuration.AgentName);
|
||||||
InvokeAsync(StateHasChanged);
|
InvokeAsync(StateHasChanged);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync() {
|
||||||
|
var authenticatedUser = await GetAuthenticatedUser();
|
||||||
|
if (authenticatedUser == null) {
|
||||||
|
instances = ImmutableArray<Instance>.Empty;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
InstanceManager.InstancesChanged.Subscribe(this, instances => {
|
InstanceManager.InstancesChanged.Subscribe(this, instances => {
|
||||||
this.instances = instances.Values
|
this.instances = instances.Values
|
||||||
|
.Where(instance => authenticatedUser.Info.HasAccessToAgent(instance.Configuration.AgentGuid))
|
||||||
.OrderBy(instance => agentNamesByGuid.TryGetValue(instance.Configuration.AgentGuid, out var agentName) ? agentName : string.Empty)
|
.OrderBy(instance => agentNamesByGuid.TryGetValue(instance.Configuration.AgentGuid, out var agentName) ? agentName : string.Empty)
|
||||||
.ThenBy(static instance => instance.Configuration.InstanceName)
|
.ThenBy(static instance => instance.Configuration.InstanceName)
|
||||||
.ToImmutableArray();
|
.ToImmutableArray();
|
||||||
|
@ -278,10 +278,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync() {
|
protected override async Task OnInitializedAsync() {
|
||||||
|
var authenticatedUser = await GetAuthenticatedUser();
|
||||||
var agentJavaRuntimesTask = ControllerConnection.Send<GetAgentJavaRuntimesMessage, ImmutableDictionary<Guid, ImmutableArray<TaggedJavaRuntime>>>(new GetAgentJavaRuntimesMessage(), TimeSpan.FromSeconds(30));
|
var agentJavaRuntimesTask = ControllerConnection.Send<GetAgentJavaRuntimesMessage, ImmutableDictionary<Guid, ImmutableArray<TaggedJavaRuntime>>>(new GetAgentJavaRuntimesMessage(), TimeSpan.FromSeconds(30));
|
||||||
var minecraftVersionsTask = ControllerConnection.Send<GetMinecraftVersionsMessage, ImmutableArray<MinecraftVersion>>(new GetMinecraftVersionsMessage(), TimeSpan.FromSeconds(30));
|
var minecraftVersionsTask = ControllerConnection.Send<GetMinecraftVersionsMessage, ImmutableArray<MinecraftVersion>>(new GetMinecraftVersionsMessage(), TimeSpan.FromSeconds(30));
|
||||||
|
|
||||||
allAgentsByGuid = AgentManager.ToDictionaryByGuid();
|
allAgentsByGuid = AgentManager.ToDictionaryByGuid(authenticatedUser);
|
||||||
allAgentJavaRuntimes = await agentJavaRuntimesTask;
|
allAgentJavaRuntimes = await agentJavaRuntimesTask;
|
||||||
allMinecraftVersions = await minecraftVersionsTask;
|
allMinecraftVersions = await minecraftVersionsTask;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user