Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(connector): adjust connector deletion #968

Merged
merged 2 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public class ConnectorsBusinessLogic(
IOptions<ConnectorsSettings> options,
ISdFactoryBusinessLogic sdFactoryBusinessLogic,
IIdentityService identityService,
IServiceAccountManagement serviceAccountManagement,
ILogger<ConnectorsBusinessLogic> logger)
: IConnectorsBusinessLogic
{
Expand Down Expand Up @@ -242,22 +243,26 @@ await sdFactoryBusinessLogic
}

/// <inheritdoc/>
public async Task DeleteConnectorAsync(Guid connectorId)
public async Task DeleteConnectorAsync(Guid connectorId, bool deleteServiceAccount)
{
var companyId = _identityData.CompanyId;
var connectorsRepository = portalRepositories.GetInstance<IConnectorsRepository>();
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<IUserRepository>().AttachAndModifyIdentity(result.ServiceAccountId.Value, null, i =>
{
i.UserStatusId = UserStatusId.INACTIVE;
});
await serviceAccountManagement.DeleteServiceAccount(result.ServiceAccountId!.Value, result.DeleteServiceAccountData).ConfigureAwait(false);
}

switch (result.ConnectorStatus)
Expand Down Expand Up @@ -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;
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ public interface IConnectorsBusinessLogic
/// Remove a connector from persistence layer by id.
/// </summary>
/// <param name="connectorId">ID of the connector to be deleted.</param>
Task DeleteConnectorAsync(Guid connectorId);
/// <param name="deleteServiceAccount">if <c>true</c> the linked service account will be deleted, otherwise the connection to the connector will just be removed</param>
Task DeleteConnectorAsync(Guid connectorId, bool deleteServiceAccount);

/// <summary>
/// Retrieve connector end point along with bpns
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -44,7 +43,8 @@ public class ServiceAccountBusinessLogic(
IPortalRepositories portalRepositories,
IOptions<ServiceAccountSettings> options,
IServiceAccountCreation serviceAccountCreation,
IIdentityService identityService)
IIdentityService identityService,
IServiceAccountManagement serviceAccountManagement)
: IServiceAccountBusinessLogic
{
private readonly IIdentityData _identityData = identityService.IdentityData;
Expand Down Expand Up @@ -119,34 +119,7 @@ public async Task<int> 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<IUserRepository>().AttachAndModifyIdentity(
serviceAccountId,
i =>
{
i.UserStatusId = UserStatusId.PENDING;
},
i =>
{
i.UserStatusId = userStatus;
});
portalRepositories.GetInstance<IUserRolesRepository>().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);
Expand All @@ -168,28 +141,6 @@ private void ModifyConnectorForDeleteServiceAccount(Guid serviceAccountId, OwnSe
}
}

private async Task<UserStatusId> 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<IProcessStepRepository>()
.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<ServiceAccountConnectorOfferData> GetOwnCompanyServiceAccountDetailsAsync(Guid serviceAccountId)
{
var companyId = _identityData.CompanyId;
Expand Down
Original file line number Diff line number Diff line change
@@ -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<IUserRepository>().AttachAndModifyIdentity(
serviceAccountId,
i =>
{
i.UserStatusId = UserStatusId.PENDING;
},
i =>
{
i.UserStatusId = userStatus;
});
portalRepositories.GetInstance<IUserRolesRepository>().DeleteCompanyUserAssignedRoles(result.UserRoleIds.Select(userRoleId => (serviceAccountId, userRoleId)));
}

private async Task<UserStatusId> 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<IProcessStepRepository>()
.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<ProcessStep>(",", 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,8 @@ public async Task<CreatedAtRouteResult> CreateManagedConnectorAsync([FromForm] M
/// Removes a connector from persistence layer by id.
/// </summary>
/// <param name="connectorId" example="5636F9B9-C3DE-4BA5-8027-00D17A2FECFB">ID of the connector to be deleted.</param>
/// <remarks>Example: DELETE: /api/administration/connectors/5636F9B9-C3DE-4BA5-8027-00D17A2FECFB</remarks>
/// <param name="deleteServiceAccount">if <c>true</c> the linked service account will be deleted, otherwise the connection to the connector will just be removed</param>
/// <remarks>Example: DELETE: /api/administration/connectors/{connectorId}?deleteServiceAccount=true</remarks>
/// <response code="204">Empty response on success.</response>
/// <response code="404">Record not found.</response>
/// <response code="409">Connector status does not match a deletion scenario. Deletion declined.</response>
Expand All @@ -161,9 +162,9 @@ public async Task<CreatedAtRouteResult> CreateManagedConnectorAsync([FromForm] M
[ProducesResponseType(typeof(IActionResult), StatusCodes.Status204NoContent)]
[ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status409Conflict)]
public async Task<IActionResult> DeleteConnectorAsync([FromRoute] Guid connectorId)
public async Task<IActionResult> DeleteConnectorAsync([FromRoute] Guid connectorId, [FromQuery] bool deleteServiceAccount = false)
{
await logic.DeleteConnectorAsync(connectorId);
await logic.DeleteConnectorAsync(connectorId, deleteServiceAccount);
return NoContent();
}

Expand Down
1 change: 1 addition & 0 deletions src/administration/Administration.Service/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ await WebAppHelper
builder.Services
.AddTransient<ISubscriptionConfigurationBusinessLogic, SubscriptionConfigurationBusinessLogic>()
.AddTransient<IServiceAccountManagement, ServiceAccountManagement>()
.AddPartnerRegistration(builder.Configuration)
.AddNetworkRegistrationProcessHelper()
.AddIssuerComponentService(builder.Configuration.GetSection("Issuer"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ public record DeleteConnectorData(
ConnectorStatusId ConnectorStatus,
IEnumerable<ConnectorOfferSubscription> ConnectorOfferSubscriptions,
UserStatusId? UserStatusId,
Guid? ServiceAccountId
Guid? ServiceAccountId,
DeleteServiceAccountData DeleteServiceAccountData
);
public record ConnectorOfferSubscription(Guid AssignedOfferSubscriptionIds, OfferSubscriptionStatusId OfferSubscriptionStatus);

Expand Down
Original file line number Diff line number Diff line change
@@ -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<Guid> UserRoleIds,
string? ClientClientId,
bool IsDimServiceAccount,
bool CreationProcessInProgress,
Guid? ProcessId
);
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,3 @@ public record OwnServiceAccountData(
bool CreationProcessInProgress,
Guid? ProcessId
);

public record ProcessData(Guid ProcessId, IEnumerable<Guid> ProcessStepIds);
Loading
Loading