< Summary

Information
Class: Elsa.ExternalAuthentication.Endpoints.Previews.AuthorizePreview
Assembly: Elsa.ExternalAuthentication
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.ExternalAuthentication/Endpoints/Previews/PreviewSignInEndpoints.cs
Line coverage
100%
Covered lines: 11
Uncovered lines: 0
Coverable lines: 11
Total lines: 121
Line coverage: 100%
Branch coverage
100%
Covered branches: 2
Total branches: 2
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
Configure()100%11100%
HandleAsync()100%22100%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.ExternalAuthentication/Endpoints/Previews/PreviewSignInEndpoints.cs

#LineLine coverage
 1using Elsa.Abstractions;
 2using Elsa.Common.Multitenancy;
 3using Elsa.ExternalAuthentication.Constants;
 4using Elsa.ExternalAuthentication.Models;
 5using Elsa.ExternalAuthentication.Permissions;
 6using Elsa.ExternalAuthentication.Services;
 7using Microsoft.AspNetCore.Builder;
 8using Microsoft.AspNetCore.Http;
 9
 10namespace Elsa.ExternalAuthentication.Endpoints.Previews;
 11
 12internal sealed class InitiatePreview(PreviewSignInService previews, ITenantAccessor tenantAccessor) : ElsaEndpointWitho
 13{
 14    public override void Configure()
 15    {
 16        Post("/external-authentication/connections/{connectionId}/preview");
 17        RequirePermission(ExternalAuthenticationResourcePermissions.Connections, ExternalAuthenticationVerbs.Preview);
 18    }
 19
 20    public override async Task HandleAsync(CancellationToken cancellationToken)
 21    {
 22        if (!Connections.ConnectionEndpointSupport.TryGetExpectedRevision(HttpContext, out var revision))
 23        {
 24            HttpContext.Response.StatusCode = StatusCodes.Status428PreconditionRequired;
 25            return;
 26        }
 27        var result = await previews.InitiateAsync(Route<string>("connectionId")!, revision, tenantAccessor.TenantId, Use
 28        switch (result)
 29        {
 30            case PreviewInitiationResult.Started(var handle, var expiresAt):
 31                await HttpContext.Response.WriteAsJsonAsync(new PreviewInitiationResponse(BuildAuthorizePath(HttpContext
 32                return;
 33            case PreviewInitiationResult.PreconditionFailed(var currentRevision):
 34                HttpContext.Response.StatusCode = StatusCodes.Status412PreconditionFailed;
 35                await HttpContext.Response.WriteAsJsonAsync(new { error = "precondition_failed", currentRevision }, canc
 36                return;
 37            case PreviewInitiationResult.NotFound:
 38                HttpContext.Response.StatusCode = StatusCodes.Status404NotFound;
 39                return;
 40            default:
 41                HttpContext.Response.StatusCode = StatusCodes.Status403Forbidden;
 42                return;
 43        }
 44    }
 45
 46    internal static string BuildAuthorizePath(PathString pathBase, PathString requestPath, string handle)
 47    {
 48        const string routeMarker = "/external-authentication/connections/";
 49        var requestPathValue = requestPath.Value ?? string.Empty;
 50        var markerIndex = requestPathValue.LastIndexOf(routeMarker, StringComparison.OrdinalIgnoreCase);
 51        var routePrefix = markerIndex >= 0 ? requestPathValue[..markerIndex].TrimEnd('/') : string.Empty;
 52        var applicationPrefix = pathBase.Value?.TrimEnd('/') ?? string.Empty;
 53        return $"{applicationPrefix}{routePrefix}/external-authentication/previews/{Uri.EscapeDataString(handle)}/author
 54    }
 55}
 56
 457internal sealed class AuthorizePreview(PreviewSignInService previews) : ElsaEndpointWithoutRequest
 58{
 59    public override void Configure()
 60    {
 261        Get("/external-authentication/previews/{previewHandle}/authorize");
 262        AllowAnonymous();
 463        Options(x => x.RequireRateLimiting(ExternalAuthenticationRateLimitPolicyNames.ExternalInitiation));
 264    }
 65
 66    public override async Task HandleAsync(CancellationToken cancellationToken)
 67    {
 268        var result = await previews.AuthorizeAsync(Route<string>("previewHandle")!, cancellationToken);
 269        if (result is PreviewAuthorizeResult.Redirect(var navigationUri))
 70        {
 171            await Send.RedirectAsync(navigationUri.ToString(), allowRemoteRedirects: true);
 172            return;
 73        }
 174        await Send.StatusCodeAsync(StatusCodes.Status410Gone, cancellationToken);
 275    }
 76}
 77
 78internal sealed class CompletePreview(PreviewSignInService previews) : ElsaEndpointWithoutRequest
 79{
 80    public override void Configure()
 81    {
 82        Get("/external-authentication/previews/callback/{connectionId}");
 83        AllowAnonymous();
 84        Options(x => x.RequireRateLimiting(ExternalAuthenticationRateLimitPolicyNames.ProviderCallback));
 85    }
 86
 87    public override async Task HandleAsync(CancellationToken cancellationToken)
 88    {
 89        var parameters = HttpContext.Request.Query.ToDictionary(x => x.Key, x => (IReadOnlyCollection<string>)x.Value.Wh
 90        var result = await previews.CompleteAsync(Route<string>("connectionId")!, HttpContext.Request.Query["state"].ToS
 91        HttpContext.Response.StatusCode = result is PreviewCallbackResult.Completed ? StatusCodes.Status200OK : StatusCo
 92        await HttpContext.Response.WriteAsJsonAsync(new { completed = result is PreviewCallbackResult.Completed }, cance
 93    }
 94}
 95
 96internal sealed class GetPreviewResult(PreviewSignInService previews, ITenantAccessor tenantAccessor) : ElsaEndpointWith
 97{
 98    public override void Configure()
 99    {
 100        Get("/external-authentication/previews/{previewHandle}");
 101        RequirePermission(ExternalAuthenticationResourcePermissions.Connections, ExternalAuthenticationVerbs.Preview);
 102    }
 103
 104    public override async Task HandleAsync(CancellationToken cancellationToken)
 105    {
 106        var result = await previews.TakeResultAsync(Route<string>("previewHandle")!, tenantAccessor.TenantId, User, canc
 107        if (result is TakeResult<PreviewResult>.Taken { Value: var preview })
 108        {
 109            await HttpContext.Response.WriteAsJsonAsync(PreviewResultDocument.From(preview), cancellationToken);
 110            return;
 111        }
 112        var statusCode = result is TakeResult<PreviewResult>.Expired or TakeResult<PreviewResult>.AlreadyConsumed ? Stat
 113        await Send.StatusCodeAsync(statusCode, cancellationToken);
 114    }
 115}
 116
 117internal sealed record PreviewInitiationResponse(string NavigationUrl, DateTimeOffset ExpiresAt);
 118internal sealed record PreviewResultDocument(string ConnectionId, string MaterialRevision, string Issuer, string MaskedS
 119{
 120    public static PreviewResultDocument From(PreviewResult value) => new(value.ConnectionId, value.MaterialRevision, val
 121}