Skip to content

Commit

Permalink
Finish mediator configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
vanderlan committed Oct 31, 2023
1 parent a3ecc1f commit 82fded5
Show file tree
Hide file tree
Showing 17 changed files with 92 additions and 88 deletions.
2 changes: 1 addition & 1 deletion src/Orion.Api/Bootstrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,6 @@ public static void ConfigureServices(this IServiceCollection services, IConfigur
services.AddHttpContextAccessor();
services.AddScoped<ICurrentUser, CurrentUser>();

services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(ApplicationAssembly).Assembly));
services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(IApplicationAssembly).Assembly));
}
}
3 changes: 2 additions & 1 deletion src/Orion.Api/Configuration/AuthenticationConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Security.Claims;
using System.Text;
using Orion.Application.Core.Commands.LoginWithCredentials;
using Orion.Domain.Core.Extensions;

namespace Orion.Api.Configuration;

Expand Down Expand Up @@ -44,7 +45,7 @@ public static (string Token, DateTime ValidTo)CreateToken(LoginWithCredentialsRe
new Claim(ClaimTypes.Email, loginWithCredentialsResponse.Email),
new Claim(ClaimTypes.GivenName, loginWithCredentialsResponse.Name),
new Claim(ClaimTypes.Sid, loginWithCredentialsResponse.PublicId),
new Claim(ClaimTypes.Role, loginWithCredentialsResponse.ProfileDescription),
new Claim(ClaimTypes.Role, loginWithCredentialsResponse.Profile.Description()),
};

var signinKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtOptions.SymmetricSecurityKey));
Expand Down
2 changes: 1 addition & 1 deletion src/Orion.Application.Core/ApplicationAssembly.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
namespace Orion.Application.Core;

public sealed class ApplicationAssembly { }
public interface IApplicationAssembly { }
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using MediatR;
using Orion.Domain.Core.Entities;
using Orion.Domain.Core.Extensions;
using Orion.Domain.Core.Services.Interfaces;

namespace Orion.Application.Core.Commands.LoginWithCredentials;
Expand All @@ -16,14 +14,7 @@ public LoginWithCredentialsRequestHandler(IUserService userService)

public async Task<LoginWithCredentialsResponse> Handle(LoginWithCredentialsRequest request, CancellationToken cancellationToken)
{
var user = await _userService.LoginAsync(request.Email, request.Password);

var refreshToken = await _userService.AddRefreshTokenAsync(
new RefreshToken
{
Email = user.Email,
Refreshtoken = Guid.NewGuid().ToString().ToSha512()
});
var (user, refreshToken) = await _userService.SignInWithCredentialsAsync(request.Email, request.Password);

return new LoginWithCredentialsResponse
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Orion.Domain.Core.Authentication;
using Orion.Domain.Core.Entities.Enuns;
using Orion.Domain.Core.Entities.Enuns;

namespace Orion.Application.Core.Commands.LoginWithCredentials;

Expand All @@ -9,6 +8,5 @@ public class LoginWithCredentialsResponse
public string Name { get; set; }
public string Email { get; set; }
public UserProfile Profile { get; set; }
public string ProfileDescription => Profile == UserProfile.Admin ? AuthorizationConfiguration.Roles.Admin : AuthorizationConfiguration.Roles.Customer;
public string RefreshToken { get; set; }
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using MediatR;
using Orion.Domain.Core.Entities;
using Orion.Domain.Core.Extensions;
using Orion.Domain.Core.Services.Interfaces;

namespace Orion.Application.Core.Commands.LoginWithRefreshToken;
Expand All @@ -16,14 +14,7 @@ public LoginWithRefreshTokenRequestHandler(IUserService userService)

public async Task<LoginWithRefreshTokenResponse> Handle(LoginWithRefreshTokenRequest request, CancellationToken cancellationToken)
{
var user = await _userService.SignInWithRehreshTokenAsync(request.RefreshToken, request.Token);

var refreshToken = await _userService.AddRefreshTokenAsync(
new RefreshToken
{
Email = user.Email,
Refreshtoken = Guid.NewGuid().ToString().ToSha512()
});
var (user, refreshToken) = await _userService.SignInWithRefreshTokenAsync(request.RefreshToken, request.Token);

return new LoginWithRefreshTokenResponse
{
Expand Down
11 changes: 7 additions & 4 deletions src/Orion.Croscutting.Resources/Messages/MessagesKeys.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
namespace Orion.Croscutting.Resources.Messages;

/// <summary>
/// Update all Resource Files to mantain the globalization support
/// Update all resource files to maintain globalization support
/// </summary>
public static class MessagesKeys
{
Expand All @@ -13,13 +13,16 @@ public static class ExceptionsTitles

public static class UserMessages
{
//Auth
public const string InvalidCredentials = "User.InvalidCredentials";
public const string InvalidRefreshToken = "User.InvalidRefreshToken";

//User
public const string NullEntity = "User.NullEntity";
public const string EmptyName = "User.EmptyName";
public const string EmptyPasword = "User.EmptyPassword";
public const string EmptyEmail = "User.EmptyEmail";
public const string EmptyId = "User.EmptyId"; //todo: add to resource
public const string EmptyId = "User.EmptyId";
public const string EmailExists = "User.EmailExists";
public const string InvalidCredentials = "User.InvalidCredentials";
public const string InvalidRefreshToken = "User.InvalidRefreshToken";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -144,4 +144,7 @@
<data name="User.EmptyEmail" xml:space="preserve">
<value>Is necessary to inform the E-mail</value>
</data>
<data name="User.EmptyId" xml:space="preserve">
<value>Is necessary to inform the Id</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -144,4 +144,7 @@
<data name="User.EmptyEmail" xml:space="preserve">
<value>É necessário informar o E-mail</value>
</data>
<data name="User.EmptyId" xml:space="preserve">
<value>É necessário informar o Id</value>
</data>
</root>
9 changes: 4 additions & 5 deletions src/Orion.Domain.Core/Authentication/CurrentUser.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using Microsoft.AspNetCore.Http;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;

Expand All @@ -17,10 +16,10 @@ public CurrentUser(IHttpContextAccessor accessor)
_claims = _accessor.HttpContext.User.Claims.ToList();
}

public string Name => IsAuthenticated() ? _claims.FirstOrDefault(x => x.Type == ClaimTypes.GivenName)?.Value : string.Empty;
public string Id => IsAuthenticated() ? _claims.FirstOrDefault(x => x.Type == ClaimTypes.Sid)?.Value: string.Empty;
public string Email => IsAuthenticated() ? _claims.FirstOrDefault(x => x.Type == ClaimTypes.Email)?.Value: string.Empty;
public string Profile => IsAuthenticated() ? _claims.FirstOrDefault(x => x.Type == ClaimTypes.Role)?.Value: string.Empty;
public string Name => IsAuthenticated() ? _claims.Find(x => x.Type == ClaimTypes.GivenName)?.Value : string.Empty;
public string Id => IsAuthenticated() ? _claims.Find(x => x.Type == ClaimTypes.Sid)?.Value: string.Empty;
public string Email => IsAuthenticated() ? _claims.Find(x => x.Type == ClaimTypes.Email)?.Value: string.Empty;
public string Profile => IsAuthenticated() ? _claims.Find(x => x.Type == ClaimTypes.Role)?.Value: string.Empty;

public bool IsAuthenticated()
{
Expand Down
3 changes: 3 additions & 0 deletions src/Orion.Domain.Core/Authentication/ICurrentUser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@
public interface ICurrentUser
{
string Name { get; }
public string Id { get; }
public string Email { get; }
public string Profile { get; }
bool IsAuthenticated();
}
5 changes: 3 additions & 2 deletions src/Orion.Domain.Core/Entities/Enuns/UserProfile.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
using System.ComponentModel;
using static Orion.Domain.Core.Authentication.AuthorizationConfiguration;

namespace Orion.Domain.Core.Entities.Enuns;

public enum UserProfile
{
[Description("Admin")]
[Description(Roles.Admin)]
Admin = 1,

[Description("Customer")]
[Description(Roles.Customer)]
Customer = 2
}
20 changes: 20 additions & 0 deletions src/Orion.Domain.Core/Extensions/EnumExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System.ComponentModel;
using System.Linq;

namespace Orion.Domain.Core.Extensions
{
public static class EnumExtensions
{
public static string Description<T>(this T source)
{
var fieldInfo = source.GetType().GetField(source.ToString());

var attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);

if (attributes.Any())
return attributes.First().Description;

return string.Empty;
}
}
}
5 changes: 2 additions & 3 deletions src/Orion.Domain.Core/Services/Interfaces/IUserService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ namespace Orion.Domain.Core.Services.Interfaces;

public interface IUserService : IBaseService<User>
{
Task<User> LoginAsync(string email, string password);
Task<RefreshToken> AddRefreshTokenAsync(RefreshToken refreshToken);
Task<User> SignInWithRehreshTokenAsync(string refreshToken, string expiredToken);
Task<(User User, RefreshToken RefreshToken)> SignInWithCredentialsAsync(string email, string password);
Task<(User User, RefreshToken RefreshToken)> SignInWithRefreshTokenAsync(string refreshToken, string expiredToken);
Task<PagedList<User>> ListPaginateAsync(UserFilter filter);
}
26 changes: 19 additions & 7 deletions src/Orion.Domain.Core/Services/UserService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,17 @@ public async Task<User> FindByIdAsync(string publicId)
return await _unitOfWork.UserRepository.GetByIdAsync(publicId);
}

public async Task<User> LoginAsync(string email, string password)
public async Task<(User User, RefreshToken RefreshToken)> SignInWithCredentialsAsync(string email, string password)
{
var user = await _unitOfWork.UserRepository.LoginAsync(email, password.ToSha512());

return user ?? throw new UnauthorizedUserException(_messages[UserMessages.InvalidCredentials], _messages[ExceptionsTitles.AuthenticationError]);
if (user is not null)
{
var refreshToken = await AddRefreshTokenAsync(user.Email);
return (user, refreshToken);
}

throw new UnauthorizedUserException(_messages[UserMessages.InvalidCredentials], _messages[ExceptionsTitles.AuthenticationError]);
}

public async Task UpdateAsync(User user)
Expand All @@ -71,20 +77,26 @@ public async Task UpdateAsync(User user)
await _unitOfWork.CommitAsync();
}

public async Task<RefreshToken> AddRefreshTokenAsync(RefreshToken refreshToken)
private async Task<RefreshToken> AddRefreshTokenAsync(string userEmail)
{
var existantRefreshToken = (await _unitOfWork.RefreshTokenRepository.SearchByAsync(x => x.Email == refreshToken.Email)).FirstOrDefault();
var existantRefreshToken = (await _unitOfWork.RefreshTokenRepository.SearchByAsync(x => x.Email == userEmail)).FirstOrDefault();

if (existantRefreshToken is not null)
return existantRefreshToken;

var refreshToken = new RefreshToken
{
Email = userEmail,
Refreshtoken = Guid.NewGuid().ToString().ToSha512()
};

var addedRefreshToken = await _unitOfWork.RefreshTokenRepository.AddAsync(refreshToken);
await _unitOfWork.CommitAsync();

return addedRefreshToken;
}

public async Task<User> SignInWithRehreshTokenAsync(string refreshToken, string expiredToken)
public async Task<(User User, RefreshToken RefreshToken)> SignInWithRefreshTokenAsync(string refreshToken, string expiredToken)
{
if (string.IsNullOrEmpty(refreshToken))
{
Expand All @@ -105,9 +117,9 @@ public async Task<User> SignInWithRehreshTokenAsync(string refreshToken, string
if (user is not null)
{
await _unitOfWork.RefreshTokenRepository.DeleteAsync(userRefreshToken.PublicId);
await _unitOfWork.CommitAsync();
var newRefreshToken = await AddRefreshTokenAsync(user.Email);

return user;
return (user, newRefreshToken);
}
}

Expand Down
20 changes: 15 additions & 5 deletions tests/Orion.Test/Api/Controllers/AuthControllerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Orion.Application.Core.Commands.LoginWithCredentials;
using Orion.Application.Core.Commands.LoginWithRefreshToken;
using Orion.Domain.Core.Entities;
using Orion.Domain.Core.Entities.Enuns;
using Orion.Test.Api.Controllers.BaseController;
using Orion.Test.Faker;
using System;
Expand Down Expand Up @@ -78,9 +79,16 @@ public async Task Login_WithInvalidCredentials_RetunsUnauthorized()
public async Task RefreshToken_WithValidRefreshToken_ReturnsNewToken()
{
//arrange & act
var (token, _) = AuthenticationConfiguration.CreateToken(new LoginWithCredentialsResponse { Email = _validUser.Email, Name = _validUser.Name, PublicId = _validUser.PublicId }, _configuration);
var (token, _) = AuthenticationConfiguration.CreateToken(new LoginWithCredentialsResponse
{
Email = _validUser.Email,
Name = _validUser.Name,
PublicId = _validUser.PublicId,
Profile = UserProfile.Admin
},
_configuration);

var result = await _authController.RefreshToken(new LoginWithRefreshTokenRequest { RefreshToken = _validRefreshToken.Refreshtoken, Token = token});
var result = await _authController.RefreshToken(new LoginWithRefreshTokenRequest { RefreshToken = _validRefreshToken.Refreshtoken, Token = token });

var contentResult = (OkObjectResult)result;
var userApiToken = (UserApiTokenModel)contentResult.Value;
Expand Down Expand Up @@ -113,14 +121,15 @@ private void SetupMediatorMock()
{
var mediatorMock = new Mock<IMediator>();

mediatorMock.Setup(x => x.Send(It.Is<LoginWithCredentialsRequest>(x => x.Password == _validUser.Password && x.Email == _validUser.Email),
mediatorMock.Setup(x => x.Send(It.Is<LoginWithCredentialsRequest>(x => x.Password == _validUser.Password && x.Email == _validUser.Email),
It.IsAny<CancellationToken>()))
.ReturnsAsync(new LoginWithCredentialsResponse
{
Email = _validUser.Email,
Name = _validUser.Name,
Profile = _validUser.Profile,
PublicId = _validUser.PublicId
PublicId = _validUser.PublicId,
RefreshToken = _validRefreshToken.Refreshtoken
});

mediatorMock.Setup(x => x.Send(It.Is<LoginWithRefreshTokenRequest>(x => x.RefreshToken == _validRefreshToken.Refreshtoken),
Expand All @@ -130,7 +139,8 @@ private void SetupMediatorMock()
Email = _validUser.Email,
Name = _validUser.Name,
Profile = _validUser.Profile,
PublicId = _validUser.PublicId
PublicId = _validUser.PublicId,
RefreshToken = _validRefreshToken.Refreshtoken
});

var inMemorySettings = new Dictionary<string, string> {
Expand Down
Loading

0 comments on commit 82fded5

Please sign in to comment.