Merge remote-tracking branch 'origin/main' into codex/security-identity-secret-hashing
# Conflicts: # src/modules/Elsa.Identity/README.md
This commit is contained in:
commit
8b9f005f1d
|
|
@ -0,0 +1,12 @@
|
|||
namespace Elsa.Options;
|
||||
|
||||
/// <summary>
|
||||
/// Options for the localhost permission requirement.
|
||||
/// </summary>
|
||||
public class LocalHostPermissionRequirementOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets whether localhost requests may satisfy the security-root permission requirement without other credentials.
|
||||
/// </summary>
|
||||
public bool EnableLocalHostPermissionGrant { get; set; }
|
||||
}
|
||||
|
|
@ -1,14 +1,16 @@
|
|||
using System.Security.Claims;
|
||||
using Elsa.Extensions;
|
||||
using Elsa.Options;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Elsa.Requirements;
|
||||
|
||||
/// <summary>
|
||||
/// Add the "create:application" permission to the current user if the request is local.
|
||||
/// Adds security-root bootstrap permissions to the current user when explicit localhost permission grants are enabled and the request is local.
|
||||
/// </summary>
|
||||
public class LocalHostPermissionRequirement : IAuthorizationRequirement
|
||||
{
|
||||
|
|
@ -18,32 +20,58 @@ public class LocalHostPermissionRequirement : IAuthorizationRequirement
|
|||
[PublicAPI]
|
||||
public class LocalHostPermissionRequirementHandler : AuthorizationHandler<LocalHostPermissionRequirement>
|
||||
{
|
||||
private static readonly string[] BootstrapPermissions =
|
||||
[
|
||||
"create:application",
|
||||
"create:user",
|
||||
"create:role"
|
||||
];
|
||||
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly IOptions<LocalHostPermissionRequirementOptions> _options;
|
||||
|
||||
/// <inheritdoc />
|
||||
public LocalHostPermissionRequirementHandler(IHttpContextAccessor httpContextAccessor)
|
||||
public LocalHostPermissionRequirementHandler(IHttpContextAccessor httpContextAccessor) : this(
|
||||
httpContextAccessor,
|
||||
Microsoft.Extensions.Options.Options.Create(new LocalHostPermissionRequirementOptions()))
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public LocalHostPermissionRequirementHandler(IHttpContextAccessor httpContextAccessor, IOptions<LocalHostPermissionRequirementOptions> options)
|
||||
{
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_options = options;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, LocalHostPermissionRequirement requirement)
|
||||
{
|
||||
if (_httpContextAccessor.HttpContext?.Request.IsLocal() == false)
|
||||
if (!_options.Value.EnableLocalHostPermissionGrant)
|
||||
return Task.CompletedTask;
|
||||
|
||||
var currentIdentity = context.User.Identity;
|
||||
|
||||
if (currentIdentity?.IsAuthenticated == false)
|
||||
if (_httpContextAccessor.HttpContext?.Request.IsLocal() != true)
|
||||
return Task.CompletedTask;
|
||||
|
||||
if (context.User.Identities.Any(x => x.IsAuthenticated))
|
||||
{
|
||||
var identity = new ClaimsIdentity(JwtBearerDefaults.AuthenticationScheme);
|
||||
identity.AddClaim(new Claim("permissions", "create:application"));
|
||||
identity.AddClaim(new Claim("permissions", "create:user"));
|
||||
identity.AddClaim(new Claim("permissions", "create:role"));
|
||||
context.User.AddIdentity(identity);
|
||||
if (HasBootstrapPermissions(context.User))
|
||||
context.Succeed(requirement);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
var identity = new ClaimsIdentity(JwtBearerDefaults.AuthenticationScheme);
|
||||
identity.AddClaims(BootstrapPermissions.Select(permission => new Claim(PermissionNames.ClaimType, permission)));
|
||||
context.User.AddIdentity(identity);
|
||||
|
||||
context.Succeed(requirement);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool HasBootstrapPermissions(ClaimsPrincipal user)
|
||||
{
|
||||
var permissions = user.FindAll(PermissionNames.ClaimType).Select(x => x.Value).ToHashSet(StringComparer.Ordinal);
|
||||
return permissions.Contains(PermissionNames.All) || BootstrapPermissions.All(permissions.Contains);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using Elsa.Features.Services;
|
|||
using Elsa.Identity.Constants;
|
||||
using Elsa.Identity.Options;
|
||||
using Elsa.Identity.Providers;
|
||||
using Elsa.Options;
|
||||
using Elsa.Requirements;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
|
|
@ -22,17 +23,32 @@ public class DefaultAuthenticationFeature : FeatureBase
|
|||
{
|
||||
private const string MultiScheme = "Jwt-or-ApiKey";
|
||||
private Func<AuthenticationBuilder, AuthenticationBuilder> _configureApiKeyAuthorization = builder => builder.AddApiKeyInAuthorizationHeader<DefaultApiKeyProvider>();
|
||||
private Action<AuthorizationOptions>? _configureAuthorizationOptions;
|
||||
|
||||
/// <inheritdoc />
|
||||
public DefaultAuthenticationFeature(IModule module) : base(module)
|
||||
{
|
||||
ConfigureAuthorizationOptions = ConfigureDefaultSecurityRootPolicy;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="ApiKeyProviderType"/>.
|
||||
/// </summary>
|
||||
public Type ApiKeyProviderType { get; set; } = typeof(DefaultApiKeyProvider);
|
||||
public Action<AuthorizationOptions> ConfigureAuthorizationOptions { get; set; } = options => options.AddPolicy(IdentityPolicyNames.SecurityRoot, policy => policy.AddRequirements(new LocalHostPermissionRequirement()));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the authorization options configuration.
|
||||
/// </summary>
|
||||
public Action<AuthorizationOptions> ConfigureAuthorizationOptions
|
||||
{
|
||||
get => _configureAuthorizationOptions ?? ConfigureDefaultSecurityRootPolicy;
|
||||
set => _configureAuthorizationOptions = value ?? ConfigureDefaultSecurityRootPolicy;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether localhost requests may satisfy the security-root permission requirement without other credentials.
|
||||
/// </summary>
|
||||
public bool EnableLocalHostPermissionGrant { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Configures the API key provider type.
|
||||
|
|
@ -79,23 +95,40 @@ public class DefaultAuthenticationFeature : FeatureBase
|
|||
/// </summary>
|
||||
/// <returns>The current <see cref="DefaultAuthenticationFeature"/>.</returns>
|
||||
public DefaultAuthenticationFeature UseDevelopmentAdminApiKey() => UseAdminApiKey(AdminApiKeyProvider.DevelopmentApiKey);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Disables the local host requirement for the security root policy.
|
||||
/// This is useful when privileged identity bootstrap is handled through features such as <see cref="DefaultAdminUserFeature"/>.
|
||||
/// Enables the legacy localhost permission grant for the security root policy.
|
||||
/// </summary>
|
||||
public DefaultAuthenticationFeature DisableLocalHostRequirement()
|
||||
public DefaultAuthenticationFeature EnableLocalHostPermissionGrantForSecurityRoot()
|
||||
{
|
||||
ConfigureAuthorizationOptions = options => options.AddPolicy(IdentityPolicyNames.SecurityRoot, policy => policy.RequireAuthenticatedUser());
|
||||
EnableLocalHostPermissionGrant = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disables the localhost permission grant for the security root policy.
|
||||
/// This is useful when privileged identity bootstrap is handled through features such as <see cref="DefaultAdminUserFeature"/>.
|
||||
/// </summary>
|
||||
public DefaultAuthenticationFeature DisableLocalHostPermissionGrantForSecurityRoot()
|
||||
{
|
||||
EnableLocalHostPermissionGrant = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disables the legacy localhost permission grant for the security root policy.
|
||||
/// This is useful when privileged identity bootstrap is handled through features such as <see cref="DefaultAdminUserFeature"/>.
|
||||
/// </summary>
|
||||
[Obsolete("Use DisableLocalHostPermissionGrantForSecurityRoot instead.")]
|
||||
public DefaultAuthenticationFeature DisableLocalHostRequirement() => DisableLocalHostPermissionGrantForSecurityRoot();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Apply()
|
||||
{
|
||||
Services.ConfigureOptions<ConfigureJwtBearerOptions>();
|
||||
Services.Configure<AdminApiKeyOptions>(_ => { });
|
||||
Services.AddIdentityTokenOptionsValidation();
|
||||
Services.Configure<LocalHostPermissionRequirementOptions>(options => options.EnableLocalHostPermissionGrant = EnableLocalHostPermissionGrant);
|
||||
|
||||
var authBuilder = Services
|
||||
.AddAuthentication(MultiScheme)
|
||||
|
|
@ -119,4 +152,22 @@ public class DefaultAuthenticationFeature : FeatureBase
|
|||
Services.AddScoped<IApiKeyProvider>(sp => (IApiKeyProvider)sp.GetRequiredService(ApiKeyProviderType));
|
||||
Services.AddAuthorization(ConfigureAuthorizationOptions);
|
||||
}
|
||||
|
||||
private static void ConfigureAuthenticatedSecurityRootPolicy(AuthorizationOptions options)
|
||||
{
|
||||
options.AddPolicy(IdentityPolicyNames.SecurityRoot, policy => policy.RequireAuthenticatedUser());
|
||||
}
|
||||
|
||||
private void ConfigureDefaultSecurityRootPolicy(AuthorizationOptions options)
|
||||
{
|
||||
if (EnableLocalHostPermissionGrant)
|
||||
ConfigureLocalHostSecurityRootPolicy(options);
|
||||
else
|
||||
ConfigureAuthenticatedSecurityRootPolicy(options);
|
||||
}
|
||||
|
||||
private static void ConfigureLocalHostSecurityRootPolicy(AuthorizationOptions options)
|
||||
{
|
||||
options.AddPolicy(IdentityPolicyNames.SecurityRoot, policy => policy.AddRequirements(new LocalHostPermissionRequirement()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ identity.UseDefaultAdmin("admin", "REPLACE_WITH_SECURE_BOOTSTRAP_PASSWORD", "adm
|
|||
- Do not keep development defaults in production.
|
||||
- Prefer environment variables or a secret manager for admin credentials.
|
||||
- After first bootstrap, rotate credentials according to your security policy.
|
||||
- Localhost requests no longer satisfy `SecurityRoot` by default. Legacy localhost bootstrap requires an explicit opt-in: call `EnableLocalHostPermissionGrantForSecurityRoot()` in code-first configuration or set `EnableLocalHostPermissionGrant` on the shell `DefaultAuthentication` feature; prefer `DefaultAdminUser` instead.
|
||||
|
||||
## Secret Hashing
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ using Elsa.Extensions;
|
|||
using Elsa.Identity.Constants;
|
||||
using Elsa.Identity.Options;
|
||||
using Elsa.Identity.Providers;
|
||||
using Elsa.Options;
|
||||
using Elsa.PackageManifest.Generator.Hints;
|
||||
using Elsa.Requirements;
|
||||
using JetBrains.Annotations;
|
||||
|
|
@ -53,6 +54,11 @@ public class DefaultAuthenticationFeature : IShellFeature
|
|||
RestartRequired = true)]
|
||||
public bool UseDevelopmentAdminApiKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether localhost requests may satisfy the security-root permission requirement without other credentials.
|
||||
/// </summary>
|
||||
public bool EnableLocalHostPermissionGrant { get; set; }
|
||||
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
var resolvedAdminApiKey = UseDevelopmentAdminApiKey ? AdminApiKeyProvider.DevelopmentApiKey : AdminApiKey;
|
||||
|
|
@ -61,6 +67,7 @@ public class DefaultAuthenticationFeature : IShellFeature
|
|||
|
||||
services.ConfigureOptions<ConfigureJwtBearerOptions>();
|
||||
services.AddIdentityTokenOptionsValidation();
|
||||
services.Configure<LocalHostPermissionRequirementOptions>(options => options.EnableLocalHostPermissionGrant = EnableLocalHostPermissionGrant);
|
||||
services.Configure<AdminApiKeyOptions>(options =>
|
||||
{
|
||||
options.ApiKey = resolvedAdminApiKey;
|
||||
|
|
@ -93,7 +100,10 @@ public class DefaultAuthenticationFeature : IShellFeature
|
|||
|
||||
services.AddAuthorization(options =>
|
||||
{
|
||||
options.AddPolicy(IdentityPolicyNames.SecurityRoot, policy => policy.RequireAuthenticatedUser());
|
||||
if (EnableLocalHostPermissionGrant)
|
||||
options.AddPolicy(IdentityPolicyNames.SecurityRoot, policy => policy.AddRequirements(new LocalHostPermissionRequirement()));
|
||||
else
|
||||
options.AddPolicy(IdentityPolicyNames.SecurityRoot, policy => policy.RequireAuthenticatedUser());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,85 @@
|
|||
using Elsa.Features.Services;
|
||||
using Elsa.Identity.Features;
|
||||
using Elsa.Requirements;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Authorization.Infrastructure;
|
||||
using NSubstitute;
|
||||
|
||||
namespace Elsa.Identity.UnitTests.Features;
|
||||
|
||||
public class DefaultAuthenticationFeatureTests
|
||||
{
|
||||
[Fact]
|
||||
public void DefaultSecurityRootPolicyRequiresAuthenticatedUser()
|
||||
{
|
||||
var feature = new DefaultAuthenticationFeature(Substitute.For<IModule>());
|
||||
var options = new AuthorizationOptions();
|
||||
|
||||
feature.ConfigureAuthorizationOptions(options);
|
||||
|
||||
var policy = options.GetPolicy(IdentityPolicyNames.SecurityRoot);
|
||||
|
||||
Assert.NotNull(policy);
|
||||
Assert.Contains(policy.Requirements, requirement => requirement is DenyAnonymousAuthorizationRequirement);
|
||||
Assert.DoesNotContain(policy.Requirements, requirement => requirement is LocalHostPermissionRequirement);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EnableLocalHostPermissionGrantForSecurityRootConfiguresExplicitLocalhostPolicy()
|
||||
{
|
||||
var feature = new DefaultAuthenticationFeature(Substitute.For<IModule>());
|
||||
var options = new AuthorizationOptions();
|
||||
|
||||
feature.EnableLocalHostPermissionGrantForSecurityRoot();
|
||||
feature.ConfigureAuthorizationOptions(options);
|
||||
|
||||
var policy = options.GetPolicy(IdentityPolicyNames.SecurityRoot);
|
||||
|
||||
Assert.True(feature.EnableLocalHostPermissionGrant);
|
||||
Assert.NotNull(policy);
|
||||
Assert.Contains(policy.Requirements, requirement => requirement is LocalHostPermissionRequirement);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EnableLocalHostPermissionGrantDoesNotOverwriteCustomAuthorizationConfiguration()
|
||||
{
|
||||
var feature = new DefaultAuthenticationFeature(Substitute.For<IModule>());
|
||||
feature.ConfigureAuthorizationOptions = options => options.AddPolicy("Custom", policy => policy.RequireAuthenticatedUser());
|
||||
|
||||
feature.EnableLocalHostPermissionGrantForSecurityRoot();
|
||||
var options = new AuthorizationOptions();
|
||||
feature.ConfigureAuthorizationOptions(options);
|
||||
|
||||
Assert.True(feature.EnableLocalHostPermissionGrant);
|
||||
Assert.NotNull(options.GetPolicy("Custom"));
|
||||
Assert.Null(options.GetPolicy(IdentityPolicyNames.SecurityRoot));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NullConfigureAuthorizationOptionsFallsBackToDefaultSecurityRootPolicy()
|
||||
{
|
||||
var feature = new DefaultAuthenticationFeature(Substitute.For<IModule>())
|
||||
{
|
||||
ConfigureAuthorizationOptions = null!
|
||||
};
|
||||
var options = new AuthorizationOptions();
|
||||
|
||||
feature.ConfigureAuthorizationOptions(options);
|
||||
|
||||
var policy = options.GetPolicy(IdentityPolicyNames.SecurityRoot);
|
||||
|
||||
Assert.NotNull(policy);
|
||||
Assert.Contains(policy.Requirements, requirement => requirement is DenyAnonymousAuthorizationRequirement);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DisableLocalHostPermissionGrantForSecurityRootClearsOptInFlag()
|
||||
{
|
||||
var feature = new DefaultAuthenticationFeature(Substitute.For<IModule>());
|
||||
|
||||
feature.EnableLocalHostPermissionGrantForSecurityRoot();
|
||||
feature.DisableLocalHostPermissionGrantForSecurityRoot();
|
||||
|
||||
Assert.False(feature.EnableLocalHostPermissionGrant);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue