refactor: Complexity & Method length

This commit is contained in:
Timothy Schenk 2023-11-12 15:29:20 +01:00
parent 9356a729b0
commit 1a13c3fb1b
2 changed files with 71 additions and 38 deletions

View file

@ -0,0 +1,15 @@
using Microsoft.Extensions.Logging;
using Server.PacketHandlers;
namespace Server.LoggerMessages;
public static partial class LoginHandlerLoggerMessages
{
[LoggerMessage(EventId = 0, Level = LogLevel.Information,
Message = "Login data: Username {Username} & Password {Password}")]
public static partial void LoginData(this ILogger<LoginHandler> logger, string username, string password);
[LoggerMessage(EventId = 1, Level = LogLevel.Information,
Message = "Requested account for user: {Username} does not exist")]
public static partial void RequestedAccountDoesNotExist(this ILogger<LoginHandler> logger, string username);
}

View file

@ -1,5 +1,6 @@
using System.Security.Cryptography;
using System.Text;
using Server.LoggerMessages;
using Wonderking.Packets.Incoming;
using Wonderking.Packets.Outgoing;
@ -25,63 +26,79 @@ public class LoginHandler : IPacketHandler<LoginInfoPacket>
this._configuration = configuration;
}
public async Task HandleAsync(LoginInfoPacket packet, TcpSession session)
private static Task<byte[]> GetPasswordHashAsync(string password, byte[] salt, Guid userId)
{
LoginResponseReason loginResponseReason;
this._logger.LogInformation("Login data: Username {Username} & Password {Password}", packet.Username,
packet.Password);
var account = this._wonderkingContext.Accounts.FirstOrDefault(a => a.Username == packet.Username);
// https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Chea1t_Sheet.html#argon2id
// "Use Argon2id with a minimum configuration of 19 MiB of memory, an iteration count of 2, and 1 degree of parallelism."
var argon2Id = new Argon2id(Encoding.ASCII.GetBytes(packet.Password))
var argon2Id = new Argon2id(Encoding.ASCII.GetBytes(password))
{
MemorySize = 1024 * 19,
Iterations = 2,
DegreeOfParallelism = 1
DegreeOfParallelism = 1,
Salt = salt,
AssociatedData = userId.ToByteArray()
};
return argon2Id.GetBytesAsync(16);
}
private async Task<LoginResponseReason> CreateAccountOnLoginAsync(string username, string password)
{
LoginResponseReason loginResponseReason;
var transaction =
await _wonderkingContext.Database.BeginTransactionAsync().ConfigureAwait(true);
await using (transaction.ConfigureAwait(false))
{
try
{
var salt = RandomNumberGenerator.GetBytes(16);
var finalAccount =
await this._wonderkingContext.Accounts.AddAsync(new Account(username,
Array.Empty<byte>(), "",
0, salt)).ConfigureAwait(true);
await this._wonderkingContext.SaveChangesAsync().ConfigureAwait(true);
finalAccount.Entity.Password =
await LoginHandler.GetPasswordHashAsync(password, salt, finalAccount.Entity.Id)
.ConfigureAwait(true);
this._wonderkingContext.Accounts.Update(finalAccount.Entity);
loginResponseReason = LoginResponseReason.Ok;
await this._wonderkingContext.SaveChangesAsync().ConfigureAwait(true);
await transaction.CommitAsync().ConfigureAwait(true);
}
catch (Exception)
{
await transaction.RollbackAsync().ConfigureAwait(true); // Rollback the transaction on error
throw;
}
}
return loginResponseReason;
}
public async Task HandleAsync(LoginInfoPacket packet, TcpSession session)
{
LoginResponseReason loginResponseReason;
this._logger.LoginData(packet.Username, packet.Password);
var account = this._wonderkingContext.Accounts.FirstOrDefault(a => a.Username == packet.Username);
if (account == null)
{
if (this._configuration.GetSection("Testing").GetValue<bool>("CreateAccountOnLogin"))
{
var transaction =
await _wonderkingContext.Database.BeginTransactionAsync().ConfigureAwait(true);
await using (transaction.ConfigureAwait(true))
{
try
{
argon2Id.Salt = RandomNumberGenerator.GetBytes(16);
var finalAccount =
await this._wonderkingContext.Accounts.AddAsync(new Account(packet.Username,
Array.Empty<byte>(), "",
0, argon2Id.Salt)).ConfigureAwait(true);
await this._wonderkingContext.SaveChangesAsync().ConfigureAwait(true);
argon2Id.AssociatedData = finalAccount.Entity.Id.ToByteArray();
finalAccount.Entity.Password = await argon2Id.GetBytesAsync(16).ConfigureAwait(true);
this._wonderkingContext.Accounts.Update(finalAccount.Entity);
loginResponseReason = LoginResponseReason.Ok;
await this._wonderkingContext.SaveChangesAsync().ConfigureAwait(true);
await transaction.CommitAsync().ConfigureAwait(true);
}
catch (Exception)
{
await transaction.RollbackAsync().ConfigureAwait(true); // Rollback the transaction on error
throw;
}
}
loginResponseReason = await CreateAccountOnLoginAsync(packet.Username, packet.Password)
.ConfigureAwait(true);
}
else
{
this._logger.LogInformation("Requested account for user: {Username} does not exist", packet.Username);
this._logger.RequestedAccountDoesNotExist(packet.Username);
loginResponseReason = LoginResponseReason.AccountDoesNotExit;
}
}
else
{
argon2Id.Salt = account.Salt;
argon2Id.AssociatedData = account.Id.ToByteArray();
var tempPasswordBytes = await argon2Id.GetBytesAsync(16).ConfigureAwait(true);
var salt = account.Salt;
var tempPasswordBytes = await LoginHandler.GetPasswordHashAsync(packet.Password, salt, account.Id)
.ConfigureAwait(false);
loginResponseReason = tempPasswordBytes.SequenceEqual(account.Password)
? LoginResponseReason.Ok
: LoginResponseReason.WrongPassword;
@ -100,6 +117,7 @@ public class LoginHandler : IPacketHandler<LoginInfoPacket>
sess.AccountId = account.Id;
}
_logger.LogInformation("LoginResponsePacket: {@LoginResponsePacket}", loginResponsePacket);
sess?.Send(loginResponsePacket);
}
}