Skip to content

Commit

Permalink
feat: add process to add and remove bpn from users
Browse files Browse the repository at this point in the history
Refs: #1098
  • Loading branch information
Phil91 committed Dec 13, 2024
1 parent 3f52501 commit 940d48d
Show file tree
Hide file tree
Showing 47 changed files with 11,148 additions and 375 deletions.
62 changes: 47 additions & 15 deletions docs/api/administration-service.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5529,11 +5529,6 @@ paths:
responses:
'200':
description: The business partner numbers have been added successfully.
content:
application/json:
schema:
type: integer
format: int32
'400':
description: Business Partner Numbers must not exceed 20 characters.
content:
Expand Down Expand Up @@ -5575,11 +5570,6 @@ paths:
responses:
'200':
description: The business partner number have been added successfully.
content:
application/json:
schema:
type: integer
format: int32
'400':
description: Business Partner Numbers must not exceed 20 characters.
content:
Expand Down Expand Up @@ -5630,11 +5620,6 @@ paths:
responses:
'200':
description: Empty response on success.
content:
application/json:
schema:
type: integer
format: int32
'403':
description: ForbiddenException if both users does not belongs to same company
content:
Expand Down Expand Up @@ -5935,6 +5920,45 @@ paths:
description: Internal Server Error
'401':
description: The User is unauthorized
'/api/administration/user/{processId}/retrigger-user-bpn-process':
post:
tags:
- User
summary: 'Retriggers the last failed step (Authorization required - Roles: modify_user_account)'
parameters:
- name: processId
in: path
description: Id of the process that should be triggered
required: true
schema:
type: string
format: uuid
example: 251e4596-5ff0-4176-b544-840b04ebeb93
- name: processStepTypeId
in: query
description: Id of the process step type which should be retriggered
schema:
$ref: '#/components/schemas/ProcessStepTypeId'
example: ProcessStepTypeId.DELETE_BPN_FROM_CENTRAL_USER
responses:
'204':
description: Empty response on success.
'400':
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'404':
description: No Process found for the processId
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'500':
description: Internal Server Error
'401':
description: The User is unauthorized
components:
schemas:
AgreementConsentData:
Expand Down Expand Up @@ -7596,6 +7620,14 @@ components:
- SELF_DESCRIPTION_COMPANY_CREATION
- RETRIGGER_SELF_DESCRIPTION_CONNECTOR_CREATION
- RETRIGGER_SELF_DESCRIPTION_COMPANY_CREATION
- DELETE_BPN_FROM_CENTRAL_USER
- DELETE_BPN_FROM_IDENTITY
- RETRIGGER_DELETE_BPN_FROM_CENTRAL_USER
- CHECK_LEGAL_ENTITY_DATA
- ADD_BPN_TO_IDENTITY
- CLEANUP_USER_BPN
- RETRIGGER_CHECK_LEGAL_ENTITY_DATA
- RETRIGGER_ADD_BPN_TO_IDENTITY
type: string
ProviderDetailData:
type: object
Expand Down
8 changes: 8 additions & 0 deletions docs/api/apps-service.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3631,6 +3631,14 @@ components:
- SELF_DESCRIPTION_COMPANY_CREATION
- RETRIGGER_SELF_DESCRIPTION_CONNECTOR_CREATION
- RETRIGGER_SELF_DESCRIPTION_COMPANY_CREATION
- DELETE_BPN_FROM_CENTRAL_USER
- DELETE_BPN_FROM_IDENTITY
- RETRIGGER_DELETE_BPN_FROM_CENTRAL_USER
- CHECK_LEGAL_ENTITY_DATA
- ADD_BPN_TO_IDENTITY
- CLEANUP_USER_BPN
- RETRIGGER_CHECK_LEGAL_ENTITY_DATA
- RETRIGGER_ADD_BPN_TO_IDENTITY
type: string
SubscriberSubscriptionDetailData:
type: object
Expand Down
8 changes: 8 additions & 0 deletions docs/api/services-service.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2210,6 +2210,14 @@ components:
- SELF_DESCRIPTION_COMPANY_CREATION
- RETRIGGER_SELF_DESCRIPTION_CONNECTOR_CREATION
- RETRIGGER_SELF_DESCRIPTION_COMPANY_CREATION
- DELETE_BPN_FROM_CENTRAL_USER
- DELETE_BPN_FROM_IDENTITY
- RETRIGGER_DELETE_BPN_FROM_CENTRAL_USER
- CHECK_LEGAL_ENTITY_DATA
- ADD_BPN_TO_IDENTITY
- CLEANUP_USER_BPN
- RETRIGGER_CHECK_LEGAL_ENTITY_DATA
- RETRIGGER_ADD_BPN_TO_IDENTITY
type: string
ProviderSubscriptionDetailData:
type: object
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
using Org.Eclipse.TractusX.Portal.Backend.Administration.Service.Models;
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.Enums;
using Org.Eclipse.TractusX.Portal.Backend.Provisioning.Library.Models;

namespace Org.Eclipse.TractusX.Portal.Backend.Administration.Service.BusinessLogic;
Expand All @@ -32,8 +33,8 @@ public interface IUserBusinessLogic
Task<Guid> CreateOwnCompanyIdpUserAsync(Guid identityProviderId, UserCreationInfoIdp userCreationInfo);
Task<Pagination.Response<CompanyUserData>> GetOwnCompanyUserDatasAsync(int page, int size, GetOwnCompanyUsersFilter filter);
Task<CompanyUserDetailData> GetOwnCompanyUserDetailsAsync(Guid userId);
Task<CompanyUsersBpnDetails> AddOwnCompanyUsersBusinessPartnerNumbersAsync(Guid userId, string token, IEnumerable<string> businessPartnerNumbers, CancellationToken cancellationToken);
Task<CompanyUsersBpnDetails> AddOwnCompanyUsersBusinessPartnerNumberAsync(Guid userId, string token, string businessPartnerNumber, CancellationToken cancellationToken);
Task AddOwnCompanyUsersBusinessPartnerNumbersAsync(Guid userId, IEnumerable<string> businessPartnerNumbers, CancellationToken cancellationToken);
Task AddOwnCompanyUsersBusinessPartnerNumberAsync(Guid userId, string businessPartnerNumber, CancellationToken cancellationToken);
Task<CompanyOwnUserDetails> GetOwnUserDetails();
Task<CompanyUserDetails> UpdateOwnUserDetails(Guid companyUserId, OwnCompanyUserEditableDetails ownCompanyUserEditableDetails);

Expand All @@ -46,5 +47,6 @@ public interface IUserBusinessLogic
IAsyncEnumerable<Guid> DeleteOwnCompanyUsersAsync(IEnumerable<Guid> userIds);
Task<bool> ExecuteOwnCompanyUserPasswordReset(Guid companyUserId);
Task<Pagination.Response<CompanyAppUserDetails>> GetOwnCompanyAppUsersAsync(Guid appId, int page, int size, CompanyUserFilter filter);
Task<int> DeleteOwnUserBusinessPartnerNumbersAsync(Guid userId, string businessPartnerNumber);
Task DeleteOwnUserBusinessPartnerNumbersAsync(Guid userId, string businessPartnerNumber);
Task RetriggerUserBpnProcess(Guid processId, ProcessStepTypeId processStepTypeId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,11 @@
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.Extensions;
using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Identities;
using Org.Eclipse.TractusX.Portal.Backend.Processes.Library;
using Org.Eclipse.TractusX.Portal.Backend.Processes.Mailing.Library;
using Org.Eclipse.TractusX.Portal.Backend.Provisioning.DBAccess;
using Org.Eclipse.TractusX.Portal.Backend.Provisioning.Library;
Expand All @@ -50,7 +53,7 @@ public class UserBusinessLogic(
IIdentityService identityService,
IMailingProcessCreation mailingProcessCreation,
ILogger<UserBusinessLogic> logger,
IBpnAccess bpnAccess,
IBpdmAccessService bpdmAccessService,
IOptions<UserSettings> options) : IUserBusinessLogic
{
private readonly UserSettings _settings = options.Value;
Expand Down Expand Up @@ -267,74 +270,38 @@ await Task.WhenAll(details.IdpUserIds.Select(async x =>
details.Email);
}

public async Task<CompanyUsersBpnDetails> AddOwnCompanyUsersBusinessPartnerNumbersAsync(Guid userId, string token, IEnumerable<string> businessPartnerNumbers, CancellationToken cancellationToken)
public async Task AddOwnCompanyUsersBusinessPartnerNumbersAsync(Guid userId, IEnumerable<string> businessPartnerNumbers, CancellationToken cancellationToken)
{
var companyId = _identityData.CompanyId;

var invalidBpns = businessPartnerNumbers.Where(bpn => bpn.Length > 20);
if (invalidBpns.Any())
{
throw new ControllerArgumentException($"BusinessPartnerNumbers {string.Join(",", invalidBpns)} must not exceed 20 characters");
}

var (assignedBusinessPartnerNumbers, isValidUser) = await portalRepositories.GetInstance<IUserRepository>().GetOwnCompanyUserWithAssignedBusinessPartnerNumbersUntrackedAsync(userId, companyId).ConfigureAwait(ConfigureAwaitOptions.None);
if (!isValidUser)
{
throw new NotFoundException($"user {userId} not found in company {companyId}");
}

var iamUserId = await provisioningManager.GetUserByUserName(userId.ToString()).ConfigureAwait(ConfigureAwaitOptions.None) ??
throw new ConflictException($"user {userId} not found in keycloak");

var (successfulBpns, unsuccessfulBpns) = await businessPartnerNumbers.AggregateAwait(
(SuccessfulBpns: ImmutableList.CreateBuilder<string>(), UnsuccessfulBpns: ImmutableList.CreateBuilder<UnsuccessfulBpns>()),
async (acc, bpn) =>
{
var (bpns, error) = await CompanyUsersBpnCheck(bpn, token, cancellationToken).ConfigureAwait(false);
if (error == null)
{
acc.SuccessfulBpns.Add(bpns);
}
else
{
acc.UnsuccessfulBpns.Add(new UnsuccessfulBpns(bpns, error.Message));
}
return acc;
},
acc => (acc.SuccessfulBpns.ToImmutable(), acc.UnsuccessfulBpns.ToImmutable()),
cancellationToken
).ConfigureAwait(ConfigureAwaitOptions.None);

if (successfulBpns.Count != 0)
Action<CompanyUserAssignedBusinessPartner> CreateProcess()
{
await provisioningManager.AddBpnAttributetoUserAsync(iamUserId, successfulBpns).ConfigureAwait(false);
successfulBpns.Except(assignedBusinessPartnerNumbers).IfAny(businessPartnersToAdd =>
portalRepositories.GetInstance<IUserBusinessPartnerRepository>().CreateCompanyUserAssignedBusinessPartners(businessPartnersToAdd.Select(bpn => (userId, bpn))));
var processStepRepository = portalRepositories.GetInstance<IProcessStepRepository>();
var processId = processStepRepository.CreateProcess(ProcessTypeId.USER_BPN).Id;
processStepRepository.CreateProcessStep(ProcessStepTypeId.CHECK_LEGAL_ENTITY_DATA, ProcessStepStatusId.TODO, processId);
return x => x.ProcessId = processId;
}

await portalRepositories.SaveAsync();
return new CompanyUsersBpnDetails(successfulBpns, unsuccessfulBpns);
}

private async ValueTask<(string bpns, Exception? error)> CompanyUsersBpnCheck(string bpn, string token, CancellationToken cancellationToken)
{
Exception? error = null;
try
{
if (bpn.Length > 20)
{
throw new ControllerArgumentException("BusinessPartnerNumbers must not exceed 20 characters");
}

var legalEntity = await bpnAccess.FetchLegalEntityByBpn(bpn, token, cancellationToken).ConfigureAwait(false);
if (!bpn.Equals(legalEntity.Bpn, StringComparison.OrdinalIgnoreCase))
{
throw new ConflictException("Bpdm did return incorrect bpn legal-entity-data");
}
}
catch (Exception ex)
{
error = ex;
}
portalRepositories.GetInstance<IUserBusinessPartnerRepository>()
.CreateCompanyUserAssignedBusinessPartners(businessPartnerNumbers.Except(assignedBusinessPartnerNumbers).Select(bpn => (userId, bpn, CreateProcess())));

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

public Task<CompanyUsersBpnDetails> AddOwnCompanyUsersBusinessPartnerNumberAsync(Guid userId, string token, string businessPartnerNumber, CancellationToken cancellationToken) =>
AddOwnCompanyUsersBusinessPartnerNumbersAsync(userId, token, Enumerable.Repeat(businessPartnerNumber, 1), cancellationToken);
public Task AddOwnCompanyUsersBusinessPartnerNumberAsync(Guid userId, string businessPartnerNumber, CancellationToken cancellationToken) =>
AddOwnCompanyUsersBusinessPartnerNumbersAsync(userId, Enumerable.Repeat(businessPartnerNumber, 1), cancellationToken);

public async Task<CompanyOwnUserDetails> GetOwnUserDetails()
{
Expand Down Expand Up @@ -576,12 +543,10 @@ public async Task<bool> ExecuteOwnCompanyUserPasswordReset(Guid companyUserId)
new[] { UserStatusId.ACTIVE, UserStatusId.INACTIVE },
filter));

public async Task<int> DeleteOwnUserBusinessPartnerNumbersAsync(Guid userId, string businessPartnerNumber)
public async Task DeleteOwnUserBusinessPartnerNumbersAsync(Guid userId, string businessPartnerNumber)
{
var userBusinessPartnerRepository = portalRepositories.GetInstance<IUserBusinessPartnerRepository>();

var (isValidUser, isAssignedBusinessPartner, isSameCompany) = await userBusinessPartnerRepository.GetOwnCompanyUserWithAssignedBusinessPartnerNumbersAsync(userId, _identityData.CompanyId, businessPartnerNumber.ToUpper()).ConfigureAwait(ConfigureAwaitOptions.None);

if (!isValidUser)
{
throw new NotFoundException($"user {userId} does not exist");
Expand All @@ -597,12 +562,21 @@ public async Task<int> DeleteOwnUserBusinessPartnerNumbersAsync(Guid userId, str
throw new ForbiddenException($"userId {userId} and adminUserId {_identityData.IdentityId} do not belong to same company");
}

var iamUserId = await provisioningManager.GetUserByUserName(userId.ToString()).ConfigureAwait(ConfigureAwaitOptions.None) ?? throw new ConflictException($"user {userId} is not associated with a user in keycloak");

userBusinessPartnerRepository.DeleteCompanyUserAssignedBusinessPartner(userId, businessPartnerNumber.ToUpper());
var processStepRepository = portalRepositories.GetInstance<IProcessStepRepository>();
var processId = processStepRepository.CreateProcess(ProcessTypeId.USER_BPN).Id;
processStepRepository.CreateProcessStep(ProcessStepTypeId.DELETE_BPN_FROM_CENTRAL_USER, ProcessStepStatusId.TODO, processId);
userBusinessPartnerRepository.AttachAndModifyCompanyUserAssignedBusinessPartner(userId, businessPartnerNumber, c => c.ProcessId = processId);
await portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None);
}

await provisioningManager.DeleteCentralUserBusinessPartnerNumberAsync(iamUserId, businessPartnerNumber.ToUpper()).ConfigureAwait(ConfigureAwaitOptions.None);
public Task RetriggerUserBpnProcess(Guid processId, ProcessStepTypeId processStepTypeId)
{
var (processType, _) = processStepTypeId.GetProcessStepForRetrigger();
if (processType != ProcessTypeId.USER_BPN)
{
throw new ConflictException($"{processStepTypeId} can't be retriggered for Process {processId}");
}

return await portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None);
return processStepTypeId.TriggerProcessStep(processId, portalRepositories);
}
}
Loading

0 comments on commit 940d48d

Please sign in to comment.