mirror of
https://github.com/chylex/Minecraft-Phantom-Panel.git
synced 2025-04-28 19:15:45 +02:00
108 lines
3.7 KiB
C#
108 lines
3.7 KiB
C#
using System.Collections.Concurrent;
|
|
using System.Collections.Immutable;
|
|
using System.Runtime.CompilerServices;
|
|
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<Guid, UserSession> sessionsByUserGuid = new ();
|
|
|
|
private readonly UserManager userManager;
|
|
private readonly PermissionManager permissionManager;
|
|
private readonly IDbContextProvider dbProvider;
|
|
|
|
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) {
|
|
var user = await userManager.GetAuthenticated(username, password);
|
|
if (user == null) {
|
|
return null;
|
|
}
|
|
|
|
var permissions = await permissionManager.FetchPermissionsForUserId(user.UserGuid);
|
|
var userInfo = new AuthenticatedUserInfo(user.UserGuid, user.Name, permissions);
|
|
var token = ImmutableArray.Create(RandomNumberGenerator.GetBytes(SessionIdBytes));
|
|
|
|
sessionsByUserGuid.AddOrUpdate(user.UserGuid, UserSession.Create, UserSession.Add, new NewUserSession(userInfo, 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(userInfo, token);
|
|
}
|
|
|
|
public async Task LogOut(Guid userGuid, ImmutableArray<byte> token) {
|
|
while (true) {
|
|
if (!sessionsByUserGuid.TryGetValue(userGuid, out var oldSession)) {
|
|
return;
|
|
}
|
|
|
|
if (sessionsByUserGuid.TryUpdate(userGuid, oldSession.RemoveToken(token), oldSession)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
await using var db = dbProvider.Lazy();
|
|
|
|
var auditLogWriter = new AuditLogRepository(db).Writer(userGuid);
|
|
auditLogWriter.UserLoggedOut(userGuid);
|
|
|
|
await db.Ctx.SaveChangesAsync();
|
|
}
|
|
|
|
public AuthenticatedUserInfo? GetAuthenticatedUser(Guid userGuid, ImmutableArray<byte> token) {
|
|
return sessionsByUserGuid.TryGetValue(userGuid, out var session) && session.Tokens.Contains(token, TokenEqualityComparer.Instance) ? session.UserInfo : null;
|
|
}
|
|
|
|
private readonly record struct NewUserSession(AuthenticatedUserInfo UserInfo, ImmutableArray<byte> Token);
|
|
|
|
private sealed record UserSession(AuthenticatedUserInfo UserInfo, ImmutableList<ImmutableArray<byte>> Tokens) {
|
|
public static UserSession Create(Guid userGuid, NewUserSession newSession) {
|
|
return new UserSession(newSession.UserInfo, ImmutableList.Create(newSession.Token));
|
|
}
|
|
|
|
public static UserSession Add(Guid userGuid, UserSession oldSession, NewUserSession newSession) {
|
|
return new UserSession(newSession.UserInfo, oldSession.Tokens.Add(newSession.Token));
|
|
}
|
|
|
|
public UserSession RemoveToken(ImmutableArray<byte> token) {
|
|
return this with { Tokens = Tokens.Remove(token, TokenEqualityComparer.Instance) };
|
|
}
|
|
|
|
public bool Equals(UserSession? other) {
|
|
return ReferenceEquals(this, other);
|
|
}
|
|
|
|
public override int GetHashCode() {
|
|
return RuntimeHelpers.GetHashCode(this);
|
|
}
|
|
}
|
|
|
|
private sealed class TokenEqualityComparer : IEqualityComparer<ImmutableArray<byte>> {
|
|
public static TokenEqualityComparer Instance { get; } = new ();
|
|
|
|
private TokenEqualityComparer() {}
|
|
|
|
public bool Equals(ImmutableArray<byte> x, ImmutableArray<byte> y) {
|
|
return x.SequenceEqual(y);
|
|
}
|
|
|
|
public int GetHashCode(ImmutableArray<byte> obj) {
|
|
throw new NotImplementedException();
|
|
}
|
|
}
|
|
}
|