diff --git a/src/administration/Administration.Service/BusinessLogic/ServiceAccountBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/ServiceAccountBusinessLogic.cs index 9b9352039a..c25ebd9cfe 100644 --- a/src/administration/Administration.Service/BusinessLogic/ServiceAccountBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/ServiceAccountBusinessLogic.cs @@ -326,8 +326,21 @@ public async Task UpdateOwnCompanyServiceAccountDetailsAs portalRepositories.GetInstance().GetOwnCompanyServiceAccountsUntracked(_identityData.CompanyId, clientId, isOwner, filterUserStatusIds)); } - public IAsyncEnumerable GetServiceAccountRolesAsync(string? languageShortName) => - portalRepositories.GetInstance().GetServiceAccountRolesAsync(_identityData.CompanyId, _settings.ClientId, languageShortName ?? Constants.DefaultLanguage); + public async IAsyncEnumerable GetServiceAccountRolesAsync(string? languageShortName) + { + var userRolesRepository = portalRepositories.GetInstance(); + var userRoles = await userRolesRepository.GetUserRoleIdsUntrackedAsync(_settings.DimUserRoles) + .ToListAsync() + .ConfigureAwait(false); + await foreach (var userRole in userRolesRepository.GetServiceAccountRolesAsync( + _identityData.CompanyId, + _settings.ClientId, + userRoles, + languageShortName ?? Constants.DefaultLanguage)) + { + yield return userRole; + } + } public async Task HandleServiceAccountCreationCallback(Guid processId, AuthenticationDetail callbackData) { diff --git a/src/administration/Administration.Service/BusinessLogic/ServiceAccountSettings.cs b/src/administration/Administration.Service/BusinessLogic/ServiceAccountSettings.cs index bef6188d4e..018c6eee62 100644 --- a/src/administration/Administration.Service/BusinessLogic/ServiceAccountSettings.cs +++ b/src/administration/Administration.Service/BusinessLogic/ServiceAccountSettings.cs @@ -43,6 +43,10 @@ public class ServiceAccountSettings [Required] public string AuthServiceUrl { get; set; } = null!; + + [Required] + [DistinctValues("x => x.ClientId")] + public IEnumerable DimUserRoles { get; set; } = null!; } public static class ServiceAccountSettingsExtensions diff --git a/src/administration/Administration.Service/Controllers/ServiceAccountController.cs b/src/administration/Administration.Service/Controllers/ServiceAccountController.cs index c344aff0c2..0cbc17a7f5 100644 --- a/src/administration/Administration.Service/Controllers/ServiceAccountController.cs +++ b/src/administration/Administration.Service/Controllers/ServiceAccountController.cs @@ -19,6 +19,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; using Org.Eclipse.TractusX.Portal.Backend.Administration.Service.BusinessLogic; using Org.Eclipse.TractusX.Portal.Backend.Administration.Service.Models; using Org.Eclipse.TractusX.Portal.Backend.Dim.Library.Models; @@ -36,19 +37,8 @@ namespace Org.Eclipse.TractusX.Portal.Backend.Administration.Service.Controllers [EnvironmentRoute("MVC_ROUTING_BASEPATH", "serviceaccount")] [Produces("application/json")] [Consumes("application/json")] -public class ServiceAccountController : ControllerBase +public class ServiceAccountController(IServiceAccountBusinessLogic logic) : ControllerBase { - private readonly IServiceAccountBusinessLogic _logic; - - /// - /// Creates a new instance of - /// - /// The Service Account Buisness Logic - public ServiceAccountController(IServiceAccountBusinessLogic logic) - { - _logic = logic; - } - /// /// Creates a new technical user / service account with selected role under the same org as the requester /// @@ -66,7 +56,7 @@ public ServiceAccountController(IServiceAccountBusinessLogic logic) [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status404NotFound)] public Task> ExecuteCompanyUserCreation([FromBody] ServiceAccountCreationInfo serviceAccountCreationInfo) => - _logic.CreateOwnCompanyServiceAccountAsync(serviceAccountCreationInfo); + logic.CreateOwnCompanyServiceAccountAsync(serviceAccountCreationInfo); /// /// Deletes the service account with the given id @@ -86,7 +76,7 @@ public Task> ExecuteCompanyUserCreation([From [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status409Conflict)] public async Task DeleteServiceAccount([FromRoute] Guid serviceAccountId) { - await _logic.DeleteOwnCompanyServiceAccountAsync(serviceAccountId).ConfigureAwait(ConfigureAwaitOptions.None); + await logic.DeleteOwnCompanyServiceAccountAsync(serviceAccountId).ConfigureAwait(ConfigureAwaitOptions.None); return NoContent(); } @@ -107,7 +97,7 @@ public async Task DeleteServiceAccount([FromRoute] Guid service [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status404NotFound)] [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status409Conflict)] public Task GetServiceAccountDetails([FromRoute] Guid serviceAccountId) => - _logic.GetOwnCompanyServiceAccountDetailsAsync(serviceAccountId); + logic.GetOwnCompanyServiceAccountDetailsAsync(serviceAccountId); /// /// Updates the service account details with the given id. @@ -134,7 +124,7 @@ public Task GetServiceAccountDetails([FromRout [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status404NotFound)] [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status409Conflict)] public Task PutServiceAccountDetails([FromRoute] Guid serviceAccountId, [FromBody] ServiceAccountEditableDetails serviceAccountDetails) => - _logic.UpdateOwnCompanyServiceAccountDetailsAsync(serviceAccountId, serviceAccountDetails); + logic.UpdateOwnCompanyServiceAccountDetailsAsync(serviceAccountId, serviceAccountDetails); /// /// Resets the service account credentials for the given service account Id. @@ -156,7 +146,7 @@ public Task PutServiceAccountDetails([FromRoute] Guid ser [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status409Conflict)] [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status502BadGateway)] public Task ResetServiceAccountCredentials([FromRoute] Guid serviceAccountId) => - _logic.ResetOwnCompanyServiceAccountSecretAsync(serviceAccountId); + logic.ResetOwnCompanyServiceAccountSecretAsync(serviceAccountId); /// /// Gets the service account data as pagination @@ -176,7 +166,7 @@ public Task ResetServiceAccountCredentials([FromRoute] Gu [Route("owncompany/serviceaccounts")] [ProducesResponseType(typeof(Pagination.Response), StatusCodes.Status200OK)] public Task> GetServiceAccountsData([FromQuery] int page, [FromQuery] int size, [FromQuery] bool? isOwner, [FromQuery] string? clientId, [FromQuery] bool filterForInactive = false, [FromQuery] IEnumerable? userStatus = null) => - _logic.GetOwnCompanyServiceAccountsDataAsync(page, size, clientId, isOwner, filterForInactive, userStatus); + logic.GetOwnCompanyServiceAccountsDataAsync(page, size, clientId, isOwner, filterForInactive, userStatus); /// /// Get all service account roles @@ -190,8 +180,8 @@ public Task ResetServiceAccountCredentials([FromRoute] Gu [Authorize(Policy = PolicyTypes.ValidCompany)] [Route("user/roles")] [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] - public IAsyncEnumerable GetServiceAccountRolesAsync(string? languageShortName = null) => - _logic.GetServiceAccountRolesAsync(languageShortName); + public IAsyncEnumerable GetServiceAccountRolesAsync(string? languageShortName = null) + => logic.GetServiceAccountRolesAsync(languageShortName); /// /// Get all service account roles @@ -206,7 +196,7 @@ public IAsyncEnumerable GetServiceAccountRolesAsync(str [Route("callback/{processId}")] public async Task ServiceAccountCreationCallback([FromRoute] Guid processId, [FromBody] AuthenticationDetail callbackData) { - await _logic.HandleServiceAccountCreationCallback(processId, callbackData).ConfigureAwait(ConfigureAwaitOptions.None); + await logic.HandleServiceAccountCreationCallback(processId, callbackData).ConfigureAwait(ConfigureAwaitOptions.None); return Ok(); } @@ -222,7 +212,7 @@ public async Task ServiceAccountCreationCallback([FromRoute] Guid proc [Route("callback/{processId}/retrigger-create-dim-technical-user")] public async Task RetriggerCreateDimTechnicalUser([FromRoute] Guid processId) { - await _logic.RetriggerDimTechnicalUser(processId, ProcessStepTypeId.RETRIGGER_CREATE_DIM_TECHNICAL_USER).ConfigureAwait(ConfigureAwaitOptions.None); + await logic.RetriggerDimTechnicalUser(processId, ProcessStepTypeId.RETRIGGER_CREATE_DIM_TECHNICAL_USER).ConfigureAwait(ConfigureAwaitOptions.None); return Ok(); } @@ -238,7 +228,7 @@ public async Task RetriggerCreateDimTechnicalUser([FromRoute] Guid pro [Route("callback/{processId}/retrigger-delete-dim-technical-user")] public async Task RetriggerDeleteDimTechnicalUser([FromRoute] Guid processId) { - await _logic.RetriggerDimTechnicalUser(processId, ProcessStepTypeId.RETRIGGER_DELETE_DIM_TECHNICAL_USER).ConfigureAwait(ConfigureAwaitOptions.None); + await logic.RetriggerDimTechnicalUser(processId, ProcessStepTypeId.RETRIGGER_DELETE_DIM_TECHNICAL_USER).ConfigureAwait(ConfigureAwaitOptions.None); return Ok(); } @@ -254,7 +244,7 @@ public async Task RetriggerDeleteDimTechnicalUser([FromRoute] Guid pro [Route("callback/{processId}/delete")] public async Task ServiceAccountDeletionCallback([FromRoute] Guid processId) { - await _logic.HandleServiceAccountDeletionCallback(processId).ConfigureAwait(ConfigureAwaitOptions.None); + await logic.HandleServiceAccountDeletionCallback(processId).ConfigureAwait(ConfigureAwaitOptions.None); return Ok(); } } diff --git a/src/administration/Administration.Service/appsettings.json b/src/administration/Administration.Service/appsettings.json index 9240fc9dbf..e20478b650 100644 --- a/src/administration/Administration.Service/appsettings.json +++ b/src/administration/Administration.Service/appsettings.json @@ -249,7 +249,8 @@ "ClientId": "", "EncryptionConfigIndex": 0, "EncryptionConfigs": [], - "AuthServiceUrl": "" + "AuthServiceUrl": "", + "DimUserRoles": [] }, "Connectors": { "MaxPageSize": 20, diff --git a/src/portalbackend/PortalBackend.DBAccess/Models/UserRoleData.cs b/src/portalbackend/PortalBackend.DBAccess/Models/UserRoleData.cs index 893130dd80..edf9681b12 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Models/UserRoleData.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Models/UserRoleData.cs @@ -35,8 +35,16 @@ public record UserRoleData( public record UserRoleWithDescription( [property: JsonPropertyName("roleId")] Guid UserRoleId, [property: JsonPropertyName("roleName")] string UserRoleText, - [property: JsonPropertyName("roleDescription")] string? RoleDescription); + [property: JsonPropertyName("roleDescription")] string? RoleDescription, + [property: JsonPropertyName("roleType")] UserRoleType RoleType + ); public record UserRoleInformation( [property: JsonPropertyName("roleId")] Guid UserRoleId, [property: JsonPropertyName("roleName")] string UserRoleText); + +public enum UserRoleType +{ + Internal = 1, + External = 2 +} diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/IUserRolesRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/IUserRolesRepository.cs index f389313d5a..510693ae00 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/IUserRolesRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/IUserRolesRepository.cs @@ -48,7 +48,7 @@ public interface IUserRolesRepository IAsyncEnumerable GetAppRolesAsync(Guid offerId, Guid companyId, string languageShortName); IAsyncEnumerable GetClientRolesCompositeAsync(string keyCloakClientId); - IAsyncEnumerable GetServiceAccountRolesAsync(Guid companyId, string clientId, string languageShortName); + IAsyncEnumerable GetServiceAccountRolesAsync(Guid companyId, string clientId, IEnumerable externalRoleIds, string languageShortName); /// /// Gets all user role ids for the given offerId diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/UserRolesRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/UserRolesRepository.cs index 0f4637f53b..c054419fb7 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/UserRolesRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/UserRolesRepository.cs @@ -214,7 +214,7 @@ public IAsyncEnumerable GetClientRolesCompositeAsync(string keyCloakClie .Select(userRole => userRole.UserRoleText) .AsAsyncEnumerable(); - IAsyncEnumerable IUserRolesRepository.GetServiceAccountRolesAsync(Guid companyId, string clientId, string languageShortName) => + IAsyncEnumerable IUserRolesRepository.GetServiceAccountRolesAsync(Guid companyId, string clientId, IEnumerable externalRoleIds, string languageShortName) => _dbContext.UserRoles .AsNoTracking() .Where(ur => ur.Offer!.AppInstances.Any(ai => ai.IamClient!.ClientClientId == clientId) && @@ -225,7 +225,8 @@ IAsyncEnumerable IUserRolesRepository.GetServiceAccount userRole.Id, userRole.UserRoleText, userRole.UserRoleDescriptions.SingleOrDefault(desc => - desc.LanguageShortName == languageShortName)!.Description)) + desc.LanguageShortName == languageShortName)!.Description, + externalRoleIds.Contains(userRole.Id) ? UserRoleType.External : UserRoleType.Internal)) .AsAsyncEnumerable(); /// diff --git a/tests/administration/Administration.Service.Tests/BusinessLogic/ServiceAccountBusinessLogicTests.cs b/tests/administration/Administration.Service.Tests/BusinessLogic/ServiceAccountBusinessLogicTests.cs index 2279da64dd..bb34aefffd 100644 --- a/tests/administration/Administration.Service.Tests/BusinessLogic/ServiceAccountBusinessLogicTests.cs +++ b/tests/administration/Administration.Service.Tests/BusinessLogic/ServiceAccountBusinessLogicTests.cs @@ -640,7 +640,7 @@ public async Task GetServiceAccountRolesAsync_GetsExpectedData() // Arrange var data = _fixture.CreateMany(15); - A.CallTo(() => _userRolesRepository.GetServiceAccountRolesAsync(A._, A._, A._)) + A.CallTo(() => _userRolesRepository.GetServiceAccountRolesAsync(A._, A._, A>._, A._)) .Returns(data.ToAsyncEnumerable()); A.CallTo(() => _portalRepositories.GetInstance()).Returns(_userRolesRepository); @@ -651,7 +651,7 @@ public async Task GetServiceAccountRolesAsync_GetsExpectedData() var result = await sut.GetServiceAccountRolesAsync(null).ToListAsync(); // Assert - A.CallTo(() => _userRolesRepository.GetServiceAccountRolesAsync(_identity.CompanyId, ClientId, A._)).MustHaveHappenedOnceExactly(); + A.CallTo(() => _userRolesRepository.GetServiceAccountRolesAsync(_identity.CompanyId, ClientId, A>._, A._)).MustHaveHappenedOnceExactly(); result.Should().NotBeNull(); result.Should().HaveCount(15); diff --git a/tests/administration/Administration.Service.Tests/appsettings.IntegrationTests.json b/tests/administration/Administration.Service.Tests/appsettings.IntegrationTests.json index 3b35ac7fde..f78e27a42d 100644 --- a/tests/administration/Administration.Service.Tests/appsettings.IntegrationTests.json +++ b/tests/administration/Administration.Service.Tests/appsettings.IntegrationTests.json @@ -246,7 +246,15 @@ "PaddingMode": "PKCS7" } ], - "AuthServiceUrl": "https://auth.test/auth" + "AuthServiceUrl": "https://auth.test/auth", + "DimUserRoles": [ + { + "ClientId": "technical_roles_management", + "UserRoleNames": [ + "Identity Wallet Management" + ] + } + ] }, "Connectors": { "MaxPageSize": 20, diff --git a/tests/portalbackend/PortalBackend.DBAccess.Tests/UserRolesRepositoryTests.cs b/tests/portalbackend/PortalBackend.DBAccess.Tests/UserRolesRepositoryTests.cs index ef531c33b5..38e6421a67 100644 --- a/tests/portalbackend/PortalBackend.DBAccess.Tests/UserRolesRepositoryTests.cs +++ b/tests/portalbackend/PortalBackend.DBAccess.Tests/UserRolesRepositoryTests.cs @@ -19,6 +19,7 @@ using Org.Eclipse.TractusX.Portal.Backend.Framework.Models; using Org.Eclipse.TractusX.Portal.Backend.Framework.Models.Configuration; +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.DBAccess.Tests.Setup; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Enums; @@ -131,11 +132,13 @@ public async Task GetServiceAccountRolesAsync_WithValidData_ReturnsExpected() var sut = await CreateSut(); // Act - var data = await sut.GetServiceAccountRolesAsync(_validCompanyId, ClientId, Constants.DefaultLanguage).ToListAsync(); + var data = await sut.GetServiceAccountRolesAsync(_validCompanyId, ClientId, Enumerable.Repeat(new Guid("607818be-4978-41f4-bf63-fa8d2de51157"), 1), Constants.DefaultLanguage).ToListAsync(); // Assert data.Should().HaveCount(13); data.Should().OnlyHaveUniqueItems(); + data.Where(x => x.RoleType == UserRoleType.Internal).Should().HaveCount(12); + data.Where(x => x.RoleType == UserRoleType.External).Should().ContainSingle(); } #endregion