Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve logging and error handling #67

Merged
merged 1 commit into from
Nov 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,23 +1,31 @@
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<StorageOptions> options) : IContentStorage
public class S3ContentStorage([FromKeyedServices("GenericS3:client")] IObjectClient s3Client, IOptions<StorageOptions> options,
ILogger<IContentStorage> logger) : IContentStorage
{
public ValueTask<StorageSpace> GetStorageStats()
=> new(new StorageSpace(0, 0, 0));

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)
Expand Down
10 changes: 7 additions & 3 deletions src/Argon.Api/Grains/AuthorizationGrain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,17 @@ public async Task<Either<JwtToken, AuthorizationError>> 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))
{
Expand All @@ -38,6 +41,7 @@ public async Task<Either<JwtToken, AuthorizationError>> Authorize(UserCredential
// TODO check latest send otp time (evade ddos)
await grainFactory.GetGrain<IEmailManager>(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;
}

Expand All @@ -63,14 +67,14 @@ public async Task<Maybe<RegistrationError>> 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;
}

Expand Down
8 changes: 6 additions & 2 deletions src/Argon.Api/Services/IPasswordHashingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<IPasswordHashingService> logger) : IPasswordHashingService
{
public unsafe string? HashPassword(string? password)
{
Expand All @@ -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]);
}
Expand Down