Enforce dashboard authorization on all component routes
Fixes code-review findings Server-001 (Critical) and Server-003 (High). Server-001: the dashboard Razor components were mapped with no authorization policy, so every dashboard page — including the API Keys page — was reachable unauthenticated. MapRazorComponents<App>() now requires DashboardAuthenticationDefaults.AuthorizationPolicy; unauthenticated requests are challenged by the cookie scheme and redirected to the login page. Server-003: DashboardAuthenticator.CreatePrincipal never issued the 'scope' claim that DashboardAuthorizationHandler checks when Dashboard:RequireAdminScope is enabled, so enforcing the policy would have denied every LDAP login. CreatePrincipal (reached only after the required-group check passes) now emits the admin scope claim. Replaces the GatewayApplicationTests case that asserted dashboard routes allow anonymous access — it encoded the bug as expected behavior — with tests that verify component routes require the policy and the login/logout/denied endpoints allow anonymous. All 309 MxGateway.Tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MxGateway.Server;
|
||||
using MxGateway.Server.Dashboard;
|
||||
using MxGateway.Server.Metrics;
|
||||
|
||||
namespace MxGateway.Tests.Gateway;
|
||||
@@ -54,19 +55,48 @@ public sealed class GatewayApplicationTests
|
||||
endpoint.Metadata.GetMetadata<IEndpointNameMetadata>()?.EndpointName == "DashboardLogout");
|
||||
}
|
||||
|
||||
/// <summary>Verifies that Build does not map dashboard routes when the dashboard is disabled.</summary>
|
||||
/// <summary>Verifies that the dashboard login, logout, and denied endpoints allow anonymous access.</summary>
|
||||
[Fact]
|
||||
public void Build_WhenDashboardEnabled_DashboardRoutesAllowAnonymousAccess()
|
||||
public void Build_WhenDashboardEnabled_AuthEndpointsAllowAnonymousAccess()
|
||||
{
|
||||
WebApplication app = GatewayApplication.Build([]);
|
||||
IReadOnlyList<RouteEndpoint> endpoints = GetRouteEndpoints(app)
|
||||
.Where(endpoint => endpoint.RoutePattern.RawText?.StartsWith(
|
||||
"/dashboard",
|
||||
StringComparison.Ordinal) == true)
|
||||
.ToArray();
|
||||
IReadOnlyList<RouteEndpoint> endpoints = GetRouteEndpoints(app);
|
||||
|
||||
Assert.NotEmpty(endpoints);
|
||||
Assert.DoesNotContain(endpoints, endpoint => endpoint.Metadata.GetMetadata<IAuthorizeData>() is not null);
|
||||
string[] anonymousEndpointNames =
|
||||
["DashboardLogin", "DashboardLoginPost", "DashboardLogout", "DashboardAccessDenied"];
|
||||
foreach (string endpointName in anonymousEndpointNames)
|
||||
{
|
||||
RouteEndpoint endpoint = Assert.Single(
|
||||
endpoints,
|
||||
candidate => candidate.Metadata.GetMetadata<IEndpointNameMetadata>()?.EndpointName == endpointName);
|
||||
|
||||
Assert.NotNull(endpoint.Metadata.GetMetadata<IAllowAnonymous>());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Verifies that dashboard Razor component routes require the dashboard authorization policy.</summary>
|
||||
[Fact]
|
||||
public void Build_WhenDashboardEnabled_ComponentRoutesRequireAuthorization()
|
||||
{
|
||||
WebApplication app = GatewayApplication.Build([]);
|
||||
IReadOnlyList<RouteEndpoint> endpoints = GetRouteEndpoints(app);
|
||||
|
||||
string[] componentRoutes =
|
||||
["/dashboard/", "/dashboard/sessions", "/dashboard/workers", "/dashboard/events", "/dashboard/settings"];
|
||||
foreach (string route in componentRoutes)
|
||||
{
|
||||
RouteEndpoint[] matches = endpoints
|
||||
.Where(endpoint => endpoint.RoutePattern.RawText == route)
|
||||
.ToArray();
|
||||
|
||||
Assert.NotEmpty(matches);
|
||||
Assert.All(matches, endpoint =>
|
||||
{
|
||||
IAuthorizeData? authorize = endpoint.Metadata.GetMetadata<IAuthorizeData>();
|
||||
Assert.NotNull(authorize);
|
||||
Assert.Equal(DashboardAuthenticationDefaults.AuthorizationPolicy, authorize.Policy);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
Reference in New Issue
Block a user