1
0
mirror of https://github.com/chylex/Minecraft-Phantom-Panel.git synced 2025-04-22 02:15:41 +02:00

Fix missing audit logs for user login/logout

This commit is contained in:
chylex 2024-03-30 17:18:27 +01:00
parent c7b57fac97
commit d591318340
Signed by: chylex
GPG Key ID: 4DE42C8F19A80548
7 changed files with 78 additions and 28 deletions
Common/Phantom.Common.Messages.Web
Controller/Phantom.Controller.Services
Web/Phantom.Web.Services/Authentication

View File

@ -0,0 +1,10 @@
using System.Collections.Immutable;
using MemoryPack;
namespace Phantom.Common.Messages.Web.ToController;
[MemoryPackable(GenerateType.VersionTolerant)]
public sealed partial record LogOutMessage(
[property: MemoryPackOrder(0)] Guid UserGuid,
[property: MemoryPackOrder(1)] ImmutableArray<byte> SessionToken
) : IMessageToController;

View File

@ -24,21 +24,22 @@ public static class WebMessageRegistries {
ToController.Add<RegisterWebMessage>(0);
ToController.Add<UnregisterWebMessage>(1);
ToController.Add<LogInMessage, LogInSuccess?>(2);
ToController.Add<CreateOrUpdateAdministratorUserMessage, CreateOrUpdateAdministratorUserResult>(3);
ToController.Add<CreateUserMessage, CreateUserResult>(4);
ToController.Add<DeleteUserMessage, DeleteUserResult>(5);
ToController.Add<GetUsersMessage, ImmutableArray<UserInfo>>(6);
ToController.Add<GetRolesMessage, ImmutableArray<RoleInfo>>(7);
ToController.Add<GetUserRolesMessage, ImmutableDictionary<Guid, ImmutableArray<Guid>>>(8);
ToController.Add<ChangeUserRolesMessage, ChangeUserRolesResult>(9);
ToController.Add<CreateOrUpdateInstanceMessage, InstanceActionResult<CreateOrUpdateInstanceResult>>(10);
ToController.Add<LaunchInstanceMessage, InstanceActionResult<LaunchInstanceResult>>(11);
ToController.Add<StopInstanceMessage, InstanceActionResult<StopInstanceResult>>(12);
ToController.Add<SendCommandToInstanceMessage, InstanceActionResult<SendCommandToInstanceResult>>(13);
ToController.Add<GetMinecraftVersionsMessage, ImmutableArray<MinecraftVersion>>(14);
ToController.Add<GetAgentJavaRuntimesMessage, ImmutableDictionary<Guid, ImmutableArray<TaggedJavaRuntime>>>(15);
ToController.Add<GetAuditLogMessage, ImmutableArray<AuditLogItem>>(16);
ToController.Add<GetEventLogMessage, ImmutableArray<EventLogItem>>(17);
ToController.Add<LogOutMessage>(3);
ToController.Add<CreateOrUpdateAdministratorUserMessage, CreateOrUpdateAdministratorUserResult>(4);
ToController.Add<CreateUserMessage, CreateUserResult>(5);
ToController.Add<DeleteUserMessage, DeleteUserResult>(6);
ToController.Add<GetUsersMessage, ImmutableArray<UserInfo>>(7);
ToController.Add<GetRolesMessage, ImmutableArray<RoleInfo>>(8);
ToController.Add<GetUserRolesMessage, ImmutableDictionary<Guid, ImmutableArray<Guid>>>(9);
ToController.Add<ChangeUserRolesMessage, ChangeUserRolesResult>(10);
ToController.Add<CreateOrUpdateInstanceMessage, InstanceActionResult<CreateOrUpdateInstanceResult>>(11);
ToController.Add<LaunchInstanceMessage, InstanceActionResult<LaunchInstanceResult>>(12);
ToController.Add<StopInstanceMessage, InstanceActionResult<StopInstanceResult>>(13);
ToController.Add<SendCommandToInstanceMessage, InstanceActionResult<SendCommandToInstanceResult>>(14);
ToController.Add<GetMinecraftVersionsMessage, ImmutableArray<MinecraftVersion>>(15);
ToController.Add<GetAgentJavaRuntimesMessage, ImmutableDictionary<Guid, ImmutableArray<TaggedJavaRuntime>>>(16);
ToController.Add<GetAuditLogMessage, ImmutableArray<AuditLogItem>>(17);
ToController.Add<GetEventLogMessage, ImmutableArray<EventLogItem>>(18);
ToController.Add<ReplyMessage>(127);
ToWeb.Add<RegisterWebResultMessage>(0);

View File

@ -59,7 +59,7 @@ public sealed class ControllerServices : IDisposable {
this.PermissionManager = new PermissionManager(dbProvider);
this.UserRoleManager = new UserRoleManager(dbProvider);
this.UserLoginManager = new UserLoginManager(UserManager, PermissionManager);
this.UserLoginManager = new UserLoginManager(UserManager, PermissionManager, dbProvider);
this.AuditLogManager = new AuditLogManager(dbProvider);
this.EventLogManager = new EventLogManager(ActorSystem, dbProvider, shutdownCancellationToken);

View File

@ -69,6 +69,8 @@ sealed class WebMessageHandlerActor : ReceiveActor<IMessageToController> {
ReceiveAsync<RegisterWebMessage>(HandleRegisterWeb);
Receive<UnregisterWebMessage>(HandleUnregisterWeb);
ReceiveAndReplyLater<LogInMessage, LogInSuccess?>(HandleLogIn);
Receive<LogOutMessage>(HandleLogOut);
ReceiveAndReplyLater<CreateOrUpdateAdministratorUserMessage, CreateOrUpdateAdministratorUserResult>(HandleCreateOrUpdateAdministratorUser);
ReceiveAndReplyLater<CreateUserMessage, CreateUserResult>(HandleCreateUser);
ReceiveAndReplyLater<GetUsersMessage, ImmutableArray<UserInfo>>(HandleGetUsers);
@ -84,7 +86,6 @@ sealed class WebMessageHandlerActor : ReceiveActor<IMessageToController> {
ReceiveAndReply<GetAgentJavaRuntimesMessage, ImmutableDictionary<Guid, ImmutableArray<TaggedJavaRuntime>>>(HandleGetAgentJavaRuntimes);
ReceiveAndReplyLater<GetAuditLogMessage, ImmutableArray<AuditLogItem>>(HandleGetAuditLog);
ReceiveAndReplyLater<GetEventLogMessage, ImmutableArray<EventLogItem>>(HandleGetEventLog);
ReceiveAndReplyLater<LogInMessage, LogInSuccess?>(HandleLogIn);
Receive<ReplyMessage>(HandleReply);
}
@ -96,6 +97,14 @@ sealed class WebMessageHandlerActor : ReceiveActor<IMessageToController> {
connection.Close();
}
private Task<LogInSuccess?> HandleLogIn(LogInMessage message) {
return userLoginManager.LogIn(message.Username, message.Password);
}
private void HandleLogOut(LogOutMessage message) {
_ = userLoginManager.LogOut(message.UserGuid, message.SessionToken);
}
private Task<CreateOrUpdateAdministratorUserResult> HandleCreateOrUpdateAdministratorUser(CreateOrUpdateAdministratorUserMessage message) {
return userManager.CreateOrUpdateAdministrator(message.Username, message.Password);
}
@ -156,10 +165,6 @@ sealed class WebMessageHandlerActor : ReceiveActor<IMessageToController> {
return eventLogManager.GetMostRecentItems(message.Count);
}
private Task<LogInSuccess?> HandleLogIn(LogInMessage message) {
return userLoginManager.LogIn(message.Username, message.Password);
}
private void HandleReply(ReplyMessage message) {
connection.Receive(message);
}

View File

@ -2,19 +2,23 @@
using System.Collections.Immutable;
using System.Security.Cryptography;
using Phantom.Common.Data.Web.Users;
using Phantom.Controller.Database;
using Phantom.Controller.Database.Repositories;
namespace Phantom.Controller.Services.Users;
sealed class UserLoginManager {
private const int SessionIdBytes = 20;
private readonly ConcurrentDictionary<string, List<ImmutableArray<byte>>> sessionTokensByUsername = new ();
private readonly ConcurrentDictionary<Guid, List<ImmutableArray<byte>>> sessionTokensByUserGuid = new ();
private readonly UserManager userManager;
private readonly PermissionManager permissionManager;
private readonly IDbContextProvider dbProvider;
public UserLoginManager(UserManager userManager, PermissionManager permissionManager) {
public UserLoginManager(UserManager userManager, PermissionManager permissionManager, IDbContextProvider dbProvider) {
this.userManager = userManager;
this.permissionManager = permissionManager;
this.dbProvider = dbProvider;
}
public async Task<LogInSuccess?> LogIn(string username, string password) {
@ -24,11 +28,37 @@ sealed class UserLoginManager {
}
var token = ImmutableArray.Create(RandomNumberGenerator.GetBytes(SessionIdBytes));
var sessionTokens = sessionTokensByUsername.GetOrAdd(username, static _ => new List<ImmutableArray<byte>>());
var sessionTokens = sessionTokensByUserGuid.GetOrAdd(user.UserGuid, static _ => new List<ImmutableArray<byte>>());
lock (sessionTokens) {
sessionTokens.Add(token);
}
await using (var db = dbProvider.Lazy()) {
var auditLogWriter = new AuditLogRepository(db).Writer(user.UserGuid);
auditLogWriter.UserLoggedIn(user);
await db.Ctx.SaveChangesAsync();
}
return new LogInSuccess(user.UserGuid, await permissionManager.FetchPermissionsForUserId(user.UserGuid), token);
}
public async Task LogOut(Guid userGuid, ImmutableArray<byte> sessionToken) {
if (!sessionTokensByUserGuid.TryGetValue(userGuid, out var sessionTokens)) {
return;
}
lock (sessionTokens) {
if (sessionTokens.RemoveAll(token => token.SequenceEqual(sessionToken)) == 0) {
return;
}
}
await using var db = dbProvider.Lazy();
var auditLogWriter = new AuditLogRepository(db).Writer(userGuid);
auditLogWriter.UserLoggedOut(userGuid);
await db.Ctx.SaveChangesAsync();
}
}

View File

@ -53,8 +53,8 @@ public sealed class UserLoginManager {
public async Task LogOut() {
var stored = await sessionBrowserStorage.Delete();
if (stored != null) {
sessionManager.Remove(stored.UserGuid, stored.Token);
if (stored != null && sessionManager.Remove(stored.UserGuid, stored.Token)) {
await controllerConnection.Send(new LogOutMessage(stored.UserGuid, stored.Token));
}
await navigation.NavigateTo(string.Empty);

View File

@ -23,9 +23,13 @@ public sealed class UserSessionManager {
return userSessions.TryGetValue(userGuid, out var sessions) && sessions.HasToken(token) ? sessions.UserInfo : null;
}
internal void Remove(Guid userGuid, ImmutableArray<byte> token) {
internal bool Remove(Guid userGuid, ImmutableArray<byte> token) {
if (userSessions.TryGetValue(userGuid, out var sessions)) {
sessions.RemoveToken(token);
return true;
}
else {
return false;
}
}
}