Guard optional Identity role services
This commit is contained in:
parent
c1ea463e0e
commit
62fcc302c7
|
|
@ -20,8 +20,8 @@ namespace Elsa.ExternalAuthentication.Services;
|
||||||
public sealed class ExternalAuthenticationRoleDeletionDependencyContributor(
|
public sealed class ExternalAuthenticationRoleDeletionDependencyContributor(
|
||||||
IIdentityProviderConnectionStore store,
|
IIdentityProviderConnectionStore store,
|
||||||
IOptionsMonitor<ExternalAuthenticationOptions> options,
|
IOptionsMonitor<ExternalAuthenticationOptions> options,
|
||||||
IRoleAuthorizationService roleAuthorizationService,
|
IEnumerable<IRoleAuthorizationService> roleAuthorizationServices,
|
||||||
IRoleStore roleStore,
|
IEnumerable<IRoleStore> roleStores,
|
||||||
IConnectionRegistryVersionStore registryVersions,
|
IConnectionRegistryVersionStore registryVersions,
|
||||||
ConnectionRevisionCalculator revisionCalculator,
|
ConnectionRevisionCalculator revisionCalculator,
|
||||||
ExternalAuthenticationSecurityNotifier notifier,
|
ExternalAuthenticationSecurityNotifier notifier,
|
||||||
|
|
@ -67,6 +67,9 @@ public sealed class ExternalAuthenticationRoleDeletionDependencyContributor(
|
||||||
|
|
||||||
public async ValueTask<RoleReferenceRemovalValidationResult> ValidateRemovalAsync(RoleReferenceRemovalRequest request, CancellationToken cancellationToken = default)
|
public async ValueTask<RoleReferenceRemovalValidationResult> ValidateRemovalAsync(RoleReferenceRemovalRequest request, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
|
var roleAuthorizationService = roleAuthorizationServices.SingleOrDefault();
|
||||||
|
if (roleAuthorizationService is null)
|
||||||
|
return new RoleReferenceRemovalValidationResult.Forbidden("role_authorization_unavailable");
|
||||||
if (!permissionEvaluator.HasPermission(request.Actor, ExternalAuthenticationResourcePermissions.Connections, CoreVerbs.Update) ||
|
if (!permissionEvaluator.HasPermission(request.Actor, ExternalAuthenticationResourcePermissions.Connections, CoreVerbs.Update) ||
|
||||||
!permissionEvaluator.HasPermission(request.Actor, ExternalAuthenticationResourcePermissions.Policies, CoreVerbs.Update) ||
|
!permissionEvaluator.HasPermission(request.Actor, ExternalAuthenticationResourcePermissions.Policies, CoreVerbs.Update) ||
|
||||||
!permissionEvaluator.HasPermission(request.Actor, ExternalAuthenticationResourcePermissions.PolicyDefaultRoles, CoreVerbs.Update))
|
!permissionEvaluator.HasPermission(request.Actor, ExternalAuthenticationResourcePermissions.PolicyDefaultRoles, CoreVerbs.Update))
|
||||||
|
|
@ -116,6 +119,9 @@ public sealed class ExternalAuthenticationRoleDeletionDependencyContributor(
|
||||||
|
|
||||||
public async ValueTask<RoleReferenceRemovalResult> RemoveEditableReferencesAsync(RoleReferenceRemovalRequest request, CancellationToken cancellationToken = default)
|
public async ValueTask<RoleReferenceRemovalResult> RemoveEditableReferencesAsync(RoleReferenceRemovalRequest request, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
|
var roleAuthorizationService = roleAuthorizationServices.SingleOrDefault();
|
||||||
|
if (roleAuthorizationService is null)
|
||||||
|
return new RoleReferenceRemovalResult.Failed("role_authorization_unavailable", []);
|
||||||
var validation = await ValidateRemovalAsync(request, cancellationToken);
|
var validation = await ValidateRemovalAsync(request, cancellationToken);
|
||||||
if (validation is RoleReferenceRemovalValidationResult.Forbidden forbidden)
|
if (validation is RoleReferenceRemovalValidationResult.Forbidden forbidden)
|
||||||
return new RoleReferenceRemovalResult.Failed(forbidden.Code, []);
|
return new RoleReferenceRemovalResult.Failed(forbidden.Code, []);
|
||||||
|
|
@ -141,6 +147,9 @@ public sealed class ExternalAuthenticationRoleDeletionDependencyContributor(
|
||||||
|
|
||||||
if (request.SelectedReferences is not null && removesLastDefaultRole)
|
if (request.SelectedReferences is not null && removesLastDefaultRole)
|
||||||
{
|
{
|
||||||
|
var roleStore = roleStores.SingleOrDefault();
|
||||||
|
if (roleStore is null)
|
||||||
|
return new RoleReferenceRemovalResult.Failed("replacement_role_unavailable_or_unauthorized", changedOwnerIds);
|
||||||
var replacement = await roleStore.FindAsync(new() { Id = request.ReplacementRoleId }, cancellationToken);
|
var replacement = await roleStore.FindAsync(new() { Id = request.ReplacementRoleId }, cancellationToken);
|
||||||
if (replacement is null ||
|
if (replacement is null ||
|
||||||
!await roleAuthorizationService.CanAssignRolesAsync(request.Actor, [replacement.Id], cancellationToken))
|
!await roleAuthorizationService.CanAssignRolesAsync(request.Actor, [replacement.Id], cancellationToken))
|
||||||
|
|
|
||||||
|
|
@ -188,8 +188,8 @@ public class ExternalAuthenticationRoleDeletionDependencyContributorTests
|
||||||
var contributor = new ExternalAuthenticationRoleDeletionDependencyContributor(
|
var contributor = new ExternalAuthenticationRoleDeletionDependencyContributor(
|
||||||
connectionStore,
|
connectionStore,
|
||||||
new MutableOptionsMonitor<ExternalAuthenticationOptions>(new ExternalAuthenticationOptions()),
|
new MutableOptionsMonitor<ExternalAuthenticationOptions>(new ExternalAuthenticationOptions()),
|
||||||
roleAuthorizationService,
|
[roleAuthorizationService],
|
||||||
roleStore,
|
[roleStore],
|
||||||
versions,
|
versions,
|
||||||
new ConnectionRevisionCalculator(),
|
new ConnectionRevisionCalculator(),
|
||||||
new ExternalAuthenticationSecurityNotifier(services),
|
new ExternalAuthenticationSecurityNotifier(services),
|
||||||
|
|
@ -291,8 +291,8 @@ public class ExternalAuthenticationRoleDeletionDependencyContributorTests
|
||||||
var contributor = new ExternalAuthenticationRoleDeletionDependencyContributor(
|
var contributor = new ExternalAuthenticationRoleDeletionDependencyContributor(
|
||||||
store,
|
store,
|
||||||
new MutableOptionsMonitor<ExternalAuthenticationOptions>(new ExternalAuthenticationOptions { ConfigurationConnections = configuredConnections.ToList() }),
|
new MutableOptionsMonitor<ExternalAuthenticationOptions>(new ExternalAuthenticationOptions { ConfigurationConnections = configuredConnections.ToList() }),
|
||||||
new RoleAuthorizationService(new StoreBasedRoleProvider(roleStore), new PermissionEvaluator()),
|
[new RoleAuthorizationService(new StoreBasedRoleProvider(roleStore), new PermissionEvaluator())],
|
||||||
roleStore,
|
[roleStore],
|
||||||
versions,
|
versions,
|
||||||
new ConnectionRevisionCalculator(),
|
new ConnectionRevisionCalculator(),
|
||||||
new ExternalAuthenticationSecurityNotifier(services),
|
new ExternalAuthenticationSecurityNotifier(services),
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ using Elsa.ExternalAuthentication.Permissions;
|
||||||
using Elsa.ExternalAuthentication.Providers;
|
using Elsa.ExternalAuthentication.Providers;
|
||||||
using Elsa.ExternalAuthentication.Services;
|
using Elsa.ExternalAuthentication.Services;
|
||||||
using Elsa.ExternalAuthentication.Stores.InMemory;
|
using Elsa.ExternalAuthentication.Stores.InMemory;
|
||||||
|
using Elsa.Identity.Contracts;
|
||||||
using Microsoft.AspNetCore.RateLimiting;
|
using Microsoft.AspNetCore.RateLimiting;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
@ -62,4 +63,23 @@ public class ExternalAuthenticationServiceCollectionTests
|
||||||
Assert.Contains(services, descriptor => descriptor.ServiceType == typeof(IProviderHttpClient) && descriptor.ImplementationFactory is not null);
|
Assert.Contains(services, descriptor => descriptor.ServiceType == typeof(IProviderHttpClient) && descriptor.ImplementationFactory is not null);
|
||||||
Assert.Contains(services, descriptor => descriptor.ServiceType == typeof(OpenIdConnectExternalAuthenticationAdapter));
|
Assert.Contains(services, descriptor => descriptor.ServiceType == typeof(OpenIdConnectExternalAuthenticationAdapter));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RoleDeletionContributorResolvesWhenIdentityIsNotRegistered()
|
||||||
|
{
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
services.AddSingleton<ISystemClock>(new TestSystemClock(DateTimeOffset.UnixEpoch));
|
||||||
|
services.AddExternalAuthenticationServices(options =>
|
||||||
|
{
|
||||||
|
options.AllowedUnlinkedIdentityPolicyTypes.Clear();
|
||||||
|
options.AllowedPermissionGrantSourceTypes.Clear();
|
||||||
|
});
|
||||||
|
|
||||||
|
using var serviceProvider = services.BuildServiceProvider(new ServiceProviderOptions { ValidateScopes = true });
|
||||||
|
using var scope = serviceProvider.CreateScope();
|
||||||
|
|
||||||
|
var contributor = Assert.Single(scope.ServiceProvider.GetServices<IRoleDeletionDependencyContributor>());
|
||||||
|
Assert.IsType<ExternalAuthenticationRoleDeletionDependencyContributor>(contributor);
|
||||||
|
Assert.Empty(scope.ServiceProvider.GetServices<IRoleStore>());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue