* 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 | |||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCore.CAP.NATS", "src\DotNetCore.CAP.NATS\DotNetCore.CAP.NATS.csproj", "{8B2FD3EA-E72B-4A82-B182-B87EC0C15D07}" | |||
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 | |||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | |||
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}.Release|Any CPU.ActiveCfg = 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 | |||
GlobalSection(SolutionProperties) = preSolution | |||
HideSolutionNode = FALSE | |||
@@ -185,6 +191,7 @@ Global | |||
{43475E00-51B7-443D-BC2D-FC21F9D8A0B4} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} | |||
{B187DD15-092D-4B72-9807-50856607D237} = {3A6B6931-A123-477A-9469-8B468B5385AF} | |||
{8B2FD3EA-E72B-4A82-B182-B87EC0C15D07} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} | |||
{54F6C206-2A23-4971-AE5A-FC47EB772452} = {3A6B6931-A123-477A-9469-8B468B5385AF} | |||
EndGlobalSection | |||
GlobalSection(ExtensibilityGlobals) = postSolution | |||
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.Resources; | |||
using DotNetCore.CAP.Persistence; | |||
using Microsoft.AspNetCore.Authentication; | |||
using Microsoft.AspNetCore.Builder; | |||
using Microsoft.AspNetCore.Hosting; | |||
using Microsoft.AspNetCore.Http; | |||
@@ -74,9 +75,9 @@ namespace DotNetCore.CAP | |||
{ | |||
return app => | |||
{ | |||
app.UseCapDashboard(); | |||
next(app); | |||
app.UseCapDashboard(); | |||
}; | |||
} | |||
} | |||
@@ -133,6 +134,12 @@ namespace DotNetCore.CAP | |||
var isAuthenticated = context.User?.Identity?.IsAuthenticated; | |||
if (_options.UseChallengeOnAuth) | |||
{ | |||
await context.ChallengeAsync(); | |||
return; | |||
} | |||
context.Response.StatusCode = isAuthenticated == true | |||
? (int)HttpStatusCode.Forbidden | |||
: (int)HttpStatusCode.Unauthorized; | |||
@@ -15,8 +15,14 @@ namespace DotNetCore.CAP | |||
PathMatch = "/cap"; | |||
Authorization = new[] {new LocalRequestsOnlyAuthorizationFilter()}; | |||
StatsPollingInterval = 2000; | |||
UseChallengeOnAuth = false; | |||
} | |||
/// <summary> | |||
/// Indicates if executes a Challenge for Auth within ASP.NET middlewares | |||
/// </summary> | |||
public bool UseChallengeOnAuth { get; set; } | |||
/// <summary> | |||
/// The path for the Back To Site link. Set to <see langword="null" /> in order to hide the Back To Site link. | |||
/// </summary> | |||