From e39d82d5fd91b181cc4cb670977f9dca79475c7b Mon Sep 17 00:00:00 2001 From: AnuragNagpure <145100366+AnuragNagpure@users.noreply.github.com> Date: Fri, 6 Sep 2024 18:01:30 +0530 Subject: [PATCH 1/5] fix(connectors): add company connectors type filter to get connectors (#972) Refs: #945 --- .../Repositories/ConnectorsRepository.cs | 4 +++- .../ConnectorsBusinessLogicTests.cs | 24 +++++++++++++++++++ .../ConnectorsControllerIntegrationTests.cs | 2 +- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/ConnectorsRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/ConnectorsRepository.cs index bfef9302d8..4268eba203 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/ConnectorsRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/ConnectorsRepository.cs @@ -46,7 +46,9 @@ public ConnectorsRepository(PortalDbContext portalDbContext) skip, take, _context.Connectors.AsNoTracking() - .Where(x => x.ProviderId == companyId && x.StatusId != ConnectorStatusId.INACTIVE) + .Where(x => x.ProviderId == companyId && + x.StatusId != ConnectorStatusId.INACTIVE && + x.TypeId == ConnectorTypeId.COMPANY_CONNECTOR) .GroupBy(c => c.ProviderId), connector => connector.OrderByDescending(connector => connector.Name), con => new ConnectorData( diff --git a/tests/administration/Administration.Service.Tests/BusinessLogic/ConnectorsBusinessLogicTests.cs b/tests/administration/Administration.Service.Tests/BusinessLogic/ConnectorsBusinessLogicTests.cs index ae7ef30b4f..71f852372b 100644 --- a/tests/administration/Administration.Service.Tests/BusinessLogic/ConnectorsBusinessLogicTests.cs +++ b/tests/administration/Administration.Service.Tests/BusinessLogic/ConnectorsBusinessLogicTests.cs @@ -133,6 +133,30 @@ public async Task GetAllCompanyConnectorDatas_WithValidData_ReturnsExpected(int result.Content.Should().HaveCount(resultPageSize); } + [Theory] + [InlineData(0, 10, 5, 1, 0, 5)] + public async Task GetAllCompanyConnectorDatasWithCheckTypeCompanyConnector_WithValidData_ReturnsExpected(int page, int size, int numberOfElements, int numberOfPages, int resultPage, int resultPageSize) + { + var data = _fixture.Build() + .With(x => x.Type, ConnectorTypeId.COMPANY_CONNECTOR) + .CreateMany(numberOfElements).ToImmutableArray(); + + A.CallTo(() => _connectorsRepository.GetAllCompanyConnectorsForCompanyId(A._)) + .Returns((int skip, int take) => Task.FromResult?>(new(data.Length, data.Skip(skip).Take(take)))); + + // Act + var result = await _logic.GetAllCompanyConnectorDatas(page, size); + + // Assert + A.CallTo(() => _connectorsRepository.GetAllCompanyConnectorsForCompanyId(_identity.CompanyId)).MustHaveHappenedOnceExactly(); + result.Should().NotBeNull(); + result.Meta.NumberOfElements.Should().Be(numberOfElements); + result.Meta.NumberOfPages.Should().Be(numberOfPages); + result.Meta.Page.Should().Be(resultPage); + result.Meta.PageSize.Should().Be(resultPageSize); + result.Content.Should().HaveCount(resultPageSize); + result.Content.Should().Contain(x => x.Type == ConnectorTypeId.COMPANY_CONNECTOR); + } #endregion #region Create Connector diff --git a/tests/administration/Administration.Service.Tests/IntegrationTests/ConnectorsControllerIntegrationTests.cs b/tests/administration/Administration.Service.Tests/IntegrationTests/ConnectorsControllerIntegrationTests.cs index 6be973ce5a..a4fb38a4a5 100644 --- a/tests/administration/Administration.Service.Tests/IntegrationTests/ConnectorsControllerIntegrationTests.cs +++ b/tests/administration/Administration.Service.Tests/IntegrationTests/ConnectorsControllerIntegrationTests.cs @@ -50,6 +50,6 @@ public async Task GetCompanyConnectorsForCurrentUserAsync_WithTwoConnectors_Retu // Assert response.StatusCode.Should().Be(HttpStatusCode.OK); var pagination = await response.GetResultFromContent>(); - pagination.Content.Should().HaveCount(2); + pagination.Content.Should().HaveCount(1); } } From 321f678301507814b99219373c5fb68964e1dbac Mon Sep 17 00:00:00 2001 From: Phil Schneider Date: Tue, 10 Sep 2024 02:21:33 +0200 Subject: [PATCH 2/5] feat(app): adjust status query param for subscription-status (#969) Refs: #592 --- .../Apps.Service/BusinessLogic/AppsBusinessLogic.cs | 6 +++--- .../Apps.Service/BusinessLogic/IAppsBusinessLogic.cs | 5 +++-- .../Apps.Service/Controllers/AppsController.cs | 7 ++++--- .../Offers.Library/Service/IOfferService.cs | 3 ++- .../Offers.Library/Service/OfferService.cs | 4 ++-- .../BusinessLogic/IServiceBusinessLogic.cs | 2 +- .../BusinessLogic/ServiceBusinessLogic.cs | 4 ++-- .../Controllers/ServicesController.cs | 7 ++++--- .../Repositories/IOfferSubscriptionsRepository.cs | 3 ++- .../Repositories/OfferSubscriptionsRepository.cs | 6 ++++-- .../BusinessLogic/AppBusinessLogicTests.cs | 6 +++--- .../Controllers/AppsControllerTests.cs | 4 ++-- .../Service/OfferServiceTests.cs | 12 ++++++------ .../BusinessLogic/ServiceBusinessLogicTests.cs | 6 +++--- .../Controllers/ServiceControllerTest.cs | 4 ++-- .../OfferSubscriptionRepositoryTest.cs | 4 ++-- 16 files changed, 45 insertions(+), 38 deletions(-) diff --git a/src/marketplace/Apps.Service/BusinessLogic/AppsBusinessLogic.cs b/src/marketplace/Apps.Service/BusinessLogic/AppsBusinessLogic.cs index b33d9ccea0..57032b90e2 100644 --- a/src/marketplace/Apps.Service/BusinessLogic/AppsBusinessLogic.cs +++ b/src/marketplace/Apps.Service/BusinessLogic/AppsBusinessLogic.cs @@ -152,11 +152,11 @@ public async Task AddFavouriteAppForUserAsync(Guid appId) } /// - public Task> GetCompanySubscribedAppSubscriptionStatusesForUserAsync(int page, int size, OfferSubscriptionStatusId? statusId) => - _offerService.GetCompanySubscribedOfferSubscriptionStatusesForUserAsync(page, size, OfferTypeId.APP, DocumentTypeId.APP_LEADIMAGE, statusId); + public Task> GetCompanySubscribedAppSubscriptionStatusesForUserAsync(int page, int size, OfferSubscriptionStatusId? statusId, string? name) => + _offerService.GetCompanySubscribedOfferSubscriptionStatusesForUserAsync(page, size, OfferTypeId.APP, DocumentTypeId.APP_LEADIMAGE, statusId, name); /// - public async Task> GetCompanyProvidedAppSubscriptionStatusesForUserAsync(int page, int size, SubscriptionStatusSorting? sorting, OfferSubscriptionStatusId? statusId, Guid? offerId, string? companyName = null) + public async Task> GetCompanyProvidedAppSubscriptionStatusesForUserAsync(int page, int size, SubscriptionStatusSorting? sorting, OfferSubscriptionStatusId? statusId, Guid? offerId, string? companyName) { if (!string.IsNullOrWhiteSpace(companyName) && !Company.IsMatch(companyName)) { diff --git a/src/marketplace/Apps.Service/BusinessLogic/IAppsBusinessLogic.cs b/src/marketplace/Apps.Service/BusinessLogic/IAppsBusinessLogic.cs index 11f9636933..ec3739ed97 100644 --- a/src/marketplace/Apps.Service/BusinessLogic/IAppsBusinessLogic.cs +++ b/src/marketplace/Apps.Service/BusinessLogic/IAppsBusinessLogic.cs @@ -76,8 +76,9 @@ public interface IAppsBusinessLogic /// page /// size /// + /// /// Returns the details of the subscription status for App user - public Task> GetCompanySubscribedAppSubscriptionStatusesForUserAsync(int page, int size, OfferSubscriptionStatusId? statusId); + public Task> GetCompanySubscribedAppSubscriptionStatusesForUserAsync(int page, int size, OfferSubscriptionStatusId? statusId, string? name); /// /// Retrieves subscription statuses of provided apps of the provided user's company. @@ -89,7 +90,7 @@ public interface IAppsBusinessLogic /// /// /// Async enumberable of user's company's provided apps' statuses. - public Task> GetCompanyProvidedAppSubscriptionStatusesForUserAsync(int page, int size, SubscriptionStatusSorting? sorting, OfferSubscriptionStatusId? statusId, Guid? offerId, string? companyName = null); + public Task> GetCompanyProvidedAppSubscriptionStatusesForUserAsync(int page, int size, SubscriptionStatusSorting? sorting, OfferSubscriptionStatusId? statusId, Guid? offerId, string? companyName); /// /// Adds a subscription relation between an application and a user's company. diff --git a/src/marketplace/Apps.Service/Controllers/AppsController.cs b/src/marketplace/Apps.Service/Controllers/AppsController.cs index f09f50114c..ca4f0388ae 100644 --- a/src/marketplace/Apps.Service/Controllers/AppsController.cs +++ b/src/marketplace/Apps.Service/Controllers/AppsController.cs @@ -158,7 +158,8 @@ public async Task RemoveFavouriteAppForCurrentUserAsync([FromRout /// Example: GET: /api/apps/subscribed/subscription-status /// The page that should be displayed /// The size per page of elements that should be returned - /// Filter for the offer subscription status. If not set all elements will be returned + /// Filter for the offer subscription status. If not set, all elements will be returned + /// An optional search query for the name /// Returns list of applicable apps subscription statuses. /// If sub claim is empty/invalid or user does not exist. [HttpGet] @@ -167,8 +168,8 @@ public async Task RemoveFavouriteAppForCurrentUserAsync([FromRout [Authorize(Policy = PolicyTypes.ValidCompany)] [ProducesResponseType(typeof(Pagination.Response), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status400BadRequest)] - public Task> GetCompanySubscribedAppSubscriptionStatusesForUserAsync([FromQuery] int page = 0, [FromQuery] int size = 15, [FromQuery] OfferSubscriptionStatusId? statusId = null) => - _appsBusinessLogic.GetCompanySubscribedAppSubscriptionStatusesForUserAsync(page, size, statusId); + public Task> GetCompanySubscribedAppSubscriptionStatusesForUserAsync([FromQuery] int page = 0, [FromQuery] int size = 15, [FromQuery] OfferSubscriptionStatusId? status = null, [FromQuery] string? name = null) => + _appsBusinessLogic.GetCompanySubscribedAppSubscriptionStatusesForUserAsync(page, size, status, name); /// /// Retrieves subscription statuses of provided apps of the currently logged in user's company. diff --git a/src/marketplace/Offers.Library/Service/IOfferService.cs b/src/marketplace/Offers.Library/Service/IOfferService.cs index edce3661c1..09ad726b3a 100644 --- a/src/marketplace/Offers.Library/Service/IOfferService.cs +++ b/src/marketplace/Offers.Library/Service/IOfferService.cs @@ -240,8 +240,9 @@ Task CreateOrUpdateOfferSubscriptionAgreementConsentAsync(Guid subscriptionId, /// /// /// + /// /// Returns the details of the subscription status for user by OfferType - Task> GetCompanySubscribedOfferSubscriptionStatusesForUserAsync(int page, int size, OfferTypeId offerTypeId, DocumentTypeId documentTypeId, OfferSubscriptionStatusId? statusId); + Task> GetCompanySubscribedOfferSubscriptionStatusesForUserAsync(int page, int size, OfferTypeId offerTypeId, DocumentTypeId documentTypeId, OfferSubscriptionStatusId? statusId, string? name); /// /// Gets the information for the subscription for the provider diff --git a/src/marketplace/Offers.Library/Service/OfferService.cs b/src/marketplace/Offers.Library/Service/OfferService.cs index bc6a5acd23..61f6f82657 100644 --- a/src/marketplace/Offers.Library/Service/OfferService.cs +++ b/src/marketplace/Offers.Library/Service/OfferService.cs @@ -889,12 +889,12 @@ private async Task> ValidateRoleData(IEnumerable - public async Task> GetCompanySubscribedOfferSubscriptionStatusesForUserAsync(int page, int size, OfferTypeId offerTypeId, DocumentTypeId documentTypeId, OfferSubscriptionStatusId? statusId) + public async Task> GetCompanySubscribedOfferSubscriptionStatusesForUserAsync(int page, int size, OfferTypeId offerTypeId, DocumentTypeId documentTypeId, OfferSubscriptionStatusId? statusId, string? name) { async Task?> GetCompanySubscribedOfferSubscriptionStatusesData(int skip, int take) { var offerCompanySubscriptionResponse = await _portalRepositories.GetInstance() - .GetOwnCompanySubscribedOfferSubscriptionStatusAsync(_identityData.CompanyId, offerTypeId, documentTypeId, statusId)(skip, take).ConfigureAwait(ConfigureAwaitOptions.None); + .GetOwnCompanySubscribedOfferSubscriptionStatusAsync(_identityData.CompanyId, offerTypeId, documentTypeId, statusId, name)(skip, take).ConfigureAwait(ConfigureAwaitOptions.None); return offerCompanySubscriptionResponse == null ? null diff --git a/src/marketplace/Services.Service/BusinessLogic/IServiceBusinessLogic.cs b/src/marketplace/Services.Service/BusinessLogic/IServiceBusinessLogic.cs index 580d8a5c1e..c45304a0c9 100644 --- a/src/marketplace/Services.Service/BusinessLogic/IServiceBusinessLogic.cs +++ b/src/marketplace/Services.Service/BusinessLogic/IServiceBusinessLogic.cs @@ -134,7 +134,7 @@ public interface IServiceBusinessLogic /// size /// /// Returns the details of the subscription status for Service user - Task> GetCompanySubscribedServiceSubscriptionStatusesForUserAsync(int page, int size, OfferSubscriptionStatusId? statusId); + Task> GetCompanySubscribedServiceSubscriptionStatusesForUserAsync(int page, int size, OfferSubscriptionStatusId? statusId, string? name); /// /// Starts the auto setup process. diff --git a/src/marketplace/Services.Service/BusinessLogic/ServiceBusinessLogic.cs b/src/marketplace/Services.Service/BusinessLogic/ServiceBusinessLogic.cs index c3f87b85e0..62f723216f 100644 --- a/src/marketplace/Services.Service/BusinessLogic/ServiceBusinessLogic.cs +++ b/src/marketplace/Services.Service/BusinessLogic/ServiceBusinessLogic.cs @@ -219,8 +219,8 @@ public Task GetSubscriptionDetailForSubscriber _offerService.GetSubscriptionDetailsForSubscriberAsync(serviceId, subscriptionId, OfferTypeId.SERVICE, _settings.SalesManagerRoles); /// - public Task> GetCompanySubscribedServiceSubscriptionStatusesForUserAsync(int page, int size, OfferSubscriptionStatusId? statusId) => - _offerService.GetCompanySubscribedOfferSubscriptionStatusesForUserAsync(page, size, OfferTypeId.SERVICE, DocumentTypeId.SERVICE_LEADIMAGE, statusId); + public Task> GetCompanySubscribedServiceSubscriptionStatusesForUserAsync(int page, int size, OfferSubscriptionStatusId? statusId, string? name) => + _offerService.GetCompanySubscribedOfferSubscriptionStatusesForUserAsync(page, size, OfferTypeId.SERVICE, DocumentTypeId.SERVICE_LEADIMAGE, statusId, name); /// public Task StartAutoSetupAsync(OfferAutoSetupData data) => diff --git a/src/marketplace/Services.Service/Controllers/ServicesController.cs b/src/marketplace/Services.Service/Controllers/ServicesController.cs index 045af23642..a10b6a9926 100644 --- a/src/marketplace/Services.Service/Controllers/ServicesController.cs +++ b/src/marketplace/Services.Service/Controllers/ServicesController.cs @@ -297,7 +297,8 @@ public Task GetSubscriptionDetailForSubscriber /// Example: GET: /api/services/subscribed/subscription-status /// The page that should be displayed /// The size per page of elements that should be returned - /// Filter for the offer subscription status. If not set all elements will be returned + /// Filter for the offer subscription status. If not set, all elements will be returned + /// Optional search query to filter for the name /// Returns list of applicable service subscription statuses. /// If sub claim is empty/invalid or user does not exist. [HttpGet] @@ -306,8 +307,8 @@ public Task GetSubscriptionDetailForSubscriber [Authorize(Policy = PolicyTypes.ValidCompany)] [ProducesResponseType(typeof(Pagination.Response), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status400BadRequest)] - public Task> GetCompanySubscribedServiceSubscriptionStatusesForUserAsync([FromQuery] int page = 0, [FromQuery] int size = 15, [FromQuery] OfferSubscriptionStatusId? statusId = null) => - _serviceBusinessLogic.GetCompanySubscribedServiceSubscriptionStatusesForUserAsync(page, size, statusId); + public Task> GetCompanySubscribedServiceSubscriptionStatusesForUserAsync([FromQuery] int page = 0, [FromQuery] int size = 15, [FromQuery] OfferSubscriptionStatusId? status = null, [FromQuery] string? name = null) => + _serviceBusinessLogic.GetCompanySubscribedServiceSubscriptionStatusesForUserAsync(page, size, status, name); /// /// Unsubscribes an service from the current user's company's subscriptions. diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/IOfferSubscriptionsRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/IOfferSubscriptionsRepository.cs index df7fc3b27c..ba49f924f5 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/IOfferSubscriptionsRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/IOfferSubscriptionsRepository.cs @@ -141,10 +141,11 @@ public interface IOfferSubscriptionsRepository /// Id of the offer type /// Id of the document type /// + /// /// Returns a func with skip, take and the pagination of the source Func?>> GetOwnCompanySubscribedOfferSubscriptionStatusAsync(Guid userCompanyId, OfferTypeId offerTypeId, - DocumentTypeId documentTypeId, OfferSubscriptionStatusId? statusId); + DocumentTypeId documentTypeId, OfferSubscriptionStatusId? statusId, string? name); /// /// diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/OfferSubscriptionsRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/OfferSubscriptionsRepository.cs index 86c2caef5b..d486968057 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/OfferSubscriptionsRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/OfferSubscriptionsRepository.cs @@ -309,7 +309,8 @@ public void AttachAndModifyAppSubscriptionDetail(Guid detailId, Guid subscriptio /// public Func?>> - GetOwnCompanySubscribedOfferSubscriptionStatusAsync(Guid userCompanyId, OfferTypeId offerTypeId, DocumentTypeId documentTypeId, OfferSubscriptionStatusId? statusId) => + GetOwnCompanySubscribedOfferSubscriptionStatusAsync(Guid userCompanyId, OfferTypeId offerTypeId, + DocumentTypeId documentTypeId, OfferSubscriptionStatusId? statusId, string? name) => (skip, take) => Pagination.CreateSourceQueryAsync( skip, take, @@ -318,7 +319,8 @@ public void AttachAndModifyAppSubscriptionDetail(Guid detailId, Guid subscriptio .Where(os => os.Offer!.OfferTypeId == offerTypeId && os.CompanyId == userCompanyId && - (statusId == null || os.OfferSubscriptionStatusId == statusId)) + (statusId == null || os.OfferSubscriptionStatusId == statusId) && + (name == null || (os.Offer.Name != null && EF.Functions.ILike(os.Offer!.Name, $"%{name.EscapeForILike()}%")))) .GroupBy(os => os.CompanyId), null, os => new OfferSubscriptionStatusData( diff --git a/tests/marketplace/Apps.Service.Tests/BusinessLogic/AppBusinessLogicTests.cs b/tests/marketplace/Apps.Service.Tests/BusinessLogic/AppBusinessLogicTests.cs index b7ae4b0d1b..cdd5c1807a 100644 --- a/tests/marketplace/Apps.Service.Tests/BusinessLogic/AppBusinessLogicTests.cs +++ b/tests/marketplace/Apps.Service.Tests/BusinessLogic/AppBusinessLogicTests.cs @@ -471,18 +471,18 @@ public async Task GetCompanySubscribedAppSubscriptionStatusesForUserAsync_Return // Arrange var data = _fixture.CreateMany(5).ToImmutableArray(); var pagination = new Pagination.Response(new Pagination.Metadata(data.Length, 1, 0, data.Length), data); - A.CallTo(() => _offerService.GetCompanySubscribedOfferSubscriptionStatusesForUserAsync(A._, A._, A._, A._, A._)) + A.CallTo(() => _offerService.GetCompanySubscribedOfferSubscriptionStatusesForUserAsync(A._, A._, A._, A._, A._, A._)) .Returns(pagination); var sut = new AppsBusinessLogic(_portalRepositories, null!, _offerService, null!, _fixture.Create>(), _identityService); // Act - var result = await sut.GetCompanySubscribedAppSubscriptionStatusesForUserAsync(0, 10, null); + var result = await sut.GetCompanySubscribedAppSubscriptionStatusesForUserAsync(0, 10, null, null); // Assert result.Meta.NumberOfElements.Should().Be(5); result.Content.Should().HaveCount(5); - A.CallTo(() => _offerService.GetCompanySubscribedOfferSubscriptionStatusesForUserAsync(0, 10, OfferTypeId.APP, DocumentTypeId.APP_LEADIMAGE, null)).MustHaveHappenedOnceExactly(); + A.CallTo(() => _offerService.GetCompanySubscribedOfferSubscriptionStatusesForUserAsync(0, 10, OfferTypeId.APP, DocumentTypeId.APP_LEADIMAGE, null, null)).MustHaveHappenedOnceExactly(); } #endregion diff --git a/tests/marketplace/Apps.Service.Tests/Controllers/AppsControllerTests.cs b/tests/marketplace/Apps.Service.Tests/Controllers/AppsControllerTests.cs index 526b3060d3..f998dbe29d 100644 --- a/tests/marketplace/Apps.Service.Tests/Controllers/AppsControllerTests.cs +++ b/tests/marketplace/Apps.Service.Tests/Controllers/AppsControllerTests.cs @@ -160,14 +160,14 @@ public async Task GetCompanySubscribedAppSubscriptionStatusesForCurrentUserAsync //Arrange var data = _fixture.CreateMany(3).ToImmutableArray(); var pagination = new Pagination.Response(new Pagination.Metadata(data.Length, 1, 0, data.Length), data); - A.CallTo(() => _logic.GetCompanySubscribedAppSubscriptionStatusesForUserAsync(A._, A._, A._)) + A.CallTo(() => _logic.GetCompanySubscribedAppSubscriptionStatusesForUserAsync(A._, A._, A._, A._)) .Returns(pagination); //Act var result = await _controller.GetCompanySubscribedAppSubscriptionStatusesForUserAsync(); //Assert - A.CallTo(() => _logic.GetCompanySubscribedAppSubscriptionStatusesForUserAsync(0, 15, null)).MustHaveHappenedOnceExactly(); + A.CallTo(() => _logic.GetCompanySubscribedAppSubscriptionStatusesForUserAsync(0, 15, null, null)).MustHaveHappenedOnceExactly(); result.Content.Should().HaveCount(3).And.ContainInOrder(data); } diff --git a/tests/marketplace/Offers.Library.Tests/Service/OfferServiceTests.cs b/tests/marketplace/Offers.Library.Tests/Service/OfferServiceTests.cs index ec1ff7d8ca..5750cafa27 100644 --- a/tests/marketplace/Offers.Library.Tests/Service/OfferServiceTests.cs +++ b/tests/marketplace/Offers.Library.Tests/Service/OfferServiceTests.cs @@ -2442,11 +2442,11 @@ public async Task GetCompanySubscribedOfferSubscriptionStatusesForUserAsync_Retu { // Arrange var data = _fixture.CreateMany(5).ToImmutableArray(); - A.CallTo(() => _offerSubscriptionsRepository.GetOwnCompanySubscribedOfferSubscriptionStatusAsync(A._, A._, A._, A._)) + A.CallTo(() => _offerSubscriptionsRepository.GetOwnCompanySubscribedOfferSubscriptionStatusAsync(A._, A._, A._, A._, A._)) .Returns((skip, take) => Task.FromResult(new Pagination.Source(data.Length, data.Skip(skip).Take(take)))!); // Act - var result = await _sut.GetCompanySubscribedOfferSubscriptionStatusesForUserAsync(0, 10, offerTypeId, documentTypeId, null); + var result = await _sut.GetCompanySubscribedOfferSubscriptionStatusesForUserAsync(0, 10, offerTypeId, documentTypeId, null, null); // Assert result.Meta.NumberOfElements.Should().Be(5); @@ -2457,7 +2457,7 @@ public async Task GetCompanySubscribedOfferSubscriptionStatusesForUserAsync_Retu x => x.OfferId == data[3].OfferId && x.OfferName == data[3].OfferName && x.Provider == data[3].Provider && x.OfferSubscriptionStatusId == data[3].OfferSubscriptionStatusId && x.OfferSubscriptionId == data[3].OfferSubscriptionId && x.DocumentId == data[3].DocumentId, x => x.OfferId == data[4].OfferId && x.OfferName == data[4].OfferName && x.Provider == data[4].Provider && x.OfferSubscriptionStatusId == data[4].OfferSubscriptionStatusId && x.OfferSubscriptionId == data[4].OfferSubscriptionId && x.DocumentId == data[4].DocumentId ); - A.CallTo(() => _offerSubscriptionsRepository.GetOwnCompanySubscribedOfferSubscriptionStatusAsync(_companyId, offerTypeId, documentTypeId, null)) + A.CallTo(() => _offerSubscriptionsRepository.GetOwnCompanySubscribedOfferSubscriptionStatusAsync(_companyId, offerTypeId, documentTypeId, null, null)) .MustHaveHappenedOnceExactly(); } @@ -2467,16 +2467,16 @@ public async Task GetCompanySubscribedOfferSubscriptionStatusesForUserAsync_Retu public async Task GetCompanySubscribedOfferSubscriptionStatusesForUserAsync_WithQueryNullResult_ReturnsExpected(OfferTypeId offerTypeId, DocumentTypeId documentTypeId) { // Arrange - A.CallTo(() => _offerSubscriptionsRepository.GetOwnCompanySubscribedOfferSubscriptionStatusAsync(A._, A._, A._, A._)) + A.CallTo(() => _offerSubscriptionsRepository.GetOwnCompanySubscribedOfferSubscriptionStatusAsync(A._, A._, A._, A._, A._)) .Returns((skip, take) => Task.FromResult?>(null)); // Act - var result = await _sut.GetCompanySubscribedOfferSubscriptionStatusesForUserAsync(0, 10, offerTypeId, documentTypeId, null); + var result = await _sut.GetCompanySubscribedOfferSubscriptionStatusesForUserAsync(0, 10, offerTypeId, documentTypeId, null, null); // Assert result.Meta.NumberOfElements.Should().Be(0); result.Content.Should().BeEmpty(); - A.CallTo(() => _offerSubscriptionsRepository.GetOwnCompanySubscribedOfferSubscriptionStatusAsync(_companyId, offerTypeId, documentTypeId, null)) + A.CallTo(() => _offerSubscriptionsRepository.GetOwnCompanySubscribedOfferSubscriptionStatusAsync(_companyId, offerTypeId, documentTypeId, null, null)) .MustHaveHappenedOnceExactly(); } diff --git a/tests/marketplace/Services.Service.Tests/BusinessLogic/ServiceBusinessLogicTests.cs b/tests/marketplace/Services.Service.Tests/BusinessLogic/ServiceBusinessLogicTests.cs index 54d0f510a2..6dbb3a41cf 100644 --- a/tests/marketplace/Services.Service.Tests/BusinessLogic/ServiceBusinessLogicTests.cs +++ b/tests/marketplace/Services.Service.Tests/BusinessLogic/ServiceBusinessLogicTests.cs @@ -584,18 +584,18 @@ public async Task GetCompanySubscribedServiceSubscriptionStatusesForUserAsync_Re // Arrange var data = _fixture.CreateMany(5).ToImmutableArray(); var paginationResponse = new Pagination.Response(new Pagination.Metadata(data.Length, 1, 0, data.Length), data); - A.CallTo(() => _offerService.GetCompanySubscribedOfferSubscriptionStatusesForUserAsync(A._, A._, A._, A._, A._)) + A.CallTo(() => _offerService.GetCompanySubscribedOfferSubscriptionStatusesForUserAsync(A._, A._, A._, A._, A._, A._)) .Returns(paginationResponse); var sut = new ServiceBusinessLogic(null!, _offerService, null!, null!, _identityService, Options.Create(new ServiceSettings())); // Act - var result = await sut.GetCompanySubscribedServiceSubscriptionStatusesForUserAsync(0, 10, null); + var result = await sut.GetCompanySubscribedServiceSubscriptionStatusesForUserAsync(0, 10, null, null); // Assert result.Meta.NumberOfElements.Should().Be(5); result.Content.Should().HaveCount(5); - A.CallTo(() => _offerService.GetCompanySubscribedOfferSubscriptionStatusesForUserAsync(0, 10, OfferTypeId.SERVICE, DocumentTypeId.SERVICE_LEADIMAGE, null)) + A.CallTo(() => _offerService.GetCompanySubscribedOfferSubscriptionStatusesForUserAsync(0, 10, OfferTypeId.SERVICE, DocumentTypeId.SERVICE_LEADIMAGE, null, null)) .MustHaveHappenedOnceExactly(); } diff --git a/tests/marketplace/Services.Service.Tests/Controllers/ServiceControllerTest.cs b/tests/marketplace/Services.Service.Tests/Controllers/ServiceControllerTest.cs index 09ea138462..e5b4ad377f 100644 --- a/tests/marketplace/Services.Service.Tests/Controllers/ServiceControllerTest.cs +++ b/tests/marketplace/Services.Service.Tests/Controllers/ServiceControllerTest.cs @@ -306,14 +306,14 @@ public async Task GetCompanySubscribedServiceSubscriptionStatusesForCurrentUserA //Arrange var data = _fixture.CreateMany(3).ToImmutableArray(); var pagination = new Pagination.Response(new Pagination.Metadata(data.Length, 1, 0, data.Length), data); - A.CallTo(() => _logic.GetCompanySubscribedServiceSubscriptionStatusesForUserAsync(A._, A._, A._)) + A.CallTo(() => _logic.GetCompanySubscribedServiceSubscriptionStatusesForUserAsync(A._, A._, A._, A._)) .Returns(pagination); //Act var result = await _controller.GetCompanySubscribedServiceSubscriptionStatusesForUserAsync(); //Assert - A.CallTo(() => _logic.GetCompanySubscribedServiceSubscriptionStatusesForUserAsync(0, 15, null)).MustHaveHappenedOnceExactly(); + A.CallTo(() => _logic.GetCompanySubscribedServiceSubscriptionStatusesForUserAsync(0, 15, null, null)).MustHaveHappenedOnceExactly(); result.Content.Should().HaveCount(3).And.ContainInOrder(data); } diff --git a/tests/portalbackend/PortalBackend.DBAccess.Tests/OfferSubscriptionRepositoryTest.cs b/tests/portalbackend/PortalBackend.DBAccess.Tests/OfferSubscriptionRepositoryTest.cs index 0d2588e610..4434915836 100644 --- a/tests/portalbackend/PortalBackend.DBAccess.Tests/OfferSubscriptionRepositoryTest.cs +++ b/tests/portalbackend/PortalBackend.DBAccess.Tests/OfferSubscriptionRepositoryTest.cs @@ -858,7 +858,7 @@ public async Task GetOwnCompanySubscribedOfferSubscriptionStatusAsync_ReturnsExp var (sut, _) = await CreateSut(); // Act - var result = await sut.GetOwnCompanySubscribedOfferSubscriptionStatusAsync(companyId, offerTypeId, documentTypeId, null)(0, 15); + var result = await sut.GetOwnCompanySubscribedOfferSubscriptionStatusAsync(companyId, offerTypeId, documentTypeId, null, null)(0, 15); // Assert switch (offerTypeId) @@ -912,7 +912,7 @@ public async Task GetOwnCompanySubscribedOfferSubscriptionStatusAsync_WithStatus var (sut, _) = await CreateSut(); // Act - var result = await sut.GetOwnCompanySubscribedOfferSubscriptionStatusAsync(new Guid("2dc4249f-b5ca-4d42-bef1-7a7a950a4f87"), OfferTypeId.APP, DocumentTypeId.APP_LEADIMAGE, OfferSubscriptionStatusId.ACTIVE)(0, 15); + var result = await sut.GetOwnCompanySubscribedOfferSubscriptionStatusAsync(new Guid("2dc4249f-b5ca-4d42-bef1-7a7a950a4f87"), OfferTypeId.APP, DocumentTypeId.APP_LEADIMAGE, OfferSubscriptionStatusId.ACTIVE, null)(0, 15); // Assert result.Should().NotBeNull(); From 57f5120c0175c247208e707d56044e492a7576d0 Mon Sep 17 00:00:00 2001 From: Phil Schneider Date: Tue, 10 Sep 2024 02:45:35 +0200 Subject: [PATCH 3/5] feat(idp): add search functionallity to idp endpoint (#982) Refs: #981 --- .../IIdentityProviderBusinessLogic.cs | 2 +- .../IdentityProviderBusinessLogic.cs | 160 ++++++++---------- .../Controllers/IdentityProviderController.cs | 42 ++--- .../IIdentityProviderRepository.cs | 2 +- .../IdentityProviderRepository.cs | 8 +- .../IdentityProviderBusinessLogicTests.cs | 82 ++++++++- .../IdentityProviderRepositoryTests.cs | 15 +- 7 files changed, 188 insertions(+), 123 deletions(-) diff --git a/src/administration/Administration.Service/BusinessLogic/IIdentityProviderBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/IIdentityProviderBusinessLogic.cs index 5f1231e530..2d4d5eaa7a 100644 --- a/src/administration/Administration.Service/BusinessLogic/IIdentityProviderBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/IIdentityProviderBusinessLogic.cs @@ -26,7 +26,7 @@ namespace Org.Eclipse.TractusX.Portal.Backend.Administration.Service.BusinessLog public interface IIdentityProviderBusinessLogic { - IAsyncEnumerable GetOwnCompanyIdentityProvidersAsync(); + IAsyncEnumerable GetOwnCompanyIdentityProvidersAsync(string? displayName, string? alias); ValueTask CreateOwnCompanyIdentityProviderAsync(IamIdentityProviderProtocol protocol, IdentityProviderTypeId typeId, string? displayName); ValueTask GetOwnCompanyIdentityProviderAsync(Guid identityProviderId); ValueTask SetOwnCompanyIdentityProviderStatusAsync(Guid identityProviderId, bool enabled); diff --git a/src/administration/Administration.Service/BusinessLogic/IdentityProviderBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/IdentityProviderBusinessLogic.cs index 191793bd29..18a9f38bcf 100644 --- a/src/administration/Administration.Service/BusinessLogic/IdentityProviderBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/IdentityProviderBusinessLogic.cs @@ -41,47 +41,35 @@ namespace Org.Eclipse.TractusX.Portal.Backend.Administration.Service.BusinessLogic; -public class IdentityProviderBusinessLogic : IIdentityProviderBusinessLogic +public class IdentityProviderBusinessLogic( + IPortalRepositories portalRepositories, + IProvisioningManager provisioningManager, + IIdentityService identityService, + IErrorMessageService errorMessageService, + IMailingProcessCreation mailingProcessCreation, + IOptions options, + ILogger logger) + : IIdentityProviderBusinessLogic { - private readonly IPortalRepositories _portalRepositories; - private readonly IProvisioningManager _provisioningManager; - private readonly IIdentityData _identityData; - private readonly IErrorMessageService _errorMessageService; - private readonly IMailingProcessCreation _mailingProcessCreation; - private readonly IdentityProviderSettings _settings; - private readonly ILogger _logger; + private readonly IIdentityData _identityData = identityService.IdentityData; + private readonly IdentityProviderSettings _settings = options.Value; private static readonly Regex DisplayNameValidationExpression = new(@"^[a-zA-Z0-9\!\?\@\&\#\'\x22\(\)_\-\=\/\*\.\,\;\: ]+$", RegexOptions.None, TimeSpan.FromSeconds(1)); - public IdentityProviderBusinessLogic( - IPortalRepositories portalRepositories, - IProvisioningManager provisioningManager, - IIdentityService identityService, - IErrorMessageService errorMessageService, - IMailingProcessCreation mailingProcessCreation, - IOptions options, - ILogger logger) - { - _portalRepositories = portalRepositories; - _provisioningManager = provisioningManager; - _identityData = identityService.IdentityData; - _errorMessageService = errorMessageService; - _mailingProcessCreation = mailingProcessCreation; - _settings = options.Value; - _logger = logger; - } - - public async IAsyncEnumerable GetOwnCompanyIdentityProvidersAsync() + public async IAsyncEnumerable GetOwnCompanyIdentityProvidersAsync(string? displayName, string? alias) { var companyId = _identityData.CompanyId; - await foreach (var identityProviderData in _portalRepositories.GetInstance().GetCompanyIdentityProviderCategoryDataUntracked(companyId).ConfigureAwait(false)) + await foreach (var identityProviderData in portalRepositories.GetInstance().GetCompanyIdentityProviderCategoryDataUntracked(companyId, alias).ConfigureAwait(false)) { - yield return identityProviderData.CategoryId switch + var details = identityProviderData.CategoryId switch { IdentityProviderCategoryId.KEYCLOAK_OIDC => await GetIdentityProviderDetailsOidc(identityProviderData.IdentityProviderId, identityProviderData.Alias, identityProviderData.CategoryId, identityProviderData.TypeId, identityProviderData.MetadataUrl).ConfigureAwait(false), IdentityProviderCategoryId.KEYCLOAK_SAML => await GetIdentityProviderDetailsSaml(identityProviderData.IdentityProviderId, identityProviderData.Alias, identityProviderData.TypeId), _ => throw new ControllerArgumentException($"unexpected value for category '{identityProviderData.CategoryId}'") }; + + if (displayName == null || (details.DisplayName != null && details.DisplayName.Contains(displayName))) + yield return details; } } @@ -122,8 +110,8 @@ private static void ValidateDisplayName(string displayName) private async ValueTask CreateOwnCompanyIdentityProviderInternalAsync(IdentityProviderCategoryId identityProviderCategory, IamIdentityProviderProtocol protocol, IdentityProviderTypeId typeId, string? displayName, IEnumerable requiredCompanyRoles) { var companyId = _identityData.CompanyId; - var identityProviderRepository = _portalRepositories.GetInstance(); - var result = await _portalRepositories.GetInstance().CheckCompanyAndCompanyRolesAsync(companyId, requiredCompanyRoles).ConfigureAwait(ConfigureAwaitOptions.None); + var identityProviderRepository = portalRepositories.GetInstance(); + var result = await portalRepositories.GetInstance().CheckCompanyAndCompanyRolesAsync(companyId, requiredCompanyRoles).ConfigureAwait(ConfigureAwaitOptions.None); if (!result.IsValidCompany) { throw new ControllerArgumentException($"company {companyId} does not exist", nameof(companyId)); @@ -134,14 +122,14 @@ private async ValueTask CreateOwnCompanyIdentityProvide throw new ForbiddenException($"Not allowed to create an identityProvider of type {typeId}"); } - var alias = await _provisioningManager.CreateOwnIdpAsync(displayName ?? result.CompanyName, result.CompanyName, protocol).ConfigureAwait(ConfigureAwaitOptions.None); + var alias = await provisioningManager.CreateOwnIdpAsync(displayName ?? result.CompanyName, result.CompanyName, protocol).ConfigureAwait(ConfigureAwaitOptions.None); var identityProviderId = identityProviderRepository.CreateIdentityProvider(identityProviderCategory, typeId, companyId, null).Id; if (typeId == IdentityProviderTypeId.OWN) { identityProviderRepository.CreateCompanyIdentityProvider(companyId, identityProviderId); } identityProviderRepository.CreateIamIdentityProvider(identityProviderId, alias); - await _portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); + await portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); return protocol switch { @@ -166,7 +154,7 @@ public async ValueTask GetOwnCompanyIdentityProviderAsy private async ValueTask<(string Alias, IdentityProviderCategoryId Category, IdentityProviderTypeId TypeId, string? MetadataUrl)> ValidateGetOwnCompanyIdentityProviderArguments(Guid identityProviderId) { var companyId = _identityData.CompanyId; - var (alias, category, isOwnOrOwnerCompany, typeId, metadataUrl) = await _portalRepositories.GetInstance().GetOwnCompanyIdentityProviderAliasUntrackedAsync(identityProviderId, companyId).ConfigureAwait(ConfigureAwaitOptions.None); + var (alias, category, isOwnOrOwnerCompany, typeId, metadataUrl) = await portalRepositories.GetInstance().GetOwnCompanyIdentityProviderAliasUntrackedAsync(identityProviderId, companyId).ConfigureAwait(ConfigureAwaitOptions.None); if (!isOwnOrOwnerCompany) { throw new ConflictException($"identityProvider {identityProviderId} is not associated with company {companyId}"); @@ -192,17 +180,17 @@ public async ValueTask SetOwnCompanyIdentityProviderSta switch (category) { case IdentityProviderCategoryId.KEYCLOAK_OIDC when typeId is IdentityProviderTypeId.SHARED: - await _provisioningManager.SetSharedIdentityProviderStatusAsync(alias, enabled).ConfigureAwait(false); + await provisioningManager.SetSharedIdentityProviderStatusAsync(alias, enabled).ConfigureAwait(false); return await GetIdentityProviderDetailsOidc(identityProviderId, alias, category, typeId, null).ConfigureAwait(false); case IdentityProviderCategoryId.KEYCLOAK_OIDC: - await _provisioningManager.SetCentralIdentityProviderStatusAsync(alias, enabled).ConfigureAwait(false); + await provisioningManager.SetCentralIdentityProviderStatusAsync(alias, enabled).ConfigureAwait(false); if (typeId == IdentityProviderTypeId.MANAGED && !enabled && companyUsersLinked) { await SendIdpMail(identityProviderId, alias, ownerCompanyName, _settings.DeactivateIdpRoles).ConfigureAwait(ConfigureAwaitOptions.None); } return await GetIdentityProviderDetailsOidc(identityProviderId, alias, category, typeId, metadataUrl).ConfigureAwait(false); case IdentityProviderCategoryId.KEYCLOAK_SAML: - await _provisioningManager.SetCentralIdentityProviderStatusAsync(alias, enabled).ConfigureAwait(false); + await provisioningManager.SetCentralIdentityProviderStatusAsync(alias, enabled).ConfigureAwait(false); if (typeId == IdentityProviderTypeId.MANAGED && !enabled && companyUsersLinked) { await SendIdpMail(identityProviderId, alias, ownerCompanyName, _settings.DeactivateIdpRoles).ConfigureAwait(ConfigureAwaitOptions.None); @@ -214,7 +202,7 @@ public async ValueTask SetOwnCompanyIdentityProviderSta } private Task SendIdpMail(Guid identityProviderId, string? alias, string ownerCompanyName, IEnumerable idpRoles) => - _mailingProcessCreation.RoleBaseSendMailForIdp( + mailingProcessCreation.RoleBaseSendMailForIdp( idpRoles, new[] { ("idpAlias", alias ?? identityProviderId.ToString()), ("ownerCompanyName", ownerCompanyName) }, ("username", "User"), @@ -224,7 +212,7 @@ private Task SendIdpMail(Guid identityProviderId, string? alias, string ownerCom private async ValueTask<(IdentityProviderCategoryId Category, string Alias, IdentityProviderTypeId TypeId, bool CompanyUsersLinked, string OwnerCompanyName, string? MetadataUrl)> ValidateSetOwnCompanyIdentityProviderStatusArguments(Guid identityProviderId, bool enabled) { var companyId = _identityData.CompanyId; - var result = await _portalRepositories.GetInstance().GetOwnCompanyIdentityProviderStatusUpdateData(identityProviderId, companyId, !enabled).ConfigureAwait(ConfigureAwaitOptions.None); + var result = await portalRepositories.GetInstance().GetOwnCompanyIdentityProviderStatusUpdateData(identityProviderId, companyId, !enabled).ConfigureAwait(ConfigureAwaitOptions.None); if (result == default) { throw new NotFoundException($"identityProvider {identityProviderId} does not exist"); @@ -274,7 +262,7 @@ public async ValueTask UpdateOwnCompanyIdentityProvider var companyId = _identityData.CompanyId; ValidateDisplayName(details.DisplayName); - var result = await _portalRepositories.GetInstance().GetOwnCompanyIdentityProviderUpdateData(identityProviderId, companyId).ConfigureAwait(ConfigureAwaitOptions.None); + var result = await portalRepositories.GetInstance().GetOwnCompanyIdentityProviderUpdateData(identityProviderId, companyId).ConfigureAwait(ConfigureAwaitOptions.None); if (result == default) { throw new NotFoundException($"identityProvider {identityProviderId} does not exist"); @@ -301,7 +289,7 @@ private async ValueTask UpdateIdentityProviderOidc(string alias, string? metadat { throw new ControllerArgumentException("property 'saml' must be null", nameof(details.Saml)); } - await _provisioningManager.UpdateCentralIdentityProviderDataOIDCAsync( + await provisioningManager.UpdateCentralIdentityProviderDataOIDCAsync( new IdentityProviderEditableConfigOidc( alias, details.DisplayName, @@ -311,7 +299,7 @@ await _provisioningManager.UpdateCentralIdentityProviderDataOIDCAsync( details.Oidc.Secret, details.Oidc.SignatureAlgorithm), cancellationToken) .ConfigureAwait(false); - _portalRepositories.GetInstance() + portalRepositories.GetInstance() .AttachAndModifyIamIdentityProvider( alias, iamIdentityProvider => iamIdentityProvider.MetadataUrl = metadataUrl, @@ -328,7 +316,7 @@ private async ValueTask UpdateIdentityProviderSaml(string alias, IdentityProvide { throw new ControllerArgumentException("property 'oidc' must be null", nameof(details.Oidc)); } - await _provisioningManager.UpdateCentralIdentityProviderDataSAMLAsync( + await provisioningManager.UpdateCentralIdentityProviderDataSAMLAsync( new IdentityProviderEditableConfigSaml( alias, details.DisplayName, @@ -347,19 +335,19 @@ private async ValueTask UpdateIdentityProviderShared(string alias, IdentityProvi { throw new ControllerArgumentException("property 'saml' must be null", nameof(details.Saml)); } - await _provisioningManager.UpdateSharedIdentityProviderAsync(alias, details.DisplayName).ConfigureAwait(false); + await provisioningManager.UpdateSharedIdentityProviderAsync(alias, details.DisplayName).ConfigureAwait(false); } private async ValueTask ValidateOtherActiveIdentityProvider(string? alias, IEnumerable<(Guid CompanyId, IEnumerable Aliase)> companyIdAliase) { - var aliasStatus = (await Task.WhenAll(companyIdAliase.SelectMany(x => x.Aliase).Where(x => x != alias).Distinct().Select(async alias => (Alias: alias, Enabled: await _provisioningManager.IsCentralIdentityProviderEnabled(alias).ConfigureAwait(false)))).ConfigureAwait(ConfigureAwaitOptions.None)).ToDictionary(x => x.Alias, x => x.Enabled); + var aliasStatus = (await Task.WhenAll(companyIdAliase.SelectMany(x => x.Aliase).Where(x => x != alias).Distinct().Select(async alias => (Alias: alias, Enabled: await provisioningManager.IsCentralIdentityProviderEnabled(alias).ConfigureAwait(false)))).ConfigureAwait(ConfigureAwaitOptions.None)).ToDictionary(x => x.Alias, x => x.Enabled); return companyIdAliase.All(x => x.Aliase.Where(a => a != alias).Any(a => aliasStatus[a])); } public async ValueTask DeleteCompanyIdentityProviderAsync(Guid identityProviderId) { - var identityProviderRepository = _portalRepositories.GetInstance(); + var identityProviderRepository = portalRepositories.GetInstance(); var (alias, typeId, ownerCompanyName) = await ValidateDeleteOwnCompanyIdentityProviderArguments(identityProviderId, identityProviderRepository).ConfigureAwait(false); if (alias != null) @@ -367,9 +355,9 @@ public async ValueTask DeleteCompanyIdentityProviderAsync(Guid identityProviderI identityProviderRepository.DeleteIamIdentityProvider(alias); if (typeId == IdentityProviderTypeId.SHARED) { - await _provisioningManager.DeleteSharedIdpRealmAsync(alias).ConfigureAwait(false); + await provisioningManager.DeleteSharedIdpRealmAsync(alias).ConfigureAwait(false); } - await _provisioningManager.DeleteCentralIdentityProviderAsync(alias).ConfigureAwait(ConfigureAwaitOptions.None); + await provisioningManager.DeleteCentralIdentityProviderAsync(alias).ConfigureAwait(ConfigureAwaitOptions.None); } if (typeId == IdentityProviderTypeId.MANAGED) @@ -378,19 +366,19 @@ public async ValueTask DeleteCompanyIdentityProviderAsync(Guid identityProviderI } identityProviderRepository.DeleteIdentityProvider(identityProviderId); - await _portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); + await portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); } private async Task DeleteManagedIdpLinks(Guid identityProviderId, string? alias, string ownerCompanyName, IIdentityProviderRepository identityProviderRepository) { - var roleIds = await _mailingProcessCreation.GetRoleData(_settings.DeleteIdpRoles).ConfigureAwait(ConfigureAwaitOptions.None); + var roleIds = await mailingProcessCreation.GetRoleData(_settings.DeleteIdpRoles).ConfigureAwait(ConfigureAwaitOptions.None); var idpLinkedData = identityProviderRepository.GetManagedIdpLinkedData(identityProviderId, roleIds.Distinct()); async IAsyncEnumerable<(string Email, IReadOnlyDictionary Parameters)> DeleteLinksReturningMaildata() { - var companyRepository = _portalRepositories.GetInstance(); - var userRepository = _portalRepositories.GetInstance(); - var userRolesRepository = _portalRepositories.GetInstance(); + var companyRepository = portalRepositories.GetInstance(); + var userRepository = portalRepositories.GetInstance(); + var userRolesRepository = portalRepositories.GetInstance(); await foreach (var data in idpLinkedData.ConfigureAwait(false)) { @@ -420,7 +408,7 @@ private async Task DeleteManagedIdpLinks(Guid identityProviderId, string? alias, await foreach (var mailData in DeleteLinksReturningMaildata().ConfigureAwait(false)) { - _mailingProcessCreation.CreateMailProcess(mailData.Email, "DeleteManagedIdp", mailData.Parameters); + mailingProcessCreation.CreateMailProcess(mailData.Email, "DeleteManagedIdp", mailData.Parameters); } } @@ -429,9 +417,9 @@ private async Task DeleteKeycloakUsers(IEnumerable identityIds) foreach (var identityId in identityIds) { string? userId; - if ((userId = await _provisioningManager.GetUserByUserName(identityId.ToString()).ConfigureAwait(ConfigureAwaitOptions.None)) != null) + if ((userId = await provisioningManager.GetUserByUserName(identityId.ToString()).ConfigureAwait(ConfigureAwaitOptions.None)) != null) { - await _provisioningManager.DeleteCentralRealmUserAsync(userId).ConfigureAwait(ConfigureAwaitOptions.None); + await provisioningManager.DeleteCentralRealmUserAsync(userId).ConfigureAwait(ConfigureAwaitOptions.None); } } } @@ -456,7 +444,7 @@ private async Task DeleteKeycloakUsers(IEnumerable identityIds) return (alias, typeId, ownerCompanyName); } - if (await _provisioningManager.IsCentralIdentityProviderEnabled(alias).ConfigureAwait(false)) + if (await provisioningManager.IsCentralIdentityProviderEnabled(alias).ConfigureAwait(false)) { throw new ControllerArgumentException($"cannot delete identityProvider {identityProviderId} as it is enabled"); } @@ -481,19 +469,19 @@ private async ValueTask GetIdentityProviderDetailsOidc( bool aliasExisting; try { - identityProviderDataOidc = await _provisioningManager.GetCentralIdentityProviderDataOIDCAsync(alias) + identityProviderDataOidc = await provisioningManager.GetCentralIdentityProviderDataOIDCAsync(alias) .ConfigureAwait(false); aliasExisting = true; } catch (KeycloakEntityNotFoundException ex) { - _logger.LogInformation("Can't receive oidc data for {Alias} with following exception {Exception}", alias, ex.Message); + logger.LogInformation("Can't receive oidc data for {Alias} with following exception {Exception}", alias, ex.Message); aliasExisting = false; } if (aliasExisting) { - identityProviderMapper = await _provisioningManager.GetIdentityProviderMappers(alias).ToListAsync().ConfigureAwait(false); + identityProviderMapper = await provisioningManager.GetIdentityProviderMappers(alias).ToListAsync().ConfigureAwait(false); } } @@ -530,19 +518,19 @@ private async ValueTask GetIdentityProviderDetailsSaml( bool aliasExisting; try { - identityProviderDataSaml = await _provisioningManager + identityProviderDataSaml = await provisioningManager .GetCentralIdentityProviderDataSAMLAsync(alias).ConfigureAwait(false); aliasExisting = true; } catch (KeycloakEntityNotFoundException ex) { - _logger.LogInformation("Can't receive saml data for {Alias} with following exception {Exception}", alias, ex.Message); + logger.LogInformation("Can't receive saml data for {Alias} with following exception {Exception}", alias, ex.Message); aliasExisting = false; } if (aliasExisting) { - identityProviderMapper = await _provisioningManager.GetIdentityProviderMappers(alias).ToListAsync() + identityProviderMapper = await provisioningManager.GetIdentityProviderMappers(alias).ToListAsync() .ConfigureAwait(false); } } @@ -572,14 +560,14 @@ public async ValueTask CreateOrUpdateOwnCompanyUse try { - await _provisioningManager.DeleteProviderUserLinkToCentralUserAsync(iamUserId, alias); + await provisioningManager.DeleteProviderUserLinkToCentralUserAsync(iamUserId, alias); } catch (KeycloakEntityNotFoundException) { // for create-and-update semantics this is expected and not an error } - await _provisioningManager.AddProviderUserLinkToCentralUserAsync( + await provisioningManager.AddProviderUserLinkToCentralUserAsync( iamUserId, new IdentityProviderLink( alias, @@ -598,7 +586,7 @@ public async ValueTask GetOwnCompanyUserIdentityPr var companyId = _identityData.CompanyId; var (iamUserId, alias) = await GetUserAliasDataAsync(companyUserId, identityProviderId, companyId).ConfigureAwait(false); - var result = await _provisioningManager.GetProviderUserLinkDataForCentralUserIdAsync(iamUserId).FirstOrDefaultAsync(identityProviderLink => identityProviderLink.Alias == alias).ConfigureAwait(false); + var result = await provisioningManager.GetProviderUserLinkDataForCentralUserIdAsync(iamUserId).FirstOrDefaultAsync(identityProviderLink => identityProviderLink.Alias == alias).ConfigureAwait(false); if (result == default) { throw new NotFoundException($"identityProviderLink for identityProvider {identityProviderId} not found in keycloak for user {companyUserId}"); @@ -616,7 +604,7 @@ public async ValueTask DeleteOwnCompanyUserIdentityProviderDataAsync(Guid compan var (iamUserId, alias) = await GetUserAliasDataAsync(companyUserId, identityProviderId, companyId).ConfigureAwait(false); try { - await _provisioningManager.DeleteProviderUserLinkToCentralUserAsync(iamUserId, alias).ConfigureAwait(ConfigureAwaitOptions.None); + await provisioningManager.DeleteProviderUserLinkToCentralUserAsync(iamUserId, alias).ConfigureAwait(ConfigureAwaitOptions.None); } catch (KeycloakEntityNotFoundException e) { @@ -628,7 +616,7 @@ public async ValueTask GetOwnIden { var companyId = _identityData.CompanyId; - var (alias, category, isOwnerCompany, typeId, metadataUrl, connectedCompanies) = await _portalRepositories.GetInstance().GetOwnIdentityProviderWithConnectedCompanies(identityProviderId, companyId).ConfigureAwait(ConfigureAwaitOptions.None); + var (alias, category, isOwnerCompany, typeId, metadataUrl, connectedCompanies) = await portalRepositories.GetInstance().GetOwnIdentityProviderWithConnectedCompanies(identityProviderId, companyId).ConfigureAwait(ConfigureAwaitOptions.None); if (!isOwnerCompany) { throw new ConflictException($"identityProvider {identityProviderId} is not associated with company {companyId}"); @@ -701,7 +689,7 @@ public ValueTask UploadOwnCompanyUsersIdentityProvi private async ValueTask UploadOwnCompanyUsersIdentityProviderLinkDataInternalAsync(IFormFile document, CancellationToken cancellationToken) { - var userRepository = _portalRepositories.GetInstance(); + var userRepository = portalRepositories.GetInstance(); var companyId = _identityData.CompanyId; var (sharedIdp, existingAliase) = await GetCompanyAliasDataAsync(companyId).ConfigureAwait(false); @@ -734,7 +722,7 @@ private async ValueTask UploadOwnCompanyUsersIdenti private UserUpdateError CreateUserUpdateError(int line, Exception error) => error switch { - DetailException detailException when detailException.HasDetails => new UserUpdateError(line, detailException.GetErrorMessage(_errorMessageService), detailException.GetErrorDetails(_errorMessageService)), + DetailException detailException when detailException.HasDetails => new UserUpdateError(line, detailException.GetErrorMessage(errorMessageService), detailException.GetErrorDetails(errorMessageService)), _ => new UserUpdateError(line, error.Message, Enumerable.Empty()) }; @@ -792,8 +780,8 @@ private UserUpdateError CreateUserUpdateError(int line, Exception error) => private async ValueTask<((Guid IdentityProviderId, string Alias) SharedIdp, IEnumerable<(Guid IdentityProviderId, string Alias)> ValidAliase)> GetCompanyAliasDataAsync(Guid companyId) { - var identityProviderCategoryData = await _portalRepositories.GetInstance() - .GetCompanyIdentityProviderCategoryDataUntracked(companyId) + var identityProviderCategoryData = await portalRepositories.GetInstance() + .GetCompanyIdentityProviderCategoryDataUntracked(companyId, null) .Where(data => data.Alias != null) .Select(data => (data.IdentityProviderId, data.TypeId, Alias: data.Alias!)) .ToListAsync().ConfigureAwait(false); @@ -811,12 +799,12 @@ private UserUpdateError CreateUserUpdateError(int line, Exception error) => } var (existingFirstName, existingLastName, existingEmail) = userEntityData; - var iamUserId = await _provisioningManager.GetUserByUserName(companyUserId.ToString()).ConfigureAwait(ConfigureAwaitOptions.None) ?? throw new ConflictException($"user {companyUserId} does not exist in keycloak"); + var iamUserId = await provisioningManager.GetUserByUserName(companyUserId.ToString()).ConfigureAwait(ConfigureAwaitOptions.None) ?? throw new ConflictException($"user {companyUserId} does not exist in keycloak"); return ( iamUserId, new UserProfile(existingFirstName, existingLastName, existingEmail), - _provisioningManager.GetProviderUserLinkDataForCentralUserIdAsync(iamUserId) + provisioningManager.GetProviderUserLinkDataForCentralUserIdAsync(iamUserId) ); } @@ -845,16 +833,16 @@ private async ValueTask UpdateIdentityProviderLinksAsync( if (existingLink != null) { - await _provisioningManager.DeleteProviderUserLinkToCentralUserAsync(iamUserId, alias).ConfigureAwait(ConfigureAwaitOptions.None); + await provisioningManager.DeleteProviderUserLinkToCentralUserAsync(iamUserId, alias).ConfigureAwait(ConfigureAwaitOptions.None); } - await _provisioningManager.AddProviderUserLinkToCentralUserAsync(iamUserId, identityProviderLink).ConfigureAwait(ConfigureAwaitOptions.None); + await provisioningManager.AddProviderUserLinkToCentralUserAsync(iamUserId, identityProviderLink).ConfigureAwait(ConfigureAwaitOptions.None); await InsertUpdateCompanyUserAssignedIdentityProvider(companyUserId, existingIdps.Single(x => x.Alias == alias).IdentityProviderId, identityProviderLink).ConfigureAwait(ConfigureAwaitOptions.None); return true; } private async Task InsertUpdateCompanyUserAssignedIdentityProvider(Guid companyUserId, Guid identityProviderId, IdentityProviderLink providerLink) { - var userRepository = _portalRepositories.GetInstance(); + var userRepository = portalRepositories.GetInstance(); var data = await userRepository.GetCompanyUserAssignedIdentityProvider(companyUserId, identityProviderId).ConfigureAwait(ConfigureAwaitOptions.None); if (data == default) { @@ -880,14 +868,14 @@ private async ValueTask UpdateUserProfileAsync(IUserRepository userRepository, s { var (firstName, lastName, email) = (profile.FirstName ?? "", profile.LastName ?? "", profile.Email ?? ""); - await _provisioningManager.UpdateCentralUserAsync(iamUserId, firstName, lastName, email).ConfigureAwait(ConfigureAwaitOptions.None); + await provisioningManager.UpdateCentralUserAsync(iamUserId, firstName, lastName, email).ConfigureAwait(ConfigureAwaitOptions.None); if (sharedIdp != default) { var sharedIdpLink = existingLinks.FirstOrDefault(link => link.Alias == sharedIdp.Alias); if (sharedIdpLink != default) { - await _provisioningManager.UpdateSharedRealmUserAsync(sharedIdp.Alias, sharedIdpLink.UserId, firstName, lastName, email).ConfigureAwait(ConfigureAwaitOptions.None); + await provisioningManager.UpdateSharedRealmUserAsync(sharedIdp.Alias, sharedIdpLink.UserId, firstName, lastName, email).ConfigureAwait(ConfigureAwaitOptions.None); } } @@ -897,7 +885,7 @@ private async ValueTask UpdateUserProfileAsync(IUserRepository userRepository, s companyUser.Lastname = profile.LastName; companyUser.Email = profile.Email; }); - await _portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); + await portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); } private int ParseCSVFirstLineReturningNumIdps(string firstLine) @@ -1050,7 +1038,7 @@ private async IAsyncEnumerable GetOwnCompanyUsersIdentityProviderDataLin { throw new ControllerArgumentException("at least one identityProviderId must be specified", nameof(identityProviderIds)); } - var identityProviderData = await _portalRepositories.GetInstance().GetOwnCompanyIdentityProviderAliasDataUntracked(companyId, identityProviderIds).ToListAsync().ConfigureAwait(false); + var identityProviderData = await portalRepositories.GetInstance().GetOwnCompanyIdentityProviderAliasDataUntracked(companyId, identityProviderIds).ToListAsync().ConfigureAwait(false); identityProviderIds.Except(identityProviderData.Select(data => data.IdentityProviderId)).IfAny(invalidIds => { @@ -1062,7 +1050,7 @@ private async IAsyncEnumerable GetOwnCompanyUsersIdentityProviderDataLin private async IAsyncEnumerable<(Guid CompanyUserId, UserProfile UserProfile, IAsyncEnumerable LinkDatas)> GetOwnCompanyIdentityProviderLinkDataInternalAsync(Guid companyId) { - await foreach (var (companyUserId, firstName, lastName, email) in _portalRepositories.GetInstance() + await foreach (var (companyUserId, firstName, lastName, email) in portalRepositories.GetInstance() .GetOwnCompanyUserQuery(companyId) .Select(companyUser => new ValueTuple( @@ -1072,13 +1060,13 @@ private async IAsyncEnumerable GetOwnCompanyUsersIdentityProviderDataLin companyUser.Email)) .ToAsyncEnumerable().ConfigureAwait(false)) { - var iamUserId = await _provisioningManager.GetUserByUserName(companyUserId.ToString()).ConfigureAwait(ConfigureAwaitOptions.None); + var iamUserId = await provisioningManager.GetUserByUserName(companyUserId.ToString()).ConfigureAwait(ConfigureAwaitOptions.None); if (iamUserId != null) { yield return ( companyUserId, new UserProfile(firstName, lastName, email), - _provisioningManager.GetProviderUserLinkDataForCentralUserIdAsync(iamUserId) + provisioningManager.GetProviderUserLinkDataForCentralUserIdAsync(iamUserId) ); } } @@ -1086,7 +1074,7 @@ private async IAsyncEnumerable GetOwnCompanyUsersIdentityProviderDataLin private async ValueTask<(string IamUserId, string Alias)> GetUserAliasDataAsync(Guid companyUserId, Guid identityProviderId, Guid companyId) { - var (isValidUser, alias, isSameCompany) = await _portalRepositories.GetInstance().GetIamUserIsOwnCompanyIdentityProviderAliasAsync(companyUserId, identityProviderId, companyId).ConfigureAwait(ConfigureAwaitOptions.None); + var (isValidUser, alias, isSameCompany) = await portalRepositories.GetInstance().GetIamUserIsOwnCompanyIdentityProviderAliasAsync(companyUserId, identityProviderId, companyId).ConfigureAwait(ConfigureAwaitOptions.None); if (!isValidUser) { throw new NotFoundException($"companyUserId {companyUserId} does not exist"); @@ -1099,7 +1087,7 @@ private async IAsyncEnumerable GetOwnCompanyUsersIdentityProviderDataLin { throw new ForbiddenException($"identityProvider {identityProviderId} is not associated with company {companyId}"); } - var iamUserId = await _provisioningManager.GetUserByUserName(companyUserId.ToString()).ConfigureAwait(ConfigureAwaitOptions.None); + var iamUserId = await provisioningManager.GetUserByUserName(companyUserId.ToString()).ConfigureAwait(ConfigureAwaitOptions.None); if (iamUserId == null) { throw new UnexpectedConditionException($"companyUserId {companyUserId} is not linked to keycloak"); diff --git a/src/administration/Administration.Service/Controllers/IdentityProviderController.cs b/src/administration/Administration.Service/Controllers/IdentityProviderController.cs index 8adb9f2d00..c29c4518f8 100644 --- a/src/administration/Administration.Service/Controllers/IdentityProviderController.cs +++ b/src/administration/Administration.Service/Controllers/IdentityProviderController.cs @@ -34,21 +34,9 @@ namespace Org.Eclipse.TractusX.Portal.Backend.Administration.Service.Controllers /// [EnvironmentRoute("MVC_ROUTING_BASEPATH", "identityprovider")] [ApiController] -public class IdentityProviderController : ControllerBase +public class IdentityProviderController(IIdentityProviderBusinessLogic businessLogic) : ControllerBase { - private readonly IIdentityProviderBusinessLogic _businessLogic; - - /// - /// Constructor. - /// - /// IdentityProvider business logic. - public IdentityProviderController(IIdentityProviderBusinessLogic identityProviderBusinessLogic) - { - _businessLogic = identityProviderBusinessLogic; - } - /// - /// /// Gets the details of the own company identity provider /// /// Returns the details of the own company identity provider @@ -63,8 +51,8 @@ public IdentityProviderController(IIdentityProviderBusinessLogic identityProvide [Route("owncompany/identityproviders")] [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status502BadGateway)] - public ValueTask> GetOwnCompanyIdentityProviderDetails() => - _businessLogic.GetOwnCompanyIdentityProvidersAsync().ToListAsync(); + public ValueTask> GetOwnCompanyIdentityProviderDetails(string? displayName = null, string? alias = null) => + businessLogic.GetOwnCompanyIdentityProvidersAsync(displayName, alias).ToListAsync(); /// /// Create an identity provider @@ -88,7 +76,7 @@ public ValueTask> GetOwnCompanyIdentityProviderDet [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status502BadGateway)] public async ValueTask> CreateOwnCompanyIdentityProvider([FromQuery] IamIdentityProviderProtocol protocol, [FromQuery] IdentityProviderTypeId typeId, [FromQuery] string? displayName = null) { - var details = await _businessLogic.CreateOwnCompanyIdentityProviderAsync(protocol, typeId, displayName).ConfigureAwait(false); + var details = await businessLogic.CreateOwnCompanyIdentityProviderAsync(protocol, typeId, displayName).ConfigureAwait(false); return (ActionResult)CreatedAtRoute(nameof(GetOwnCompanyIdentityProvider), new { identityProviderId = details.IdentityProviderId }, details); } @@ -113,7 +101,7 @@ public async ValueTask> CreateOwnCompanyId [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status500InternalServerError)] [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status502BadGateway)] public ValueTask GetOwnIdentityProviderWithConnectedCompanies([FromRoute] Guid identityProviderId) => - _businessLogic.GetOwnIdentityProviderWithConnectedCompanies(identityProviderId); + businessLogic.GetOwnIdentityProviderWithConnectedCompanies(identityProviderId); /// /// Gets a specific identity provider @@ -136,7 +124,7 @@ public ValueTask GetOwnIdentityPr [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status500InternalServerError)] [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status502BadGateway)] public ValueTask GetOwnCompanyIdentityProvider([FromRoute] Guid identityProviderId) => - _businessLogic.GetOwnCompanyIdentityProviderAsync(identityProviderId); + businessLogic.GetOwnCompanyIdentityProviderAsync(identityProviderId); /// /// Sets the status of the given Identity Provider @@ -164,7 +152,7 @@ public ValueTask GetOwnCompanyIdentityProvider([FromRou [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status500InternalServerError)] [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status502BadGateway)] public ValueTask SetOwnCompanyIdentityProviderStatus([FromRoute] Guid identityProviderId, [FromQuery] bool enabled) => - _businessLogic.SetOwnCompanyIdentityProviderStatusAsync(identityProviderId, enabled); + businessLogic.SetOwnCompanyIdentityProviderStatusAsync(identityProviderId, enabled); /// /// Updates the details of the identity provider @@ -193,7 +181,7 @@ public ValueTask SetOwnCompanyIdentityProviderStatus([F [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status500InternalServerError)] [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status502BadGateway)] public ValueTask UpdateOwnCompanyIdentityProvider([FromRoute] Guid identityProviderId, [FromBody] IdentityProviderEditableDetails details, CancellationToken cancellationToken) => - _businessLogic.UpdateOwnCompanyIdentityProviderAsync(identityProviderId, details, cancellationToken); + businessLogic.UpdateOwnCompanyIdentityProviderAsync(identityProviderId, details, cancellationToken); /// /// Deletes the identity provider with the given id @@ -221,7 +209,7 @@ public ValueTask UpdateOwnCompanyIdentityProvider([From [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status502BadGateway)] public async Task DeleteOwnCompanyIdentityProvider([FromRoute] Guid identityProviderId) { - await _businessLogic.DeleteCompanyIdentityProviderAsync(identityProviderId).ConfigureAwait(false); + await businessLogic.DeleteCompanyIdentityProviderAsync(identityProviderId).ConfigureAwait(false); return NoContent(); } @@ -245,7 +233,7 @@ public async Task DeleteOwnCompanyIdentityProvider([FromRoute] [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status502BadGateway)] public IAsyncEnumerable GetOwnCompanyUsersIdentityProviderDataAsync([FromQuery] IEnumerable identityProviderIds, [FromQuery] bool unlinkedUsersOnly = false) => - _businessLogic.GetOwnCompanyUsersIdentityProviderDataAsync(identityProviderIds, unlinkedUsersOnly); + businessLogic.GetOwnCompanyUsersIdentityProviderDataAsync(identityProviderIds, unlinkedUsersOnly); /// /// Gets the company users for the identity providers as a file @@ -268,7 +256,7 @@ public IAsyncEnumerable GetOwnCompanyUsersIdentityProv [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status502BadGateway)] public IActionResult GetOwnCompanyUsersIdentityProviderFileAsync([FromQuery] IEnumerable identityProviderIds, [FromQuery] bool unlinkedUsersOnly = false) { - var (stream, contentType, fileName, encoding) = _businessLogic.GetOwnCompanyUsersIdentityProviderLinkDataStream(identityProviderIds, unlinkedUsersOnly); + var (stream, contentType, fileName, encoding) = businessLogic.GetOwnCompanyUsersIdentityProviderLinkDataStream(identityProviderIds, unlinkedUsersOnly); return File(stream, string.Join("; ", contentType, encoding.WebName), fileName); } @@ -297,7 +285,7 @@ public IActionResult GetOwnCompanyUsersIdentityProviderFileAsync([FromQuery] IEn [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status415UnsupportedMediaType)] [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status502BadGateway)] public ValueTask UploadOwnCompanyUsersIdentityProviderFileAsync([FromForm(Name = "document")] IFormFile document, CancellationToken cancellationToken) => - _businessLogic.UploadOwnCompanyUsersIdentityProviderLinkDataAsync(document, cancellationToken); + businessLogic.UploadOwnCompanyUsersIdentityProviderLinkDataAsync(document, cancellationToken); /// /// Updates the given user for the given identity provider @@ -326,7 +314,7 @@ public ValueTask UploadOwnCompanyUsersIdentityProvi [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status500InternalServerError)] [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status502BadGateway)] public ValueTask CreateOrUpdateOwnCompanyUserIdentityProviderDataAsync([FromRoute] Guid companyUserId, [FromRoute] Guid identityProviderId, [FromBody] UserLinkData userLinkData) => - _businessLogic.CreateOrUpdateOwnCompanyUserIdentityProviderLinkDataAsync(companyUserId, identityProviderId, userLinkData); + businessLogic.CreateOrUpdateOwnCompanyUserIdentityProviderLinkDataAsync(companyUserId, identityProviderId, userLinkData); /// /// Gets the given user for the given identity provider @@ -354,7 +342,7 @@ public ValueTask CreateOrUpdateOwnCompanyUserIdent [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status500InternalServerError)] [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status502BadGateway)] public ValueTask GetOwnCompanyUserIdentityProviderDataAsync([FromRoute] Guid companyUserId, [FromRoute] Guid identityProviderId) => - _businessLogic.GetOwnCompanyUserIdentityProviderLinkDataAsync(companyUserId, identityProviderId); + businessLogic.GetOwnCompanyUserIdentityProviderLinkDataAsync(companyUserId, identityProviderId); /// /// Deletes the given user on the given identity provider @@ -383,7 +371,7 @@ public ValueTask GetOwnCompanyUserIdentityProvider [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status502BadGateway)] public async ValueTask DeleteOwnCompanyUserIdentityProviderDataAsync([FromRoute] Guid companyUserId, [FromRoute] Guid identityProviderId) { - await _businessLogic.DeleteOwnCompanyUserIdentityProviderDataAsync(companyUserId, identityProviderId).ConfigureAwait(false); + await businessLogic.DeleteOwnCompanyUserIdentityProviderDataAsync(companyUserId, identityProviderId).ConfigureAwait(false); return NoContent(); } } diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/IIdentityProviderRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/IIdentityProviderRepository.cs index 1d288aa6dc..44ec276a19 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/IIdentityProviderRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/IIdentityProviderRepository.cs @@ -44,7 +44,7 @@ public interface IIdentityProviderRepository Task<(bool IsOwner, (string? Alias, IdentityProviderCategoryId IdentityProviderCategory, IdentityProviderTypeId IdentityProviderTypeId, string? MetadataUrl) IdentityProviderData, IEnumerable<(Guid CompanyId, IEnumerable Aliase)>? CompanyIdAliase, bool CompanyUsersLinked, string IdpOwnerName)> GetOwnCompanyIdentityProviderStatusUpdateData(Guid identityProviderId, Guid companyId, bool queryAliase); Task<(bool IsOwner, string? Alias, IdentityProviderCategoryId IdentityProviderCategory, IdentityProviderTypeId IdentityProviderTypeId, string? MetadataUrl)> GetOwnCompanyIdentityProviderUpdateData(Guid identityProviderId, Guid companyId); Task<(bool IsOwner, string? Alias, IdentityProviderTypeId IdentityProviderTypeId, IEnumerable<(Guid CompanyId, IEnumerable Aliase)> CompanyIdAliase, string IdpOwnerName)> GetOwnCompanyIdentityProviderUpdateDataForDelete(Guid identityProviderId, Guid companyId); - IAsyncEnumerable<(Guid IdentityProviderId, IdentityProviderCategoryId CategoryId, string? Alias, IdentityProviderTypeId TypeId, string? MetadataUrl)> GetCompanyIdentityProviderCategoryDataUntracked(Guid companyId); + IAsyncEnumerable<(Guid IdentityProviderId, IdentityProviderCategoryId CategoryId, string? Alias, IdentityProviderTypeId TypeId, string? MetadataUrl)> GetCompanyIdentityProviderCategoryDataUntracked(Guid companyId, string? alias); IAsyncEnumerable<(Guid IdentityProviderId, string Alias)> GetOwnCompanyIdentityProviderAliasDataUntracked(Guid companyId, IEnumerable identityProviderIds); Task<(Guid IdentityProviderId, string? Alias)> GetSingleManagedIdentityProviderAliasDataUntracked(Guid companyId); IAsyncEnumerable<(Guid IdentityProviderId, string? Alias)> GetManagedIdentityProviderAliasDataUntracked(Guid companyId, IEnumerable identityProviderIds); diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/IdentityProviderRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/IdentityProviderRepository.cs index 43d7149a19..8f97dbce56 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/IdentityProviderRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/IdentityProviderRepository.cs @@ -18,6 +18,7 @@ ********************************************************************************/ using Microsoft.EntityFrameworkCore; +using Org.Eclipse.TractusX.Portal.Backend.Framework.DBAccess; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Models; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Entities; @@ -195,10 +196,13 @@ public void AttachAndModifyIamIdentityProvider(string idpAlias, Action GetCompanyIdentityProviderCategoryDataUntracked(Guid companyId) => + public IAsyncEnumerable<(Guid IdentityProviderId, IdentityProviderCategoryId CategoryId, string? Alias, IdentityProviderTypeId TypeId, string? MetadataUrl)> GetCompanyIdentityProviderCategoryDataUntracked(Guid companyId, string? alias) => _context.IdentityProviders .AsNoTracking() - .Where(identityProvider => identityProvider.OwnerId == companyId || identityProvider.Companies.Any(company => company.Id == companyId)) + .Where(identityProvider => + (identityProvider.OwnerId == companyId || identityProvider.Companies.Any(company => company.Id == companyId)) && + (alias == null || EF.Functions.ILike(identityProvider.IamIdentityProvider!.IamIdpAlias, $"{alias.EscapeForILike()}%")) + ) .Select(identityProvider => new ValueTuple( identityProvider.Id, identityProvider.IdentityProviderCategoryId, diff --git a/tests/administration/Administration.Service.Tests/BusinessLogic/IdentityProviderBusinessLogicTests.cs b/tests/administration/Administration.Service.Tests/BusinessLogic/IdentityProviderBusinessLogicTests.cs index 5b1bba1af8..eb0409efcc 100644 --- a/tests/administration/Administration.Service.Tests/BusinessLogic/IdentityProviderBusinessLogicTests.cs +++ b/tests/administration/Administration.Service.Tests/BusinessLogic/IdentityProviderBusinessLogicTests.cs @@ -939,7 +939,7 @@ public async Task GetOwnCompanyIdentityProvidersAsync_WithValidId_ReturnsExpecte var samlGuid = Guid.NewGuid(); var oidc = (oidcGuid, IdentityProviderCategoryId.KEYCLOAK_OIDC, "oidc-alias", IdentityProviderTypeId.OWN, "http://metadata"); var saml = (samlGuid, IdentityProviderCategoryId.KEYCLOAK_SAML, "saml-alias", IdentityProviderTypeId.OWN, default(string?)); - A.CallTo(() => _identityProviderRepository.GetCompanyIdentityProviderCategoryDataUntracked(A._)) + A.CallTo(() => _identityProviderRepository.GetCompanyIdentityProviderCategoryDataUntracked(A._, A._)) .Returns(new (Guid, IdentityProviderCategoryId, string?, IdentityProviderTypeId, string?)[] { oidc, saml }.ToAsyncEnumerable()); A.CallTo(() => _provisioningManager.GetCentralIdentityProviderDataOIDCAsync("oidc-alias")) .Returns(_fixture.Build().With(x => x.Enabled, true).With(x => x.DisplayName, "dis-oidc").Create()); @@ -951,16 +951,88 @@ public async Task GetOwnCompanyIdentityProvidersAsync_WithValidId_ReturnsExpecte .Returns(_fixture.CreateMany(2).ToAsyncEnumerable()); // Act - var result = await sut.GetOwnCompanyIdentityProvidersAsync().ToListAsync(); + var result = await sut.GetOwnCompanyIdentityProvidersAsync(null, null).ToListAsync(); // Assert - A.CallTo(() => _identityProviderRepository.GetCompanyIdentityProviderCategoryDataUntracked(_companyId)).MustHaveHappenedOnceExactly(); + A.CallTo(() => _identityProviderRepository.GetCompanyIdentityProviderCategoryDataUntracked(_companyId, null)).MustHaveHappenedOnceExactly(); result.Should().HaveCount(2).And.Satisfy( x => x.DisplayName == "dis-oidc" && x.Mappers != null && x.Mappers.Count() == 3, x => x.DisplayName == "dis-saml" && x.Mappers != null && x.Mappers.Count() == 2 ); } + [Fact] + public async Task GetOwnCompanyIdentityProvidersAsync_WithValidIdAndDisplayNameFilter_ReturnsExpected() + { + // Arrange + var sut = new IdentityProviderBusinessLogic( + _portalRepositories, + _provisioningManager, + _identityService, + _errorMessageService, + _mailingProcessCreation, + _options, + _logger); + var oidcGuid = Guid.NewGuid(); + var samlGuid = Guid.NewGuid(); + var oidc = (oidcGuid, IdentityProviderCategoryId.KEYCLOAK_OIDC, "oidc-alias", IdentityProviderTypeId.OWN, "http://metadata"); + var saml = (samlGuid, IdentityProviderCategoryId.KEYCLOAK_SAML, "saml-alias", IdentityProviderTypeId.OWN, default(string?)); + A.CallTo(() => _identityProviderRepository.GetCompanyIdentityProviderCategoryDataUntracked(A._, A._)) + .Returns(new (Guid, IdentityProviderCategoryId, string?, IdentityProviderTypeId, string?)[] { oidc, saml }.ToAsyncEnumerable()); + A.CallTo(() => _provisioningManager.GetCentralIdentityProviderDataOIDCAsync("oidc-alias")) + .Returns(_fixture.Build().With(x => x.Enabled, true).With(x => x.DisplayName, "dis-oidc").Create()); + A.CallTo(() => _provisioningManager.GetIdentityProviderMappers("oidc-alias")) + .Returns(_fixture.CreateMany(3).ToAsyncEnumerable()); + A.CallTo(() => _provisioningManager.GetCentralIdentityProviderDataSAMLAsync("saml-alias")) + .Returns(_fixture.Build().With(x => x.Enabled, true).With(x => x.DisplayName, "dis-saml").Create()); + A.CallTo(() => _provisioningManager.GetIdentityProviderMappers("saml-alias")) + .Returns(_fixture.CreateMany(2).ToAsyncEnumerable()); + + // Act + var result = await sut.GetOwnCompanyIdentityProvidersAsync("oidc", null).ToListAsync(); + + // Assert + A.CallTo(() => _identityProviderRepository.GetCompanyIdentityProviderCategoryDataUntracked(_companyId, null)).MustHaveHappenedOnceExactly(); + result.Should().ContainSingle().And.Satisfy( + x => x.DisplayName == "dis-oidc" && x.Mappers != null && x.Mappers.Count() == 3 + ); + } + + [Fact] + public async Task GetOwnCompanyIdentityProvidersAsync_WithValidIdAndAliasFilter_ReturnsExpected() + { + // Arrange + var sut = new IdentityProviderBusinessLogic( + _portalRepositories, + _provisioningManager, + _identityService, + _errorMessageService, + _mailingProcessCreation, + _options, + _logger); + var samlGuid = Guid.NewGuid(); + var saml = (samlGuid, IdentityProviderCategoryId.KEYCLOAK_SAML, "saml-alias", IdentityProviderTypeId.OWN, default(string?)); + A.CallTo(() => _identityProviderRepository.GetCompanyIdentityProviderCategoryDataUntracked(A._, A._)) + .Returns(new (Guid, IdentityProviderCategoryId, string?, IdentityProviderTypeId, string?)[] { saml }.ToAsyncEnumerable()); + A.CallTo(() => _provisioningManager.GetCentralIdentityProviderDataOIDCAsync("oidc-alias")) + .Returns(_fixture.Build().With(x => x.Enabled, true).With(x => x.DisplayName, "dis-oidc").Create()); + A.CallTo(() => _provisioningManager.GetIdentityProviderMappers("oidc-alias")) + .Returns(_fixture.CreateMany(3).ToAsyncEnumerable()); + A.CallTo(() => _provisioningManager.GetCentralIdentityProviderDataSAMLAsync("saml-alias")) + .Returns(_fixture.Build().With(x => x.Enabled, true).With(x => x.DisplayName, "dis-saml").Create()); + A.CallTo(() => _provisioningManager.GetIdentityProviderMappers("saml-alias")) + .Returns(_fixture.CreateMany(2).ToAsyncEnumerable()); + + // Act + var result = await sut.GetOwnCompanyIdentityProvidersAsync(null, "saml").ToListAsync(); + + // Assert + A.CallTo(() => _identityProviderRepository.GetCompanyIdentityProviderCategoryDataUntracked(_companyId, "saml")).MustHaveHappenedOnceExactly(); + result.Should().ContainSingle().And.Satisfy( + x => x.DisplayName == "dis-saml" && x.Mappers != null && x.Mappers.Count() == 2 + ); + } + #endregion #region GetOwnCompanyIdentityProviderAsync @@ -2501,9 +2573,9 @@ private void SetupFakes(IEnumerable userData, IEnumerable (Guid companyUserId, Guid _) => _existingUserId == companyUserId ? (true, _userProviderId, _username) : default((bool, string, string))); - A.CallTo(() => _identityProviderRepository.GetCompanyIdentityProviderCategoryDataUntracked(A.That.Not.IsEqualTo(_companyId))).Returns( + A.CallTo(() => _identityProviderRepository.GetCompanyIdentityProviderCategoryDataUntracked(A.That.Not.IsEqualTo(_companyId), A._)).Returns( Enumerable.Empty<(Guid, IdentityProviderCategoryId, string?, IdentityProviderTypeId, string?)>().ToAsyncEnumerable()); - A.CallTo(() => _identityProviderRepository.GetCompanyIdentityProviderCategoryDataUntracked(A.That.IsEqualTo(_companyId))).Returns( + A.CallTo(() => _identityProviderRepository.GetCompanyIdentityProviderCategoryDataUntracked(A.That.IsEqualTo(_companyId), A._)).Returns( new (Guid, IdentityProviderCategoryId, string?, IdentityProviderTypeId, string?)[] { (_sharedIdentityProviderId, IdentityProviderCategoryId.KEYCLOAK_OIDC, _sharedIdpAlias, IdentityProviderTypeId.SHARED, null), (_otherIdentityProviderId, IdentityProviderCategoryId.KEYCLOAK_OIDC, _otherIdpAlias, IdentityProviderTypeId.OWN, "http://metadata"), diff --git a/tests/portalbackend/PortalBackend.DBAccess.Tests/IdentityProviderRepositoryTests.cs b/tests/portalbackend/PortalBackend.DBAccess.Tests/IdentityProviderRepositoryTests.cs index dc3dd5cecd..637ddf143a 100644 --- a/tests/portalbackend/PortalBackend.DBAccess.Tests/IdentityProviderRepositoryTests.cs +++ b/tests/portalbackend/PortalBackend.DBAccess.Tests/IdentityProviderRepositoryTests.cs @@ -222,7 +222,7 @@ public async Task GetCompanyIdentityProviderCategoryDataUntracked_WithValid_Retu { var sut = await CreateSut(); - var results = await sut.GetCompanyIdentityProviderCategoryDataUntracked(_companyId).ToListAsync(); + var results = await sut.GetCompanyIdentityProviderCategoryDataUntracked(_companyId, null).ToListAsync(); // Assert results.Should().HaveCount(3) @@ -232,6 +232,19 @@ public async Task GetCompanyIdentityProviderCategoryDataUntracked_WithValid_Retu x => x.Alias == "Managed-Alias" && x.CategoryId == IdentityProviderCategoryId.KEYCLOAK_OIDC && x.TypeId == IdentityProviderTypeId.MANAGED); } + [Fact] + public async Task GetCompanyIdentityProviderCategoryDataUntracked_WithValidAndAlias_ReturnsExpected() + { + var sut = await CreateSut(); + + var results = await sut.GetCompanyIdentityProviderCategoryDataUntracked(_companyId, "idp").ToListAsync(); + + // Assert + results.Should().ContainSingle() + .And.Satisfy( + x => x.Alias == "Idp-123" && x.CategoryId == IdentityProviderCategoryId.KEYCLOAK_OIDC && x.TypeId == IdentityProviderTypeId.MANAGED); + } + #endregion #region GetSingleManagedIdentityProviderAliasDataUntracked From c84034ccc428dcc443e40f1e87c93d00b955829e Mon Sep 17 00:00:00 2001 From: Phil Schneider Date: Tue, 10 Sep 2024 11:19:06 +0200 Subject: [PATCH 4/5] feat(connector): adjust connector deletion (#968) * add flag to define whether a linked service account should be deleted * adjust deletion logic for service accounts when deleting a connector ------------------------ Refs: #966 #967 Reviewed-By: Evelyn Gurschler --- .../BusinessLogic/ConnectorsBusinessLogic.cs | 20 ++- .../BusinessLogic/IConnectorsBusinessLogic.cs | 3 +- .../IServiceAccountManagement.cs | 28 +++ .../ServiceAccountBusinessLogic.cs | 55 +----- .../BusinessLogic/ServiceAccountManagement.cs | 92 ++++++++++ .../Controllers/ConnectorsController.cs | 7 +- .../Administration.Service/Program.cs | 1 + .../Models/ConnectorData.cs | 3 +- .../Models/DeleteServiceAccountData.cs | 28 +++ .../Models/OwnServiceAccountData.cs | 2 - .../Repositories/ConnectorsRepository.cs | 62 ++++--- .../Repositories/IConnectorsRepository.cs | 4 +- .../ConnectorsBusinessLogicTests.cs | 103 +++++------ .../ServiceAccountBusinessLogicTests.cs | 165 +++++++----------- .../ServiceAccountManagementTests.cs | 136 +++++++++++++++ .../Controllers/ConnectorsControllerTests.cs | 2 +- .../EndpointSetup/ConnectorsEndpoints.cs | 2 +- .../ConnectorsControllerIntegrationTests.cs | 2 +- .../appsettings.IntegrationTests.json | 2 +- .../ConnectorRepositoryTests.cs | 16 +- 20 files changed, 467 insertions(+), 266 deletions(-) create mode 100644 src/administration/Administration.Service/BusinessLogic/IServiceAccountManagement.cs create mode 100644 src/administration/Administration.Service/BusinessLogic/ServiceAccountManagement.cs create mode 100644 src/portalbackend/PortalBackend.DBAccess/Models/DeleteServiceAccountData.cs create mode 100644 tests/administration/Administration.Service.Tests/BusinessLogic/ServiceAccountManagementTests.cs diff --git a/src/administration/Administration.Service/BusinessLogic/ConnectorsBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/ConnectorsBusinessLogic.cs index 942ebb4e78..14d16f234e 100644 --- a/src/administration/Administration.Service/BusinessLogic/ConnectorsBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/ConnectorsBusinessLogic.cs @@ -45,6 +45,7 @@ public class ConnectorsBusinessLogic( IOptions options, ISdFactoryBusinessLogic sdFactoryBusinessLogic, IIdentityService identityService, + IServiceAccountManagement serviceAccountManagement, ILogger logger) : IConnectorsBusinessLogic { @@ -242,22 +243,26 @@ await sdFactoryBusinessLogic } /// - public async Task DeleteConnectorAsync(Guid connectorId) + public async Task DeleteConnectorAsync(Guid connectorId, bool deleteServiceAccount) { var companyId = _identityData.CompanyId; var connectorsRepository = portalRepositories.GetInstance(); - var result = await connectorsRepository.GetConnectorDeleteDataAsync(connectorId, companyId).ConfigureAwait(ConfigureAwaitOptions.None) ?? throw NotFoundException.Create(AdministrationConnectorErrors.CONNECTOR_NOT_FOUND, new ErrorParameter[] { new("connectorId", connectorId.ToString()) }); + var processStepsToFilter = new[] + { + ProcessStepTypeId.CREATE_DIM_TECHNICAL_USER, ProcessStepTypeId.RETRIGGER_CREATE_DIM_TECHNICAL_USER, + ProcessStepTypeId.AWAIT_CREATE_DIM_TECHNICAL_USER_RESPONSE, + ProcessStepTypeId.RETRIGGER_AWAIT_CREATE_DIM_TECHNICAL_USER_RESPONSE + }; + + var result = await connectorsRepository.GetConnectorDeleteDataAsync(connectorId, companyId, processStepsToFilter).ConfigureAwait(ConfigureAwaitOptions.None) ?? throw NotFoundException.Create(AdministrationConnectorErrors.CONNECTOR_NOT_FOUND, new ErrorParameter[] { new("connectorId", connectorId.ToString()) }); if (!result.IsProvidingOrHostCompany) { throw ForbiddenException.Create(AdministrationConnectorErrors.CONNECTOR_NOT_PROVIDER_COMPANY_NOR_HOST, new ErrorParameter[] { new("companyId", companyId.ToString()), new("connectorId", connectorId.ToString()) }); } - if (result.ServiceAccountId.HasValue && result.UserStatusId != UserStatusId.INACTIVE) + if (result is { ServiceAccountId: not null, UserStatusId: UserStatusId.ACTIVE or UserStatusId.PENDING } && deleteServiceAccount) { - portalRepositories.GetInstance().AttachAndModifyIdentity(result.ServiceAccountId.Value, null, i => - { - i.UserStatusId = UserStatusId.INACTIVE; - }); + await serviceAccountManagement.DeleteServiceAccount(result.ServiceAccountId!.Value, result.DeleteServiceAccountData).ConfigureAwait(false); } switch (result.ConnectorStatus) @@ -290,6 +295,7 @@ private async Task DeleteUpdateConnectorDetail(Guid connectorId, IConnectorsRepo { connectorsRepository.AttachAndModifyConnector(connectorId, null, con => { + con.CompanyServiceAccountId = null; con.StatusId = ConnectorStatusId.INACTIVE; con.DateLastChanged = DateTimeOffset.UtcNow; }); diff --git a/src/administration/Administration.Service/BusinessLogic/IConnectorsBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/IConnectorsBusinessLogic.cs index c1ae735aa8..3c3ee577de 100644 --- a/src/administration/Administration.Service/BusinessLogic/IConnectorsBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/IConnectorsBusinessLogic.cs @@ -67,7 +67,8 @@ public interface IConnectorsBusinessLogic /// Remove a connector from persistence layer by id. /// /// ID of the connector to be deleted. - Task DeleteConnectorAsync(Guid connectorId); + /// if true the linked service account will be deleted, otherwise the connection to the connector will just be removed + Task DeleteConnectorAsync(Guid connectorId, bool deleteServiceAccount); /// /// Retrieve connector end point along with bpns diff --git a/src/administration/Administration.Service/BusinessLogic/IServiceAccountManagement.cs b/src/administration/Administration.Service/BusinessLogic/IServiceAccountManagement.cs new file mode 100644 index 0000000000..81cc828866 --- /dev/null +++ b/src/administration/Administration.Service/BusinessLogic/IServiceAccountManagement.cs @@ -0,0 +1,28 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +using Org.Eclipse.TractusX.Portal.Backend.Administration.Service.Models; +using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Models; + +namespace Org.Eclipse.TractusX.Portal.Backend.Administration.Service.BusinessLogic; + +public interface IServiceAccountManagement +{ + Task DeleteServiceAccount(Guid serviceAccountId, DeleteServiceAccountData result); +} diff --git a/src/administration/Administration.Service/BusinessLogic/ServiceAccountBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/ServiceAccountBusinessLogic.cs index 971667785d..87115f18c2 100644 --- a/src/administration/Administration.Service/BusinessLogic/ServiceAccountBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/ServiceAccountBusinessLogic.cs @@ -28,7 +28,6 @@ using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Models; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Repositories; -using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Entities; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Enums; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Identities; using Org.Eclipse.TractusX.Portal.Backend.Processes.Library; @@ -44,7 +43,8 @@ public class ServiceAccountBusinessLogic( IPortalRepositories portalRepositories, IOptions options, IServiceAccountCreation serviceAccountCreation, - IIdentityService identityService) + IIdentityService identityService, + IServiceAccountManagement serviceAccountManagement) : IServiceAccountBusinessLogic { private readonly IIdentityData _identityData = identityService.IdentityData; @@ -119,34 +119,7 @@ public async Task DeleteOwnCompanyServiceAccountAsync(Guid serviceAccountId } // serviceAccount - var userStatus = UserStatusId.DELETED; - switch (result) - { - case { IsDimServiceAccount: true, CreationProcessInProgress: false }: - userStatus = await CreateDeletionProcess(serviceAccountId, result).ConfigureAwait(ConfigureAwaitOptions.None); - break; - case { IsDimServiceAccount: true, CreationProcessInProgress: true }: - throw ConflictException.Create(AdministrationServiceAccountErrors.TECHNICAL_USER_CREATION_IN_PROGRESS); - default: - if (!string.IsNullOrWhiteSpace(result.ClientClientId)) - { - await provisioningManager.DeleteCentralClientAsync(result.ClientClientId).ConfigureAwait(ConfigureAwaitOptions.None); - } - - break; - } - - portalRepositories.GetInstance().AttachAndModifyIdentity( - serviceAccountId, - i => - { - i.UserStatusId = UserStatusId.PENDING; - }, - i => - { - i.UserStatusId = userStatus; - }); - portalRepositories.GetInstance().DeleteCompanyUserAssignedRoles(result.UserRoleIds.Select(userRoleId => (serviceAccountId, userRoleId))); + await serviceAccountManagement.DeleteServiceAccount(serviceAccountId, new DeleteServiceAccountData(result.UserRoleIds, result.ClientClientId, result.IsDimServiceAccount, result.CreationProcessInProgress, result.ProcessId)).ConfigureAwait(ConfigureAwaitOptions.None); ModifyConnectorForDeleteServiceAccount(serviceAccountId, result); return await portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); @@ -168,28 +141,6 @@ private void ModifyConnectorForDeleteServiceAccount(Guid serviceAccountId, OwnSe } } - private async Task CreateDeletionProcess(Guid serviceAccountId, OwnServiceAccountData result) - { - var processId = result.ProcessId ?? throw ConflictException.Create(AdministrationServiceAccountErrors.SERVICE_ACCOUNT_NOT_LINKED_TO_PROCESS, [new("serviceAccountId", serviceAccountId.ToString())]); - - var processData = await portalRepositories.GetInstance() - .GetProcessDataForServiceAccountDeletionCallback(processId, null) - .ConfigureAwait(ConfigureAwaitOptions.None); - - var context = processData.ProcessData.CreateManualProcessData(null, - portalRepositories, () => $"externalId {processId}"); - - context.ProcessSteps.Where(step => step.ProcessStepTypeId != ProcessStepTypeId.DELETE_DIM_TECHNICAL_USER).IfAny(pending => - throw ConflictException.Create(AdministrationServiceAccountErrors.SERVICE_ACCOUNT_PENDING_PROCESS_STEPS, [new("serviceAccountId", serviceAccountId.ToString()), new("processStepTypeIds", string.Join(",", pending))])); - - if (context.ProcessSteps.Any(step => step.ProcessStepTypeId == ProcessStepTypeId.DELETE_DIM_TECHNICAL_USER)) - return UserStatusId.DELETED; - - context.ScheduleProcessSteps([ProcessStepTypeId.DELETE_DIM_TECHNICAL_USER]); - context.FinalizeProcessStep(); - return UserStatusId.PENDING_DELETION; - } - public async Task GetOwnCompanyServiceAccountDetailsAsync(Guid serviceAccountId) { var companyId = _identityData.CompanyId; diff --git a/src/administration/Administration.Service/BusinessLogic/ServiceAccountManagement.cs b/src/administration/Administration.Service/BusinessLogic/ServiceAccountManagement.cs new file mode 100644 index 0000000000..b1f018c1af --- /dev/null +++ b/src/administration/Administration.Service/BusinessLogic/ServiceAccountManagement.cs @@ -0,0 +1,92 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +using Org.Eclipse.TractusX.Portal.Backend.Administration.Service.ErrorHandling; +using Org.Eclipse.TractusX.Portal.Backend.Administration.Service.Models; +using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; +using Org.Eclipse.TractusX.Portal.Backend.Framework.Linq; +using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess; +using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Models; +using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Repositories; +using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Entities; +using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Enums; +using Org.Eclipse.TractusX.Portal.Backend.Processes.Library; +using Org.Eclipse.TractusX.Portal.Backend.Provisioning.Library; + +namespace Org.Eclipse.TractusX.Portal.Backend.Administration.Service.BusinessLogic; + +public class ServiceAccountManagement(IProvisioningManager provisioningManager, IPortalRepositories portalRepositories) : IServiceAccountManagement +{ + public async Task DeleteServiceAccount(Guid serviceAccountId, DeleteServiceAccountData result) + { + var userStatus = UserStatusId.DELETED; + switch (result) + { + case { IsDimServiceAccount: true, CreationProcessInProgress: false }: + userStatus = await CreateDeletionProcess(serviceAccountId, result.ProcessId).ConfigureAwait(ConfigureAwaitOptions.None); + break; + case { IsDimServiceAccount: true, CreationProcessInProgress: true }: + throw ConflictException.Create(AdministrationServiceAccountErrors.TECHNICAL_USER_CREATION_IN_PROGRESS); + default: + if (!string.IsNullOrWhiteSpace(result.ClientClientId)) + { + await provisioningManager.DeleteCentralClientAsync(result.ClientClientId).ConfigureAwait(ConfigureAwaitOptions.None); + } + + break; + } + + portalRepositories.GetInstance().AttachAndModifyIdentity( + serviceAccountId, + i => + { + i.UserStatusId = UserStatusId.PENDING; + }, + i => + { + i.UserStatusId = userStatus; + }); + portalRepositories.GetInstance().DeleteCompanyUserAssignedRoles(result.UserRoleIds.Select(userRoleId => (serviceAccountId, userRoleId))); + } + + private async Task CreateDeletionProcess(Guid serviceAccountId, Guid? processId) + { + if (processId == null) + { + throw ConflictException.Create(AdministrationServiceAccountErrors.SERVICE_ACCOUNT_NOT_LINKED_TO_PROCESS, [new ErrorParameter("serviceAccountId", serviceAccountId.ToString())]); + } + + var processData = await portalRepositories.GetInstance() + .GetProcessDataForServiceAccountDeletionCallback(processId.Value, null) + .ConfigureAwait(ConfigureAwaitOptions.None); + + var context = processData.ProcessData.CreateManualProcessData(null, + portalRepositories, () => $"externalId {processId}"); + + context.ProcessSteps.Where(step => step.ProcessStepTypeId != ProcessStepTypeId.DELETE_DIM_TECHNICAL_USER).IfAny(pending => + throw ConflictException.Create(AdministrationServiceAccountErrors.SERVICE_ACCOUNT_PENDING_PROCESS_STEPS, [new ErrorParameter("serviceAccountId", serviceAccountId.ToString()), new("processStepTypeIds", string.Join(",", pending))])); + + if (context.ProcessSteps.Any(step => step.ProcessStepTypeId == ProcessStepTypeId.DELETE_DIM_TECHNICAL_USER)) + return UserStatusId.DELETED; + + context.ScheduleProcessSteps([ProcessStepTypeId.DELETE_DIM_TECHNICAL_USER]); + context.FinalizeProcessStep(); + return UserStatusId.PENDING_DELETION; + } +} diff --git a/src/administration/Administration.Service/Controllers/ConnectorsController.cs b/src/administration/Administration.Service/Controllers/ConnectorsController.cs index afd393a032..ff8aa8a64a 100644 --- a/src/administration/Administration.Service/Controllers/ConnectorsController.cs +++ b/src/administration/Administration.Service/Controllers/ConnectorsController.cs @@ -150,7 +150,8 @@ public async Task CreateManagedConnectorAsync([FromForm] M /// Removes a connector from persistence layer by id. /// /// ID of the connector to be deleted. - /// Example: DELETE: /api/administration/connectors/5636F9B9-C3DE-4BA5-8027-00D17A2FECFB + /// if true the linked service account will be deleted, otherwise the connection to the connector will just be removed + /// Example: DELETE: /api/administration/connectors/{connectorId}?deleteServiceAccount=true /// Empty response on success. /// Record not found. /// Connector status does not match a deletion scenario. Deletion declined. @@ -161,9 +162,9 @@ public async Task CreateManagedConnectorAsync([FromForm] M [ProducesResponseType(typeof(IActionResult), StatusCodes.Status204NoContent)] [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status404NotFound)] [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status409Conflict)] - public async Task DeleteConnectorAsync([FromRoute] Guid connectorId) + public async Task DeleteConnectorAsync([FromRoute] Guid connectorId, [FromQuery] bool deleteServiceAccount = false) { - await logic.DeleteConnectorAsync(connectorId); + await logic.DeleteConnectorAsync(connectorId, deleteServiceAccount); return NoContent(); } diff --git a/src/administration/Administration.Service/Program.cs b/src/administration/Administration.Service/Program.cs index a6868d2408..7ac0d28e1b 100644 --- a/src/administration/Administration.Service/Program.cs +++ b/src/administration/Administration.Service/Program.cs @@ -84,6 +84,7 @@ await WebAppHelper builder.Services .AddTransient() + .AddTransient() .AddPartnerRegistration(builder.Configuration) .AddNetworkRegistrationProcessHelper() .AddIssuerComponentService(builder.Configuration.GetSection("Issuer")); diff --git a/src/portalbackend/PortalBackend.DBAccess/Models/ConnectorData.cs b/src/portalbackend/PortalBackend.DBAccess/Models/ConnectorData.cs index 54451cf40b..587c3b2ac6 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Models/ConnectorData.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Models/ConnectorData.cs @@ -74,7 +74,8 @@ public record DeleteConnectorData( ConnectorStatusId ConnectorStatus, IEnumerable ConnectorOfferSubscriptions, UserStatusId? UserStatusId, - Guid? ServiceAccountId + Guid? ServiceAccountId, + DeleteServiceAccountData DeleteServiceAccountData ); public record ConnectorOfferSubscription(Guid AssignedOfferSubscriptionIds, OfferSubscriptionStatusId OfferSubscriptionStatus); diff --git a/src/portalbackend/PortalBackend.DBAccess/Models/DeleteServiceAccountData.cs b/src/portalbackend/PortalBackend.DBAccess/Models/DeleteServiceAccountData.cs new file mode 100644 index 0000000000..87e60850ab --- /dev/null +++ b/src/portalbackend/PortalBackend.DBAccess/Models/DeleteServiceAccountData.cs @@ -0,0 +1,28 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +namespace Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Models; + +public record DeleteServiceAccountData( + IEnumerable UserRoleIds, + string? ClientClientId, + bool IsDimServiceAccount, + bool CreationProcessInProgress, + Guid? ProcessId +); diff --git a/src/portalbackend/PortalBackend.DBAccess/Models/OwnServiceAccountData.cs b/src/portalbackend/PortalBackend.DBAccess/Models/OwnServiceAccountData.cs index 99c59d7936..07df725b72 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Models/OwnServiceAccountData.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Models/OwnServiceAccountData.cs @@ -33,5 +33,3 @@ public record OwnServiceAccountData( bool CreationProcessInProgress, Guid? ProcessId ); - -public record ProcessData(Guid ProcessId, IEnumerable ProcessStepIds); diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/ConnectorsRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/ConnectorsRepository.cs index 4268eba203..267a6a3b85 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/ConnectorsRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/ConnectorsRepository.cs @@ -26,31 +26,20 @@ namespace Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Repositories; -/// Implementation of accessing database with EF Core. -public class ConnectorsRepository : IConnectorsRepository +/// +public class ConnectorsRepository(PortalDbContext dbContext) : IConnectorsRepository { - private readonly PortalDbContext _context; - - /// - /// Constructor. - /// - /// PortalDb context. - public ConnectorsRepository(PortalDbContext portalDbContext) - { - _context = portalDbContext; - } - /// public Func?>> GetAllCompanyConnectorsForCompanyId(Guid companyId) => (skip, take) => Pagination.CreateSourceQueryAsync( skip, take, - _context.Connectors.AsNoTracking() + dbContext.Connectors.AsNoTracking() .Where(x => x.ProviderId == companyId && x.StatusId != ConnectorStatusId.INACTIVE && x.TypeId == ConnectorTypeId.COMPANY_CONNECTOR) .GroupBy(c => c.ProviderId), - connector => connector.OrderByDescending(connector => connector.Name), + connector => connector.OrderByDescending(c => c.Name), con => new ConnectorData( con.Name, con.Location!.Alpha2Code, @@ -73,7 +62,7 @@ public ConnectorsRepository(PortalDbContext portalDbContext) (skip, take) => Pagination.CreateSourceQueryAsync( skip, take, - _context.Connectors.AsNoTracking() + dbContext.Connectors.AsNoTracking() .Where(c => c.HostId == companyId && c.StatusId != ConnectorStatusId.INACTIVE && c.TypeId == ConnectorTypeId.CONNECTOR_AS_A_SERVICE) @@ -96,7 +85,7 @@ public ConnectorsRepository(PortalDbContext portalDbContext) ).SingleOrDefaultAsync(); public Task<(ConnectorData ConnectorData, bool IsProviderCompany)> GetConnectorByIdForCompany(Guid connectorId, Guid companyId) => - _context.Connectors + dbContext.Connectors .AsNoTracking() .Where(connector => connector.Id == connectorId && connector.StatusId != ConnectorStatusId.INACTIVE) .Select(connector => new ValueTuple( @@ -120,7 +109,7 @@ public ConnectorsRepository(PortalDbContext portalDbContext) .SingleOrDefaultAsync(); public Task<(ConnectorInformationData ConnectorInformationData, bool IsProviderUser)> GetConnectorInformationByIdForIamUser(Guid connectorId, Guid userCompanyId) => - _context.Connectors + dbContext.Connectors .AsNoTracking() .Where(connector => connector.Id == connectorId && connector.StatusId != ConnectorStatusId.INACTIVE) .Select(connector => new ValueTuple( @@ -134,12 +123,12 @@ public Connector CreateConnector(string name, string location, string connectorU { var connector = new Connector(Guid.NewGuid(), name, location, connectorUrl); setupOptionalFields?.Invoke(connector); - return _context.Connectors.Add(connector).Entity; + return dbContext.Connectors.Add(connector).Entity; } /// public IAsyncEnumerable<(string BusinessPartnerNumber, string ConnectorEndpoint)> GetConnectorEndPointDataAsync(IEnumerable bpns) => - _context.Connectors + dbContext.Connectors .AsNoTracking() .Where(connector => connector.StatusId == ConnectorStatusId.ACTIVE && (!bpns.Any() || bpns.Contains(connector.Provider!.BusinessPartnerNumber))) .OrderBy(connector => connector.ProviderId) @@ -155,21 +144,21 @@ public Connector AttachAndModifyConnector(Guid connectorId, Action? i { var connector = new Connector(connectorId, null!, null!, null!); initialize?.Invoke(connector); - _context.Attach(connector); + dbContext.Attach(connector); setOptionalParameters(connector); return connector; } /// public Task<(Guid ConnectorId, Guid? SelfDescriptionDocumentId)> GetConnectorDataById(Guid connectorId) => - _context.Connectors + dbContext.Connectors .Where(x => x.Id == connectorId && x.StatusId != ConnectorStatusId.INACTIVE) .Select(x => new ValueTuple(x.Id, x.SelfDescriptionDocumentId)) .SingleOrDefaultAsync(); /// - public Task GetConnectorDeleteDataAsync(Guid connectorId, Guid companyId) => - _context.Connectors + public Task GetConnectorDeleteDataAsync(Guid connectorId, Guid companyId, IEnumerable processStepsToFilter) => + dbContext.Connectors .Where(x => x.Id == connectorId) .Select(connector => new DeleteConnectorData( connector.ProviderId == companyId || connector.HostId == companyId, @@ -181,12 +170,21 @@ public Connector AttachAndModifyConnector(Guid connectorId, Action? i x.OfferSubscription!.OfferSubscriptionStatusId )), connector.CompanyServiceAccount!.Identity!.UserStatusId, - connector.CompanyServiceAccountId + connector.CompanyServiceAccountId, + new DeleteServiceAccountData( + connector.CompanyServiceAccount!.Identity!.IdentityAssignedRoles.Select(r => r.UserRoleId), + connector.CompanyServiceAccount.ClientClientId, + connector.CompanyServiceAccount.CompanyServiceAccountKindId == CompanyServiceAccountKindId.EXTERNAL, + connector.CompanyServiceAccount.DimUserCreationData!.Process!.ProcessSteps + .Any(ps => + ps.ProcessStepStatusId == ProcessStepStatusId.TODO && + processStepsToFilter.Contains(ps.ProcessStepTypeId)), + connector.CompanyServiceAccount.DimUserCreationData == null ? null : connector.CompanyServiceAccount.DimUserCreationData!.ProcessId) )).SingleOrDefaultAsync(); /// public Task GetConnectorUpdateInformation(Guid connectorId, Guid companyId) => - _context.Connectors + dbContext.Connectors .Where(c => c.Id == connectorId) .Select(c => new ConnectorUpdateInformation( c.StatusId, @@ -198,21 +196,21 @@ public Connector AttachAndModifyConnector(Guid connectorId, Action? i .SingleOrDefaultAsync(); public void DeleteConnector(Guid connectorId) => - _context.Connectors.Remove(new Connector(connectorId, null!, null!, null!)); + dbContext.Connectors.Remove(new Connector(connectorId, null!, null!, null!)); /// public ConnectorAssignedOfferSubscription CreateConnectorAssignedSubscriptions(Guid connectorId, Guid subscriptionId) => - _context.ConnectorAssignedOfferSubscriptions.Add(new ConnectorAssignedOfferSubscription(connectorId, subscriptionId)).Entity; + dbContext.ConnectorAssignedOfferSubscriptions.Add(new ConnectorAssignedOfferSubscription(connectorId, subscriptionId)).Entity; /// public void DeleteConnectorAssignedSubscriptions(Guid connectorId, IEnumerable assignedOfferSubscriptions) => - _context.ConnectorAssignedOfferSubscriptions.RemoveRange(assignedOfferSubscriptions.Select(x => new ConnectorAssignedOfferSubscription(connectorId, x))); + dbContext.ConnectorAssignedOfferSubscriptions.RemoveRange(assignedOfferSubscriptions.Select(x => new ConnectorAssignedOfferSubscription(connectorId, x))); public Func?>> GetConnectorsWithMissingSdDocument() => (skip, take) => Pagination.CreateSourceQueryAsync( skip, take, - _context.Connectors.AsNoTracking() + dbContext.Connectors.AsNoTracking() .Where(x => x.StatusId == ConnectorStatusId.ACTIVE && x.SelfDescriptionDocumentId == null) .GroupBy(c => c.StatusId), connector => connector.OrderByDescending(c => c.Name), @@ -224,13 +222,13 @@ public void DeleteConnectorAssignedSubscriptions(Guid connectorId, IEnumerable GetConnectorIdsWithMissingSelfDescription() => - _context.Connectors + dbContext.Connectors .Where(c => c.StatusId == ConnectorStatusId.ACTIVE && c.SelfDescriptionDocumentId == null && c.Provider!.SelfDescriptionDocumentId != null) .Select(c => c.Id) .ToAsyncEnumerable(); public Task<(Guid Id, string? BusinessPartnerNumber, Guid SelfDescriptionDocumentId)> GetConnectorForProcessId(Guid processId) => - _context.Connectors + dbContext.Connectors .Where(c => c.SdCreationProcessId == processId) .Select(c => new ValueTuple(c.Id, c.Provider!.BusinessPartnerNumber, c.Provider.SelfDescriptionDocumentId!.Value)) .SingleOrDefaultAsync(); diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/IConnectorsRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/IConnectorsRepository.cs index 0d4fd47593..07da1a7c26 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/IConnectorsRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/IConnectorsRepository.cs @@ -20,6 +20,7 @@ using Org.Eclipse.TractusX.Portal.Backend.Framework.Models; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Models; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Entities; +using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Enums; namespace Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Repositories; @@ -84,8 +85,9 @@ public interface IConnectorsRepository /// /// Id of the connector /// Id of the company + /// /// returns SelfDescriptionDocument Data/c> - Task GetConnectorDeleteDataAsync(Guid connectorId, Guid companyId); + Task GetConnectorDeleteDataAsync(Guid connectorId, Guid companyId, IEnumerable processStepsToFilter); /// /// Gets the data required for the connector update diff --git a/tests/administration/Administration.Service.Tests/BusinessLogic/ConnectorsBusinessLogicTests.cs b/tests/administration/Administration.Service.Tests/BusinessLogic/ConnectorsBusinessLogicTests.cs index 71f852372b..fcd1fc8056 100644 --- a/tests/administration/Administration.Service.Tests/BusinessLogic/ConnectorsBusinessLogicTests.cs +++ b/tests/administration/Administration.Service.Tests/BusinessLogic/ConnectorsBusinessLogicTests.cs @@ -63,8 +63,8 @@ public class ConnectorsBusinessLogicTests private readonly ConnectorsBusinessLogic _logic; private readonly IDocumentRepository _documentRepository; private readonly IServiceAccountRepository _serviceAccountRepository; - private readonly IOptions _options; private readonly IIdentityService _identityService; + private readonly IServiceAccountManagement _serviceAccountManagement; public ConnectorsBusinessLogicTests() { @@ -76,6 +76,7 @@ public ConnectorsBusinessLogicTests() _connectorsRepository = A.Fake(); _userRepository = A.Fake(); _sdFactoryBusinessLogic = A.Fake(); + _serviceAccountManagement = A.Fake(); _serviceAccountRepository = A.Fake(); _offerSubscriptionRepository = A.Fake(); _identityService = A.Fake(); @@ -83,7 +84,7 @@ public ConnectorsBusinessLogicTests() _portalRepositories = A.Fake(); _identity = A.Fake(); _connectors = new List(); - _options = A.Fake>(); + var options = A.Fake>(); var settings = new ConnectorsSettings { MaxPageSize = 15, @@ -97,12 +98,12 @@ public ConnectorsBusinessLogicTests() _documentRepository = A.Fake(); SetupRepositoryMethods(); - A.CallTo(() => _options.Value).Returns(settings); + A.CallTo(() => options.Value).Returns(settings); A.CallTo(() => _identityService.IdentityData).Returns(_identity); SetupIdentity(); - _logic = new ConnectorsBusinessLogic(_portalRepositories, _options, _sdFactoryBusinessLogic, _identityService, A.Fake>()); + _logic = new ConnectorsBusinessLogic(_portalRepositories, options, _sdFactoryBusinessLogic, _identityService, _serviceAccountManagement, A.Fake>()); } #region GetAllCompanyConnectorDatas @@ -177,7 +178,7 @@ public async Task CreateConnectorAsync_WithValidInput_ReturnsCreatedConnectorDat "application/pkix-cert" }, ClearinghouseConnectDisabled = clearingHouseDisabled - }), _sdFactoryBusinessLogic, _identityService, A.Fake>()); + }), _sdFactoryBusinessLogic, _identityService, _serviceAccountManagement, A.Fake>()); var connectorInput = new ConnectorInputModel("connectorName", "https://test.de", "de", ServiceAccountUserId); @@ -242,7 +243,7 @@ public async Task CreateConnectorAsync_WithoutSelfDescriptionDocumentAndSdConnec "application/pkix-cert" }, ClearinghouseConnectDisabled = true - }), _sdFactoryBusinessLogic, _identityService, A.Fake>()); + }), _sdFactoryBusinessLogic, _identityService, _serviceAccountManagement, A.Fake>()); var connectorInput = new ConnectorInputModel("connectorName", "https://test.de", "de", null); A.CallTo(() => _identity.CompanyId).Returns(CompanyIdWithoutSdDocument); @@ -334,7 +335,7 @@ public async Task CreateManagedConnectorAsync_WithValidInput_ReturnsCreatedConne "application/pkix-cert" }, ClearinghouseConnectDisabled = clearingHouseDisabled - }), _sdFactoryBusinessLogic, _identityService, A.Fake>()); + }), _sdFactoryBusinessLogic, _identityService, _serviceAccountManagement, A.Fake>()); var connectorInput = new ManagedConnectorInputModel("connectorName", "https://test.de", "de", _validOfferSubscriptionId, ServiceAccountUserId); SetupCheckActiveServiceAccountExistsForCompanyAsyncForManaged(); @@ -366,7 +367,7 @@ public async Task CreateManagedConnectorAsync_WithTechnicalUser_ReturnsCreatedCo "application/pkix-cert" }, ClearinghouseConnectDisabled = clearingHouseDisabled - }), _sdFactoryBusinessLogic, _identityService, A.Fake>()); + }), _sdFactoryBusinessLogic, _identityService, _serviceAccountManagement, A.Fake>()); var connectorInput = new ManagedConnectorInputModel("connectorName", "https://test.de", "de", _validOfferSubscriptionId, null); @@ -612,9 +613,8 @@ public async Task DeleteConnectorAsync_WithDocumentId_AndActiveUser_ExpectedCall new ConnectorOfferSubscription(_fixture.Create(), OfferSubscriptionStatusId.PENDING), }; var userId = Guid.NewGuid(); - Identity? identity = null; - A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(A._, _identity.CompanyId)) - .Returns(new DeleteConnectorData(true, selfDescriptionDocumentId, DocumentStatusId, ConnectorStatusId.ACTIVE, connectorOfferSubscriptions, UserStatusId.ACTIVE, userId)); + A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(A._, _identity.CompanyId, A>._)) + .Returns(new DeleteConnectorData(true, selfDescriptionDocumentId, DocumentStatusId, ConnectorStatusId.ACTIVE, connectorOfferSubscriptions, UserStatusId.ACTIVE, userId, _fixture.Create())); A.CallTo(() => _documentRepository.AttachAndModifyDocument(A._, A>._, A>._)) .Invokes((Guid docId, Action? initialize, Action modify) @@ -630,21 +630,14 @@ public async Task DeleteConnectorAsync_WithDocumentId_AndActiveUser_ExpectedCall initialize?.Invoke(connector); setOptionalFields.Invoke(connector); }); - A.CallTo(() => _userRepository.AttachAndModifyIdentity(A._, A>._, A>._)) - .Invokes((Guid id, Action? initialize, Action modify) => - { - identity = new Identity(id, default, Guid.Empty, default, default); - initialize?.Invoke(identity); - modify.Invoke(identity); - }); + // Act - await _logic.DeleteConnectorAsync(connectorId); + await _logic.DeleteConnectorAsync(connectorId, true); // Assert connector.StatusId.Should().Be(ConnectorStatusId.INACTIVE); - A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(connectorId, _identity.CompanyId)).MustHaveHappenedOnceExactly(); - A.CallTo(() => _userRepository.AttachAndModifyIdentity(userId, A>._, A>._)).MustHaveHappenedOnceExactly(); - identity.Should().NotBeNull().And.Match(x => x.Id == userId && x.UserStatusId == UserStatusId.INACTIVE); + A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(connectorId, _identity.CompanyId, A>._)).MustHaveHappenedOnceExactly(); + A.CallTo(() => _serviceAccountManagement.DeleteServiceAccount(userId, A._)).MustHaveHappenedOnceExactly(); A.CallTo(() => _documentRepository.AttachAndModifyDocument(selfDescriptionDocumentId, A>._, A>._)).MustHaveHappenedOnceExactly(); A.CallTo(() => _connectorsRepository.AttachAndModifyConnector(connectorId, A>._, A>._)).MustHaveHappenedOnceExactly(); A.CallTo(() => _connectorsRepository.DeleteConnectorAssignedSubscriptions(connectorId, A>.That.Matches(x => x.Count() == 2))).MustHaveHappenedOnceExactly(); @@ -666,8 +659,8 @@ public async Task DeleteConnectorAsync_WithDocumentId_WithInactiveOrNoUser_Expec new ConnectorOfferSubscription(_fixture.Create(), OfferSubscriptionStatusId.PENDING), }; var userId = id == null ? default(Guid?) : new Guid(id); - A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(A._, _identity.CompanyId)) - .Returns(new DeleteConnectorData(true, selfDescriptionDocumentId, DocumentStatusId, ConnectorStatusId.ACTIVE, connectorOfferSubscriptions, statusId, userId)); + A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(A._, _identity.CompanyId, A>._)) + .Returns(new DeleteConnectorData(true, selfDescriptionDocumentId, DocumentStatusId, ConnectorStatusId.ACTIVE, connectorOfferSubscriptions, statusId, userId, _fixture.Create())); A.CallTo(() => _documentRepository.AttachAndModifyDocument(A._, A>._, A>._)) .Invokes((Guid docId, Action? initialize, Action modify) @@ -684,11 +677,11 @@ public async Task DeleteConnectorAsync_WithDocumentId_WithInactiveOrNoUser_Expec setOptionalFields.Invoke(connector); }); // Act - await _logic.DeleteConnectorAsync(connectorId); + await _logic.DeleteConnectorAsync(connectorId, true); // Assert connector.StatusId.Should().Be(ConnectorStatusId.INACTIVE); - A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(connectorId, _identity.CompanyId)).MustHaveHappenedOnceExactly(); + A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(connectorId, _identity.CompanyId, A>._)).MustHaveHappenedOnceExactly(); A.CallTo(() => _userRepository.AttachAndModifyIdentity(A._, A>._, A>._)).MustNotHaveHappened(); A.CallTo(() => _documentRepository.AttachAndModifyDocument(selfDescriptionDocumentId, A>._, A>._)).MustHaveHappenedOnceExactly(); A.CallTo(() => _connectorsRepository.AttachAndModifyConnector(connectorId, A>._, A>._)).MustHaveHappenedOnceExactly(); @@ -705,14 +698,14 @@ public async Task DeleteConnectorAsync_WithPendingAndWithoutDocumentId_ExpectedC new ConnectorOfferSubscription(_fixture.Create(), OfferSubscriptionStatusId.PENDING), new ConnectorOfferSubscription(_fixture.Create(), OfferSubscriptionStatusId.PENDING), }; - A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(A._, _identity.CompanyId)) - .Returns(new DeleteConnectorData(true, null, null, ConnectorStatusId.PENDING, connectorOfferSubscriptions, UserStatusId.ACTIVE, Guid.NewGuid())); + A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(A._, _identity.CompanyId, A>._)) + .Returns(new DeleteConnectorData(true, null, null, ConnectorStatusId.PENDING, connectorOfferSubscriptions, UserStatusId.ACTIVE, Guid.NewGuid(), _fixture.Create())); // Act - await _logic.DeleteConnectorAsync(connectorId); + await _logic.DeleteConnectorAsync(connectorId, true); // Assert - A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(connectorId, _identity.CompanyId)).MustHaveHappenedOnceExactly(); + A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(connectorId, _identity.CompanyId, A>._)).MustHaveHappenedOnceExactly(); A.CallTo(() => _connectorsRepository.DeleteConnector(connectorId)).MustHaveHappenedOnceExactly(); A.CallTo(() => _connectorsRepository.DeleteConnectorAssignedSubscriptions(connectorId, A>.That.Matches(x => x.Count() == 2))).MustHaveHappenedOnceExactly(); A.CallTo(() => _portalRepositories.SaveAsync()).MustHaveHappenedOnceExactly(); @@ -729,14 +722,14 @@ public async Task DeleteConnectorAsync_WithPendingAndDocumentId_ExpectedCalls() new ConnectorOfferSubscription(_fixture.Create(), OfferSubscriptionStatusId.PENDING), new ConnectorOfferSubscription(_fixture.Create(), OfferSubscriptionStatusId.PENDING), }; - A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(A._, _identity.CompanyId)) - .Returns(new DeleteConnectorData(true, selfDescriptionDocumentId, DocumentStatusId, ConnectorStatusId.PENDING, connectorOfferSubscriptions, UserStatusId.ACTIVE, Guid.NewGuid())); + A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(A._, _identity.CompanyId, A>._)) + .Returns(new DeleteConnectorData(true, selfDescriptionDocumentId, DocumentStatusId, ConnectorStatusId.PENDING, connectorOfferSubscriptions, UserStatusId.ACTIVE, Guid.NewGuid(), _fixture.Create())); // Act - await _logic.DeleteConnectorAsync(connectorId); + await _logic.DeleteConnectorAsync(connectorId, true); // Assert - A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(connectorId, _identity.CompanyId)).MustHaveHappenedOnceExactly(); + A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(connectorId, _identity.CompanyId, A>._)).MustHaveHappenedOnceExactly(); A.CallTo(() => _documentRepository.RemoveDocument(selfDescriptionDocumentId)).MustHaveHappenedOnceExactly(); A.CallTo(() => _connectorsRepository.DeleteConnector(connectorId)).MustHaveHappenedOnceExactly(); A.CallTo(() => _connectorsRepository.DeleteConnectorAssignedSubscriptions(connectorId, A>.That.Matches(x => x.Count() == 2))).MustHaveHappenedOnceExactly(); @@ -750,14 +743,14 @@ public async Task DeleteConnectorAsync_WithoutAssignedOfferSubscriptions_Expecte const DocumentStatusId DocumentStatusId = DocumentStatusId.LOCKED; var connectorId = Guid.NewGuid(); var selfDescriptionDocumentId = Guid.NewGuid(); - A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(A._, _identity.CompanyId)) - .Returns(new DeleteConnectorData(true, selfDescriptionDocumentId, DocumentStatusId, ConnectorStatusId.PENDING, Enumerable.Empty(), UserStatusId.ACTIVE, Guid.NewGuid())); + A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(A._, _identity.CompanyId, A>._)) + .Returns(new DeleteConnectorData(true, selfDescriptionDocumentId, DocumentStatusId, ConnectorStatusId.PENDING, Enumerable.Empty(), UserStatusId.ACTIVE, Guid.NewGuid(), _fixture.Create())); // Act - await _logic.DeleteConnectorAsync(connectorId); + await _logic.DeleteConnectorAsync(connectorId, true); // Assert - A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(connectorId, _identity.CompanyId)).MustHaveHappenedOnceExactly(); + A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(connectorId, _identity.CompanyId, A>._)).MustHaveHappenedOnceExactly(); A.CallTo(() => _documentRepository.RemoveDocument(selfDescriptionDocumentId)).MustHaveHappenedOnceExactly(); A.CallTo(() => _connectorsRepository.DeleteConnector(connectorId)).MustHaveHappenedOnceExactly(); A.CallTo(() => _connectorsRepository.DeleteConnectorAssignedSubscriptions(connectorId, A>._)).MustNotHaveHappened(); @@ -769,11 +762,11 @@ public async Task DeleteConnectorAsync_WithOutDocumentId_ExpectedCalls() { // Arrange var connectorId = Guid.NewGuid(); - A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(connectorId, _identity.CompanyId)) - .Returns(new DeleteConnectorData(true, null, null, ConnectorStatusId.ACTIVE, Enumerable.Empty(), UserStatusId.ACTIVE, Guid.NewGuid())); + A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(connectorId, _identity.CompanyId, A>._)) + .Returns(new DeleteConnectorData(true, null, null, ConnectorStatusId.ACTIVE, Enumerable.Empty(), UserStatusId.ACTIVE, Guid.NewGuid(), _fixture.Create())); // Act - async Task Act() => await _logic.DeleteConnectorAsync(connectorId); + async Task Act() => await _logic.DeleteConnectorAsync(connectorId, true); // Assert var ex = await Assert.ThrowsAsync(Act); @@ -785,11 +778,11 @@ public async Task DeleteConnectorAsync_WithInactiveConnector_ThrowsConflictExcep { // Arrange var connectorId = Guid.NewGuid(); - A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(connectorId, _identity.CompanyId)) - .Returns(new DeleteConnectorData(true, null, null, ConnectorStatusId.ACTIVE, Enumerable.Empty(), UserStatusId.ACTIVE, Guid.NewGuid())); + A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(connectorId, _identity.CompanyId, A>._)) + .Returns(new DeleteConnectorData(true, null, null, ConnectorStatusId.ACTIVE, Enumerable.Empty(), UserStatusId.ACTIVE, Guid.NewGuid(), _fixture.Create())); // Act - async Task Act() => await _logic.DeleteConnectorAsync(connectorId); + async Task Act() => await _logic.DeleteConnectorAsync(connectorId, true); // Assert var ex = await Assert.ThrowsAsync(Act); @@ -801,11 +794,11 @@ public async Task DeleteConnectorAsync_ThrowsNotFoundException() { // Arrange var connectorId = Guid.NewGuid(); - A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(connectorId, _identity.CompanyId)) + A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(connectorId, _identity.CompanyId, A>._)) .Returns(default(DeleteConnectorData)); // Act - async Task Act() => await _logic.DeleteConnectorAsync(connectorId); + async Task Act() => await _logic.DeleteConnectorAsync(connectorId, true); // Assert var ex = await Assert.ThrowsAsync(Act); @@ -817,11 +810,11 @@ public async Task DeleteConnectorAsync_ThrowsForbiddenException() { // Arrange var connectorId = Guid.NewGuid(); - A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(connectorId, _identity.CompanyId)) - .Returns(new DeleteConnectorData(false, null, null, default, Enumerable.Empty(), UserStatusId.ACTIVE, Guid.NewGuid())); + A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(connectorId, _identity.CompanyId, A>._)) + .Returns(new DeleteConnectorData(false, null, null, default, Enumerable.Empty(), UserStatusId.ACTIVE, Guid.NewGuid(), _fixture.Create())); // Act - async Task Act() => await _logic.DeleteConnectorAsync(connectorId); + async Task Act() => await _logic.DeleteConnectorAsync(connectorId, true); // Assert var ex = await Assert.ThrowsAsync(Act); @@ -841,11 +834,11 @@ public async Task DeleteConnectorAsync_WithPendingAndWithoutDocumentId_ThrowsFor new ConnectorOfferSubscription(offerSubscriptionId2, OfferSubscriptionStatusId.ACTIVE), new ConnectorOfferSubscription(offerSubscriptionId3, OfferSubscriptionStatusId.PENDING), }; - A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(A._, _identity.CompanyId)) - .Returns(new DeleteConnectorData(true, null, null, ConnectorStatusId.PENDING, connectorOfferSubscriptions, UserStatusId.ACTIVE, Guid.NewGuid())); + A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(A._, _identity.CompanyId, A>._)) + .Returns(new DeleteConnectorData(true, null, null, ConnectorStatusId.PENDING, connectorOfferSubscriptions, UserStatusId.ACTIVE, Guid.NewGuid(), _fixture.Create())); // Act - async Task Act() => await _logic.DeleteConnectorAsync(connectorId); + async Task Act() => await _logic.DeleteConnectorAsync(connectorId, true); // Assert var ex = await Assert.ThrowsAsync(Act); @@ -867,11 +860,11 @@ public async Task DeleteConnectorAsync_WithPendingAndDocumentId_ThrowsForbiddenE new ConnectorOfferSubscription(offerSubscriptionId2, OfferSubscriptionStatusId.ACTIVE), new ConnectorOfferSubscription(offerSubscriptionId3, OfferSubscriptionStatusId.PENDING), }; - A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(A._, _identity.CompanyId)) - .Returns(new DeleteConnectorData(true, selfDescriptionDocumentId, DocumentStatusId, ConnectorStatusId.PENDING, connectorOfferSubscriptions, UserStatusId.ACTIVE, Guid.NewGuid())); + A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(A._, _identity.CompanyId, A>._)) + .Returns(new DeleteConnectorData(true, selfDescriptionDocumentId, DocumentStatusId, ConnectorStatusId.PENDING, connectorOfferSubscriptions, UserStatusId.ACTIVE, Guid.NewGuid(), _fixture.Create())); // Act - async Task Act() => await _logic.DeleteConnectorAsync(connectorId); + async Task Act() => await _logic.DeleteConnectorAsync(connectorId, true); // Assert var ex = await Assert.ThrowsAsync(Act); diff --git a/tests/administration/Administration.Service.Tests/BusinessLogic/ServiceAccountBusinessLogicTests.cs b/tests/administration/Administration.Service.Tests/BusinessLogic/ServiceAccountBusinessLogicTests.cs index e1f768ef04..653bb9e70d 100644 --- a/tests/administration/Administration.Service.Tests/BusinessLogic/ServiceAccountBusinessLogicTests.cs +++ b/tests/administration/Administration.Service.Tests/BusinessLogic/ServiceAccountBusinessLogicTests.cs @@ -63,26 +63,29 @@ public class ServiceAccountBusinessLogicTests private readonly IServiceAccountRepository _serviceAccountRepository; private readonly IConnectorsRepository _connectorsRepository; private readonly IProvisioningManager _provisioningManager; + private readonly IServiceAccountManagement _serviceAccountManagement; private readonly IPortalRepositories _portalRepositories; private readonly IFixture _fixture; private readonly IOptions _options; private readonly IIdentityService _identityService; - private readonly byte[] _encryptionKey; public ServiceAccountBusinessLogicTests() { _fixture = new Fixture().Customize(new AutoFakeItEasyCustomization { ConfigureMembers = true }); _fixture.ConfigureFixture(); + _provisioningManager = A.Fake(); + _serviceAccountCreation = A.Fake(); + _serviceAccountManagement = A.Fake(); + _companyRepository = A.Fake(); _userRepository = A.Fake(); _userRolesRepository = A.Fake(); _serviceAccountRepository = A.Fake(); _connectorsRepository = A.Fake(); _processStepRepository = A.Fake(); - _provisioningManager = A.Fake(); _portalRepositories = A.Fake(); - _serviceAccountCreation = A.Fake(); + A.CallTo(() => _portalRepositories.GetInstance()).Returns(_processStepRepository); _identity = A.Fake(); _identityService = A.Fake(); @@ -91,14 +94,13 @@ public ServiceAccountBusinessLogicTests() A.CallTo(() => _identity.CompanyId).Returns(ValidCompanyId); A.CallTo(() => _identityService.IdentityData).Returns(_identity); - _encryptionKey = _fixture.CreateMany(32).ToArray(); - A.CallTo(() => _portalRepositories.GetInstance()).Returns(_processStepRepository); + var encryptionKey = _fixture.CreateMany(32).ToArray(); _options = Options.Create(new ServiceAccountSettings { ClientId = ClientId, EncryptionConfigIndex = 1, - EncryptionConfigs = new[] { new EncryptionModeConfig() { Index = 1, EncryptionKey = Convert.ToHexString(_encryptionKey), CipherMode = System.Security.Cryptography.CipherMode.CBC, PaddingMode = System.Security.Cryptography.PaddingMode.PKCS7 } }, + EncryptionConfigs = new[] { new EncryptionModeConfig() { Index = 1, EncryptionKey = Convert.ToHexString(encryptionKey), CipherMode = System.Security.Cryptography.CipherMode.CBC, PaddingMode = System.Security.Cryptography.PaddingMode.PKCS7 } }, }); } @@ -110,7 +112,7 @@ public async Task CreateOwnCompanyServiceAccountAsync_WithValidInput_ReturnsCrea // Arrange SetupCreateOwnCompanyServiceAccount(); var serviceAccountCreationInfos = new ServiceAccountCreationInfo("TheName", "Just a short description", IamClientAuthMethod.SECRET, Enumerable.Repeat(UserRoleId1, 1)); - var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, _serviceAccountCreation, _identityService); + var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, _serviceAccountCreation, _identityService, _serviceAccountManagement); // Act var result = await sut.CreateOwnCompanyServiceAccountAsync(serviceAccountCreationInfos); @@ -128,7 +130,7 @@ public async Task CreateOwnCompanyServiceAccountAsync_WithInvalidUser_NotFoundEx A.CallTo(() => _identityService.IdentityData).Returns(identity); SetupCreateOwnCompanyServiceAccount(); var serviceAccountCreationInfos = new ServiceAccountCreationInfo("TheName", "Just a short description", IamClientAuthMethod.SECRET, Enumerable.Repeat(UserRoleId1, 1)); - var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, _serviceAccountCreation, _identityService); + var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, _serviceAccountCreation, _identityService, _serviceAccountManagement); // Act async Task Act() => await sut.CreateOwnCompanyServiceAccountAsync(serviceAccountCreationInfos); @@ -144,7 +146,7 @@ public async Task CreateOwnCompanyServiceAccountAsync_WithEmptyName_ThrowsContro // Arrange SetupCreateOwnCompanyServiceAccount(); var serviceAccountCreationInfos = new ServiceAccountCreationInfo(string.Empty, "Just a short description", IamClientAuthMethod.SECRET, Enumerable.Repeat(UserRoleId1, 1)); - var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, _serviceAccountCreation, _identityService); + var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, _serviceAccountCreation, _identityService, _serviceAccountManagement); // Act async Task Act() => await sut.CreateOwnCompanyServiceAccountAsync(serviceAccountCreationInfos); @@ -163,7 +165,7 @@ public async Task CreateOwnCompanyServiceAccountAsync_WithInvalidIamClientAuthMe // Arrange SetupCreateOwnCompanyServiceAccount(); var serviceAccountCreationInfos = new ServiceAccountCreationInfo("TheName", "Just a short description", IamClientAuthMethod.JWT, Enumerable.Repeat(UserRoleId1, 1)); - var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, _serviceAccountCreation, _identityService); + var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, _serviceAccountCreation, _identityService, _serviceAccountManagement); // Act async Task Act() => await sut.CreateOwnCompanyServiceAccountAsync(serviceAccountCreationInfos); @@ -183,7 +185,7 @@ public async Task CreateOwnCompanyServiceAccountAsync_WithInvalidUserRoleId_Thro var wrongUserRoleId = Guid.NewGuid(); SetupCreateOwnCompanyServiceAccount(); var serviceAccountCreationInfos = new ServiceAccountCreationInfo("TheName", "Just a short description", IamClientAuthMethod.SECRET, [UserRoleId1, wrongUserRoleId]); - var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, _serviceAccountCreation, _identityService); + var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, _serviceAccountCreation, _identityService, _serviceAccountManagement); // Act async Task Act() => await sut.CreateOwnCompanyServiceAccountAsync(serviceAccountCreationInfos); @@ -206,7 +208,7 @@ public async Task GetOwnCompanyServiceAccountDetailsAsync_WithValidInput_GetsAll { // Arrange SetupGetOwnCompanyServiceAccountDetails(); - var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService); + var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); // Act var result = await sut.GetOwnCompanyServiceAccountDetailsAsync(ValidServiceAccountId); @@ -221,7 +223,7 @@ public async Task GetOwnCompanyServiceAccountDetailsAsync_WithValidInputAndDimCo { // Arrange SetupGetOwnCompanyServiceAccountDetails(); - var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService); + var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); // Act var result = await sut.GetOwnCompanyServiceAccountDetailsAsync(ValidServiceAccountWithDimDataId); @@ -241,7 +243,7 @@ public async Task GetOwnCompanyServiceAccountDetailsAsync_WithInvalidCompany_Not SetupGetOwnCompanyServiceAccountDetails(); var invalidCompanyId = Guid.NewGuid(); A.CallTo(() => _identity.CompanyId).Returns(invalidCompanyId); - var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService); + var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); // Act async Task Act() => await sut.GetOwnCompanyServiceAccountDetailsAsync(ValidServiceAccountId); @@ -257,7 +259,7 @@ public async Task GetOwnCompanyServiceAccountDetailsAsync_WithInvalidServiceAcco // Arrange SetupGetOwnCompanyServiceAccountDetails(); var invalidServiceAccountId = Guid.NewGuid(); - var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService); + var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); // Act async Task Act() => await sut.GetOwnCompanyServiceAccountDetailsAsync(invalidServiceAccountId); @@ -276,7 +278,7 @@ public async Task ResetOwnCompanyServiceAccountSecretAsync_WithValidInput_GetsAl { // Arrange SetupResetOwnCompanyServiceAccountSecret(); - var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService); + var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); // Act var result = await sut.ResetOwnCompanyServiceAccountSecretAsync(ValidServiceAccountId); @@ -293,7 +295,7 @@ public async Task ResetOwnCompanyServiceAccountSecretAsync_WithInvalidUser_NotFo SetupResetOwnCompanyServiceAccountSecret(); var invalidUser = _fixture.Create(); A.CallTo(() => _identityService.IdentityData).Returns(invalidUser); - var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService); + var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); // Act async Task Act() => await sut.ResetOwnCompanyServiceAccountSecretAsync(ValidServiceAccountId); @@ -309,7 +311,7 @@ public async Task ResetOwnCompanyServiceAccountSecretAsync_WithInvalidServiceAcc // Arrange SetupResetOwnCompanyServiceAccountSecret(); var invalidServiceAccountId = Guid.NewGuid(); - var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService); + var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); // Act async Task Act() => await sut.ResetOwnCompanyServiceAccountSecretAsync(invalidServiceAccountId); @@ -329,7 +331,7 @@ public async Task UpdateOwnCompanyServiceAccountDetailsAsync_WithValidData_Retur // Arrange SetupUpdateOwnCompanyServiceAccountDetails(); var data = new ServiceAccountEditableDetails(ValidServiceAccountId, "new name", "changed description", IamClientAuthMethod.SECRET); - var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService); + var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); CompanyServiceAccount? initial = null; CompanyServiceAccount? modified = null; @@ -364,7 +366,7 @@ public async Task UpdateOwnCompanyServiceAccountDetailsAsync_WithInvalidAuthMeth // Arrange SetupUpdateOwnCompanyServiceAccountDetails(); var data = new ServiceAccountEditableDetails(ValidServiceAccountId, "new name", "changed description", IamClientAuthMethod.JWT); - var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService); + var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); // Act async Task Act() => await sut.UpdateOwnCompanyServiceAccountDetailsAsync(ValidServiceAccountId, data); @@ -382,7 +384,7 @@ public async Task UpdateOwnCompanyServiceAccountDetailsAsync_WithDifferentServic // Arrange SetupUpdateOwnCompanyServiceAccountDetails(); var data = new ServiceAccountEditableDetails(ValidServiceAccountId, "new name", "changed description", IamClientAuthMethod.SECRET); - var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService); + var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); // Act async Task Act() => await sut.UpdateOwnCompanyServiceAccountDetailsAsync(Guid.NewGuid(), data); @@ -404,7 +406,7 @@ public async Task UpdateOwnCompanyServiceAccountDetailsAsync_WithNotExistingServ A.CallTo(() => _serviceAccountRepository.GetOwnCompanyServiceAccountWithIamClientIdAsync(invalidServiceAccountId, ValidCompanyId)) .Returns(null); var data = new ServiceAccountEditableDetails(invalidServiceAccountId, "new name", "changed description", IamClientAuthMethod.SECRET); - var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService); + var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); // Act async Task Act() => await sut.UpdateOwnCompanyServiceAccountDetailsAsync(invalidServiceAccountId, data); @@ -425,7 +427,7 @@ public async Task UpdateOwnCompanyServiceAccountDetailsAsync_WithInactiveService A.CallTo(() => _serviceAccountRepository.GetOwnCompanyServiceAccountWithIamClientIdAsync(InactiveServiceAccount, ValidCompanyId)) .Returns(inactive); var data = new ServiceAccountEditableDetails(InactiveServiceAccount, "new name", "changed description", IamClientAuthMethod.SECRET); - var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService); + var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); // Act async Task Act() => await sut.UpdateOwnCompanyServiceAccountDetailsAsync(InactiveServiceAccount, data); @@ -449,7 +451,7 @@ public async Task UpdateOwnCompanyServiceAccountDetailsAsync_WithExternalService A.CallTo(() => _serviceAccountRepository.GetOwnCompanyServiceAccountWithIamClientIdAsync(ExternalServiceAccount, ValidCompanyId)) .Returns(external); var data = new ServiceAccountEditableDetails(ExternalServiceAccount, "new name", "changed description", IamClientAuthMethod.SECRET); - var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService); + var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); // Act var result = await sut.UpdateOwnCompanyServiceAccountDetailsAsync(ExternalServiceAccount, data); @@ -484,7 +486,7 @@ public async Task GetOwnCompanyServiceAccountsDataAsync_GetsExpectedData(IEnumer .Returns((int skip, int take) => Task.FromResult?>(new(data.Count(), data.Skip(skip).Take(take)))); A.CallTo(() => _portalRepositories.GetInstance()).Returns(_serviceAccountRepository); - var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService); + var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); // Act var result = await sut.GetOwnCompanyServiceAccountsDataAsync(1, 10, null, null, isUserInactive, userStatusIds); @@ -505,9 +507,9 @@ public async Task DeleteOwnCompanyServiceAccountAsync_WithNotExistingServiceAcco { // Arrange var serviceAccountId = Guid.NewGuid(); - SetupDeleteOwnCompanyServiceAccount(false, false, false); + SetupDeleteOwnCompanyServiceAccount(); - var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService); + var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); // Act async Task Act() => await sut.DeleteOwnCompanyServiceAccountAsync(serviceAccountId); @@ -523,7 +525,7 @@ public async Task DeleteOwnCompanyServiceAccountAsync_WithExistingOfferSubscript // Arrange SetupDeleteOwnCompanyServiceAccountForValidOfferSubscription(); - var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService); + var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); // Act async Task Act() => await sut.DeleteOwnCompanyServiceAccountAsync(ValidServiceAccountId); @@ -539,7 +541,7 @@ public async Task DeleteOwnCompanyServiceAccountAsync_WithInvalidConnectorStatus // Arrange SetupDeleteOwnCompanyServiceAccountForInvalidConnectorStatus(); - var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService); + var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); // Act async Task Act() => await sut.DeleteOwnCompanyServiceAccountAsync(ValidServiceAccountId); @@ -549,58 +551,8 @@ public async Task DeleteOwnCompanyServiceAccountAsync_WithInvalidConnectorStatus ex.Message.Should().Be(AdministrationServiceAccountErrors.SERVICE_USERID_ACTIVATION_PENDING_CONFLICT.ToString()); } - [Theory] - [InlineData(true, false, false)] - [InlineData(false, false, true)] - [InlineData(false, true, true)] - [InlineData(true, false, true)] - [InlineData(false, false, false)] - [InlineData(false, true, false)] - public async Task DeleteOwnCompanyServiceAccountAsync_WithoutClient_CallsExpected(bool withClient, bool isDimServiceAccount, bool withServiceAccount) - { - // Arrange - var identity = _fixture.Build() - .With(x => x.Id, ValidServiceAccountId) - .With(x => x.UserStatusId, UserStatusId.ACTIVE) - .Create(); - var connector = _fixture.Build() - .With(x => x.CompanyServiceAccountId, ValidServiceAccountId) - .Create(); - var processId = Guid.NewGuid(); - SetupDeleteOwnCompanyServiceAccount(withServiceAccount, isDimServiceAccount, withClient, connector, identity, processId); - var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService); - - // Act - await sut.DeleteOwnCompanyServiceAccountAsync(ValidServiceAccountId); - - // Assert - if (isDimServiceAccount) - { - A.CallTo(() => _processStepRepository.CreateProcessStepRange(A>.That.Matches(x => x.Count() == 1 && x.First().ProcessStepTypeId == ProcessStepTypeId.DELETE_DIM_TECHNICAL_USER && x.First().ProcessStepStatusId == ProcessStepStatusId.TODO && x.First().ProcessId == processId))).MustHaveHappenedOnceExactly(); - A.CallTo(() => _userRepository.AttachAndModifyIdentity(A._, A>._, A>._)).MustHaveHappenedOnceExactly(); - A.CallTo(() => _provisioningManager.DeleteCentralClientAsync(A._)).MustNotHaveHappened(); - identity.UserStatusId.Should().Be(UserStatusId.PENDING_DELETION); - } - else if (withClient) - { - A.CallTo(() => _userRepository.AttachAndModifyIdentity(A._, A>._, A>._)).MustHaveHappenedOnceExactly(); - A.CallTo(() => _processStepRepository.CreateProcessStepRange(A>._)).MustNotHaveHappened(); - A.CallTo(() => _provisioningManager.DeleteCentralClientAsync(ClientId)).MustHaveHappenedOnceExactly(); - identity.UserStatusId.Should().Be(UserStatusId.DELETED); - } - - if (withServiceAccount) - { - A.CallTo(() => _connectorsRepository.AttachAndModifyConnector(ValidConnectorId, A>._, A>._)).MustHaveHappenedOnceExactly(); - connector.CompanyServiceAccountId.Should().BeNull(); - } - - var validServiceAccountUserRoleIds = _userRoleIds.Select(userRoleId => (ValidServiceAccountId, userRoleId)); - A.CallTo(() => _userRolesRepository.DeleteCompanyUserAssignedRoles(A>.That.IsSameSequenceAs(validServiceAccountUserRoleIds))).MustHaveHappenedOnceExactly(); - } - [Fact] - public async Task DeleteOwnCompanyServiceAccountAsync_WithCreationProcessInProgress_ThrowsException() + public async Task DeleteOwnCompanyServiceAccountAsync_WithoutClient_CallsExpected() { // Arrange var identity = _fixture.Build() @@ -611,15 +563,34 @@ public async Task DeleteOwnCompanyServiceAccountAsync_WithCreationProcessInProgr .With(x => x.CompanyServiceAccountId, ValidServiceAccountId) .Create(); var processId = Guid.NewGuid(); - SetupDeleteOwnCompanyServiceAccount(true, true, true, connector, identity, processId, true); - var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService); - Task Act() => sut.DeleteOwnCompanyServiceAccountAsync(ValidServiceAccountId); + SetupDeleteOwnCompanyServiceAccount(connector, identity, processId); + var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); // Act - var ex = await Assert.ThrowsAsync(Act); + await sut.DeleteOwnCompanyServiceAccountAsync(ValidServiceAccountId); // Assert - ex.Message.Should().Be("TECHNICAL_USER_CREATION_IN_PROGRESS"); + // if (isDimServiceAccount) + // { + // // A.CallTo(() => _processStepRepository.CreateProcessStepRange(A>.That.Matches(x => x.Count() == 1 && x.First().ProcessStepTypeId == ProcessStepTypeId.DELETE_DIM_TECHNICAL_USER && x.First().ProcessStepStatusId == ProcessStepStatusId.TODO && x.First().ProcessId == processId))).MustHaveHappenedOnceExactly(); + // // A.CallTo(() => _userRepository.AttachAndModifyIdentity(A._, A>._, A>._)).MustHaveHappenedOnceExactly(); + // // A.CallTo(() => _provisioningManager.DeleteCentralClientAsync(A._)).MustNotHaveHappened(); + // // identity.UserStatusId.Should().Be(UserStatusId.PENDING_DELETION); + // } + // else if (withClient) + // { + // A.CallTo(() => _userRepository.AttachAndModifyIdentity(A._, A>._, A>._)).MustHaveHappenedOnceExactly(); + // A.CallTo(() => _processStepRepository.CreateProcessStepRange(A>._)).MustNotHaveHappened(); + // A.CallTo(() => _provisioningManager.DeleteCentralClientAsync(ClientId)).MustHaveHappenedOnceExactly(); + // identity.UserStatusId.Should().Be(UserStatusId.DELETED); + // } + + A.CallTo(() => _connectorsRepository.AttachAndModifyConnector(ValidConnectorId, A>._, A>._)).MustHaveHappenedOnceExactly(); + connector.CompanyServiceAccountId.Should().BeNull(); + A.CallTo(() => _serviceAccountManagement.DeleteServiceAccount(ValidServiceAccountId, A._)) + .MustHaveHappenedOnceExactly(); + // var validServiceAccountUserRoleIds = _userRoleIds.Select(userRoleId => (ValidServiceAccountId, userRoleId)); + // A.CallTo(() => _userRolesRepository.DeleteCompanyUserAssignedRoles(A>.That.IsSameSequenceAs(validServiceAccountUserRoleIds))).MustHaveHappenedOnceExactly(); } #endregion @@ -637,7 +608,7 @@ public async Task GetServiceAccountRolesAsync_GetsExpectedData() A.CallTo(() => _portalRepositories.GetInstance()).Returns(_userRolesRepository); - var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService); + var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); // Act var result = await sut.GetServiceAccountRolesAsync(null).ToListAsync(); @@ -668,7 +639,7 @@ public async Task HandleServiceAccountCreationCallback_WithValidOfferSubscriptio A.CallTo(() => _processStepRepository.GetProcessDataForServiceAccountCallback(A._, A>._)) .Returns((ProcessTypeId.OFFER_SUBSCRIPTION, context, Guid.NewGuid(), Guid.NewGuid())); - var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, _serviceAccountCreation, _identityService); + var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, _serviceAccountCreation, _identityService, _serviceAccountManagement); // Act await sut.HandleServiceAccountCreationCallback(process.Id, _fixture.Create()); @@ -690,7 +661,7 @@ public async Task HandleServiceAccountCreationCallback_WithNotExistingProcess_Th A.CallTo(() => _processStepRepository.GetProcessDataForServiceAccountCallback(A._, A>._)) .Returns<(ProcessTypeId, VerifyProcessData, Guid?, Guid?)>(default); - var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, _serviceAccountCreation, _identityService); + var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, _serviceAccountCreation, _identityService, _serviceAccountManagement); async Task Act() => await sut.HandleServiceAccountCreationCallback(process.Id, _fixture.Create()); @@ -713,7 +684,7 @@ public async Task HandleServiceAccountCreationCallback_WithOfferSubscriptionIdNo A.CallTo(() => _processStepRepository.GetProcessDataForServiceAccountCallback(A._, A>._)) .Returns((ProcessTypeId.OFFER_SUBSCRIPTION, context, null, null)); - var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, _serviceAccountCreation, _identityService); + var sut = new ServiceAccountBusinessLogic(_provisioningManager, _portalRepositories, _options, _serviceAccountCreation, _identityService, _serviceAccountManagement); async Task Act() => await sut.HandleServiceAccountCreationCallback(process.Id, _fixture.Create()); @@ -819,26 +790,14 @@ private void SetupGetOwnCompanyServiceAccount() .Returns(null); } - private void SetupDeleteOwnCompanyServiceAccount(bool withServiceAccount, bool isDimServiceAccount, bool withClient, Connector? connector = null, Identity? identity = null, Guid? processId = null, bool withCreateStepsInTodo = false) + private void SetupDeleteOwnCompanyServiceAccount(Connector? connector = null, Identity? identity = null, Guid? processId = null) { var serviceAccount = new CompanyServiceAccount(Guid.NewGuid(), Guid.NewGuid(), "test-sa", "test", CompanyServiceAccountTypeId.OWN, CompanyServiceAccountKindId.INTERNAL); A.CallTo(() => _serviceAccountRepository.GetOwnCompanyServiceAccountWithIamServiceAccountRolesAsync(ValidServiceAccountId, ValidCompanyId, A>._)) - .Returns(new OwnServiceAccountData(_userRoleIds, serviceAccount.Id, serviceAccount.Version, withServiceAccount ? ValidConnectorId : null, withClient ? ClientId : null, ConnectorStatusId.INACTIVE, OfferSubscriptionStatusId.PENDING, isDimServiceAccount, withCreateStepsInTodo, processId)); + .Returns(new OwnServiceAccountData(_userRoleIds, serviceAccount.Id, serviceAccount.Version, ValidConnectorId, ClientId, ConnectorStatusId.INACTIVE, OfferSubscriptionStatusId.PENDING, false, false, processId)); A.CallTo(() => _serviceAccountRepository.GetOwnCompanyServiceAccountWithIamServiceAccountRolesAsync(A.That.Not.Matches(x => x == ValidServiceAccountId), A._, A>._)) .Returns(null); - if (isDimServiceAccount) - { - A.CallTo(() => _processStepRepository.GetProcessDataForServiceAccountDeletionCallback(A._, A>._)) - .ReturnsLazily((Guid id, IEnumerable? processStepTypeIds) => - ( - ProcessTypeId.OFFER_SUBSCRIPTION, - new VerifyProcessData( - new Process(id, ProcessTypeId.OFFER_SUBSCRIPTION, Guid.NewGuid()), - processStepTypeIds?.Select(stepTypeId => new ProcessStep(Guid.NewGuid(), stepTypeId, ProcessStepStatusId.TODO, id, _fixture.Create())) ?? Enumerable.Empty()), - Guid.NewGuid())); - } - if (identity != null) { A.CallTo(() => _userRepository.AttachAndModifyIdentity(ValidServiceAccountId, A>._, A>._)) diff --git a/tests/administration/Administration.Service.Tests/BusinessLogic/ServiceAccountManagementTests.cs b/tests/administration/Administration.Service.Tests/BusinessLogic/ServiceAccountManagementTests.cs new file mode 100644 index 0000000000..34853308b1 --- /dev/null +++ b/tests/administration/Administration.Service.Tests/BusinessLogic/ServiceAccountManagementTests.cs @@ -0,0 +1,136 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +using Org.Eclipse.TractusX.Portal.Backend.Administration.Service.BusinessLogic; +using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; +using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess; +using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Models; +using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Repositories; +using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Entities; +using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Enums; +using Org.Eclipse.TractusX.Portal.Backend.Provisioning.Library; +using Org.Eclipse.TractusX.Portal.Backend.Tests.Shared.Extensions; + +namespace Org.Eclipse.TractusX.Portal.Backend.Administration.Service.Tests.BusinessLogic; + +public class ServiceAccountManagementTests +{ + private const string ClientId = "Cl1-CX-Registration"; + private static readonly Guid ValidServiceAccountId = Guid.NewGuid(); + private readonly IEnumerable _userRoleIds = Enumerable.Repeat(Guid.NewGuid(), 1); + private readonly IUserRepository _userRepository; + private readonly IUserRolesRepository _userRolesRepository; + private readonly IProcessStepRepository _processStepRepository; + private readonly IProvisioningManager _provisioningManager; + private readonly IFixture _fixture; + private readonly ServiceAccountManagement _sut; + + public ServiceAccountManagementTests() + { + _fixture = new Fixture().Customize(new AutoFakeItEasyCustomization { ConfigureMembers = true }); + _fixture.ConfigureFixture(); + + _provisioningManager = A.Fake(); + + _userRepository = A.Fake(); + _userRolesRepository = A.Fake(); + _processStepRepository = A.Fake(); + var portalRepositories = A.Fake(); + A.CallTo(() => portalRepositories.GetInstance()).Returns(_userRepository); + A.CallTo(() => portalRepositories.GetInstance()).Returns(_userRolesRepository); + A.CallTo(() => portalRepositories.GetInstance()).Returns(_processStepRepository); + + _sut = new ServiceAccountManagement(_provisioningManager, portalRepositories); + } + + [Theory] + [InlineData(true, true)] + [InlineData(false, true)] + [InlineData(true, false)] + [InlineData(false, false)] + public async Task DeleteOwnCompanyServiceAccountAsync_WithoutClient_CallsExpected(bool withClient, bool isDimServiceAccount) + { + // Arrange + var identity = _fixture.Build() + .With(x => x.Id, ValidServiceAccountId) + .With(x => x.UserStatusId, UserStatusId.ACTIVE) + .Create(); + var processId = Guid.NewGuid(); + SetupDeleteOwnCompanyServiceAccount(isDimServiceAccount, identity); + + // Act + await _sut.DeleteServiceAccount(ValidServiceAccountId, new DeleteServiceAccountData(_userRoleIds, withClient ? ClientId : null, isDimServiceAccount, false, processId)); + + // Assert + if (isDimServiceAccount) + { + A.CallTo(() => _processStepRepository.CreateProcessStepRange(A>.That.Matches(x => x.Count() == 1 && x.First().ProcessStepTypeId == ProcessStepTypeId.DELETE_DIM_TECHNICAL_USER && x.First().ProcessStepStatusId == ProcessStepStatusId.TODO && x.First().ProcessId == processId))).MustHaveHappenedOnceExactly(); + A.CallTo(() => _userRepository.AttachAndModifyIdentity(A._, A>._, A>._)).MustHaveHappenedOnceExactly(); + A.CallTo(() => _provisioningManager.DeleteCentralClientAsync(A._)).MustNotHaveHappened(); + identity.UserStatusId.Should().Be(UserStatusId.PENDING_DELETION); + } + else if (withClient) + { + A.CallTo(() => _userRepository.AttachAndModifyIdentity(A._, A>._, A>._)).MustHaveHappenedOnceExactly(); + A.CallTo(() => _processStepRepository.CreateProcessStepRange(A>._)).MustNotHaveHappened(); + A.CallTo(() => _provisioningManager.DeleteCentralClientAsync(ClientId)).MustHaveHappenedOnceExactly(); + identity.UserStatusId.Should().Be(UserStatusId.DELETED); + } + + var validServiceAccountUserRoleIds = _userRoleIds.Select(userRoleId => (ValidServiceAccountId, userRoleId)); + A.CallTo(() => _userRolesRepository.DeleteCompanyUserAssignedRoles(A>.That.IsSameSequenceAs(validServiceAccountUserRoleIds))).MustHaveHappenedOnceExactly(); + } + + [Fact] + public async Task DeleteServiceAccount_WithCreationProcessInProgress_ThrowsException() + { + // Arrange + Task Act() => _sut.DeleteServiceAccount(ValidServiceAccountId, new DeleteServiceAccountData(_userRoleIds, ClientId, true, true, Guid.NewGuid())); + + // Act + var ex = await Assert.ThrowsAsync(Act); + + // Assert + ex.Message.Should().Be("TECHNICAL_USER_CREATION_IN_PROGRESS"); + } + + private void SetupDeleteOwnCompanyServiceAccount(bool isDimServiceAccount, Identity? identity = null) + { + if (isDimServiceAccount) + { + A.CallTo(() => _processStepRepository.GetProcessDataForServiceAccountDeletionCallback(A._, A>._)) + .ReturnsLazily((Guid id, IEnumerable? processStepTypeIds) => + ( + ProcessTypeId.OFFER_SUBSCRIPTION, + new VerifyProcessData( + new Process(id, ProcessTypeId.OFFER_SUBSCRIPTION, Guid.NewGuid()), + processStepTypeIds?.Select(stepTypeId => new ProcessStep(Guid.NewGuid(), stepTypeId, ProcessStepStatusId.TODO, id, _fixture.Create())) ?? Enumerable.Empty()), + Guid.NewGuid())); + } + + if (identity != null) + { + A.CallTo(() => _userRepository.AttachAndModifyIdentity(ValidServiceAccountId, A>._, A>._)) + .Invokes((Guid _, Action? _, Action modify) => + { + modify.Invoke(identity); + }); + } + } +} diff --git a/tests/administration/Administration.Service.Tests/Controllers/ConnectorsControllerTests.cs b/tests/administration/Administration.Service.Tests/Controllers/ConnectorsControllerTests.cs index 5516972c3d..ea7e0cd7e7 100644 --- a/tests/administration/Administration.Service.Tests/Controllers/ConnectorsControllerTests.cs +++ b/tests/administration/Administration.Service.Tests/Controllers/ConnectorsControllerTests.cs @@ -154,7 +154,7 @@ public async Task DeleteConnector_WithValidData_ReturnsExpectedResult() await _controller.DeleteConnectorAsync(connectorId); //Assert - A.CallTo(() => _logic.DeleteConnectorAsync(connectorId)).MustHaveHappenedOnceExactly(); + A.CallTo(() => _logic.DeleteConnectorAsync(connectorId, false)).MustHaveHappenedOnceExactly(); } [Fact] diff --git a/tests/administration/Administration.Service.Tests/EndpointSetup/ConnectorsEndpoints.cs b/tests/administration/Administration.Service.Tests/EndpointSetup/ConnectorsEndpoints.cs index c86b686cf1..cd9da6e076 100644 --- a/tests/administration/Administration.Service.Tests/EndpointSetup/ConnectorsEndpoints.cs +++ b/tests/administration/Administration.Service.Tests/EndpointSetup/ConnectorsEndpoints.cs @@ -19,7 +19,7 @@ using Org.Eclipse.TractusX.Portal.Backend.Tests.Shared.IntegrationTests.EndpointSetup; -namespace Org.Eclipse.TractusX.Portal.Backend.Administration.Service.Tests.EnpointSetup; +namespace Org.Eclipse.TractusX.Portal.Backend.Administration.Service.Tests.EndpointSetup; public class ConnectorsEndpoints { diff --git a/tests/administration/Administration.Service.Tests/IntegrationTests/ConnectorsControllerIntegrationTests.cs b/tests/administration/Administration.Service.Tests/IntegrationTests/ConnectorsControllerIntegrationTests.cs index a4fb38a4a5..0dd783fe7b 100644 --- a/tests/administration/Administration.Service.Tests/IntegrationTests/ConnectorsControllerIntegrationTests.cs +++ b/tests/administration/Administration.Service.Tests/IntegrationTests/ConnectorsControllerIntegrationTests.cs @@ -18,7 +18,7 @@ ********************************************************************************/ using Org.Eclipse.TractusX.Portal.Backend.Administration.Service.Controllers; -using Org.Eclipse.TractusX.Portal.Backend.Administration.Service.Tests.EnpointSetup; +using Org.Eclipse.TractusX.Portal.Backend.Administration.Service.Tests.EndpointSetup; using Org.Eclipse.TractusX.Portal.Backend.Administration.Service.Tests.IntegrationTests.Seeding; using Org.Eclipse.TractusX.Portal.Backend.Framework.Models; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Models; diff --git a/tests/administration/Administration.Service.Tests/appsettings.IntegrationTests.json b/tests/administration/Administration.Service.Tests/appsettings.IntegrationTests.json index 632ec19dfa..c1f690545a 100644 --- a/tests/administration/Administration.Service.Tests/appsettings.IntegrationTests.json +++ b/tests/administration/Administration.Service.Tests/appsettings.IntegrationTests.json @@ -44,7 +44,7 @@ } }, "Provisioning": { - "CentralRealm": "", + "CentralRealm": "test", "CentralRealmId": "", "IdpPrefix": "idp", "MappedBpnAttribute": "bpn", diff --git a/tests/portalbackend/PortalBackend.DBAccess.Tests/ConnectorRepositoryTests.cs b/tests/portalbackend/PortalBackend.DBAccess.Tests/ConnectorRepositoryTests.cs index 4212f85bd8..ad9b630289 100644 --- a/tests/portalbackend/PortalBackend.DBAccess.Tests/ConnectorRepositoryTests.cs +++ b/tests/portalbackend/PortalBackend.DBAccess.Tests/ConnectorRepositoryTests.cs @@ -36,6 +36,12 @@ public class ConnectorRepositoryTests : IAssemblyFixture { private readonly TestDbFixture _dbTestDbFixture; private readonly Guid _userCompanyId = new("2dc4249f-b5ca-4d42-bef1-7a7a950a4f87"); + private readonly IEnumerable _processStepsToFilter = new[] + { + ProcessStepTypeId.CREATE_DIM_TECHNICAL_USER, ProcessStepTypeId.RETRIGGER_CREATE_DIM_TECHNICAL_USER, + ProcessStepTypeId.AWAIT_CREATE_DIM_TECHNICAL_USER_RESPONSE, + ProcessStepTypeId.RETRIGGER_AWAIT_CREATE_DIM_TECHNICAL_USER_RESPONSE + }; public ConnectorRepositoryTests(TestDbFixture testDbFixture) { @@ -286,7 +292,7 @@ public async Task GetSelfDescriptionDocumentDataAsync_WithoutDocumentId_ReturnsE var (sut, _) = await CreateSut(); // Act - var result = await sut.GetConnectorDeleteDataAsync(new Guid("7e86a0b8-6903-496b-96d1-0ef508206833"), new Guid("2dc4249f-b5ca-4d42-bef1-7a7a950a4f87")); + var result = await sut.GetConnectorDeleteDataAsync(new Guid("7e86a0b8-6903-496b-96d1-0ef508206833"), new Guid("2dc4249f-b5ca-4d42-bef1-7a7a950a4f87"), _processStepsToFilter); // Assert result.Should().NotBeNull(); @@ -301,7 +307,7 @@ public async Task GetSelfDescriptionDocumentDataAsync_WithDocumentId_ReturnsExpe var (sut, _) = await CreateSut(); // Act - var result = await sut.GetConnectorDeleteDataAsync(new Guid("7e86a0b8-6903-496b-96d1-0ef508206839"), new Guid("2dc4249f-b5ca-4d42-bef1-7a7a950a4f87")); + var result = await sut.GetConnectorDeleteDataAsync(new Guid("7e86a0b8-6903-496b-96d1-0ef508206839"), new Guid("2dc4249f-b5ca-4d42-bef1-7a7a950a4f87"), _processStepsToFilter); // Assert result.Should().NotBeNull(); @@ -317,7 +323,7 @@ public async Task GetSelfDescriptionDocumentDataAsync_WithoutExistingCompanyId_R var (sut, _) = await CreateSut(); // Act - var result = await sut.GetConnectorDeleteDataAsync(new Guid("7e86a0b8-6903-496b-96d1-0ef508206839"), Guid.NewGuid()); + var result = await sut.GetConnectorDeleteDataAsync(new Guid("7e86a0b8-6903-496b-96d1-0ef508206839"), Guid.NewGuid(), _processStepsToFilter); // Assert result.Should().NotBeNull(); @@ -331,7 +337,7 @@ public async Task GetSelfDescriptionDocumentDataAsync_WithoutExistingConnectorId var (sut, _) = await CreateSut(); // Act - var result = await sut.GetConnectorDeleteDataAsync(new Guid(), new Guid("2dc4249f-b5ca-4d42-bef1-7a7a950a4f87")); + var result = await sut.GetConnectorDeleteDataAsync(new Guid(), new Guid("2dc4249f-b5ca-4d42-bef1-7a7a950a4f87"), _processStepsToFilter); // Assert result.Should().BeNull(); @@ -344,7 +350,7 @@ public async Task GetSelfDescriptionDocumentDataAsync_WithConnectorOfferSubscrip var (sut, _) = await CreateSut(); // Act - var result = await sut.GetConnectorDeleteDataAsync(new Guid("4618c650-709c-4580-956a-85b76eecd4b8"), new Guid("41fd2ab8-71cd-4546-9bef-a388d91b2542")); + var result = await sut.GetConnectorDeleteDataAsync(new Guid("4618c650-709c-4580-956a-85b76eecd4b8"), new Guid("41fd2ab8-71cd-4546-9bef-a388d91b2542"), _processStepsToFilter); // Assert result.Should().NotBeNull(); From 3744239d770c864e298fd972f750b8a4f8a9e95c Mon Sep 17 00:00:00 2001 From: Phil Schneider Date: Tue, 10 Sep 2024 12:52:51 +0200 Subject: [PATCH 5/5] build: bump version for v2.2.0-RC4 (#984) * bump version for v2.2.0-RC4 * adjust changelog for v2.2.0-RC4 ----------- Reviewed-By: Evelyn Gurschler --- CHANGELOG.md | 14 +++++++++++++- src/Directory.Build.props | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d16597c8d3..c3ab7a1bd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,23 @@ New features, fixed bugs, known defects and other noteworthy changes to each release of the Catena-X Portal Backend. +## 2.2.0-RC4 + +### Bugfixes + +* **Connectors** + * add filter for COMPANY_CONNECTOR [#972](https://github.com/eclipse-tractusx/portal-backend/pull/972) + * adjust connector deletion process [#968](https://github.com/eclipse-tractusx/portal-backend/pull/968) +* **Idp** + * add search functionality to idp endpoint [#982](https://github.com/eclipse-tractusx/portal-backend/pull/982) +* **Offer Management** + * adjust status query param for subscription-status [#969](https://github.com/eclipse-tractusx/portal-backend/pull/969) + ## 2.2.0-RC3 ### Bugfixes -* **REgistration Process** +* **Registration Process** * removed DIM authentication details from logs [#951](https://github.com/eclipse-tractusx/portal-backend/pull/951) * adjust retrigger process for sd creation [#938](https://github.com/eclipse-tractusx/portal-backend/pull/938) * **Connector creation** diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 7fdb463953..d1ee1e9c14 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -20,6 +20,6 @@ 2.2.0 - RC3 + RC4