Skip to content

Commit

Permalink
release(v1.7.0-RC3): merge main dev #338
Browse files Browse the repository at this point in the history
Reviewed-By: Evelyn Gurschler <evelyn.gurschler@bmw.de>
  • Loading branch information
Phil91 authored Nov 14, 2023
2 parents c302153 + cb59cf0 commit 2279e00
Show file tree
Hide file tree
Showing 114 changed files with 10,701 additions and 562 deletions.
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,34 @@

New features, fixed bugs, known defects and other noteworthy changes to each release of the Catena-X Portal Backend.

## 1.7.0-RC3

### Change
* Email Template
* OSP_Welcome_Email style updated (highlights added) and updated email message/text content
* OSP_Welcome_Email changed from companyName to the idp display name as dynamic value

### Feature
* App Service (AppChange)
* released new endpoints to delete and add app related documents for an active app

### Technical Support
* Database view added
* Company-Connector-View
* Company-IdP-View
* Company-Role-Collection-Role-View
* Company-User-View

### Bugfix
* Query GetCompanyRoleAgreementConsentStatusUntrackedAsync has been adjusted to refer to companyId instead of userId in the where-condition
* Modified (via migration) entries in table identity_providers setting the owner_id of shared-idps to the company_id that is linked in table company_identity_providers (if not ambiguous)
* Connector pagination of GET /api/administration/connectors fixed - pagination failed as soon as there were connectors with same provider but different hosts existing

### Known Knowns
* declineFlow OSP currently not supported
* password reset in welcome user email currently not supported
* DELETE /api/apps/AppReleaseProcess/{appId}/role/{roleId} running on system error

## 1.7.0-RC2

### Change
Expand Down
2 changes: 1 addition & 1 deletion src/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@
<Project>
<PropertyGroup>
<VersionPrefix>1.7.0</VersionPrefix>
<VersionSuffix>RC2</VersionSuffix>
<VersionSuffix>RC3</VersionSuffix>
</PropertyGroup>
</Project>
15 changes: 15 additions & 0 deletions src/Portal.Backend.sln
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceAccountSync.Executor
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceAccountSync.Executor.Tests", "..\tests\processes\ServiceAccountSync.Executor.Tests\ServiceAccountSync.Executor.Tests.csproj", "{571DA63A-6B96-4C6C-8D82-D2C1F10BDAE5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Registration.Common", "registration\Registration.Common\Registration.Common.csproj", "{09EF5799-B375-49F1-B78F-0A94D8109F8B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -1508,6 +1510,18 @@ Global
{571DA63A-6B96-4C6C-8D82-D2C1F10BDAE5}.Release|x64.Build.0 = Release|Any CPU
{571DA63A-6B96-4C6C-8D82-D2C1F10BDAE5}.Release|x86.ActiveCfg = Release|Any CPU
{571DA63A-6B96-4C6C-8D82-D2C1F10BDAE5}.Release|x86.Build.0 = Release|Any CPU
{09EF5799-B375-49F1-B78F-0A94D8109F8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{09EF5799-B375-49F1-B78F-0A94D8109F8B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{09EF5799-B375-49F1-B78F-0A94D8109F8B}.Debug|x64.ActiveCfg = Debug|Any CPU
{09EF5799-B375-49F1-B78F-0A94D8109F8B}.Debug|x64.Build.0 = Debug|Any CPU
{09EF5799-B375-49F1-B78F-0A94D8109F8B}.Debug|x86.ActiveCfg = Debug|Any CPU
{09EF5799-B375-49F1-B78F-0A94D8109F8B}.Debug|x86.Build.0 = Debug|Any CPU
{09EF5799-B375-49F1-B78F-0A94D8109F8B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{09EF5799-B375-49F1-B78F-0A94D8109F8B}.Release|Any CPU.Build.0 = Release|Any CPU
{09EF5799-B375-49F1-B78F-0A94D8109F8B}.Release|x64.ActiveCfg = Release|Any CPU
{09EF5799-B375-49F1-B78F-0A94D8109F8B}.Release|x64.Build.0 = Release|Any CPU
{09EF5799-B375-49F1-B78F-0A94D8109F8B}.Release|x86.ActiveCfg = Release|Any CPU
{09EF5799-B375-49F1-B78F-0A94D8109F8B}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -1621,5 +1635,6 @@ Global
{F1A5A73C-2B8C-4959-A128-CC5A8DECCB1B} = {323C198D-A8C6-4EB0-8B79-72624275E35F}
{B2E5EBAB-AE49-47B6-8220-4844AC9DA456} = {282CEF03-292F-4A49-83C6-997567D0FF5F}
{571DA63A-6B96-4C6C-8D82-D2C1F10BDAE5} = {323C198D-A8C6-4EB0-8B79-72624275E35F}
{09EF5799-B375-49F1-B78F-0A94D8109F8B} = {AB9C5AA2-DD5D-4A38-97C0-674A995C0AE0}
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
Expand Up @@ -43,23 +43,24 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\externalsystems\Bpdm.Library\Bpdm.Library.csproj" />
<ProjectReference Include="..\..\externalsystems\OnboardingServiceProvider.Library\OnboardingServiceProvider.Library.csproj" />
<ProjectReference Include="..\..\framework\Framework.Async\Framework.Async.csproj" />
<ProjectReference Include="..\..\framework\Framework.DateTimeProvider\Framework.DateTimeProvider.csproj" />
<ProjectReference Include="..\..\processes\ApplicationChecklist.Config\ApplicationChecklist.Config.csproj" />
<ProjectReference Include="..\..\framework\Framework.IO\Framework.IO.csproj" />
<ProjectReference Include="..\..\framework\Framework.Models\Framework.Models.csproj" />
<ProjectReference Include="..\..\framework\Framework.Token\Framework.Token.csproj" />
<ProjectReference Include="..\..\framework\Framework.BaseDependencies\Framework.BaseDependencies.csproj" />
<ProjectReference Include="..\..\framework\Framework.Web\Framework.Web.csproj" />
<ProjectReference Include="..\..\mailing\Mailing.SendMail\Mailing.SendMail.csproj" />
<ProjectReference Include="..\..\notifications\Notifications.Library\Notifications.Library.csproj" />
<ProjectReference Include="..\..\processes\NetworkRegistration.Library\NetworkRegistration.Library.csproj" />
<ProjectReference Include="..\..\processes\OfferSubscription.Library\OfferSubscription.Library.csproj" />
<ProjectReference Include="..\..\provisioning\Provisioning.Library\Provisioning.Library.csproj" />
<ProjectReference Include="..\..\portalbackend\PortalBackend.DBAccess\PortalBackend.DBAccess.csproj" />
<ProjectReference Include="..\..\processes\ApplicationChecklist.Config\ApplicationChecklist.Config.csproj" />
<ProjectReference Include="..\..\processes\NetworkRegistration.Library\NetworkRegistration.Library.csproj" />
<ProjectReference Include="..\..\processes\OfferSubscription.Library\OfferSubscription.Library.csproj" />
<ProjectReference Include="..\..\provisioning\Provisioning.DBAccess\Provisioning.DBAccess.csproj" />
<ProjectReference Include="..\..\framework\Framework.Models\Framework.Models.csproj" />
<ProjectReference Include="..\..\framework\Framework.IO\Framework.IO.csproj" />
<ProjectReference Include="..\..\framework\Framework.Async\Framework.Async.csproj" />
<ProjectReference Include="..\..\framework\Framework.Token\Framework.Token.csproj" />
<ProjectReference Include="..\..\externalsystems\Bpdm.Library\Bpdm.Library.csproj" />
<ProjectReference Include="..\..\registration\Registration.Common\Registration.Common.csproj" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@ namespace Org.Eclipse.TractusX.Portal.Backend.Administration.Service.BusinessLog
public interface INetworkBusinessLogic
{
Task HandlePartnerRegistration(PartnerRegistrationData data);
Task RetriggerProcessStep(Guid externalId, ProcessStepTypeId processStepTypeId);
Task RetriggerProcessStep(string externalId, ProcessStepTypeId processStepTypeId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ private async Task ExecuteInvitationInternalAsync(CompanyInvitationData invitati
{
{ "password", password ?? "" },
{ "companyName", invitationData.organisationName },
{ "url", $"{_settings.RegistrationAppAddress}"},
{ "url", _settings.RegistrationAppAddress },
{ "passwordResendUrl", _settings.PasswordResendAddress },
};

await _mailingService.SendMails(invitationData.email, mailParameters, new List<string> { "RegistrationTemplate", "PasswordForRegistrationTemplate" }).ConfigureAwait(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public InvitationSettings()
RegistrationAppAddress = null!;
InvitedUserInitialRoles = null!;
InitialLoginTheme = null!;
PasswordResendAddress = null!;
}

[Required(AllowEmptyStrings = false)]
Expand All @@ -42,6 +43,9 @@ public InvitationSettings()

[Required(AllowEmptyStrings = false)]
public string InitialLoginTheme { get; set; }

[Required(AllowEmptyStrings = false)]
public string PasswordResendAddress { get; set; }
}

public static class InvitationSettingsExtension
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
using Org.Eclipse.TractusX.Portal.Backend.Processes.NetworkRegistration.Library;
using Org.Eclipse.TractusX.Portal.Backend.Provisioning.Library.Models;
using Org.Eclipse.TractusX.Portal.Backend.Provisioning.Library.Service;
using Org.Eclipse.TractusX.Portal.Backend.Registration.Common;
using System.ComponentModel.DataAnnotations;
using System.Text.RegularExpressions;

Expand All @@ -39,7 +40,7 @@ namespace Org.Eclipse.TractusX.Portal.Backend.Administration.Service.BusinessLog
public class NetworkBusinessLogic : INetworkBusinessLogic
{
private static readonly Regex Name = new(ValidationExpressions.Name, RegexOptions.Compiled, TimeSpan.FromSeconds(1));
private static readonly Regex BpnRegex = new(ValidationExpressions.Bpn, RegexOptions.Compiled, TimeSpan.FromSeconds(1));
private static readonly Regex ExternalID = new("^[A-Za-z0-9\\-+_/,.]{6,36}$", RegexOptions.Compiled, TimeSpan.FromSeconds(1));

private readonly IPortalRepositories _portalRepositories;
private readonly IIdentityService _identityService;
Expand Down Expand Up @@ -82,9 +83,7 @@ public async Task HandlePartnerRegistration(PartnerRegistrationData data)
identityProviderRepository.CreateCompanyIdentityProviders(allIdentityProviderIds.Select(identityProviderId => (companyId, identityProviderId)));

Guid GetIdpId(Guid? identityProviderId) =>
identityProviderId == null
? singleIdentityProviderIdAlias?.IdentityProviderId ?? throw new UnexpectedConditionException("singleIdentityProviderIdAlias should never be null here")
: identityProviderId.Value;
identityProviderId ?? (singleIdentityProviderIdAlias?.IdentityProviderId ?? throw new UnexpectedConditionException("singleIdentityProviderIdAlias should never be null here"));

string GetIdpAlias(Guid? identityProviderId) =>
identityProviderId == null
Expand All @@ -103,7 +102,7 @@ string GetIdpAlias(Guid? identityProviderId) =>
try
{
var (_, companyUserId) = await _userProvisioningService.GetOrCreateCompanyUser(userRepository, aliasData.IdpAlias,
creationInfo, companyId, aliasData.IdpId, data.Bpn).ConfigureAwait(false);
creationInfo, companyId, aliasData.IdpId, data.BusinessPartnerNumber?.ToUpper()).ConfigureAwait(false);
identityId = companyUserId;
}
catch (Exception ex)
Expand Down Expand Up @@ -136,7 +135,7 @@ private Guid CreatePartnerCompany(ICompanyRepository companyRepository, PartnerR
var company = companyRepository.CreateCompany(data.Name, c =>
{
c.AddressId = address.Id;
c.BusinessPartnerNumber = data.Bpn;
c.BusinessPartnerNumber = data.BusinessPartnerNumber?.ToUpper();
});

companyRepository.CreateUpdateDeleteIdentifiers(company.Id, Enumerable.Empty<(UniqueIdentifierId, string)>(), data.UniqueIds.Select(x => (x.UniqueIdentifierId, x.Value)));
Expand All @@ -153,7 +152,7 @@ private Guid CreatePartnerCompany(ICompanyRepository companyRepository, PartnerR
var companyNameIdpAliasData = new CompanyNameIdpAliasData(
companyId,
data.Name,
data.Bpn,
data.BusinessPartnerNumber,
getIdentityProviderAlias(group.Key),
getIdentityProviderId(group.Key),
false
Expand All @@ -175,23 +174,17 @@ private Guid CreatePartnerCompany(ICompanyRepository companyRepository, PartnerR
return (AliasData: companyNameIdpAliasData, CreationInfos: userCreationInfos);
});

public Task RetriggerProcessStep(Guid externalId, ProcessStepTypeId processStepTypeId) =>
public Task RetriggerProcessStep(string externalId, ProcessStepTypeId processStepTypeId) =>
_processHelper.TriggerProcessStep(externalId, processStepTypeId);

private async Task<(IEnumerable<UserRoleData> RoleData, IDictionary<Guid, string>? IdentityProviderIdAliase, (Guid IdentityProviderId, string Alias)? SingleIdentityProviderIdAlias, IEnumerable<Guid> AllIdentityProviderIds)> ValidatePartnerRegistrationData(PartnerRegistrationData data, INetworkRepository networkRepository, IIdentityProviderRepository identityProviderRepository, Guid ownerCompanyId)
{
if (data.Bpn != null)
{
if (!BpnRegex.IsMatch(data.Bpn))
{
throw new ControllerArgumentException("BPN must contain exactly 16 characters and must be prefixed with BPNL", nameof(data.Bpn));
}

if (await _portalRepositories.GetInstance<ICompanyRepository>().CheckBpnExists(data.Bpn).ConfigureAwait(false))
{
throw new ControllerArgumentException($"The Bpn {data.Bpn} already exists", nameof(data.Bpn));
}
}
var countryRepository = _portalRepositories.GetInstance<ICountryRepository>();
data.ValidateData();
await data.ValidateDatabaseData(
bpn => _portalRepositories.GetInstance<ICompanyRepository>().CheckBpnExists(bpn),
alpha2Code => countryRepository.CheckCountryExistsByAlpha2CodeAsync(alpha2Code),
(countryAlpha2Code, uniqueIdentifierIds) => countryRepository.GetCountryAssignedIdentifiers(countryAlpha2Code, uniqueIdentifierIds)).ConfigureAwait(false);

if (!data.CompanyRoles.Any())
{
Expand All @@ -203,16 +196,15 @@ public Task RetriggerProcessStep(Guid externalId, ProcessStepTypeId processStepT
ValidateUsers(user);
}

if (await networkRepository.CheckExternalIdExists(data.ExternalId, ownerCompanyId)
.ConfigureAwait(false))
if (!ExternalID.IsMatch(data.ExternalId))
{
throw new ControllerArgumentException($"ExternalId {data.ExternalId} already exists", nameof(data.ExternalId));
throw new ControllerArgumentException("ExternalId must be between 6 and 36 characters");
}

if (!await _portalRepositories.GetInstance<ICountryRepository>()
.CheckCountryExistsByAlpha2CodeAsync(data.CountryAlpha2Code).ConfigureAwait(false))
if (await networkRepository.CheckExternalIdExists(data.ExternalId, ownerCompanyId)
.ConfigureAwait(false))
{
throw new ControllerArgumentException($"Location {data.CountryAlpha2Code} does not exist", nameof(data.CountryAlpha2Code));
throw new ControllerArgumentException($"ExternalId {data.ExternalId} already exists", nameof(data.ExternalId));
}

var idpResult = await ValidateIdps(data, identityProviderRepository, ownerCompanyId).ConfigureAwait(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ private async Task<CompanyWithAddressData> GetCompanyWithAddressAsyncInternal(Gu
x.FirstName ?? "",
x.LastName ?? "",
x.Email ?? "")),
companyWithAddress.CompanyIdentifiers.Select(identifier => new IdentifierData(identifier.UniqueIdentifierId, identifier.Value))
companyWithAddress.CompanyIdentifiers.Select(identifier => new UniqueIdentifierData((int)identifier.UniqueIdentifierId, identifier.UniqueIdentifierId))
);
}

Expand Down Expand Up @@ -482,7 +482,8 @@ private async Task PostRegistrationCancelEmailAsync(Guid applicationId, string c
{
{ "userName", !string.IsNullOrWhiteSpace(userName) ? userName : user.Email },
{ "companyName", companyName },
{ "declineComment", comment}
{ "declineComment", comment},
{ "helpUrl", _settings.HelpAddress }
};

await _mailingService.SendMails(user.Email, mailParameters, new[] { "EmailRegistrationDeclineTemplate" }).ConfigureAwait(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public class RegistrationSettings
public RegistrationSettings()
{
DocumentTypeIds = null!;
HelpAddress = null!;
}

public int ApplicationsMaxPageSize { get; set; }
Expand All @@ -41,6 +42,9 @@ public RegistrationSettings()
[EnumEnumeration]
[DistinctValues]
public IEnumerable<DocumentTypeId> DocumentTypeIds { get; set; }

[Required(AllowEmptyStrings = false)]
public string HelpAddress { get; set; }
}

public static class RegistrationSettingsExtension
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ private async IAsyncEnumerable<string> CreateOwnCompanyUsersInternalAsync(IEnume
{ "companyName", companyDisplayName },
{ "nameCreatedBy", nameCreatedBy },
{ "url", _settings.Portal.BasePortalAddress },
{ "passwordResendUrl", _settings.Portal.PasswordResendAddress },
};

try
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
using Org.Eclipse.TractusX.Portal.Backend.Administration.Service.Models;
using Org.Eclipse.TractusX.Portal.Backend.Framework.Async;
using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling;
using Org.Eclipse.TractusX.Portal.Backend.Framework.Linq;
using Org.Eclipse.TractusX.Portal.Backend.Framework.Models;
using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess;
using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Models;
Expand Down Expand Up @@ -210,14 +211,25 @@ private async Task<IEnumerable<UserRoleModificationData>> AddRoles(Guid companyU
{
var userRoleRepository = _portalRepositories.GetInstance<IUserRolesRepository>();
var clientRoleNames = iamClientIds.ToDictionary(clientId => clientId, _ => rolesToAdd.Select(x => x.CompanyUserRoleText));
var assignedRoles = await _provisioningManager.AssignClientRolesToCentralUserAsync(iamUserId, clientRoleNames).SingleAsync().ConfigureAwait(false);

var rolesAdded = rolesToAdd.IntersectBy(assignedRoles.Roles, role => role.CompanyUserRoleText).ToList();
foreach (var roleWithId in rolesAdded)
await _provisioningManager.AssignClientRolesToCentralUserAsync(iamUserId, clientRoleNames)
// Assign the roles in keycloak, check if all roles were added foreach client, if not throw an exception with the client and the roles that were not assigned.
.Select(assigned => (
Client: assigned.Client,
UnassingedRoles: rolesToAdd.ExceptBy(assigned.Roles, toAdd => toAdd.CompanyUserRoleText)))
.Where(x => x.UnassingedRoles.Any())
.IfAny(async unassigned =>
throw new ServiceException($"The following roles could not be added to the clients: \n {string.Join(
"\n",
await unassigned
.Select(item => $"Client: {item.Client}, Roles: {string.Join(", ", item.UnassingedRoles.Select(r => r.CompanyUserRoleText))}")
.ToListAsync()
.ConfigureAwait(false))}"))
.ConfigureAwait(false);
foreach (var roleWithId in rolesToAdd)
{
userRoleRepository.CreateIdentityAssignedRole(companyUserId, roleWithId.CompanyUserRoleId);
}
return rolesAdded;
return rolesToAdd;
}

private async Task DeleteRoles(Guid companyUserId, IEnumerable<string> iamClientIds, IEnumerable<UserRoleModificationData> rolesToDelete, string iamUserId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,17 @@ public UserSetting()
{
KeycloakClientID = null!;
BasePortalAddress = null!;
PasswordResendAddress = null!;
}

[Required(AllowEmptyStrings = false)]
public string KeycloakClientID { get; set; }

[Required(AllowEmptyStrings = false)]
public string BasePortalAddress { get; set; }

[Required(AllowEmptyStrings = false)]
public string PasswordResendAddress { get; set; }
}

public class PasswordReset
Expand Down
Loading

0 comments on commit 2279e00

Please sign in to comment.