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 Nov 4, 2024
1 parent a6936a5 commit 338c424
Show file tree
Hide file tree
Showing 48 changed files with 11,037 additions and 384 deletions.
23 changes: 8 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 @@ -7596,6 +7581,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 @@ -3630,6 +3630,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 @@ -49,7 +52,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 @@ -266,74 +269,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 @@ -575,12 +542,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 @@ -596,12 +561,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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
using Org.Eclipse.TractusX.Portal.Backend.Framework.Web;
using Org.Eclipse.TractusX.Portal.Backend.Keycloak.Authentication;
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;
using Org.Eclipse.TractusX.Portal.Backend.Web.Identity;

Expand Down Expand Up @@ -261,11 +262,11 @@ public Task<IEnumerable<UserRoleWithId>> ModifyAppUserRolesAsync([FromRoute] Gui
[Authorize(Roles = "modify_user_account")]
[Authorize(Policy = PolicyTypes.ValidCompany)]
[Route("owncompany/users/{companyUserId}/businessPartnerNumbers")]
[ProducesResponseType(typeof(int), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status404NotFound)]
public Task<CompanyUsersBpnDetails> AddOwnCompanyUserBusinessPartnerNumbers(Guid companyUserId, IEnumerable<string> businessPartnerNumbers, CancellationToken cancellationToken) =>
this.WithBearerToken(token => _logic.AddOwnCompanyUsersBusinessPartnerNumbersAsync(companyUserId, token, businessPartnerNumbers, cancellationToken));
public Task AddOwnCompanyUserBusinessPartnerNumbers(Guid companyUserId, IEnumerable<string> businessPartnerNumbers, CancellationToken cancellationToken) =>
_logic.AddOwnCompanyUsersBusinessPartnerNumbersAsync(companyUserId, businessPartnerNumbers, cancellationToken);

/// <summary>
/// Adds the given business partner number to the user for the given id.
Expand All @@ -284,13 +285,13 @@ public Task<CompanyUsersBpnDetails> AddOwnCompanyUserBusinessPartnerNumbers(Guid
[Authorize(Roles = "modify_user_account")]
[Authorize(Policy = PolicyTypes.ValidCompany)]
[Route("owncompany/users/{companyUserId}/businessPartnerNumbers/{businessPartnerNumber}")]
[ProducesResponseType(typeof(int), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status500InternalServerError)]
[ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status502BadGateway)]
public Task<CompanyUsersBpnDetails> AddOwnCompanyUserBusinessPartnerNumber(Guid companyUserId, string businessPartnerNumber, CancellationToken cancellationToken) =>
this.WithBearerToken(token => _logic.AddOwnCompanyUsersBusinessPartnerNumberAsync(companyUserId, token, businessPartnerNumber, cancellationToken));
public Task AddOwnCompanyUserBusinessPartnerNumber(Guid companyUserId, string businessPartnerNumber, CancellationToken cancellationToken) =>
_logic.AddOwnCompanyUsersBusinessPartnerNumberAsync(companyUserId, businessPartnerNumber, cancellationToken);

/// <summary>
/// Deletes the users with the given ids.
Expand Down Expand Up @@ -483,6 +484,28 @@ public Task<int> DeleteOwnUser([FromRoute] Guid companyUserId) =>
[ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status403Forbidden)]
[ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status409Conflict)]
public Task<int> DeleteOwnCompanyUserBusinessPartnerNumber([FromRoute] Guid companyUserId, [FromRoute] string businessPartnerNumber) =>
public Task DeleteOwnCompanyUserBusinessPartnerNumber([FromRoute] Guid companyUserId, [FromRoute] string businessPartnerNumber) =>
_logic.DeleteOwnUserBusinessPartnerNumbersAsync(companyUserId, businessPartnerNumber);

/// <summary>
/// Retriggers the last failed step
/// </summary>
/// <param name="processId" example="251e4596-5ff0-4176-b544-840b04ebeb93">Id of the process that should be triggered</param>
/// <param name="processStepTypeId" example="ProcessStepTypeId.DELETE_BPN_FROM_CENTRAL_USER">Id of the process step type which should be retriggered</param>
/// <returns>NoContent</returns>
/// Example: POST: api/user/{processId}/retrigger-user-bpn-process?processStepTypeId=904
/// <response code="204">Empty response on success.</response>
/// <response code="404">No Process found for the processId</response>
[HttpPost]
[Authorize(Roles = "modify_user_account")]
[Authorize(Policy = PolicyTypes.CompanyUser)]
[Route("{processId}/retrigger-user-bpn-process")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status404NotFound)]
public async Task<NoContentResult> RetriggerUserBpnProcess([FromRoute] Guid processId, [FromQuery] ProcessStepTypeId processStepTypeId)
{
await _logic.RetriggerUserBpnProcess(processId, processStepTypeId).ConfigureAwait(false);
return NoContent();
}
}
9 changes: 8 additions & 1 deletion src/administration/Administration.Service/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,13 @@
]
},
"BpnAccess": {
"BaseUrl": ""
"Username": "",
"Password": "",
"ClientId": "",
"GrantType": "",
"ClientSecret": "",
"Scope": "",
"TokenAddress": "",
"BaseAddress": ""
}
}
Loading

0 comments on commit 338c424

Please sign in to comment.