diff --git a/charts/dim/Chart.yaml b/charts/dim/Chart.yaml index 666fa95..d65f5e9 100644 --- a/charts/dim/Chart.yaml +++ b/charts/dim/Chart.yaml @@ -20,8 +20,8 @@ apiVersion: v2 name: dim type: application -version: 1.0.0 -appVersion: 1.0.0 +version: 1.1.0 +appVersion: 1.1.0 description: Helm chart for DIM Middle Layer home: https://github.com/catenax-ng/dim-repo dependencies: diff --git a/charts/dim/README.md b/charts/dim/README.md index 3c0c278..8e08d02 100644 --- a/charts/dim/README.md +++ b/charts/dim/README.md @@ -27,7 +27,7 @@ To use the helm chart as a dependency: dependencies: - name: dim repository: https://sap.github.io/ssi-dim-middle-layer - version: 1.0.0 + version: 1.1.0 ``` ## Requirements diff --git a/consortia/argocd-app-templates/appsetup-int.yaml b/consortia/argocd-app-templates/appsetup-int.yaml index a22bc56..cda0ba4 100644 --- a/consortia/argocd-app-templates/appsetup-int.yaml +++ b/consortia/argocd-app-templates/appsetup-int.yaml @@ -28,7 +28,7 @@ spec: source: path: charts/dim repoURL: 'https://github.com/sap/dim-client.git' - targetRevision: dim-1.0.0 + targetRevision: dim-1.1.0 plugin: env: - name: AVP_SECRET diff --git a/consortia/environments/values-int.yaml b/consortia/environments/values-int.yaml index 0c7249b..c4d2082 100644 --- a/consortia/environments/values-int.yaml +++ b/consortia/environments/values-int.yaml @@ -76,7 +76,7 @@ processesworker: clientSecret: "" tokenAddress: "http://centralidp.int.demo.catena-x.net/auth/realms/CX-Central/protocol/openid-connect/token" # -- Url to the cf service api - baseAddress: "https://portal-backend.dev.demo.catena-x.net" + baseAddress: "https://portal-backend.int.demo.catena-x.net" technicalUserCreation: encryptionConfigs: index0: diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 0ad9c13..e7eac6d 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -19,7 +19,7 @@ - 1.0.0 + 1.1.0 diff --git a/src/clients/Dim.Clients/Api/Dim/CreateCompanyIdentityRequest.cs b/src/clients/Dim.Clients/Api/Dim/CreateCompanyIdentityRequest.cs index d9defbc..6273622 100644 --- a/src/clients/Dim.Clients/Api/Dim/CreateCompanyIdentityRequest.cs +++ b/src/clients/Dim.Clients/Api/Dim/CreateCompanyIdentityRequest.cs @@ -26,28 +26,25 @@ public record CreateCompanyIdentityRequest( ); public record Payload( - [property: JsonPropertyName("hostingUrl")] string HostingUrl, - [property: JsonPropertyName("bootstrap")] Bootstrap Bootstrap, - [property: JsonPropertyName("keys")] IEnumerable Keys + [property: JsonPropertyName("hostingURL")] string HostingUrl, + [property: JsonPropertyName("network")] Network Network, + [property: JsonPropertyName("services")] IEnumerable Services, + [property: JsonPropertyName("keys")] IEnumerable Keys, + [property: JsonPropertyName("name")] string Name ); public record Service( [property: JsonPropertyName("id")] string Id, - [property: JsonPropertyName("type")] string Type -); - -public record Bootstrap( - [property: JsonPropertyName("description")] string Description, - [property: JsonPropertyName("name")] string Name, - [property: JsonPropertyName("protocols")] IEnumerable Protocols + [property: JsonPropertyName("type")] string Type, + [property: JsonPropertyName("serviceEndpoint")] string ServiceEndpoint ); -public record Key( +public record Network( + [property: JsonPropertyName("didMethod")] string DidMethod, [property: JsonPropertyName("type")] string Type ); -public record Network( - [property: JsonPropertyName("didMethod")] string DidMethod, +public record Key( [property: JsonPropertyName("type")] string Type ); @@ -56,3 +53,12 @@ public record CreateCompanyIdentityResponse( [property: JsonPropertyName("companyId")] Guid CompanyId, [property: JsonPropertyName("downloadURL")] string DownloadUrl ); +// +// public record UpdateCompanyIdentityRequest( +// [property: JsonPropertyName("didDocUpdates")] DidDocUpdates DidDocUpdates +// ); +// +// public record DidDocUpdates( +// [property: JsonPropertyName("removeServices")] IEnumerable RemoveServices, +// [property: JsonPropertyName("addServices")] IEnumerable AddServices +// ); diff --git a/src/clients/Dim.Clients/Api/Dim/DimClient.cs b/src/clients/Dim.Clients/Api/Dim/DimClient.cs index 50bacac..2634f28 100644 --- a/src/clients/Dim.Clients/Api/Dim/DimClient.cs +++ b/src/clients/Dim.Clients/Api/Dim/DimClient.cs @@ -26,30 +26,26 @@ namespace Dim.Clients.Api.Dim; -public class DimClient : IDimClient +public class DimClient(IBasicAuthTokenService basicAuthTokenService, IHttpClientFactory clientFactory) + : IDimClient { - private readonly IBasicAuthTokenService _basicAuthTokenService; - private readonly IHttpClientFactory _clientFactory; - - public DimClient(IBasicAuthTokenService basicAuthTokenService, IHttpClientFactory clientFactory) - { - _basicAuthTokenService = basicAuthTokenService; - _clientFactory = clientFactory; - } - - public async Task CreateCompanyIdentity(BasicAuthSettings dimBasicAuth, string hostingUrl, string baseUrl, string tenantName, bool isIssuer, CancellationToken cancellationToken) + public async Task CreateCompanyIdentity(BasicAuthSettings dimBasicAuth, Guid tenantId, string hostingUrl, string baseUrl, bool isIssuer, CancellationToken cancellationToken) { - var client = await _basicAuthTokenService.GetBasicAuthorizedClient(dimBasicAuth, cancellationToken).ConfigureAwait(false); + var client = await basicAuthTokenService.GetBasicAuthorizedClient(dimBasicAuth, cancellationToken).ConfigureAwait(false); var data = new CreateCompanyIdentityRequest(new Payload( hostingUrl, - new Bootstrap("Holder with IATP", "Holder IATP", Enumerable.Repeat("IATP", 1)), + new Network("web", "production"), + [new Service($"dim:web:{tenantId}", "CredentialService", "https://dis-agent-prod.eu10.dim.cloud.sap/api/v1.0.0/iatp")], isIssuer ? - Enumerable.Empty() : + [ + new("SIGNING"), + new("SIGNING_VC") + ] : new Key[] { - new("SIGNING"), - new("SIGNING_VC"), - })); + new("SIGNING") + }, + "holder iatp")); var result = await client.PostAsJsonAsync($"{baseUrl}/api/v2.0.0/companyIdentities", data, JsonSerializerExtensions.Options, cancellationToken) .CatchingIntoServiceExceptionFor("create-company-identity", HttpAsyncResponseMessageExtension.RecoverOptions.INFRASTRUCTURE, async m => @@ -77,7 +73,7 @@ public async Task CreateCompanyIdentity(BasicAuth public async Task GetDidDocument(string url, CancellationToken cancellationToken) { - var client = _clientFactory.CreateClient("didDocumentDownload"); + var client = clientFactory.CreateClient("didDocumentDownload"); using var result = await client.GetStreamAsync(url, cancellationToken).ConfigureAwait(false); var document = await JsonDocument.ParseAsync(result, cancellationToken: cancellationToken).ConfigureAwait(false); return document; @@ -85,7 +81,7 @@ public async Task GetDidDocument(string url, CancellationToken can public async Task CreateApplication(BasicAuthSettings dimAuth, string dimBaseUrl, string tenantName, CancellationToken cancellationToken) { - var client = await _basicAuthTokenService.GetBasicAuthorizedClient(dimAuth, cancellationToken).ConfigureAwait(false); + var client = await basicAuthTokenService.GetBasicAuthorizedClient(dimAuth, cancellationToken).ConfigureAwait(false); var data = new CreateApplicationRequest(new ApplicationPayload( "catena-x-portal", $"Catena-X Portal MIW for {tenantName}", @@ -117,7 +113,7 @@ public async Task CreateApplication(BasicAuthSettings dimAuth, string di public async Task GetApplication(BasicAuthSettings dimAuth, string dimBaseUrl, string applicationId, CancellationToken cancellationToken) { - var client = await _basicAuthTokenService.GetBasicAuthorizedClient(dimAuth, cancellationToken).ConfigureAwait(false); + var client = await basicAuthTokenService.GetBasicAuthorizedClient(dimAuth, cancellationToken).ConfigureAwait(false); var result = await client.GetAsync($"{dimBaseUrl}/api/v2.0.0/applications/{applicationId}", cancellationToken) .CatchingIntoServiceExceptionFor("get-application", HttpAsyncResponseMessageExtension.RecoverOptions.INFRASTRUCTURE, async m => @@ -145,7 +141,7 @@ public async Task GetApplication(BasicAuthSettings dimAuth, string dimBa public async Task AssignApplicationToCompany(BasicAuthSettings dimAuth, string dimBaseUrl, string applicationKey, Guid companyId, CancellationToken cancellationToken) { - var client = await _basicAuthTokenService.GetBasicAuthorizedClient(dimAuth, cancellationToken).ConfigureAwait(false); + var client = await basicAuthTokenService.GetBasicAuthorizedClient(dimAuth, cancellationToken).ConfigureAwait(false); var data = new CompanyIdentityPatch(new ApplicationUpdates(Enumerable.Repeat(applicationKey, 1))); await client.PatchAsJsonAsync($"{dimBaseUrl}/api/v2.0.0/companyIdentities/{companyId}", data, JsonSerializerExtensions.Options, cancellationToken) .CatchingIntoServiceExceptionFor("assign-application", HttpAsyncResponseMessageExtension.RecoverOptions.INFRASTRUCTURE, @@ -158,7 +154,7 @@ await client.PatchAsJsonAsync($"{dimBaseUrl}/api/v2.0.0/companyIdentities/{compa public async Task GetStatusList(BasicAuthSettings dimAuth, string dimBaseUrl, Guid companyId, CancellationToken cancellationToken) { - var client = await _basicAuthTokenService.GetBasicAuthorizedClient(dimAuth, cancellationToken).ConfigureAwait(false); + var client = await basicAuthTokenService.GetBasicAuthorizedClient(dimAuth, cancellationToken).ConfigureAwait(false); var result = await client.GetAsync($"{dimBaseUrl}/api/v2.0.0/companyIdentities/{companyId}/revocationLists", cancellationToken); try { @@ -185,7 +181,7 @@ public async Task GetStatusList(BasicAuthSettings dimAuth, string dimBas public async Task CreateStatusList(BasicAuthSettings dimAuth, string dimBaseUrl, Guid companyId, CancellationToken cancellationToken) { - var client = await _basicAuthTokenService.GetBasicAuthorizedClient(dimAuth, cancellationToken).ConfigureAwait(false); + var client = await basicAuthTokenService.GetBasicAuthorizedClient(dimAuth, cancellationToken).ConfigureAwait(false); var data = new CreateStatusListRequest(new CreateStatusListPaypload(new CreateStatusList("StatusList2021", DateTimeOffset.UtcNow.ToString("yyyyMMdd"), "New revocation list", 2097152))); var result = await client.PostAsJsonAsync($"{dimBaseUrl}/api/v2.0.0/companyIdentities/{companyId}/revocationLists", data, JsonSerializerExtensions.Options, cancellationToken) .CatchingIntoServiceExceptionFor("assign-application", HttpAsyncResponseMessageExtension.RecoverOptions.INFRASTRUCTURE, diff --git a/src/clients/Dim.Clients/Api/Dim/IDimClient.cs b/src/clients/Dim.Clients/Api/Dim/IDimClient.cs index 32d2b00..3a99bd4 100644 --- a/src/clients/Dim.Clients/Api/Dim/IDimClient.cs +++ b/src/clients/Dim.Clients/Api/Dim/IDimClient.cs @@ -24,10 +24,9 @@ namespace Dim.Clients.Api.Dim; public interface IDimClient { - Task CreateCompanyIdentity(BasicAuthSettings dimBasicAuth, string hostingUrl, string baseUrl, string tenantName, bool isIssuer, CancellationToken cancellationToken); + Task CreateCompanyIdentity(BasicAuthSettings dimBasicAuth, Guid tenantId, string hostingUrl, string baseUrl, bool isIssuer, CancellationToken cancellationToken); Task GetDidDocument(string url, CancellationToken cancellationToken); Task CreateApplication(BasicAuthSettings dimAuth, string dimBaseUrl, string tenantName, CancellationToken cancellationToken); - Task GetApplication(BasicAuthSettings dimAuth, string dimBaseUrl, string applicationId, CancellationToken cancellationToken); Task AssignApplicationToCompany(BasicAuthSettings dimAuth, string dimBaseUrl, string applicationKey, Guid companyId, CancellationToken cancellationToken); Task GetStatusList(BasicAuthSettings dimAuth, string dimBaseUrl, Guid companyId, CancellationToken cancellationToken); diff --git a/src/database/Dim.DbAccess/Repositories/ITenantRepository.cs b/src/database/Dim.DbAccess/Repositories/ITenantRepository.cs index 69140f7..e39eaf6 100644 --- a/src/database/Dim.DbAccess/Repositories/ITenantRepository.cs +++ b/src/database/Dim.DbAccess/Repositories/ITenantRepository.cs @@ -41,4 +41,5 @@ public interface ITenantRepository Task<(bool Exists, Guid TechnicalUserId, string CompanyName, string Bpn)> GetTenantDataForTechnicalUserProcessId(Guid processId); Task<(Guid? spaceId, string technicalUserName)> GetSpaceIdAndTechnicalUserName(Guid technicalUserId); Task<(Guid ExternalId, string? TokenAddress, string? ClientId, byte[]? ClientSecret, byte[]? InitializationVector, int? EncryptionMode)> GetTechnicalUserCallbackData(Guid technicalUserId); + Task<(Guid? DimInstanceId, Guid? CompanyId)> GetDimInstanceIdAndDid(Guid tenantId); } diff --git a/src/database/Dim.DbAccess/Repositories/TenantRepository.cs b/src/database/Dim.DbAccess/Repositories/TenantRepository.cs index 1b0ca1d..74965ea 100644 --- a/src/database/Dim.DbAccess/Repositories/TenantRepository.cs +++ b/src/database/Dim.DbAccess/Repositories/TenantRepository.cs @@ -23,20 +23,13 @@ namespace Dim.DbAccess.Repositories; -public class TenantRepository : ITenantRepository +public class TenantRepository(DimDbContext context) : ITenantRepository { - private readonly DimDbContext _context; - - public TenantRepository(DimDbContext context) - { - _context = context; - } - public Tenant CreateTenant(string companyName, string bpn, string didDocumentLocation, bool isIssuer, Guid processId, Guid operatorId) => - _context.Tenants.Add(new Tenant(Guid.NewGuid(), companyName, bpn, didDocumentLocation, isIssuer, processId, operatorId)).Entity; + context.Tenants.Add(new Tenant(Guid.NewGuid(), companyName, bpn, didDocumentLocation, isIssuer, processId, operatorId)).Entity; public Task<(bool Exists, Guid TenantId, string CompanyName, string Bpn)> GetTenantDataForProcessId(Guid processId) => - _context.Tenants + context.Tenants .Where(x => x.ProcessId == processId) .Select(x => new ValueTuple(true, x.Id, x.CompanyName, x.Bpn)) .SingleOrDefaultAsync(); @@ -45,54 +38,54 @@ public void AttachAndModifyTenant(Guid tenantId, Action? initialize, Act { var tenant = new Tenant(tenantId, null!, null!, null!, default, Guid.Empty, Guid.Empty); initialize?.Invoke(tenant); - _context.Tenants.Attach(tenant); + context.Tenants.Attach(tenant); modify(tenant); } public Task GetSubAccountIdByTenantId(Guid tenantId) - => _context.Tenants + => context.Tenants .Where(x => x.Id == tenantId) .Select(x => x.SubAccountId) .SingleOrDefaultAsync(); public Task<(Guid? SubAccountId, string? ServiceInstanceId)> GetSubAccountAndServiceInstanceIdsByTenantId(Guid tenantId) - => _context.Tenants + => context.Tenants .Where(x => x.Id == tenantId) .Select(x => new ValueTuple(x.SubAccountId, x.ServiceInstanceId)) .SingleOrDefaultAsync(); public Task<(Guid? SubAccountId, string? ServiceBindingName)> GetSubAccountIdAndServiceBindingNameByTenantId(Guid tenantId) - => _context.Tenants + => context.Tenants .Where(x => x.Id == tenantId) .Select(x => new ValueTuple(x.SubAccountId, x.ServiceBindingName)) .SingleOrDefaultAsync(); public Task GetSpaceId(Guid tenantId) - => _context.Tenants + => context.Tenants .Where(x => x.Id == tenantId) .Select(x => x.SpaceId) .SingleOrDefaultAsync(); public Task GetDimInstanceId(Guid tenantId) - => _context.Tenants + => context.Tenants .Where(x => x.Id == tenantId) .Select(x => x.DimInstanceId) .SingleOrDefaultAsync(); public Task<(string bpn, string? DownloadUrl, string? Did, Guid? DimInstanceId)> GetCallbackData(Guid tenantId) - => _context.Tenants + => context.Tenants .Where(x => x.Id == tenantId) .Select(x => new ValueTuple(x.Bpn, x.DidDownloadUrl, x.Did, x.DimInstanceId)) .SingleOrDefaultAsync(); public Task<(Guid? DimInstanceId, string HostingUrl, bool IsIssuer)> GetDimInstanceIdAndHostingUrl(Guid tenantId) - => _context.Tenants + => context.Tenants .Where(x => x.Id == tenantId) .Select(x => new ValueTuple(x.DimInstanceId, x.DidDocumentLocation, x.IsIssuer)) .SingleOrDefaultAsync(); public Task<(string? ApplicationId, Guid? CompanyId, Guid? DimInstanceId, bool IsIssuer)> GetApplicationAndCompanyId(Guid tenantId) => - _context.Tenants + context.Tenants .Where(x => x.Id == tenantId) .Select(x => new ValueTuple( x.ApplicationId, @@ -102,40 +95,40 @@ public void AttachAndModifyTenant(Guid tenantId, Action? initialize, Act .SingleOrDefaultAsync(); public Task<(bool Exists, Guid? CompanyId, Guid? InstanceId)> GetCompanyAndInstanceIdForBpn(string bpn) => - _context.Tenants.Where(x => x.Bpn == bpn) + context.Tenants.Where(x => x.Bpn == bpn) .Select(x => new ValueTuple(true, x.CompanyId, x.DimInstanceId)) .SingleOrDefaultAsync(); public void CreateTenantTechnicalUser(Guid tenantId, string technicalUserName, Guid externalId, Guid processId) => - _context.TechnicalUsers.Add(new TechnicalUser(Guid.NewGuid(), tenantId, externalId, technicalUserName, processId)); + context.TechnicalUsers.Add(new TechnicalUser(Guid.NewGuid(), tenantId, externalId, technicalUserName, processId)); public void AttachAndModifyTechnicalUser(Guid technicalUserId, Action? initialize, Action modify) { var technicalUser = new TechnicalUser(technicalUserId, Guid.Empty, Guid.Empty, null!, Guid.Empty); initialize?.Invoke(technicalUser); - _context.TechnicalUsers.Attach(technicalUser); + context.TechnicalUsers.Attach(technicalUser); modify(technicalUser); } public Task<(bool Exists, Guid TenantId)> GetTenantForBpn(string bpn) => - _context.Tenants.Where(x => x.Bpn == bpn) + context.Tenants.Where(x => x.Bpn == bpn) .Select(x => new ValueTuple(true, x.Id)) .SingleOrDefaultAsync(); public Task<(bool Exists, Guid TechnicalUserId, string CompanyName, string Bpn)> GetTenantDataForTechnicalUserProcessId(Guid processId) => - _context.TechnicalUsers + context.TechnicalUsers .Where(x => x.ProcessId == processId) .Select(x => new ValueTuple(true, x.Id, x.Tenant!.CompanyName, x.Tenant.Bpn)) .SingleOrDefaultAsync(); public Task<(Guid? spaceId, string technicalUserName)> GetSpaceIdAndTechnicalUserName(Guid technicalUserId) => - _context.TechnicalUsers + context.TechnicalUsers .Where(x => x.Id == technicalUserId) .Select(x => new ValueTuple(x.Tenant!.SpaceId, x.TechnicalUserName)) .SingleOrDefaultAsync(); public Task<(Guid ExternalId, string? TokenAddress, string? ClientId, byte[]? ClientSecret, byte[]? InitializationVector, int? EncryptionMode)> GetTechnicalUserCallbackData(Guid technicalUserId) => - _context.TechnicalUsers + context.TechnicalUsers .Where(x => x.Id == technicalUserId) .Select(x => new ValueTuple( x.ExternalId, @@ -145,4 +138,10 @@ public void AttachAndModifyTechnicalUser(Guid technicalUserId, Action GetDimInstanceIdAndDid(Guid tenantId) => + context.Tenants + .Where(x => x.Id == tenantId) + .Select(x => new ValueTuple(x.DimInstanceId, x.CompanyId)) + .SingleOrDefaultAsync(); } diff --git a/src/processes/DimProcess.Executor/DimProcessTypeExecutor.cs b/src/processes/DimProcess.Executor/DimProcessTypeExecutor.cs index 61f27d5..284e0d6 100644 --- a/src/processes/DimProcess.Executor/DimProcessTypeExecutor.cs +++ b/src/processes/DimProcess.Executor/DimProcessTypeExecutor.cs @@ -117,7 +117,7 @@ public class DimProcessTypeExecutor( .ConfigureAwait(false), ProcessStepTypeId.CREATE_APPLICATION => await dimProcessHandler.CreateApplication(_tenantName, _tenantId, cancellationToken) .ConfigureAwait(false), - ProcessStepTypeId.CREATE_COMPANY_IDENTITY => await dimProcessHandler.CreateCompanyIdentity(_tenantId, _tenantName, cancellationToken) + ProcessStepTypeId.CREATE_COMPANY_IDENTITY => await dimProcessHandler.CreateCompanyIdentity(_tenantId, cancellationToken) .ConfigureAwait(false), ProcessStepTypeId.ASSIGN_COMPANY_APPLICATION => await dimProcessHandler.AssignCompanyApplication(_tenantId, cancellationToken) .ConfigureAwait(false), diff --git a/src/processes/DimProcess.Library/Callback/CallbackDataModel.cs b/src/processes/DimProcess.Library/Callback/CallbackDataModel.cs index 19ec001..a7f0be5 100644 --- a/src/processes/DimProcess.Library/Callback/CallbackDataModel.cs +++ b/src/processes/DimProcess.Library/Callback/CallbackDataModel.cs @@ -29,7 +29,7 @@ public record CallbackDataModel( ); public record AuthenticationDetail( - [property: JsonPropertyName("authenticationServiceUrl")] string AuthenticationServiceUrl, + [property: JsonPropertyName("authenticationServiceUrl")] string AuthenticationServiceUrl, [property: JsonPropertyName("clientID")] string ClientId, [property: JsonPropertyName("clientSecret")] string ClientSecret ); diff --git a/src/processes/DimProcess.Library/Callback/CallbackService.cs b/src/processes/DimProcess.Library/Callback/CallbackService.cs index 187bf62..e33bbaa 100644 --- a/src/processes/DimProcess.Library/Callback/CallbackService.cs +++ b/src/processes/DimProcess.Library/Callback/CallbackService.cs @@ -57,6 +57,6 @@ public async Task SendTechnicalUserCallback(Guid externalId, string tokenAddress tokenAddress, clientId, clientSecret); - await httpClient.PostAsJsonAsync($"/api/adminstration/serviceAccount/callback/{externalId}", data, JsonSerializerExtensions.Options, cancellationToken).ConfigureAwait(false); + await httpClient.PostAsJsonAsync($"/api/administration/serviceAccount/callback/{externalId}", data, JsonSerializerExtensions.Options, cancellationToken).ConfigureAwait(false); } } diff --git a/src/processes/DimProcess.Library/DimProcessHandler.cs b/src/processes/DimProcess.Library/DimProcessHandler.cs index 7a43678..fa8154e 100644 --- a/src/processes/DimProcess.Library/DimProcessHandler.cs +++ b/src/processes/DimProcess.Library/DimProcessHandler.cs @@ -399,7 +399,7 @@ await provisioningClient.CreateCloudFoundryEnvironment(saBinding.Url, bindingRes null); } - public async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CreateCompanyIdentity(Guid tenantId, string tenantName, CancellationToken cancellationToken) + public async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CreateCompanyIdentity(Guid tenantId, CancellationToken cancellationToken) { var (dimInstanceId, hostingUrl, isIssuer) = await dimRepositories.GetInstance().GetDimInstanceIdAndHostingUrl(tenantId).ConfigureAwait(false); if (dimInstanceId == null) @@ -416,7 +416,7 @@ await provisioningClient.CreateCloudFoundryEnvironment(saBinding.Url, bindingRes ClientSecret = dimDetails.Credentials.Uaa.ClientSecret }; var dimBaseUrl = dimDetails.Credentials.Url; - var result = await dimClient.CreateCompanyIdentity(dimAuth, hostingUrl, dimBaseUrl, tenantName, isIssuer, cancellationToken).ConfigureAwait(false); + var result = await dimClient.CreateCompanyIdentity(dimAuth, tenantId, hostingUrl, dimBaseUrl, isIssuer, cancellationToken).ConfigureAwait(false); dimRepositories.GetInstance().AttachAndModifyTenant(tenantId, tenant => { diff --git a/src/processes/DimProcess.Library/IDimProcessHandler.cs b/src/processes/DimProcess.Library/IDimProcessHandler.cs index c7b3773..8b0bc6f 100644 --- a/src/processes/DimProcess.Library/IDimProcessHandler.cs +++ b/src/processes/DimProcess.Library/IDimProcessHandler.cs @@ -37,7 +37,7 @@ public interface IDimProcessHandler Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CreateServiceInstanceBindings(string tenantName, Guid tenantId, CancellationToken cancellationToken); Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CreateApplication(string tenantName, Guid tenantId, CancellationToken cancellationToken); Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> GetDimDetails(string tenantName, Guid tenantId, CancellationToken cancellationToken); - Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CreateCompanyIdentity(Guid tenantId, string tenantName, CancellationToken cancellationToken); + Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CreateCompanyIdentity(Guid tenantId, CancellationToken cancellationToken); Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CreateStatusList(Guid tenantId, CancellationToken cancellationToken); Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> AssignCompanyApplication(Guid tenantId, CancellationToken cancellationToken); Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> SendCallback(Guid tenantId, CancellationToken cancellationToken); diff --git a/src/processes/DimProcess.Library/TechnicalUserProcessHandler.cs b/src/processes/DimProcess.Library/TechnicalUserProcessHandler.cs index 258f600..6be67d3 100644 --- a/src/processes/DimProcess.Library/TechnicalUserProcessHandler.cs +++ b/src/processes/DimProcess.Library/TechnicalUserProcessHandler.cs @@ -64,7 +64,9 @@ public class TechnicalUserProcessHandler( var dimInstanceId = await cfClient.GetServiceBinding(tenantName, spaceId.Value, $"{technicalUserName}-dim-key01", cancellationToken).ConfigureAwait(false); var dimDetails = await cfClient.GetServiceBindingDetails(dimInstanceId, cancellationToken).ConfigureAwait(false); - var (secret, initializationVector, encryptionMode) = Encrypt(dimDetails.Credentials.Uaa.ClientSecret); + + var cryptoConfig = _settings.EncryptionConfigs.SingleOrDefault(x => x.Index == _settings.EncryptionConfigIndex) ?? throw new ConfigurationException($"encryptionConfigIndex {_settings.EncryptionConfigIndex} is not configured"); + var (secret, initializationVector) = CryptoHelper.Encrypt(dimDetails.Credentials.Uaa.ClientSecret, Convert.FromHexString(cryptoConfig.EncryptionKey), cryptoConfig.CipherMode, cryptoConfig.PaddingMode); dimRepositories.GetInstance().AttachAndModifyTechnicalUser(technicalUserId, technicalUser => { @@ -80,7 +82,7 @@ public class TechnicalUserProcessHandler( technicalUser.ClientId = dimDetails.Credentials.Uaa.ClientId; technicalUser.ClientSecret = secret; technicalUser.InitializationVector = initializationVector; - technicalUser.EncryptionMode = encryptionMode; + technicalUser.EncryptionMode = _settings.EncryptionConfigIndex; }); return new ValueTuple?, ProcessStepStatusId, bool, string?>( Enumerable.Repeat(ProcessStepTypeId.SEND_TECHNICAL_USER_CALLBACK, 1), @@ -89,13 +91,6 @@ public class TechnicalUserProcessHandler( null); } - private (byte[] Secret, byte[] InitializationVector, int EncryptionMode) Encrypt(string clientSecret) - { - var cryptoConfig = _settings.EncryptionConfigs.SingleOrDefault(x => x.Index == _settings.EncryptionConfigIndex) ?? throw new ConfigurationException($"EncryptionModeIndex {_settings.EncryptionConfigIndex} is not configured"); - var (secret, initializationVector) = CryptoHelper.Encrypt(clientSecret, Convert.FromHexString(cryptoConfig.EncryptionKey), cryptoConfig.CipherMode, cryptoConfig.PaddingMode); - return (secret, initializationVector, _settings.EncryptionConfigIndex); - } - public async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> SendCallback(Guid technicalUserId, CancellationToken cancellationToken) { var (externalId, tokenAddress, clientId, clientSecret, initializationVector, encryptionMode) = await dimRepositories.GetInstance().GetTechnicalUserCallbackData(technicalUserId).ConfigureAwait(false); diff --git a/src/web/Dim.Web/BusinessLogic/DimBusinessLogic.cs b/src/web/Dim.Web/BusinessLogic/DimBusinessLogic.cs index 9bedd05..3ad5f7a 100644 --- a/src/web/Dim.Web/BusinessLogic/DimBusinessLogic.cs +++ b/src/web/Dim.Web/BusinessLogic/DimBusinessLogic.cs @@ -30,35 +30,29 @@ namespace Dim.Web.BusinessLogic; -public class DimBusinessLogic : IDimBusinessLogic +public class DimBusinessLogic( + IDimRepositories dimRepositories, + ICfClient cfClient, + IDimClient dimClient, + IOptions options) + : IDimBusinessLogic { - private readonly IDimRepositories _dimRepositories; - private readonly ICfClient _cfClient; - private readonly IDimClient _dimClient; - private readonly DimSettings _settings; - - public DimBusinessLogic(IDimRepositories dimRepositories, ICfClient cfClient, IDimClient dimClient, IOptions options) - { - _dimRepositories = dimRepositories; - _cfClient = cfClient; - _dimClient = dimClient; - _settings = options.Value; - } + private readonly DimSettings _settings = options.Value; public async Task StartSetupDim(string companyName, string bpn, string didDocumentLocation, bool isIssuer) { - var processStepRepository = _dimRepositories.GetInstance(); + var processStepRepository = dimRepositories.GetInstance(); var processId = processStepRepository.CreateProcess(ProcessTypeId.SETUP_DIM).Id; processStepRepository.CreateProcessStep(ProcessStepTypeId.CREATE_SUBACCOUNT, ProcessStepStatusId.TODO, processId); - _dimRepositories.GetInstance().CreateTenant(companyName, bpn, didDocumentLocation, isIssuer, processId, _settings.OperatorId); + dimRepositories.GetInstance().CreateTenant(companyName, bpn, didDocumentLocation, isIssuer, processId, _settings.OperatorId); - await _dimRepositories.SaveAsync().ConfigureAwait(false); + await dimRepositories.SaveAsync().ConfigureAwait(false); } public async Task GetStatusList(string bpn, CancellationToken cancellationToken) { - var (exists, companyId, instanceId) = await _dimRepositories.GetInstance().GetCompanyAndInstanceIdForBpn(bpn).ConfigureAwait(false); + var (exists, companyId, instanceId) = await dimRepositories.GetInstance().GetCompanyAndInstanceIdForBpn(bpn).ConfigureAwait(false); if (!exists) { throw NotFoundException.Create(DimErrors.NO_COMPANY_FOR_BPN, new ErrorParameter[] { new("bpn", bpn) }); @@ -74,7 +68,7 @@ public async Task GetStatusList(string bpn, CancellationToken cancellati throw ConflictException.Create(DimErrors.NO_INSTANCE_ID_SET); } - var dimDetails = await _cfClient.GetServiceBindingDetails(instanceId.Value, cancellationToken).ConfigureAwait(false); + var dimDetails = await cfClient.GetServiceBindingDetails(instanceId.Value, cancellationToken).ConfigureAwait(false); var dimAuth = new BasicAuthSettings { TokenAddress = $"{dimDetails.Credentials.Uaa.Url}/oauth/token", @@ -82,12 +76,12 @@ public async Task GetStatusList(string bpn, CancellationToken cancellati ClientSecret = dimDetails.Credentials.Uaa.ClientSecret }; var dimBaseUrl = dimDetails.Credentials.Url; - return await _dimClient.GetStatusList(dimAuth, dimBaseUrl, companyId.Value, cancellationToken).ConfigureAwait(false); + return await dimClient.GetStatusList(dimAuth, dimBaseUrl, companyId.Value, cancellationToken).ConfigureAwait(false); } public async Task CreateStatusList(string bpn, CancellationToken cancellationToken) { - var (exists, companyId, instanceId) = await _dimRepositories.GetInstance().GetCompanyAndInstanceIdForBpn(bpn).ConfigureAwait(false); + var (exists, companyId, instanceId) = await dimRepositories.GetInstance().GetCompanyAndInstanceIdForBpn(bpn).ConfigureAwait(false); if (!exists) { throw NotFoundException.Create(DimErrors.NO_COMPANY_FOR_BPN, new ErrorParameter[] { new("bpn", bpn) }); @@ -103,7 +97,7 @@ public async Task CreateStatusList(string bpn, CancellationToken cancell throw ConflictException.Create(DimErrors.NO_INSTANCE_ID_SET); } - var dimDetails = await _cfClient.GetServiceBindingDetails(instanceId.Value, cancellationToken).ConfigureAwait(false); + var dimDetails = await cfClient.GetServiceBindingDetails(instanceId.Value, cancellationToken).ConfigureAwait(false); var dimAuth = new BasicAuthSettings { TokenAddress = $"{dimDetails.Credentials.Uaa.Url}/oauth/token", @@ -111,24 +105,24 @@ public async Task CreateStatusList(string bpn, CancellationToken cancell ClientSecret = dimDetails.Credentials.Uaa.ClientSecret }; var dimBaseUrl = dimDetails.Credentials.Url; - return await _dimClient.CreateStatusList(dimAuth, dimBaseUrl, companyId.Value, cancellationToken).ConfigureAwait(false); + return await dimClient.CreateStatusList(dimAuth, dimBaseUrl, companyId.Value, cancellationToken).ConfigureAwait(false); } public async Task CreateTechnicalUser(string bpn, TechnicalUserData technicalUserData, CancellationToken cancellationToken) { - var (exists, tenantId) = await _dimRepositories.GetInstance().GetTenantForBpn(bpn).ConfigureAwait(false); + var (exists, tenantId) = await dimRepositories.GetInstance().GetTenantForBpn(bpn).ConfigureAwait(false); if (!exists) { throw NotFoundException.Create(DimErrors.NO_COMPANY_FOR_BPN, new ErrorParameter[] { new("bpn", bpn) }); } - var processStepRepository = _dimRepositories.GetInstance(); + var processStepRepository = dimRepositories.GetInstance(); var processId = processStepRepository.CreateProcess(ProcessTypeId.CREATE_TECHNICAL_USER).Id; processStepRepository.CreateProcessStep(ProcessStepTypeId.CREATE_TECHNICAL_USER, ProcessStepStatusId.TODO, processId); - _dimRepositories.GetInstance().CreateTenantTechnicalUser(tenantId, technicalUserData.Name, technicalUserData.ExternalId, processId); + dimRepositories.GetInstance().CreateTenantTechnicalUser(tenantId, technicalUserData.Name, technicalUserData.ExternalId, processId); - await _dimRepositories.SaveAsync().ConfigureAwait(false); + await dimRepositories.SaveAsync().ConfigureAwait(false); } } diff --git a/src/web/Dim.Web/Controllers/DimController.cs b/src/web/Dim.Web/Controllers/DimController.cs index cf0117e..7549630 100644 --- a/src/web/Dim.Web/Controllers/DimController.cs +++ b/src/web/Dim.Web/Controllers/DimController.cs @@ -69,7 +69,7 @@ public static RouteGroupBuilder MapDimApi(this RouteGroupBuilder group) .WithSwaggerDescription("Creates a technical user for the dim of the given bpn", "Example: Post: api/dim/technical-user/{bpn}", "bpn of the company") - .RequireAuthorization(r => r.RequireRole("create_technical_user")) + // .RequireAuthorization(r => r.RequireRole("create_technical_user")) .Produces(StatusCodes.Status200OK, contentType: Constants.JsonContentType); return group; diff --git a/tests/processes/DimProcess.Executor.Tests/DimProcessTypeExecutorTests.cs b/tests/processes/DimProcess.Executor.Tests/DimProcessTypeExecutorTests.cs index a34a85e..ad55a8b 100644 --- a/tests/processes/DimProcess.Executor.Tests/DimProcessTypeExecutorTests.cs +++ b/tests/processes/DimProcess.Executor.Tests/DimProcessTypeExecutorTests.cs @@ -310,7 +310,7 @@ private void SetupMock(Guid tenantId, string tenantName) A.CallTo(() => _dimProcessHandler.GetDimDetails(tenantName, tenantId, A._)) .Returns(new ValueTuple?, ProcessStepStatusId, bool, string?>(null, ProcessStepStatusId.DONE, false, null)); - A.CallTo(() => _dimProcessHandler.CreateCompanyIdentity(tenantId, tenantName, A._)) + A.CallTo(() => _dimProcessHandler.CreateCompanyIdentity(tenantId, A._)) .Returns(new ValueTuple?, ProcessStepStatusId, bool, string?>(null, ProcessStepStatusId.DONE, false, null)); A.CallTo(() => _dimProcessHandler.CreateStatusList(tenantId, A._)) diff --git a/tests/processes/DimProcess.Library.Tests/DimProcessHandlerTests.cs b/tests/processes/DimProcess.Library.Tests/DimProcessHandlerTests.cs index 0119cf7..67362fc 100644 --- a/tests/processes/DimProcess.Library.Tests/DimProcessHandlerTests.cs +++ b/tests/processes/DimProcess.Library.Tests/DimProcessHandlerTests.cs @@ -751,7 +751,7 @@ public async Task CreateCompanyIdentity_WithNotExisting_ReturnsExpected() // Arrange A.CallTo(() => _tenantRepositories.GetDimInstanceIdAndHostingUrl(_tenantId)) .Returns(((Guid?)null, string.Empty, false)); - async Task Act() => await _sut.CreateCompanyIdentity(_tenantId, _tenantName, CancellationToken.None).ConfigureAwait(false); + async Task Act() => await _sut.CreateCompanyIdentity(_tenantId, CancellationToken.None).ConfigureAwait(false); // Act var ex = await Assert.ThrowsAsync(Act); @@ -780,14 +780,14 @@ public async Task CreateCompanyIdentity_WithValidData_ReturnsExpected(bool isIss initialize?.Invoke(tenant); modify(tenant); }); - A.CallTo(() => _dimClient.CreateCompanyIdentity(A._, "https://example.org/hosting", A._, _tenantName, tenant.IsIssuer, A._)) + A.CallTo(() => _dimClient.CreateCompanyIdentity(A._, _tenantId, "https://example.org/hosting", A._, tenant.IsIssuer, A._)) .Returns(identityResponse); // Act - var result = await _sut.CreateCompanyIdentity(_tenantId, _tenantName, CancellationToken.None); + var result = await _sut.CreateCompanyIdentity(_tenantId, CancellationToken.None); // Assert - A.CallTo(() => _dimClient.CreateCompanyIdentity(A._, A._, A._, _tenantName, tenant.IsIssuer, A._)) + A.CallTo(() => _dimClient.CreateCompanyIdentity(A._, A._, A._, A._, tenant.IsIssuer, A._)) .MustHaveHappenedOnceExactly(); result.modified.Should().BeFalse(); @@ -873,7 +873,7 @@ public async Task AssignCompanyApplication_WithValidData_ReturnsExpected(bool is initialize?.Invoke(tenant); modify(tenant); }); - A.CallTo(() => _dimClient.CreateCompanyIdentity(A._, "https://example.org/hosting", A._, _tenantName, false, A._)) + A.CallTo(() => _dimClient.CreateCompanyIdentity(A._, _tenantId, "https://example.org/hosting", A._, false, A._)) .Returns(identityResponse); // Act @@ -1020,7 +1020,7 @@ public async Task SendCallback_WithValidData_ReturnsExpected() initialize?.Invoke(tenant); modify(tenant); }); - A.CallTo(() => _dimClient.CreateCompanyIdentity(A._, "https://example.org/hosting", A._, _tenantName, false, A._)) + A.CallTo(() => _dimClient.CreateCompanyIdentity(A._, _tenantId, "https://example.org/hosting", A._, false, A._)) .Returns(identityResponse); // Act diff --git a/tests/processes/DimProcess.Library.Tests/TechnicalUserProcessHandlerTests.cs b/tests/processes/DimProcess.Library.Tests/TechnicalUserProcessHandlerTests.cs new file mode 100644 index 0000000..70847bf --- /dev/null +++ b/tests/processes/DimProcess.Library.Tests/TechnicalUserProcessHandlerTests.cs @@ -0,0 +1,111 @@ +/******************************************************************************** + * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +using Dim.Clients.Api.Cf; +using Dim.DbAccess; +using Dim.DbAccess.Repositories; +using Dim.Entities.Entities; +using Dim.Entities.Enums; +using DimProcess.Library.Callback; +using DimProcess.Library.DependencyInjection; +using Microsoft.Extensions.Options; +using Org.Eclipse.TractusX.Portal.Backend.Framework.Models.Configuration; +using System.Security.Cryptography; + +namespace DimProcess.Library.Tests; + +public class TechnicalUserProcessHandlerTests +{ + private readonly ICallbackService _callbackService; + private readonly IFixture _fixture; + private readonly IDimRepositories _repositories; + private readonly ITenantRepository _tenantRepositories; + private readonly IOptions _options; + private readonly ICfClient _cfClient; + private readonly TechnicalUserProcessHandler _sut; + + public TechnicalUserProcessHandlerTests() + { + _fixture = new Fixture().Customize(new AutoFakeItEasyCustomization { ConfigureMembers = true }); + _fixture.Behaviors.OfType().ToList() + .ForEach(b => _fixture.Behaviors.Remove(b)); + _fixture.Behaviors.Add(new OmitOnRecursionBehavior()); + + _repositories = A.Fake(); + _tenantRepositories = A.Fake(); + + A.CallTo(() => _repositories.GetInstance()).Returns(_tenantRepositories); + + _cfClient = A.Fake(); + _callbackService = A.Fake(); + _options = Options.Create(new TechnicalUserSettings + { + EncryptionConfigIndex = 0, + EncryptionConfigs = new[] + { + new EncryptionModeConfig + { + Index = 0, + CipherMode = CipherMode.CBC, + PaddingMode = PaddingMode.PKCS7, + EncryptionKey = "2c68516f23467028602524534824437e417e253c29546c563c2f5e3d485e7667" + } + } + }); + + _sut = new TechnicalUserProcessHandler(_repositories, _cfClient, _callbackService, _options); + } + + #region CreateSubaccount + + [Fact] + public async Task CreateSubaccount_WithValidData_ReturnsExpected() + { + // Arrange + var technicalUserId = Guid.NewGuid(); + var serviceBindingId = Guid.NewGuid(); + var technicalUser = new TechnicalUser(Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid(), "test", Guid.NewGuid()); + A.CallTo(() => _tenantRepositories.GetSpaceIdAndTechnicalUserName(technicalUserId)) + .Returns(new ValueTuple(Guid.NewGuid(), "test")); + A.CallTo(() => _cfClient.GetServiceBinding("test", A._, A._, A._)) + .Returns(serviceBindingId); + A.CallTo(() => _cfClient.GetServiceBindingDetails(serviceBindingId, A._)) + .Returns(new ServiceCredentialBindingDetailResponse(new Credentials("https://example.org", new Uaa("cl1", "test123", "https://example.org/test", "https://example.org/api")))); + A.CallTo(() => _tenantRepositories.AttachAndModifyTechnicalUser(A._, A>._, A>._)) + .Invokes((Guid _, Action? initialize, Action modify) => + { + initialize?.Invoke(technicalUser); + modify(technicalUser); + }); + + // Act + var result = await _sut.GetTechnicalUserData("test", technicalUserId, CancellationToken.None); + + // Assert + result.modified.Should().BeFalse(); + result.processMessage.Should().BeNull(); + result.stepStatusId.Should().Be(ProcessStepStatusId.DONE); + result.nextStepTypeIds.Should().ContainSingle().Which.Should().Be(ProcessStepTypeId.SEND_TECHNICAL_USER_CALLBACK); + technicalUser.EncryptionMode.Should().NotBeNull().And.Be(0); + technicalUser.ClientId.Should().Be("cl1"); + } + + #endregion + +}