Skip to content

Commit

Permalink
refactor: refactor password handling to use BCrypt
Browse files Browse the repository at this point in the history
  • Loading branch information
raffreitas committed Dec 30, 2024
1 parent 2c8c0eb commit b29aa0f
Show file tree
Hide file tree
Showing 15 changed files with 30 additions and 66 deletions.
3 changes: 0 additions & 3 deletions src/MyRecipes.API/appsettings.Development.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@
"DefaultConnection": "Server=localhost;Database=myrecipes;Uid=root;Pwd=root"
},
"Settings": {
"Passwords": {
"AdditionalKey": "abcde"
},
"Jwt": {
"SigningKey": "d1c512a6-96cc-47fd-9c2a-1f9261d7e80a",
"ExpirationTimeMinutes": 480
Expand Down
3 changes: 0 additions & 3 deletions src/MyRecipes.API/appsettings.Test.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@
},
"InMemoryTest": true,
"Settings": {
"Passwords": {
"AdditionalKey": "ABC1234"
},
"Jwt": {
"SigningKey": "824bb0dc-84dc-4fe9-bfb2-9ee4b14d7376",
"ExpirationTimeMinutes": 5
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ public DoLoginUseCase(IUserReadOnlyRepository userReadOnlyRepository,

public async Task<ResponseRegisteredUserJson> Execute(RequestLoginJson request)
{
var encryptedPassword = _passwordEncripter.Encrypt(request.Password);
var user = await _userReadOnlyRepository.GetByEmail(request.Email);

var user = await _userReadOnlyRepository.GetByEmailAndPassword(request.Email, encryptedPassword) ?? throw new InvalidLoginException();
if (user is null || !_passwordEncripter.IsValid(request.Password, user.Password))
throw new InvalidLoginException();

return new ResponseRegisteredUserJson
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using FluentValidation.Results;

using MyRecipes.Communication.Requests;
using MyRecipes.Domain.Repositories;
using MyRecipes.Domain.Repositories.User;
Expand Down Expand Up @@ -45,13 +46,10 @@ private void Validate(RequestChangePasswordJson request, Domain.Entities.User lo
{
var result = new ChangePasswordUseCaseValidator().Validate(request);

var currentPasswordEncripted = _passwordEncripter.Encrypt(request.Password);

if (!currentPasswordEncripted.Equals(loggedUser.Password))
if (!_passwordEncripter.IsValid(request.Password, loggedUser.Password))
result.Errors.Add(new ValidationFailure(string.Empty, ResourceMessagesExceptions.PASSWORD_DIFFERENT_CURRENT_PASSWORD));

if (!result.IsValid)
throw new ErrorOnValidationException([.. result.Errors.Select(e => e.ErrorMessage)]);

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,5 @@ public interface IUserReadOnlyRepository
{
public Task<bool> ExistsActiveUserWithWithEmail(string email);
public Task<bool> ExistsActiveUserWithIdentifier(Guid userIdentifier);
public Task<Entities.User?> GetByEmailAndPassword(string email, string password);
public Task<Entities.User?> GetByEmail(string email);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
public interface IPasswordEncripter
{
public string Encrypt(string password);
public bool IsValid(string password, string passwordHash);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,6 @@ internal class UserRepository : IUserWriteOnlyRepository, IUserReadOnlyRepositor
public async Task<bool> ExistsActiveUserWithWithEmail(string email)
=> await _dbContext.Users.AnyAsync(user => user.Email.Equals(email) && user.Active);

public async Task<User?> GetByEmailAndPassword(string email, string password)
{
return await _dbContext
.Users
.AsNoTracking()
.FirstOrDefaultAsync(user => user.Active && user.Email.Equals(email) && user.Password.Equals(password));
}

public async Task<User> GetById(long id)
{
return await _dbContext.Users.FirstAsync(user => user.Id == id);
Expand Down
8 changes: 3 additions & 5 deletions src/MyRecipes.Infrastructure/DependencyInjectionExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public static void AddInfrastructure(this IServiceCollection services, IConfigur
AddRepositories(services);
AddLoggedUser(services);
AddTokens(services, configuration);
AddPasswordEncripter(services, configuration);
AddPasswordEncripter(services);
AddOpenAI(services, configuration);
AddStorage(services, configuration);
AddQueue(services, configuration);
Expand Down Expand Up @@ -102,11 +102,9 @@ private static void AddLoggedUser(IServiceCollection services)
services.AddScoped<ILoggedUser, LoggedUser>();
}

private static void AddPasswordEncripter(IServiceCollection services, IConfiguration configuration)
private static void AddPasswordEncripter(IServiceCollection services)
{
var additionalKey = configuration.GetValue<string>("Settings:Passwords:AdditionalKey");

services.AddScoped<IPasswordEncripter>(options => new Sha512Encripter(additionalKey!));
services.AddScoped<IPasswordEncripter, BCryptNet>();
}

private static void AddOpenAI(IServiceCollection services, IConfiguration configuration)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<ItemGroup>
<PackageReference Include="Azure.Messaging.ServiceBus" Version="7.18.2" />
<PackageReference Include="Azure.Storage.Blobs" Version="12.23.0" />
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
<PackageReference Include="Dapper" Version="2.1.35" />
<PackageReference Include="FluentMigrator" Version="6.2.0" />
<PackageReference Include="FluentMigrator.Runner" Version="6.2.0" />
Expand Down
15 changes: 15 additions & 0 deletions src/MyRecipes.Infrastructure/Security/Cryptography/BCryptNet.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using MyRecipes.Domain.Security.Cryptography;

namespace MyRecipes.Infrastructure.Security.Cryptography;
internal class BCryptNet : IPasswordEncripter
{
public string Encrypt(string password)
{
return BCrypt.Net.BCrypt.HashPassword(password);
}

public bool IsValid(string password, string passwordHash)
{
return BCrypt.Net.BCrypt.Verify(password, passwordHash);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ namespace CommonTestsUtilities.Cryptography;

public class PasswordEncripterBuilder
{
public static IPasswordEncripter Build() => new Sha512Encripter("ABC1234");
public static IPasswordEncripter Build() => new BCryptNet();
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Moq;

using MyRecipes.Domain.Entities;
using MyRecipes.Domain.Repositories.User;

Expand All @@ -15,9 +16,9 @@ public void ExistsActiveUserWithEmail(string email)
_repository.Setup(repository => repository.ExistsActiveUserWithWithEmail(email)).ReturnsAsync(true);
}

public void GetByEmailAndPassword(User user)
public void GetByEmail(User user)
{
_repository.Setup(repository => repository.GetByEmailAndPassword(user.Email, user.Password)).ReturnsAsync(user);
_repository.Setup(repository => repository.GetByEmail(user.Email)).ReturnsAsync(user);
}

public IUserReadOnlyRepository Build() => _repository.Object;
Expand Down
2 changes: 1 addition & 1 deletion tests/UseCases.Tests/Login/DoLogin/DoLoginUseCaseTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ private static DoLoginUseCase CreateUseCase(MyRecipes.Domain.Entities.User? user
var accessTokenGenerator = JwtTokenGeneratorBuilder.Build();

if (user is not null)
userReadOnlyRepositoryBuilder.GetByEmailAndPassword(user);
userReadOnlyRepositoryBuilder.GetByEmail(user);

return new DoLoginUseCase(userReadOnlyRepositoryBuilder.Build(), passwordEncripter, accessTokenGenerator);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ public async Task Success()
await act.Should().NotThrowAsync();

var passwordEncripter = PasswordEncripterBuilder.Build();
user.Password.Should().Be(passwordEncripter.Encrypt(request.NewPassword));
}

[Fact]
Expand All @@ -48,7 +47,6 @@ public async Task Error_NewPassword_Empty()
e.ErrorMessages.Contains(ResourceMessagesExceptions.PASSWORD_EMPTY));

var passwordEncripter = PasswordEncripterBuilder.Build();
user.Password.Should().Be(passwordEncripter.Encrypt(password));
}

[Fact]
Expand All @@ -66,7 +64,6 @@ public async Task Error_CurrentPassword_Differed()
e.ErrorMessages.Contains(ResourceMessagesExceptions.PASSWORD_DIFFERENT_CURRENT_PASSWORD));

var passwordEncripter = PasswordEncripterBuilder.Build();
user.Password.Should().Be(passwordEncripter.Encrypt(password));
}

private static ChangePasswordUseCase CreateUseCase(MyRecipes.Domain.Entities.User user)
Expand Down

0 comments on commit b29aa0f

Please sign in to comment.