Skip to content

Commit

Permalink
fix(technicalUser): adjust deletion process (#956)
Browse files Browse the repository at this point in the history
* adjust deletion for external technical user
* adjust state of deleted users
----------------
Refs: #949 #950 #809 #952
Reviewed-By: Martin Rohrmeier <martin.ra.rohrmeier@bmw.de>
  • Loading branch information
Phil91 authored Aug 29, 2024
1 parent 69c0c09 commit c67221c
Show file tree
Hide file tree
Showing 9 changed files with 131 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
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 Down Expand Up @@ -98,8 +99,14 @@ public async Task<int> DeleteOwnCompanyServiceAccountAsync(Guid serviceAccountId
{
var serviceAccountRepository = portalRepositories.GetInstance<IServiceAccountRepository>();
var companyId = _identityData.CompanyId;
var result = await serviceAccountRepository.GetOwnCompanyServiceAccountWithIamServiceAccountRolesAsync(serviceAccountId, companyId).ConfigureAwait(ConfigureAwaitOptions.None)
?? throw ConflictException.Create(AdministrationServiceAccountErrors.SERVICE_ACCOUNT_NOT_CONFLICT, [new("serviceAccountId", serviceAccountId.ToString()), new(CompanyId, companyId.ToString())]);
var technicalUserCreationSteps = 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 serviceAccountRepository.GetOwnCompanyServiceAccountWithIamServiceAccountRolesAsync(serviceAccountId, companyId, technicalUserCreationSteps).ConfigureAwait(ConfigureAwaitOptions.None)
?? throw NotFoundException.Create(AdministrationServiceAccountErrors.SERVICE_ACCOUNT_NOT_FOUND, [new("serviceAccountId", serviceAccountId.ToString()), new(CompanyId, companyId.ToString())]);

if (result.StatusId is ConnectorStatusId.ACTIVE or ConnectorStatusId.PENDING)
{
Expand All @@ -112,41 +119,41 @@ public async Task<int> DeleteOwnCompanyServiceAccountAsync(Guid serviceAccountId
}

// serviceAccount
if (result.IsDimServiceAccount)
var userStatus = UserStatusId.DELETED;
switch (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}");
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);
}

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))]));
break;
}

if (!context.ProcessSteps.Any(step => step.ProcessStepTypeId == ProcessStepTypeId.DELETE_DIM_TECHNICAL_USER))
portalRepositories.GetInstance<IUserRepository>().AttachAndModifyIdentity(
serviceAccountId,
i =>
{
portalRepositories.GetInstance<IUserRepository>().AttachAndModifyIdentity(serviceAccountId, null, i =>
{
i.UserStatusId = UserStatusId.PENDING_DELETION;
});
context.ScheduleProcessSteps([ProcessStepTypeId.DELETE_DIM_TECHNICAL_USER]);
context.FinalizeProcessStep();
}
}
else if (!string.IsNullOrWhiteSpace(result.ClientClientId))
{
await provisioningManager.DeleteCentralClientAsync(result.ClientClientId).ConfigureAwait(ConfigureAwaitOptions.None);
portalRepositories.GetInstance<IUserRepository>().AttachAndModifyIdentity(serviceAccountId, null, i =>
i.UserStatusId = UserStatusId.PENDING;
},
i =>
{
i.UserStatusId = UserStatusId.DELETED;
i.UserStatusId = userStatus;
});
}

portalRepositories.GetInstance<IUserRolesRepository>().DeleteCompanyUserAssignedRoles(result.UserRoleIds.Select(userRoleId => (serviceAccountId, userRoleId)));
ModifyConnectorForDeleteServiceAccount(serviceAccountId, result);

return await portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None);
}

private void ModifyConnectorForDeleteServiceAccount(Guid serviceAccountId, OwnServiceAccountData result)
{
if (result.ConnectorId != null)
{
portalRepositories.GetInstance<IConnectorsRepository>().AttachAndModifyConnector(result.ConnectorId.Value,
Expand All @@ -159,8 +166,28 @@ public async Task<int> DeleteOwnCompanyServiceAccountAsync(Guid serviceAccountId
connector.CompanyServiceAccountId = null;
});
}
}

return await portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None);
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)
Expand All @@ -170,7 +197,7 @@ public async Task<ServiceAccountConnectorOfferData> GetOwnCompanyServiceAccountD

if (result == null)
{
throw NotFoundException.Create(AdministrationServiceAccountErrors.SERVICE_ACCOUNT_NOT_CONFLICT, [new("serviceAccountId", serviceAccountId.ToString()), new(CompanyId, companyId.ToString())]);
throw NotFoundException.Create(AdministrationServiceAccountErrors.SERVICE_ACCOUNT_NOT_FOUND, [new("serviceAccountId", serviceAccountId.ToString()), new(CompanyId, companyId.ToString())]);
}

IamClientAuthMethod? iamClientAuthMethod;
Expand Down Expand Up @@ -220,7 +247,7 @@ public async Task<ServiceAccountDetails> ResetOwnCompanyServiceAccountSecretAsyn
var result = await portalRepositories.GetInstance<IServiceAccountRepository>().GetOwnCompanyServiceAccountDetailedDataUntrackedAsync(serviceAccountId, companyId);
if (result == null)
{
throw ConflictException.Create(AdministrationServiceAccountErrors.SERVICE_ACCOUNT_NOT_CONFLICT, [new("serviceAccountId", serviceAccountId.ToString()), new(CompanyId, companyId.ToString())]);
throw ConflictException.Create(AdministrationServiceAccountErrors.SERVICE_ACCOUNT_NOT_FOUND, [new("serviceAccountId", serviceAccountId.ToString()), new(CompanyId, companyId.ToString())]);
}

if (result.ClientClientId == null)
Expand Down Expand Up @@ -259,7 +286,7 @@ public async Task<ServiceAccountDetails> UpdateOwnCompanyServiceAccountDetailsAs
var result = await serviceAccountRepository.GetOwnCompanyServiceAccountWithIamClientIdAsync(serviceAccountId, companyId).ConfigureAwait(ConfigureAwaitOptions.None);
if (result == null)
{
throw ConflictException.Create(AdministrationServiceAccountErrors.SERVICE_ACCOUNT_NOT_CONFLICT, [new("serviceAccountId", serviceAccountId.ToString()), new(CompanyId, companyId.ToString())]);
throw ConflictException.Create(AdministrationServiceAccountErrors.SERVICE_ACCOUNT_NOT_FOUND, [new("serviceAccountId", serviceAccountId.ToString()), new(CompanyId, companyId.ToString())]);
}

if (result.UserStatusId == UserStatusId.INACTIVE)
Expand Down Expand Up @@ -398,7 +425,7 @@ public async Task HandleServiceAccountDeletionCallback(Guid processId)
null,
i =>
{
i.UserStatusId = UserStatusId.INACTIVE;
i.UserStatusId = UserStatusId.DELETED;
});

context.FinalizeProcessStep();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public IdentityProviderController(IIdentityProviderBusinessLogic identityProvide
}

/// <summary>
///
/// Gets the details of the own company identity provider
/// </summary>
/// <returns>Returns the details of the own company identity provider</returns>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,11 @@ public Task<IEnumerable<ServiceAccountDetails>> ExecuteCompanyUserCreation([From
[ProducesResponseType(typeof(int), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status409Conflict)]
public Task<int> DeleteServiceAccount([FromRoute] Guid serviceAccountId) =>
_logic.DeleteOwnCompanyServiceAccountAsync(serviceAccountId);
public async Task<NoContentResult> DeleteServiceAccount([FromRoute] Guid serviceAccountId)
{
await _logic.DeleteOwnCompanyServiceAccountAsync(serviceAccountId).ConfigureAwait(ConfigureAwaitOptions.None);
return NoContent();
}

/// <summary>
/// Gets the service account details for the given id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,16 @@ public class AdministrationServiceAccountErrorMessageContainer : IErrorMessageCo
{ AdministrationServiceAccountErrors.SERVICE_COMPANY_NOT_EXIST_CONFLICT, "company {companyId} does not exist"},
{ AdministrationServiceAccountErrors.SERVICE_BPN_NOT_SET_CONFLICT, "bpn not set for company {companyId}"},
{ AdministrationServiceAccountErrors.SERVICE_ROLES_NOT_ASSIGN_ARGUMENT, "The roles {unassignable} are not assignable to a service account, {userRoleIds}"},
{ AdministrationServiceAccountErrors.SERVICE_ACCOUNT_NOT_CONFLICT, "serviceAccount {serviceAccountId} not found for company {companyId}"},
{ AdministrationServiceAccountErrors.SERVICE_ACCOUNT_NOT_FOUND, "serviceAccount {serviceAccountId} not found for company {companyId}"},
{ AdministrationServiceAccountErrors.SERVICE_USERID_ACTIVATION_PENDING_CONFLICT, "Technical User is linked to an active connector. Change the link or deactivate the connector to delete the technical user."},
{ AdministrationServiceAccountErrors.SERVICE_USERID_ACTIVATION_ACTIVE_CONFLICT, "Technical User is linked to an active subscription. Deactivate the subscription to delete the technical user."},
{ AdministrationServiceAccountErrors.SERVICE_UNDEFINED_CLIENTID_CONFLICT, "undefined clientId for serviceAccount {serviceAccountId}"},
{ AdministrationServiceAccountErrors.SERVICE_ID_PATH_NOT_MATCH_ARGUMENT, "serviceAccountId {serviceAccountId} from path does not match the one in body {serviceAccountDetailsServiceAccountId}"},
{ AdministrationServiceAccountErrors.SERVICE_INACTIVE_CONFLICT, "serviceAccount {serviceAccountId} is already INACTIVE"},
{ AdministrationServiceAccountErrors.SERVICE_CLIENTID_NOT_NULL_CONFLICT, "clientClientId of serviceAccount {serviceAccountId} should not be null"},
{ AdministrationServiceAccountErrors.SERVICE_ACCOUNT_NOT_LINKED_TO_PROCESS, "Service Account {serviceAccountId} is not linked to a process" },
{ AdministrationServiceAccountErrors.SERVICE_ACCOUNT_PENDING_PROCESS_STEPS, "Service Account {serviceAccountId} has pending process steps {processStepTypeIds}"}
{ AdministrationServiceAccountErrors.SERVICE_ACCOUNT_PENDING_PROCESS_STEPS, "Service Account {serviceAccountId} has pending process steps {processStepTypeIds}"},
{ AdministrationServiceAccountErrors.TECHNICAL_USER_CREATION_IN_PROGRESS, "Technical user can't be deleted because the creation progress is still running"}
}.ToImmutableDictionary(x => (int)x.Key, x => x.Value);

public Type Type { get => typeof(AdministrationServiceAccountErrors); }
Expand All @@ -52,13 +53,14 @@ public enum AdministrationServiceAccountErrors
SERVICE_COMPANY_NOT_EXIST_CONFLICT,
SERVICE_BPN_NOT_SET_CONFLICT,
SERVICE_ROLES_NOT_ASSIGN_ARGUMENT,
SERVICE_ACCOUNT_NOT_CONFLICT,
SERVICE_ACCOUNT_NOT_FOUND,
SERVICE_USERID_ACTIVATION_PENDING_CONFLICT,
SERVICE_USERID_ACTIVATION_ACTIVE_CONFLICT,
SERVICE_UNDEFINED_CLIENTID_CONFLICT,
SERVICE_ID_PATH_NOT_MATCH_ARGUMENT,
SERVICE_INACTIVE_CONFLICT,
SERVICE_CLIENTID_NOT_NULL_CONFLICT,
SERVICE_ACCOUNT_NOT_LINKED_TO_PROCESS,
SERVICE_ACCOUNT_PENDING_PROCESS_STEPS
SERVICE_ACCOUNT_PENDING_PROCESS_STEPS,
TECHNICAL_USER_CREATION_IN_PROGRESS
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,8 @@ public record OwnServiceAccountData(
ConnectorStatusId? StatusId,
OfferSubscriptionStatusId? OfferStatusId,
bool IsDimServiceAccount,
bool CreationProcessInProgress,
Guid? ProcessId
);

public record ProcessData(Guid ProcessId, IEnumerable<Guid> ProcessStepIds);
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ CompanyServiceAccount CreateCompanyServiceAccount(Guid identityId,

void AttachAndModifyCompanyServiceAccount(Guid id, Guid version, Action<CompanyServiceAccount>? initialize, Action<CompanyServiceAccount> modify);
Task<CompanyServiceAccountWithRoleDataClientId?> GetOwnCompanyServiceAccountWithIamClientIdAsync(Guid serviceAccountId, Guid userCompanyId);
Task<OwnServiceAccountData?> GetOwnCompanyServiceAccountWithIamServiceAccountRolesAsync(Guid serviceAccountId, Guid companyId);
Task<OwnServiceAccountData?> GetOwnCompanyServiceAccountWithIamServiceAccountRolesAsync(Guid serviceAccountId, Guid companyId, IEnumerable<ProcessStepTypeId> processStepsToFilter);
Task<CompanyServiceAccountDetailedData?> GetOwnCompanyServiceAccountDetailedDataUntrackedAsync(Guid serviceAccountId, Guid companyId);
Func<int, int, Task<Pagination.Source<CompanyServiceAccountData>?>> GetOwnCompanyServiceAccountsUntracked(Guid userCompanyId, string? clientId, bool? isOwner, IEnumerable<UserStatusId> userStatusIds);
Task<bool> CheckActiveServiceAccountExistsForCompanyAsync(Guid technicalUserId, Guid companyId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public void AttachAndModifyCompanyServiceAccount(
userRole.UserRoleText))))
.SingleOrDefaultAsync();

public Task<OwnServiceAccountData?> GetOwnCompanyServiceAccountWithIamServiceAccountRolesAsync(Guid serviceAccountId, Guid companyId) =>
public Task<OwnServiceAccountData?> GetOwnCompanyServiceAccountWithIamServiceAccountRolesAsync(Guid serviceAccountId, Guid companyId, IEnumerable<ProcessStepTypeId> processStepsToFilter) =>
portalDbContext.CompanyServiceAccounts
.Where(serviceAccount =>
serviceAccount.Id == serviceAccountId &&
Expand All @@ -108,8 +108,12 @@ public void AttachAndModifyCompanyServiceAccount(
sa.ClientClientId,
sa.Connector!.StatusId,
sa.OfferSubscription!.OfferSubscriptionStatusId,
sa.DimCompanyServiceAccount != null,
sa.DimUserCreationData!.ProcessId))
sa.CompanyServiceAccountKindId == CompanyServiceAccountKindId.EXTERNAL,
sa.DimUserCreationData!.Process!.ProcessSteps
.Any(ps =>
ps.ProcessStepStatusId == ProcessStepStatusId.TODO &&
processStepsToFilter.Contains(ps.ProcessStepTypeId)),
sa.DimUserCreationData == null ? null : sa.DimUserCreationData!.ProcessId))
.SingleOrDefaultAsync();

public Task<CompanyServiceAccountDetailedData?> GetOwnCompanyServiceAccountDetailedDataUntrackedAsync(Guid serviceAccountId, Guid companyId) =>
Expand Down
Loading

0 comments on commit c67221c

Please sign in to comment.