From f7fd665534dc321ae053b28a0bffebb84c1e0c08 Mon Sep 17 00:00:00 2001 From: Yuuki Wesp Date: Sun, 24 Nov 2024 09:09:12 +0300 Subject: [PATCH] Improve logging and error handling - Added logging for file uploads to S3, including success and failure messages. - Changed error logs to warnings for user not found and registration issues. - Enhanced password hashing service with critical logging on hash computation failures. --- .../Features/MediaStorage/Storages/S3ContentStorage.cs | 10 +++++++++- src/Argon.Api/Grains/AuthorizationGrain.cs | 10 +++++++--- src/Argon.Api/Services/IPasswordHashingService.cs | 8 ++++++-- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/Argon.Api/Features/MediaStorage/Storages/S3ContentStorage.cs b/src/Argon.Api/Features/MediaStorage/Storages/S3ContentStorage.cs index 52c97ca..551cb01 100644 --- a/src/Argon.Api/Features/MediaStorage/Storages/S3ContentStorage.cs +++ b/src/Argon.Api/Features/MediaStorage/Storages/S3ContentStorage.cs @@ -1,9 +1,11 @@ namespace Argon.Api.Features.MediaStorage.Storages; using Genbox.SimpleS3.Core.Abstracts.Clients; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -public class S3ContentStorage([FromKeyedServices("GenericS3:client")] IObjectClient s3Client, IOptions options) : IContentStorage +public class S3ContentStorage([FromKeyedServices("GenericS3:client")] IObjectClient s3Client, IOptions options, + ILogger logger) : IContentStorage { public ValueTask GetStorageStats() => new(new StorageSpace(0, 0, 0)); @@ -11,13 +13,19 @@ public ValueTask GetStorageStats() public async ValueTask UploadFile(StorageNameSpace block, AssetId assetId, Stream data) { var config = options.Value; + logger.LogInformation("Begin upload file to s3 storage, '{bucketName}' to '{path}'", + config.BucketName, $"{block.ToPath()}/{assetId.GetFilePath()}"); var result = await s3Client.PutObjectAsync(config.BucketName, $"{block.ToPath()}/{assetId.GetFilePath()}", data, request => { foreach (var (key, value) in assetId.GetTags(block)) request.Tags.Add(key, value); }); if (!result.IsSuccess) + { + logger.LogCritical("Failed upload file to s3 storage, '{bucketName}' to '{path}', errorCode: {errorCode}, errorMessage: {errorMessage}", + config.BucketName, $"{block.ToPath()}/{assetId.GetFilePath()}", result.Error?.Code, result.Error?.Message); throw new InvalidOperationException(); + } } public async ValueTask DeleteFile(StorageNameSpace block, AssetId assetId) diff --git a/src/Argon.Api/Grains/AuthorizationGrain.cs b/src/Argon.Api/Grains/AuthorizationGrain.cs index e964bea..9b7dc18 100644 --- a/src/Argon.Api/Grains/AuthorizationGrain.cs +++ b/src/Argon.Api/Grains/AuthorizationGrain.cs @@ -20,14 +20,17 @@ public async Task> Authorize(UserCredential var user = await context.Users.FirstOrDefaultAsync(u => u.Email == input.Email); if (user is null) { - logger.LogError("Not found user '{email}'", input.Email); + logger.LogWarning("Not found user '{email}'", input.Email); return AuthorizationError.BAD_CREDENTIALS; } var verified = passwordHashingService.VerifyPassword(input.Password, user); if (!verified) + { + logger.LogWarning("User '{email}' entered bad password, not matched", input.Email); return AuthorizationError.BAD_CREDENTIALS; + } if (string.IsNullOrEmpty(input.OtpCode)) { @@ -38,6 +41,7 @@ public async Task> Authorize(UserCredential // TODO check latest send otp time (evade ddos) await grainFactory.GetGrain(Guid.NewGuid()) .SendOtpCodeAsync(user.Email, otp.Code, TimeSpan.FromMinutes(15)); + logger.LogInformation("User '{email}' invoked a generate otp code", input.Email); return AuthorizationError.REQUIRED_OTP; } @@ -63,14 +67,14 @@ public async Task> Register(NewUserCredentialsInput inp var user = await context.Users.FirstOrDefaultAsync(u => u.Email == input.Email); if (user is not null) { - logger.LogError("Email already registered '{email}'", input.Email); + logger.LogWarning("Email already registered '{email}'", input.Email); return RegistrationError.EMAIL_ALREADY_REGISTERED; } user = await context.Users.FirstOrDefaultAsync(u => u.Username == input.Username); if (user is not null) { - logger.LogError("Username already registered '{username}'", input.Username); + logger.LogWarning("Username already registered '{username}'", input.Username); return RegistrationError.USERNAME_ALREADY_TAKEN; } diff --git a/src/Argon.Api/Services/IPasswordHashingService.cs b/src/Argon.Api/Services/IPasswordHashingService.cs index 1b9ac1d..70b5bee 100644 --- a/src/Argon.Api/Services/IPasswordHashingService.cs +++ b/src/Argon.Api/Services/IPasswordHashingService.cs @@ -13,7 +13,8 @@ public interface IPasswordHashingService OtpCode GenerateOtp(Guid userId); } -public class PasswordHashingService([FromKeyedServices(IPasswordHashingService.OneTimePassKey)] OtpGenerator otpGenerator) : IPasswordHashingService +public class PasswordHashingService([FromKeyedServices(IPasswordHashingService.OneTimePassKey)] OtpGenerator otpGenerator, + ILogger logger) : IPasswordHashingService { public unsafe string? HashPassword(string? password) { @@ -25,7 +26,10 @@ public class PasswordHashingService([FromKeyedServices(IPasswordHashingService.O Encoding.UTF8.GetBytes(password, source); if (!sha256.TryComputeHash(source, dest, out var written)) - throw new InvalidOperationException("SHA256 cannot create hash"); + { + logger.LogCritical($"Cannot compute sha256 hash, dropping operation.."); + return null; + } return Convert.ToBase64String(dest[..written]); }