* Moved app.UseCapDashboard() later in the pipeline and added new parameter to ChallengeAsync with built-in Authorization and Authentication * Added a sample for using Authenticated dashboards with OpenIdmaster
@@ -69,6 +69,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.AmazonSQS.InMemory", | |||||
EndProject | EndProject | ||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCore.CAP.NATS", "src\DotNetCore.CAP.NATS\DotNetCore.CAP.NATS.csproj", "{8B2FD3EA-E72B-4A82-B182-B87EC0C15D07}" | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCore.CAP.NATS", "src\DotNetCore.CAP.NATS\DotNetCore.CAP.NATS.csproj", "{8B2FD3EA-E72B-4A82-B182-B87EC0C15D07}" | ||||
EndProject | EndProject | ||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample.RabbitMQ.Postgres.DashboardAuth", "samples\Sample.RabbitMQ.Postgres.DashboardAuth\Sample.RabbitMQ.Postgres.DashboardAuth.csproj", "{54F6C206-2A23-4971-AE5A-FC47EB772452}" | |||||
EndProject | |||||
Global | Global | ||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||||
Debug|Any CPU = Debug|Any CPU | Debug|Any CPU = Debug|Any CPU | ||||
@@ -159,6 +161,10 @@ Global | |||||
{8B2FD3EA-E72B-4A82-B182-B87EC0C15D07}.Debug|Any CPU.Build.0 = Debug|Any CPU | {8B2FD3EA-E72B-4A82-B182-B87EC0C15D07}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
{8B2FD3EA-E72B-4A82-B182-B87EC0C15D07}.Release|Any CPU.ActiveCfg = Release|Any CPU | {8B2FD3EA-E72B-4A82-B182-B87EC0C15D07}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
{8B2FD3EA-E72B-4A82-B182-B87EC0C15D07}.Release|Any CPU.Build.0 = Release|Any CPU | {8B2FD3EA-E72B-4A82-B182-B87EC0C15D07}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
{54F6C206-2A23-4971-AE5A-FC47EB772452}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{54F6C206-2A23-4971-AE5A-FC47EB772452}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{54F6C206-2A23-4971-AE5A-FC47EB772452}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{54F6C206-2A23-4971-AE5A-FC47EB772452}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
EndGlobalSection | EndGlobalSection | ||||
GlobalSection(SolutionProperties) = preSolution | GlobalSection(SolutionProperties) = preSolution | ||||
HideSolutionNode = FALSE | HideSolutionNode = FALSE | ||||
@@ -185,6 +191,7 @@ Global | |||||
{43475E00-51B7-443D-BC2D-FC21F9D8A0B4} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} | {43475E00-51B7-443D-BC2D-FC21F9D8A0B4} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} | ||||
{B187DD15-092D-4B72-9807-50856607D237} = {3A6B6931-A123-477A-9469-8B468B5385AF} | {B187DD15-092D-4B72-9807-50856607D237} = {3A6B6931-A123-477A-9469-8B468B5385AF} | ||||
{8B2FD3EA-E72B-4A82-B182-B87EC0C15D07} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} | {8B2FD3EA-E72B-4A82-B182-B87EC0C15D07} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} | ||||
{54F6C206-2A23-4971-AE5A-FC47EB772452} = {3A6B6931-A123-477A-9469-8B468B5385AF} | |||||
EndGlobalSection | EndGlobalSection | ||||
GlobalSection(ExtensibilityGlobals) = postSolution | GlobalSection(ExtensibilityGlobals) = postSolution | ||||
SolutionGuid = {2E70565D-94CF-40B4-BFE1-AC18D5F736AB} | SolutionGuid = {2E70565D-94CF-40B4-BFE1-AC18D5F736AB} | ||||
@@ -0,0 +1,52 @@ | |||||
using System; | |||||
using System.Threading.Tasks; | |||||
using DotNetCore.CAP; | |||||
using DotNetCore.CAP.Messages; | |||||
using Microsoft.AspNetCore.Authorization; | |||||
using Microsoft.AspNetCore.Mvc; | |||||
using Microsoft.Extensions.Logging; | |||||
namespace Sample.RabbitMQ.Postgres.DashboardAuth.Controllers | |||||
{ | |||||
[Authorize] | |||||
[Route("api/[controller]")] | |||||
public class ValuesController : Controller | |||||
{ | |||||
private readonly ICapPublisher _capBus; | |||||
private readonly ILogger<ValuesController> _logger; | |||||
private const string CapGroup = "sample.rabbitmq.postgres.dashboard"; | |||||
public ValuesController(ICapPublisher capPublisher, ILogger<ValuesController> logger) | |||||
{ | |||||
_capBus = capPublisher; | |||||
_logger = logger; | |||||
} | |||||
[Route("publish")] | |||||
public async Task<IActionResult> Publish() | |||||
{ | |||||
await _capBus.PublishAsync(CapGroup, new Person() | |||||
{ | |||||
Id = 123, | |||||
Name = "Bar" | |||||
}); | |||||
return Ok(); | |||||
} | |||||
[NonAction] | |||||
[CapSubscribe(CapGroup)] | |||||
public void Subscribe(Person p, [FromCap] CapHeader header) | |||||
{ | |||||
var id = header[Headers.MessageId]; | |||||
_logger.LogInformation($@"{DateTime.Now} Subscriber invoked for message {id}, Info: {p}"); | |||||
} | |||||
public class Person | |||||
{ | |||||
public int Id { get; set; } | |||||
public string Name { get; set; } | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,21 @@ | |||||
using System; | |||||
using System.Threading.Tasks; | |||||
using DotNetCore.CAP.Dashboard; | |||||
using Microsoft.AspNetCore.Http; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
namespace Sample.RabbitMQ.Postgres.DashboardAuth | |||||
{ | |||||
public class HttpContextDashboardFilter : IDashboardAuthorizationFilter | |||||
{ | |||||
public async Task<bool> AuthorizeAsync(DashboardContext context) | |||||
{ | |||||
var httpContextAccessor = context.RequestServices.GetRequiredService<IHttpContextAccessor>(); | |||||
if (httpContextAccessor is null) | |||||
throw new ArgumentException("Configure IHttpContextAccessor as a service on Startup"); | |||||
return httpContextAccessor.HttpContext?.User?.Identity?.IsAuthenticated == true; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,17 @@ | |||||
using Microsoft.AspNetCore.Hosting; | |||||
using Microsoft.Extensions.Hosting; | |||||
namespace Sample.RabbitMQ.Postgres.DashboardAuth | |||||
{ | |||||
public class Program | |||||
{ | |||||
public static void Main(string[] args) | |||||
{ | |||||
CreateHostBuilder(args).Build().Run(); | |||||
} | |||||
public static IHostBuilder CreateHostBuilder(string[] args) => | |||||
Host.CreateDefaultBuilder(args) | |||||
.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }); | |||||
} | |||||
} |
@@ -0,0 +1,17 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk.Web"> | |||||
<PropertyGroup> | |||||
<TargetFramework>net5.0</TargetFramework> | |||||
</PropertyGroup> | |||||
<ItemGroup> | |||||
<ProjectReference Include="..\..\src\DotNetCore.CAP.Dashboard\DotNetCore.CAP.Dashboard.csproj" /> | |||||
<ProjectReference Include="..\..\src\DotNetCore.CAP.RabbitMQ\DotNetCore.CAP.RabbitMQ.csproj" /> | |||||
<ProjectReference Include="..\..\src\DotNetCore.CAP.PostgreSql\DotNetCore.CAP.PostgreSql.csproj" /> | |||||
<ProjectReference Include="..\..\src\DotNetCore.CAP\DotNetCore.CAP.csproj" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="5.0.3" /> | |||||
</ItemGroup> | |||||
</Project> |
@@ -0,0 +1,84 @@ | |||||
using Microsoft.AspNetCore.Authentication.Cookies; | |||||
using Microsoft.AspNetCore.Authentication.OpenIdConnect; | |||||
using Microsoft.AspNetCore.Builder; | |||||
using Microsoft.Extensions.Configuration; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
namespace Sample.RabbitMQ.Postgres.DashboardAuth | |||||
{ | |||||
public class Startup | |||||
{ | |||||
private readonly IConfiguration _configuration; | |||||
public Startup(IConfiguration configuration) | |||||
{ | |||||
_configuration = configuration; | |||||
} | |||||
public void ConfigureServices(IServiceCollection services) | |||||
{ | |||||
services.AddHttpContextAccessor(); | |||||
services | |||||
.AddAuthorization() | |||||
.AddAuthentication(options => | |||||
{ | |||||
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; | |||||
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; | |||||
}) | |||||
.AddCookie() | |||||
.AddOpenIdConnect(options => | |||||
{ | |||||
options.Authority = "https://demo.identityserver.io/"; | |||||
options.ClientId = "interactive.confidential"; | |||||
options.ClientSecret = "secret"; | |||||
options.ResponseType = "code"; | |||||
options.UsePkce = true; | |||||
options.Scope.Clear(); | |||||
options.Scope.Add("openid"); | |||||
options.Scope.Add("profile"); | |||||
}); | |||||
services.AddCap(cap => | |||||
{ | |||||
cap.UsePostgreSql(p => | |||||
{ | |||||
p.ConnectionString = _configuration.GetConnectionString("Postgres"); | |||||
}); | |||||
/* | |||||
* Use the command below to start a rabbitmq instance locally: | |||||
* docker run -d --name rabbitmq -p 15672:15672 -p 5672:5672 rabbitmq:management | |||||
*/ | |||||
cap.UseRabbitMQ(r => | |||||
{ | |||||
r.Port = 5672; | |||||
r.HostName = "127.0.0.1"; | |||||
r.UserName = "guest"; | |||||
r.Password = "guest"; | |||||
}); | |||||
cap.UseDashboard(d => | |||||
{ | |||||
d.UseChallengeOnAuth = true; | |||||
d.Authorization = new[] {new HttpContextDashboardFilter()}; | |||||
}); | |||||
}); | |||||
services.AddControllers(); | |||||
} | |||||
public void Configure(IApplicationBuilder app) | |||||
{ | |||||
app.UseAuthentication(); | |||||
app.UseRouting(); | |||||
app.UseAuthorization(); | |||||
app.UseEndpoints(endpoints => | |||||
{ | |||||
endpoints.MapControllers(); | |||||
}); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,13 @@ | |||||
{ | |||||
"Logging": { | |||||
"LogLevel": { | |||||
"Default": "Information", | |||||
"Microsoft": "Warning", | |||||
"Microsoft.Hosting.Lifetime": "Information" | |||||
} | |||||
}, | |||||
"AllowedHosts": "*", | |||||
"ConnectionStrings": { | |||||
"Postgres": "Server=127.0.0.1;Port=5432;Database=cap;Uid=postgres;Pwd=root;Include Error Detail=true;" | |||||
} | |||||
} |
@@ -10,6 +10,7 @@ using DotNetCore.CAP.Dashboard.GatewayProxy; | |||||
using DotNetCore.CAP.Dashboard.NodeDiscovery; | using DotNetCore.CAP.Dashboard.NodeDiscovery; | ||||
using DotNetCore.CAP.Dashboard.Resources; | using DotNetCore.CAP.Dashboard.Resources; | ||||
using DotNetCore.CAP.Persistence; | using DotNetCore.CAP.Persistence; | ||||
using Microsoft.AspNetCore.Authentication; | |||||
using Microsoft.AspNetCore.Builder; | using Microsoft.AspNetCore.Builder; | ||||
using Microsoft.AspNetCore.Hosting; | using Microsoft.AspNetCore.Hosting; | ||||
using Microsoft.AspNetCore.Http; | using Microsoft.AspNetCore.Http; | ||||
@@ -74,9 +75,9 @@ namespace DotNetCore.CAP | |||||
{ | { | ||||
return app => | return app => | ||||
{ | { | ||||
app.UseCapDashboard(); | |||||
next(app); | next(app); | ||||
app.UseCapDashboard(); | |||||
}; | }; | ||||
} | } | ||||
} | } | ||||
@@ -133,6 +134,12 @@ namespace DotNetCore.CAP | |||||
var isAuthenticated = context.User?.Identity?.IsAuthenticated; | var isAuthenticated = context.User?.Identity?.IsAuthenticated; | ||||
if (_options.UseChallengeOnAuth) | |||||
{ | |||||
await context.ChallengeAsync(); | |||||
return; | |||||
} | |||||
context.Response.StatusCode = isAuthenticated == true | context.Response.StatusCode = isAuthenticated == true | ||||
? (int)HttpStatusCode.Forbidden | ? (int)HttpStatusCode.Forbidden | ||||
: (int)HttpStatusCode.Unauthorized; | : (int)HttpStatusCode.Unauthorized; | ||||
@@ -15,8 +15,14 @@ namespace DotNetCore.CAP | |||||
PathMatch = "/cap"; | PathMatch = "/cap"; | ||||
Authorization = new[] {new LocalRequestsOnlyAuthorizationFilter()}; | Authorization = new[] {new LocalRequestsOnlyAuthorizationFilter()}; | ||||
StatsPollingInterval = 2000; | StatsPollingInterval = 2000; | ||||
UseChallengeOnAuth = false; | |||||
} | } | ||||
/// <summary> | |||||
/// Indicates if executes a Challenge for Auth within ASP.NET middlewares | |||||
/// </summary> | |||||
public bool UseChallengeOnAuth { get; set; } | |||||
/// <summary> | /// <summary> | ||||
/// The path for the Back To Site link. Set to <see langword="null" /> in order to hide the Back To Site link. | /// The path for the Back To Site link. Set to <see langword="null" /> in order to hide the Back To Site link. | ||||
/// </summary> | /// </summary> | ||||