diff --git a/SecurityService.BusinessLogic/Oidc/OidcCommands.cs b/SecurityService.BusinessLogic/Oidc/OidcCommands.cs index ff4112ee..b8d8b1e4 100644 --- a/SecurityService.BusinessLogic/Oidc/OidcCommands.cs +++ b/SecurityService.BusinessLogic/Oidc/OidcCommands.cs @@ -14,4 +14,5 @@ public sealed record VerifyGetQuery(HttpContext HttpContext) : IRequest>; public sealed record ConsentGetQuery(HttpContext HttpContext, string ReturnUrl) : IRequest>; public sealed record ConsentPostCommand(string ReturnUrl, string Button, IReadOnlyCollection SelectedScopes) : IRequest>; + public sealed record DiagnosticsQuery(HttpContext HttpContext) : IRequest>; } diff --git a/SecurityService.BusinessLogic/Oidc/OidcResults.cs b/SecurityService.BusinessLogic/Oidc/OidcResults.cs index 90941599..7a74c114 100644 --- a/SecurityService.BusinessLogic/Oidc/OidcResults.cs +++ b/SecurityService.BusinessLogic/Oidc/OidcResults.cs @@ -84,3 +84,17 @@ public abstract record ConsentPostCommandResult; public sealed record ConsentPostRedirectResult(string Url) : ConsentPostCommandResult; public sealed record ConsentPostPageResult(string ModelError) : ConsentPostCommandResult; + +// ---- Diagnostics endpoint ---- + +public abstract record DiagnosticsQueryResult; + +public sealed record DiagnosticsPageResult( + IReadOnlyCollection Claims, + IReadOnlyCollection Properties) : DiagnosticsQueryResult; + +public sealed record DiagnosticsNotFoundResult : DiagnosticsQueryResult; + +public sealed record DiagnosticsChallengeResult(string AuthenticationScheme) : DiagnosticsQueryResult; + +public sealed record DiagnosticItem(string Type, string Value); diff --git a/SecurityService.BusinessLogic/RequestHandlers/DiagnosticsRequestHandler.cs b/SecurityService.BusinessLogic/RequestHandlers/DiagnosticsRequestHandler.cs new file mode 100644 index 00000000..459d489c --- /dev/null +++ b/SecurityService.BusinessLogic/RequestHandlers/DiagnosticsRequestHandler.cs @@ -0,0 +1,41 @@ +using System.Net; +using MediatR; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Identity; +using SecurityService.BusinessLogic.Oidc; +using SimpleResults; + +namespace SecurityService.BusinessLogic.RequestHandlers; + +public sealed class DiagnosticsRequestHandler : + IRequestHandler> +{ + public async Task> Handle(OidcCommands.DiagnosticsQuery query, CancellationToken cancellationToken) + { + var context = query.HttpContext; + + if (context.Connection.RemoteIpAddress is { } remoteIpAddress && + IPAddress.IsLoopback(remoteIpAddress) == false) + { + return Result.Success(new DiagnosticsNotFoundResult()); + } + + var result = await context.AuthenticateAsync(IdentityConstants.ApplicationScheme); + if (result.Succeeded == false || result.Principal is null) + { + return Result.Success(new DiagnosticsChallengeResult(IdentityConstants.ApplicationScheme)); + } + + var claims = result.Principal.Claims + .Select(claim => new DiagnosticItem(claim.Type, claim.Value)) + .OrderBy(item => item.Type, StringComparer.OrdinalIgnoreCase) + .ToArray(); + + var properties = result.Properties?.Items + .OrderBy(item => item.Key, StringComparer.OrdinalIgnoreCase) + .Select(item => new DiagnosticItem(item.Key, item.Value ?? string.Empty)) + .ToArray() ?? Array.Empty(); + + return Result.Success(new DiagnosticsPageResult(claims, properties)); + } +} diff --git a/SecurityService/Pages/Diagnostics/Index.cshtml.cs b/SecurityService/Pages/Diagnostics/Index.cshtml.cs index 420db1a9..c115cf05 100644 --- a/SecurityService/Pages/Diagnostics/Index.cshtml.cs +++ b/SecurityService/Pages/Diagnostics/Index.cshtml.cs @@ -1,43 +1,41 @@ -using System.Net; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Identity; +using MediatR; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; +using SecurityService.BusinessLogic.Oidc; namespace SecurityService.Pages.Diagnostics; public sealed class IndexModel : PageModel { + private readonly IMediator _mediator; + + public IndexModel(IMediator mediator) + { + this._mediator = mediator; + } + public IReadOnlyCollection Claims { get; private set; } = Array.Empty(); public IReadOnlyCollection Properties { get; private set; } = Array.Empty(); - public async Task OnGetAsync() + public async Task OnGetAsync(CancellationToken cancellationToken) { - if (this.Request.HttpContext.Connection.RemoteIpAddress is { } remoteIpAddress && - IPAddress.IsLoopback(remoteIpAddress) == false) - { - return this.NotFound(); - } + var result = await this._mediator.Send(new OidcCommands.DiagnosticsQuery(this.HttpContext), cancellationToken); - var result = await this.HttpContext.AuthenticateAsync(IdentityConstants.ApplicationScheme); - if (result.Succeeded == false || result.Principal is null) + return result.Data switch { - return this.Challenge(IdentityConstants.ApplicationScheme); - } - - this.Claims = result.Principal.Claims - .Select(claim => new DiagnosticItem(claim.Type, claim.Value)) - .OrderBy(item => item.Type, StringComparer.OrdinalIgnoreCase) - .ToArray(); - - this.Properties = result.Properties?.Items - .OrderBy(item => item.Key, StringComparer.OrdinalIgnoreCase) - .Select(item => new DiagnosticItem(item.Key, item.Value ?? string.Empty)) - .ToArray() ?? Array.Empty(); + DiagnosticsNotFoundResult => this.NotFound(), + DiagnosticsChallengeResult challenge => this.Challenge(challenge.AuthenticationScheme), + DiagnosticsPageResult page => this.ApplyPageResult(page), + _ => this.Page() + }; + } + private IActionResult ApplyPageResult(DiagnosticsPageResult page) + { + this.Claims = page.Claims; + this.Properties = page.Properties; return this.Page(); } } -public sealed record DiagnosticItem(string Type, string Value);