1
0
mirror of https://github.com/chylex/Minecraft-Phantom-Panel.git synced 2025-04-28 19:15:45 +02:00
Minecraft-Phantom-Panel/Controller/Phantom.Controller.Services/Users/UserLoginManager.cs

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();
}
}
}