* Require opt-in for localhost auth grant * Preserve custom authorization configuration * Address localhost auth review feedback * Address localhost bootstrap review comments
125 lines
5.2 KiB
C#
125 lines
5.2 KiB
C#
using System.Net;
|
|
using System.Security.Claims;
|
|
using Elsa;
|
|
using Elsa.Options;
|
|
using Elsa.Requirements;
|
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Http;
|
|
using OptionsFactory = Microsoft.Extensions.Options.Options;
|
|
|
|
namespace Elsa.Identity.UnitTests.Requirements;
|
|
|
|
public class LocalHostPermissionRequirementHandlerTests
|
|
{
|
|
[Fact]
|
|
public async Task DoesNotGrantPermissionsToLocalhostRequestsByDefault()
|
|
{
|
|
var context = await AuthorizeAsync(enableLocalHostPermissionGrant: false, isLocal: true);
|
|
|
|
Assert.False(context.HasSucceeded);
|
|
Assert.Empty(context.User.FindAll(PermissionNames.ClaimType));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GrantsPermissionsToLocalhostRequestsWhenExplicitlyEnabled()
|
|
{
|
|
var context = await AuthorizeAsync(enableLocalHostPermissionGrant: true, isLocal: true);
|
|
var permissions = context.User.FindAll(PermissionNames.ClaimType).Select(x => x.Value).ToList();
|
|
|
|
Assert.True(context.HasSucceeded);
|
|
Assert.Contains("create:application", permissions);
|
|
Assert.Contains("create:user", permissions);
|
|
Assert.Contains("create:role", permissions);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DoesNotGrantPermissionsToAuthenticatedLocalhostRequestsWhenExplicitlyEnabled()
|
|
{
|
|
var context = await AuthorizeAsync(enableLocalHostPermissionGrant: true, isLocal: true, isAuthenticated: true);
|
|
|
|
Assert.False(context.HasSucceeded);
|
|
Assert.True(context.User.Identity?.IsAuthenticated);
|
|
Assert.Empty(context.User.FindAll(PermissionNames.ClaimType));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task SucceedsForAuthenticatedLocalhostRequestsWithExistingBootstrapPermissionsWhenExplicitlyEnabled()
|
|
{
|
|
var context = await AuthorizeAsync(enableLocalHostPermissionGrant: true, isLocal: true, isAuthenticated: true, permissions: BootstrapPermissions);
|
|
var permissions = context.User.FindAll(PermissionNames.ClaimType).Select(x => x.Value).ToList();
|
|
|
|
Assert.True(context.HasSucceeded);
|
|
Assert.True(context.User.Identity?.IsAuthenticated);
|
|
Assert.Equal(3, permissions.Count);
|
|
Assert.Contains("create:application", permissions);
|
|
Assert.Contains("create:user", permissions);
|
|
Assert.Contains("create:role", permissions);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DoesNotDuplicateBootstrapPermissionsAcrossMultipleEvaluations()
|
|
{
|
|
var requirement = new LocalHostPermissionRequirement();
|
|
var user = new ClaimsPrincipal(new ClaimsIdentity());
|
|
var authorizationContext = new AuthorizationHandlerContext(new[] { requirement }, user, null);
|
|
var handler = CreateHandler(enableLocalHostPermissionGrant: true, isLocal: true);
|
|
|
|
await handler.HandleAsync(authorizationContext);
|
|
await handler.HandleAsync(authorizationContext);
|
|
|
|
Assert.True(authorizationContext.HasSucceeded);
|
|
Assert.Equal(1, authorizationContext.User.Identities.Count(x => x.AuthenticationType == JwtBearerDefaults.AuthenticationScheme));
|
|
Assert.Equal(3, authorizationContext.User.FindAll(PermissionNames.ClaimType).Count());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DoesNotGrantPermissionsToRemoteRequestsWhenExplicitlyEnabled()
|
|
{
|
|
var context = await AuthorizeAsync(enableLocalHostPermissionGrant: true, isLocal: false);
|
|
|
|
Assert.False(context.HasSucceeded);
|
|
Assert.Empty(context.User.FindAll(PermissionNames.ClaimType));
|
|
}
|
|
|
|
private static readonly string[] BootstrapPermissions =
|
|
[
|
|
"create:application",
|
|
"create:user",
|
|
"create:role"
|
|
];
|
|
|
|
private static async Task<AuthorizationHandlerContext> AuthorizeAsync(bool enableLocalHostPermissionGrant, bool isLocal, bool isAuthenticated = false, params string[] permissions)
|
|
{
|
|
var requirement = new LocalHostPermissionRequirement();
|
|
var user = new ClaimsPrincipal(new ClaimsIdentity(isAuthenticated ? "Test" : null));
|
|
user.Identities.First().AddClaims(permissions.Select(x => new Claim(PermissionNames.ClaimType, x)));
|
|
var authorizationContext = new AuthorizationHandlerContext(new[] { requirement }, user, null);
|
|
var handler = CreateHandler(enableLocalHostPermissionGrant, isLocal);
|
|
|
|
await handler.HandleAsync(authorizationContext);
|
|
|
|
return authorizationContext;
|
|
}
|
|
|
|
private static LocalHostPermissionRequirementHandler CreateHandler(bool enableLocalHostPermissionGrant, bool isLocal)
|
|
{
|
|
var httpContext = CreateHttpContext(isLocal);
|
|
var httpContextAccessor = new HttpContextAccessor { HttpContext = httpContext };
|
|
var options = OptionsFactory.Create(new LocalHostPermissionRequirementOptions
|
|
{
|
|
EnableLocalHostPermissionGrant = enableLocalHostPermissionGrant
|
|
});
|
|
|
|
return new LocalHostPermissionRequirementHandler(httpContextAccessor, options);
|
|
}
|
|
|
|
private static HttpContext CreateHttpContext(bool isLocal)
|
|
{
|
|
var httpContext = new DefaultHttpContext();
|
|
httpContext.Connection.LocalIpAddress = IPAddress.Loopback;
|
|
httpContext.Connection.RemoteIpAddress = isLocal ? IPAddress.Loopback : IPAddress.Parse("10.0.0.1");
|
|
return httpContext;
|
|
}
|
|
}
|