using Elsa.Authorization; using System.Net; using System.Security.Claims; using System.Text.Encodings.Web; using Elsa.ExternalAuthentication.Features; using Elsa.ExternalAuthentication.Contracts; using Elsa.ExternalAuthentication.Permissions; using Elsa.ExternalAuthentication.Services; using Elsa.Common; using Elsa.Common.Multitenancy; using Elsa.Common.Services; using Elsa.Identity.Contracts; using Elsa.Identity.Entities; using Elsa.Identity.Providers; using Elsa.Identity.Services; using Elsa.Workflows; using FastEndpoints; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.Logging; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using NSubstitute; using Elsa.ExternalAuthentication.IntegrationTests.Fixtures; namespace Elsa.ExternalAuthentication.IntegrationTests.Links; [Collection(nameof(EndpointSecurityCollection))] public class IdentityLinkAuthorizationTests : IAsyncLifetime { private WebApplication? _app; private HttpClient? _client; private bool _wasSecurityEnabled; public async Task InitializeAsync() { _wasSecurityEnabled = EndpointSecurityOptions.SecurityIsEnabled; EndpointSecurityOptions.SecurityIsEnabled = true; var builder = WebApplication.CreateSlimBuilder(); builder.WebHost.UseTestServer(); builder.Services.AddAuthentication(TestAuthenticationHandler.AuthenticationScheme).AddScheme(TestAuthenticationHandler.AuthenticationScheme, _ => { }); builder.Services.AddAuthorization(); builder.Services.AddSingleton>(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(Substitute.For()); var tenant = Substitute.For(); tenant.TenantId.Returns("tenant-a"); builder.Services.AddSingleton(tenant); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddSingleton(Substitute.For()); builder.Services.AddScoped(); builder.Services.AddScoped(services => services.GetRequiredService()); builder.Services.AddScoped(services => services.GetRequiredService()); builder.Services.AddScoped(); builder.Services.AddFastEndpoints(options => { options.Assemblies = [typeof(ExternalAuthenticationFeature).Assembly]; options.Filter = endpoint => endpoint.Namespace == "Elsa.ExternalAuthentication.Endpoints.IdentityLinks"; }); _app = builder.Build(); _app.UseAuthentication(); _app.UseAuthorization(); _app.UseFastEndpoints(); await _app.StartAsync(); _client = _app.GetTestClient(); } public async Task DisposeAsync() { EndpointSecurityOptions.SecurityIsEnabled = _wasSecurityEnabled; _client?.Dispose(); if (_app is not null) { await _app.StopAsync(); await _app.DisposeAsync(); } } [Fact] public async Task UserOptionsRequiresTheLinkManagementPermissionRatherThanAnUnrelatedPermission() { using var request = new HttpRequestMessage(HttpMethod.Get, "/external-authentication/user-options"); request.Headers.Add(TestAuthenticationHandler.PermissionHeader, $"{ExternalAuthenticationResourcePermissions.Connections}:{CoreVerbs.View}"); Assert.Equal(HttpStatusCode.Forbidden, (await _client!.SendAsync(request)).StatusCode); } private sealed class TestAuthenticationHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder) : AuthenticationHandler(options, logger, encoder) { public const string AuthenticationScheme = "test"; public const string PermissionHeader = "X-Test-Permissions"; protected override Task HandleAuthenticateAsync() { var permissions = Request.Headers[PermissionHeader].SelectMany(x => x?.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) ?? []); var identity = new ClaimsIdentity(permissions.Select(x => new Claim(PermissionNames.ClaimType, x)), AuthenticationScheme); return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(new ClaimsPrincipal(identity), AuthenticationScheme))); } } }