From 3b1a8d5f09d99a970386bceda170df0eedf50f06 Mon Sep 17 00:00:00 2001 From: Phil Schneider Date: Fri, 20 Sep 2024 14:51:49 +0200 Subject: [PATCH] fix(idp): delete iam identity provider (#1026) Refs: #1025 Reviewed-By: Norbert Truchsess Co-authored-by: Norbert Truchsess --- .../IIdentityProviderRepository.cs | 1 + .../IdentityProviderRepository.cs | 6 +++ ...ProviderProvisioningProcessTypeExecutor.cs | 45 +++++++++---------- .../IdentityProviderRepositoryTests.cs | 30 +++++++++++++ ...derProvisioningProcessTypeExecutorTests.cs | 15 +++---- 5 files changed, 66 insertions(+), 31 deletions(-) diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/IIdentityProviderRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/IIdentityProviderRepository.cs index 44ec276a19..4f3aeeb91d 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/IIdentityProviderRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/IIdentityProviderRepository.cs @@ -62,4 +62,5 @@ public interface IIdentityProviderRepository IAsyncEnumerable<(string Email, string? FirstName, string? LastName)> GetCompanyUserEmailForIdpWithoutOwnerAndRoleId(IEnumerable userRoleIds, Guid identityProviderId); Task GetIdentityProviderDataForProcessIdAsync(Guid processId); void CreateIdentityProviderAssignedProcessRange(IEnumerable<(Guid IdentityProviderId, Guid ProcessId)> identityProviderProcessIds); + Task GetIamIdentityProviderForIdp(Guid identityProviderId); } diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/IdentityProviderRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/IdentityProviderRepository.cs index 8f97dbce56..b106fa03a3 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/IdentityProviderRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/IdentityProviderRepository.cs @@ -364,4 +364,10 @@ public IAsyncEnumerable GetIdpLinkedCompanyUserIds(Guid identityProviderId public void CreateIdentityProviderAssignedProcessRange(IEnumerable<(Guid IdentityProviderId, Guid ProcessId)> identityProviderProcessIds) => _context.AddRange(identityProviderProcessIds.Select(x => new IdentityProviderAssignedProcess(x.IdentityProviderId, x.ProcessId))); + + public Task GetIamIdentityProviderForIdp(Guid identityProviderId) => + _context.IamIdentityProviders + .Where(x => x.IdentityProviderId == identityProviderId) + .Select(x => x.IamIdpAlias) + .SingleOrDefaultAsync(); } diff --git a/src/processes/IdentityProviderProvisioning.Executor/IdentityProviderProvisioningProcessTypeExecutor.cs b/src/processes/IdentityProviderProvisioning.Executor/IdentityProviderProvisioningProcessTypeExecutor.cs index 3079d0f439..aef3ba4502 100644 --- a/src/processes/IdentityProviderProvisioning.Executor/IdentityProviderProvisioningProcessTypeExecutor.cs +++ b/src/processes/IdentityProviderProvisioning.Executor/IdentityProviderProvisioningProcessTypeExecutor.cs @@ -28,11 +28,11 @@ namespace Org.Eclipse.TractusX.Portal.Backend.Processes.IdentityProviderProvisioning.Executor; -public class IdentityProviderProvisioningProcessTypeExecutor : IProcessTypeExecutor +public class IdentityProviderProvisioningProcessTypeExecutor( + IPortalRepositories portalRepositories, + IProvisioningManager provisioningManager) + : IProcessTypeExecutor { - private readonly IPortalRepositories _portalRepositories; - private readonly IProvisioningManager _provisioningManager; - private static readonly IEnumerable ExecutableProcessSteps = [ ProcessStepTypeId.DELETE_IDP_SHARED_REALM, @@ -41,13 +41,7 @@ public class IdentityProviderProvisioningProcessTypeExecutor : IProcessTypeExecu ProcessStepTypeId.DELETE_IDENTITY_PROVIDER, ]; - private IdpData? _idpData = null; - - public IdentityProviderProvisioningProcessTypeExecutor(IPortalRepositories portalRepositories, IProvisioningManager provisioningManager) - { - _portalRepositories = portalRepositories; - _provisioningManager = provisioningManager; - } + private IdpData? _idpData; public ProcessTypeId GetProcessTypeId() => ProcessTypeId.IDENTITYPROVIDER_PROVISIONING; public bool IsExecutableStepTypeId(ProcessStepTypeId processStepTypeId) => ExecutableProcessSteps.Contains(processStepTypeId); @@ -56,13 +50,9 @@ public IdentityProviderProvisioningProcessTypeExecutor(IPortalRepositories porta public async ValueTask InitializeProcess(Guid processId, IEnumerable processStepTypeIds) { - var idpData = await _portalRepositories.GetInstance().GetIdentityProviderDataForProcessIdAsync(processId).ConfigureAwait(ConfigureAwaitOptions.None); + _idpData = await portalRepositories.GetInstance().GetIdentityProviderDataForProcessIdAsync(processId).ConfigureAwait(ConfigureAwaitOptions.None) + ?? throw new ConflictException($"process {processId} does not exist or is not associated with an Identity Provider"); - if (idpData == null) - { - throw new ConflictException($"process {processId} does not exist or is not associated with an Identity Provider"); - } - _idpData = idpData; return new IProcessTypeExecutor.InitializationResult(false, null); } @@ -85,7 +75,7 @@ public IdentityProviderProvisioningProcessTypeExecutor(IPortalRepositories porta ProcessStepTypeId.DELETE_IDP_SHARED_REALM => await DeleteSharedRealmAsync(_idpData).ConfigureAwait(ConfigureAwaitOptions.None), ProcessStepTypeId.DELETE_IDP_SHARED_SERVICEACCOUNT => await DeleteIdpSharedServiceAccount(_idpData).ConfigureAwait(ConfigureAwaitOptions.None), ProcessStepTypeId.DELETE_CENTRAL_IDENTITY_PROVIDER => await DeleteCentralIdentityProvider(_idpData.IamAlias).ConfigureAwait(ConfigureAwaitOptions.None), - ProcessStepTypeId.DELETE_IDENTITY_PROVIDER => DeleteIdentityProvider(_idpData.IdentityProviderId), + ProcessStepTypeId.DELETE_IDENTITY_PROVIDER => await DeleteIdentityProvider(_idpData.IdentityProviderId).ConfigureAwait(ConfigureAwaitOptions.None), _ => (null, ProcessStepStatusId.TODO, false, null) }; } @@ -94,6 +84,7 @@ public IdentityProviderProvisioningProcessTypeExecutor(IPortalRepositories porta (stepStatusId, processMessage, nextStepTypeIds) = ProcessError(ex, processStepTypeId); modified = true; } + return new IProcessTypeExecutor.StepExecutionResult(modified, stepStatusId, nextStepTypeIds, null, processMessage); } @@ -112,9 +103,10 @@ private static (ProcessStepStatusId StatusId, string? ProcessMessage, IEnumerabl { return ([ProcessStepTypeId.DELETE_CENTRAL_IDENTITY_PROVIDER], ProcessStepStatusId.SKIPPED, false, $"IdentityProvider {idpData.IamAlias} is not a shared idp"); } + try { - await _provisioningManager.DeleteSharedRealmAsync(idpData.IamAlias).ConfigureAwait(ConfigureAwaitOptions.None); + await provisioningManager.DeleteSharedRealmAsync(idpData.IamAlias).ConfigureAwait(ConfigureAwaitOptions.None); return ([ProcessStepTypeId.DELETE_IDP_SHARED_SERVICEACCOUNT], ProcessStepStatusId.DONE, false, null); } catch (KeycloakEntityNotFoundException) @@ -129,9 +121,10 @@ private static (ProcessStepStatusId StatusId, string? ProcessMessage, IEnumerabl { return ([ProcessStepTypeId.DELETE_CENTRAL_IDENTITY_PROVIDER], ProcessStepStatusId.SKIPPED, false, $"IdentityProvider {idpData.IamAlias} is not a shared idp"); } + try { - await _provisioningManager.DeleteIdpSharedServiceAccount(idpData.IamAlias).ConfigureAwait(ConfigureAwaitOptions.None); + await provisioningManager.DeleteIdpSharedServiceAccount(idpData.IamAlias).ConfigureAwait(ConfigureAwaitOptions.None); return ([ProcessStepTypeId.DELETE_CENTRAL_IDENTITY_PROVIDER], ProcessStepStatusId.DONE, false, null); } catch (KeycloakEntityNotFoundException) @@ -144,7 +137,7 @@ private static (ProcessStepStatusId StatusId, string? ProcessMessage, IEnumerabl { try { - await _provisioningManager.DeleteCentralIdentityProviderAsync(alias).ConfigureAwait(ConfigureAwaitOptions.None); + await provisioningManager.DeleteCentralIdentityProviderAsync(alias).ConfigureAwait(ConfigureAwaitOptions.None); return ([ProcessStepTypeId.DELETE_IDENTITY_PROVIDER], ProcessStepStatusId.DONE, false, null); } catch (KeycloakEntityNotFoundException) @@ -153,9 +146,15 @@ private static (ProcessStepStatusId StatusId, string? ProcessMessage, IEnumerabl } } - private (IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage) DeleteIdentityProvider(Guid identityProviderId) + private async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> DeleteIdentityProvider(Guid identityProviderId) { - var identityProviderRepository = _portalRepositories.GetInstance(); + var identityProviderRepository = portalRepositories.GetInstance(); + var alias = await identityProviderRepository.GetIamIdentityProviderForIdp(identityProviderId).ConfigureAwait(ConfigureAwaitOptions.None); + if (alias != null) + { + identityProviderRepository.DeleteIamIdentityProvider(alias); + } + identityProviderRepository.DeleteIdentityProvider(identityProviderId); return (null, ProcessStepStatusId.DONE, true, null); } diff --git a/tests/portalbackend/PortalBackend.DBAccess.Tests/IdentityProviderRepositoryTests.cs b/tests/portalbackend/PortalBackend.DBAccess.Tests/IdentityProviderRepositoryTests.cs index 637ddf143a..1b1c6d7ec9 100644 --- a/tests/portalbackend/PortalBackend.DBAccess.Tests/IdentityProviderRepositoryTests.cs +++ b/tests/portalbackend/PortalBackend.DBAccess.Tests/IdentityProviderRepositoryTests.cs @@ -509,6 +509,36 @@ public async Task DeleteCompanyIdentityProviderRange_ReturnsExpected() #endregion + #region GetIamIdentityProviderForIdp + + [Fact] + public async Task GetIamIdentityProviderForIdp_WithExisting_ReturnsAlias() + { + // Arrange + var sut = await CreateSut(); + + // Act + var result = await sut.GetIamIdentityProviderForIdp(new Guid("38f56465-ce26-4f25-9745-1791620dc203")); + + // Assert + result.Should().Be("to-decline-alias"); + } + + [Fact] + public async Task GetIamIdentityProviderForIdp_WithNotExisting_ReturnsNull() + { + // Arrange + var sut = await CreateSut(); + + // Act + var result = await sut.GetIamIdentityProviderForIdp(Guid.NewGuid()); + + // Assert + result.Should().BeNull(); + } + + #endregion + #region Setup private async Task<(IdentityProviderRepository, PortalDbContext)> CreateSutWithContext() diff --git a/tests/processes/IdentityProviderProvisioning.Executor.Tests/IdentityProviderProvisioningProcessTypeExecutorTests.cs b/tests/processes/IdentityProviderProvisioning.Executor.Tests/IdentityProviderProvisioningProcessTypeExecutorTests.cs index 23b4c74432..f77ef349ed 100644 --- a/tests/processes/IdentityProviderProvisioning.Executor.Tests/IdentityProviderProvisioningProcessTypeExecutorTests.cs +++ b/tests/processes/IdentityProviderProvisioning.Executor.Tests/IdentityProviderProvisioningProcessTypeExecutorTests.cs @@ -31,8 +31,6 @@ public class IdentityProviderProvisioningProcessTypeExecutorTests private readonly Guid _ownProcessId = Guid.NewGuid(); private readonly IdpData _sharedIdpData; private readonly IdpData _ownIdpData; - private readonly IPortalRepositories _portalRepositories; - private readonly IProvisioningManager _provisioningManager; private readonly IIdentityProviderRepository _identityProviderRepository; private readonly IFixture _fixture; private readonly IdentityProviderProvisioningProcessTypeExecutor _executor; @@ -44,18 +42,18 @@ public IdentityProviderProvisioningProcessTypeExecutorTests() .ForEach(b => _fixture.Behaviors.Remove(b)); _fixture.Behaviors.Add(new OmitOnRecursionBehavior()); - _portalRepositories = A.Fake(); - _provisioningManager = A.Fake(); + var portalRepositories = A.Fake(); + var provisioningManager = A.Fake(); _identityProviderRepository = A.Fake(); _sharedIdpData = new IdpData(Guid.NewGuid(), "sharedIdp", IdentityProviderTypeId.SHARED); _ownIdpData = new IdpData(Guid.NewGuid(), "ownIdp", IdentityProviderTypeId.OWN); - A.CallTo(() => _portalRepositories.GetInstance()) + A.CallTo(() => portalRepositories.GetInstance()) .Returns(_identityProviderRepository); - _executor = new IdentityProviderProvisioningProcessTypeExecutor(_portalRepositories, _provisioningManager); + _executor = new IdentityProviderProvisioningProcessTypeExecutor(portalRepositories, provisioningManager); SetupFakes(); } @@ -184,6 +182,7 @@ public async Task ExecuteProcessStep_WithValidTriggerData_CallsExpected(bool sha result.ScheduleStepTypeIds.Should().ContainSingle() .Which.Should().Be(nextprocessStepTypeId); } + result.ProcessStepStatusId.Should().Be(stepStatus); result.ProcessMessage.Should().Be(message); result.SkipStepTypeIds.Should().BeNull(); @@ -191,7 +190,8 @@ public async Task ExecuteProcessStep_WithValidTriggerData_CallsExpected(bool sha #endregion - #region SetUp + #region SetUp + private void SetupFakes() { A.CallTo(() => _identityProviderRepository.GetIdentityProviderDataForProcessIdAsync(_sharedProcessId)) @@ -201,5 +201,4 @@ private void SetupFakes() } #endregion - }