Skip to content

Commit

Permalink
Add Mediator Notifications and Current user context
Browse files Browse the repository at this point in the history
  • Loading branch information
vanderlan committed Oct 30, 2023
1 parent 68c5147 commit c631e7a
Show file tree
Hide file tree
Showing 14 changed files with 408 additions and 40 deletions.
8 changes: 4 additions & 4 deletions Orion.sln
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Orion.Infra.Data", "src\Ori
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Orion.Croscutting.Resources", "src\Orion.Croscutting.Resources\Orion.Croscutting.Resources.csproj", "{105D1EB4-5091-493E-B75E-2131169CC9AC}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Devops", "Devops", "{2D8CC498-597C-4415-95BF-48E623039F90}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution items", "Solution items", "{2D8CC498-597C-4415-95BF-48E623039F90}"
ProjectSection(SolutionItems) = preProject
.dockerignore = .dockerignore
docker-compose.yaml = docker-compose.yaml
Expand All @@ -43,7 +43,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "template-config", "template
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "4 - Croscutting", "4 - Croscutting", "{34D11C13-159D-47B0-A5B8-3DF32677792F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orion.Application.Core", "src\Orion.Application.Core\Orion.Application.Core.csproj", "{1F3FDF5B-999B-4D96-81F7-25F854EB40D6}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Orion.Application.Core", "src\Orion.Application.Core\Orion.Application.Core.csproj", "{1F3FDF5B-999B-4D96-81F7-25F854EB40D6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -87,11 +87,11 @@ Global
{8C8470D6-0951-4B42-8935-779D8672F5B1} = {11C424D2-7519-493F-B4BB-A5901B33C56B}
{AA8C7554-18FC-40D7-A318-123D2AAAB023} = {B333633B-8361-476B-A0A3-6B983D28904E}
{7C877ABA-17F2-470A-99EB-5E172C6EA44B} = {434E301F-C36C-4203-B334-D3948130B660}
{105D1EB4-5091-493E-B75E-2131169CC9AC} = {34D11C13-159D-47B0-A5B8-3DF32677792F}
{8C70C31A-892F-4C8F-B7B0-A710C97A55FA} = {2D8CC498-597C-4415-95BF-48E623039F90}
{BF0248B6-C870-4419-862D-1DCAD4561946} = {34D11C13-159D-47B0-A5B8-3DF32677792F}
{9F14F904-BBC6-4616-BB80-76E485EAEC14} = {67A0BEBC-35B1-436C-9F05-AC8F60AF1688}
{309303F2-F07E-4578-8079-C7D2CE2AE329} = {2D8CC498-597C-4415-95BF-48E623039F90}
{BF0248B6-C870-4419-862D-1DCAD4561946} = {34D11C13-159D-47B0-A5B8-3DF32677792F}
{105D1EB4-5091-493E-B75E-2131169CC9AC} = {34D11C13-159D-47B0-A5B8-3DF32677792F}
{1F3FDF5B-999B-4D96-81F7-25F854EB40D6} = {67A0BEBC-35B1-436C-9F05-AC8F60AF1688}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
Expand Down
19 changes: 11 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ The main objective is to start projects with a clean and simple architecture, wi
+ CreatedAt and UpdatedAt by default;
+ Pagination.

+ AutoMapper
+ MediaR
+ Swagger
+ Fluent Validation
+ Authentication and Authorization
Expand All @@ -44,14 +44,17 @@ The main objective is to start projects with a clean and simple architecture, wi
10. Docker and Docker-Compose;
11. API Version Configuration (by x-api-version header attribute);
12. Globalization;
13. In-Memory database for Testing.
13. In-Memory database for Testing;
14. Mediator + CQRS + Notification Pattern
15. Logs with Correlation Id

**CI & CD**

1. Automated tests;
2. Continuous Integration;
3. Continuous Delivery;
4. DockerHub Integration.
2. Continuous Integration (GitHub CI);
3. Continuous Delivery (GitHub CI);
4. DockerHub Integration;
5. Sonar Cloud Integration.


# **Create your Project based on the Orion Api Template**
Expand All @@ -60,13 +63,13 @@ The main objective is to start projects with a clean and simple architecture, wi

dotnet new install .

dotnet new orion-api -o MyNewProject --firstEntity "EntityName"
dotnet new orion-api -o MyNewProject

**Migrations**

# in the src/ folder

dotnet ef migrations add MigrationName -p Orion.Data -s Orion.Api
dotnet ef database update -p Orion.Data -s Orion.Api --verbose
dotnet ef migrations add MigrationName -p Orion.Infra.Data -s Orion.Api
dotnet ef database update -p Orion.Infra.Data -s Orion.Api --verbose

Author: https://github.com/vanderlan
4 changes: 3 additions & 1 deletion src/Orion.Api/Bootstrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Orion.Application.Core;
using Orion.Application.Core.Commands.UserCreate;
using Orion.Croscutting.Ioc.Dependencies;
using Orion.Domain.Core.Authentication;

namespace Orion.Api;

Expand Down Expand Up @@ -77,7 +78,8 @@ public static void ConfigureServices(this IServiceCollection services, IConfigur
services.AddDomainServices();

services.AddHttpContextAccessor();

services.AddScoped<ICurrentUser, CurrentUser>();

services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(ApplicationAssembly).Assembly));
}
}
6 changes: 3 additions & 3 deletions src/Orion.Api/Configuration/AuthenticationConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ public static (string Token, DateTime ValidTo)CreateToken(LoginWithCredentialsRe

var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Email, loginWithCredentialsResponse.Email),
new Claim(JwtRegisteredClaimNames.GivenName, loginWithCredentialsResponse.Name),
new Claim(JwtRegisteredClaimNames.UniqueName, loginWithCredentialsResponse.PublicId),
new Claim(ClaimTypes.Email, loginWithCredentialsResponse.Email),
new Claim(ClaimTypes.GivenName, loginWithCredentialsResponse.Name),
new Claim(ClaimTypes.Sid, loginWithCredentialsResponse.PublicId),
new Claim(ClaimTypes.Role, loginWithCredentialsResponse.ProfileDescription),
};

Expand Down
18 changes: 1 addition & 17 deletions src/Orion.Api/Controllers/Base/ApiController.cs
Original file line number Diff line number Diff line change
@@ -1,33 +1,17 @@
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;
using MediatR;
using Orion.Api.Models;
using Microsoft.AspNetCore.Mvc;

namespace Orion.Api.Controllers.Base;

[ApiController]
public abstract class ApiController : ControllerBase
{
protected readonly IMediator Mediator;
protected AuthUserModel AuthUser => GetAuthenticatedUser();
protected ApiController(IMediator mediator)
{
Mediator = mediator;
}

private AuthUserModel GetAuthenticatedUser()
{
var email = ((ClaimsIdentity)User.Identity)?.FindFirst(ClaimTypes.Email);
var givenName = ((ClaimsIdentity)User.Identity)?.FindFirst(ClaimTypes.GivenName);

return new AuthUserModel
(
PublicId: User?.Identity?.Name,
Email: email?.Value,
Name: givenName?.Value
);
}

protected CreatedResult Created(object entity)
{
return base.Created("{id}", entity);
Expand Down
3 changes: 0 additions & 3 deletions src/Orion.Api/Models/AuthUserModel.cs

This file was deleted.

2 changes: 1 addition & 1 deletion src/Orion.Api/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"Dev": {
"commandName": "Project",
"launchUrl": "swagger",
"launchBrowser": true,
"launchBrowser": false,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,27 @@
using MediatR;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Orion.Domain.Core.Authentication;

namespace Orion.Application.Core.Notifications.UserCreated
{
public class UserCreatedNotificationHandler : INotificationHandler<UserCreatedNotification>
{
private readonly ILogger<UserCreatedNotificationHandler> _logger;
private readonly ICurrentUser _currentUser;

public UserCreatedNotificationHandler(ILogger<UserCreatedNotificationHandler> logger)
public UserCreatedNotificationHandler(ILogger<UserCreatedNotificationHandler> logger, ICurrentUser currentUser)
{
_logger = logger;
_currentUser = currentUser;
}

public async Task Handle(UserCreatedNotification notification, CancellationToken cancellationToken)
{
_logger.LogInformation("A notification {notificationType} has been received. Notification details: {notification}",
_logger.LogInformation("A notification {notificationType} has been received. Notification details: {notification}. Action performed by: {currentUserName}",
nameof(UserCreatedNotification),
JsonConvert.SerializeObject(notification, Formatting.Indented));
JsonConvert.SerializeObject(notification, Formatting.Indented),
_currentUser.ToString());

await Task.CompletedTask;
}
Expand Down
37 changes: 37 additions & 0 deletions src/Orion.Domain.Core/Authentication/CurrentUser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using Microsoft.AspNetCore.Http;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;

namespace Orion.Domain.Core.Authentication;

public class CurrentUser : ICurrentUser
{
private readonly IHttpContextAccessor _accessor;
private readonly List<Claim> _claims;

public CurrentUser(IHttpContextAccessor accessor)
{
_accessor = 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 bool IsAuthenticated()
{
return _accessor.HttpContext.User.Identity.IsAuthenticated;
}

public override string ToString()
{
if(IsAuthenticated())
return $"Name: {Name} - Id: {Id} - Email: {Email} - Profile: {Profile}";

return "Anonymous User";
}
}
7 changes: 7 additions & 0 deletions src/Orion.Domain.Core/Authentication/ICurrentUser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Orion.Domain.Core.Authentication;

public interface ICurrentUser
{
string Name { get; }
bool IsAuthenticated();
}
1 change: 1 addition & 0 deletions src/Orion.Domain.Core/Orion.Domain.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

<ItemGroup>
<PackageReference Include="MediatR" Version="12.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.0.3" />
</ItemGroup>

Expand Down
124 changes: 124 additions & 0 deletions src/Orion.Infra.Data/Migrations/20231030205851_Initial.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit c631e7a

Please sign in to comment.