Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions charts/dim/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ processesworker:
# -- Sets the type of the status list which will be created for the issuer wallet
# -- valid types are: StatusList2021, BitstringStatusList
statusListType: "StatusList2021"
issuerDid: ""
issuerName: ""
provisioning:
clientId: ""
clientSecret: ""
Expand Down
2 changes: 2 additions & 0 deletions environments/helm-values/values-int.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ processesworker:
default: "Debug"
dim:
applicationName: "catena-x-portal"
issuerDid: ""
issuerName: ""
provisioning:
clientId: "<path:portal/data/dim/int/provisioning#clientId>"
clientSecret: "<path:portal/data/dim/int/provisioning#clientSecret>"
Expand Down
2 changes: 2 additions & 0 deletions environments/helm-values/values-stable.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ processesworker:
default: "Debug"
dim:
applicationName: "catena-x-portal"
issuerDid: ""
issuerName: ""
provisioning:
clientId: "<path:portal/data/dim/stable/provisioning#clientId>"
clientSecret: "<path:portal/data/dim/stable/provisioning#clientSecret>"
Expand Down
28 changes: 28 additions & 0 deletions src/clients/Dim.Clients/Api/Dim/DimClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,34 @@ public async Task<CompanyData> GetCompanyData(BasicAuthSettings dimAuth, string
}
}

public async Task<string> UpdateCompanyStatus(BasicAuthSettings dimAuth, string dimBaseUrl, Guid companyId, CancellationToken cancellationToken)
{
var client = await basicAuthTokenService.GetBasicAuthorizedClient<DimClient>(dimAuth, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None);
var result = await client.PatchAsync($"{dimBaseUrl}/api/v2.0.0/companyIdentities/{companyId}/status", null, cancellationToken)
.CatchingIntoServiceExceptionFor("update-company-status", HttpAsyncResponseMessageExtension.RecoverOptions.INFRASTRUCTURE,
async m =>
{
var message = await m.Content.ReadAsStringAsync().ConfigureAwait(ConfigureAwaitOptions.None);
return (false, message);
}).ConfigureAwait(false);
try
{
var response = await result.Content
.ReadFromJsonAsync<CompanyStatusResponse>(JsonSerializerExtensions.Options, cancellationToken)
.ConfigureAwait(ConfigureAwaitOptions.None);
if (response?.Status == null || response.Status != "successful")
{
throw new ServiceException("Company status update failed", true);
}

return response.Status;
}
catch (JsonException je)
{
throw new ServiceException(je.Message);
}
}

public async Task<string> GetStatusList(BasicAuthSettings dimAuth, string dimBaseUrl, Guid companyId, StatusListType statusListType, CancellationToken cancellationToken)
{
var client = await basicAuthTokenService.GetBasicAuthorizedClient<DimClient>(dimAuth, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None);
Expand Down
1 change: 1 addition & 0 deletions src/clients/Dim.Clients/Api/Dim/IDimClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ namespace Dim.Clients.Api.Dim;
public interface IDimClient
{
Task<CompanyData> GetCompanyData(BasicAuthSettings dimAuth, string dimBaseUrl, string tenantName, string application, CancellationToken cancellationToken);
Task<string> UpdateCompanyStatus(BasicAuthSettings dimAuth, string dimBaseUrl, Guid companyId, CancellationToken cancellationToken);
Task<string> GetStatusList(BasicAuthSettings dimAuth, string dimBaseUrl, Guid companyId, StatusListType statusListType, CancellationToken cancellationToken);
Task<string> CreateStatusList(BasicAuthSettings dimAuth, string dimBaseUrl, Guid companyId, StatusListType statusListType, CancellationToken cancellationToken);
}
5 changes: 5 additions & 0 deletions src/clients/Dim.Clients/Api/Dim/Models/CompanyData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,8 @@ public record CompanyData(
[property: JsonPropertyName("id")] Guid CompanyId,
[property: JsonPropertyName("downloadURL")] string DownloadUrl
);

public record CompanyStatusResponse(
[property: JsonPropertyName("operation")] string Operation,
[property: JsonPropertyName("status")] string Status
);
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (c) 2024 BMW Group AG
* Copyright (c) 2025 BMW Group AG
* 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
Expand Down
2 changes: 1 addition & 1 deletion src/clients/Dim.Clients/Api/Div/IProvisioningClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ namespace Dim.Clients.Api.Div;

public interface IProvisioningClient
{
Task<Guid> CreateOperation(Guid customerId, string customerName, string applicationName, string companyName, string didDocumentLocation, bool isIssuer, CancellationToken cancellationToken);
Task<Guid> CreateOperation(Guid customerId, string customerName, string applicationName, string companyName, string didDocumentLocation, bool isIssuer, string issuerDid, string issuerName, CancellationToken cancellationToken);
Task<OperationResponse> GetOperation(Guid operationId, CancellationToken cancellationToken);
Task<Guid> CreateServiceKey(string technicalUserName, Guid walletId, CancellationToken cancellationToken);
Task<Guid?> DeleteServiceKey(Guid walletId, Guid serviceKeyId, CancellationToken cancellationToken);
Expand Down
5 changes: 3 additions & 2 deletions src/clients/Dim.Clients/Api/Div/Models/OperationRequest.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (c) 2024 BMW Group AG
* Copyright (c) 2025 BMW Group AG
* 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
Expand Down Expand Up @@ -48,7 +48,7 @@ public record WalletApplication(
public record ApplicationCompany(
[property: JsonPropertyName("name")] string Name,
[property: JsonPropertyName("hostingURL")] string HostingUrl,
[property: JsonPropertyName("services")] IEnumerable<ApplicationCompanyService> Services,
[property: JsonPropertyName("protocols")] string[] Protocols,
[property: JsonPropertyName("keys")] IEnumerable<ApplicationCompanyKey> Keys
);

Expand All @@ -64,6 +64,7 @@ public record ApplicationCompanyKey(
public record TrustedIssuer(
[property: JsonPropertyName("name")] string Name,
[property: JsonPropertyName("did")] string Did,
[property: JsonPropertyName("credentialTypes")] string[] CredentialTypes,
[property: JsonPropertyName("ignoreMissingHashlist")] bool IgnoreMissingHashlist
);

Expand Down
20 changes: 15 additions & 5 deletions src/clients/Dim.Clients/Api/Div/ProvisioningClient.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (c) 2024 BMW Group AG
* Copyright (c) 2025 BMW Group AG
* 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
Expand Down Expand Up @@ -35,7 +35,7 @@ public class ProvisioningClient(IBasicAuthTokenService basicAuthTokenService, IO
{
private readonly ProvisioningSettings _settings = options.Value;

public async Task<Guid> CreateOperation(Guid customerId, string customerName, string applicationName, string companyName, string didDocumentLocation, bool isIssuer, CancellationToken cancellationToken)
public async Task<Guid> CreateOperation(Guid customerId, string customerName, string applicationName, string companyName, string didDocumentLocation, bool isIssuer, string issuerDid, string issuerName, CancellationToken cancellationToken)
{
var data = new OperationCreationRequest(
"provision",
Expand All @@ -50,7 +50,7 @@ public async Task<Guid> CreateOperation(Guid customerId, string customerName, st
Enumerable.Repeat(new ApplicationCompany(
companyName,
didDocumentLocation,
[new ApplicationCompanyService("CredentialService", "https://dis-agent-prod.eu10.dim.cloud.sap/api/v1.0.0/iatp")],
isIssuer ? ["dcp-issuer", "dcp-holder"] : ["dcp-holder"],
isIssuer ?
[
new("SIGNING"),
Expand All @@ -61,7 +61,17 @@ [new ApplicationCompanyService("CredentialService", "https://dis-agent-prod.eu10
new("SIGNING")
}
), 1),
Enumerable.Empty<TrustedIssuer>()
isIssuer
? Enumerable.Empty<TrustedIssuer>()
: new[]
{
new TrustedIssuer(
issuerName,
issuerDid,
new[] { "BpnCredential", "MembershipCredential", "DataExchangeGovernanceCredential" },
false
)
}
), 1)
)
)
Expand Down Expand Up @@ -139,7 +149,7 @@ public async Task<Guid> CreateServiceKey(string technicalUserName, Guid walletId
new ServiceKeyCreationPayloadData(
walletId,
technicalUserName,
new ServiceKeyParameter(new[] { "IatpOperations", "ReadCompanyIdentity", "ResolveDID" })
new ServiceKeyParameter(new[] { "DcpOperations", "ReadCompanyIdentity", "ResolveDID" })
)
);
var client = await basicAuthTokenService
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public interface ITenantRepository
Task<(string? BaseUrl, WalletData WalletData)> GetCompanyRequestData(Guid tenantId);
Task<(bool Exists, Guid? CompanyId, string? BaseUrl, WalletData WalletData)> GetCompanyAndWalletDataForBpn(string bpn);
Task<(Guid? CompanyId, string? BaseUrl, WalletData WalletData)> GetStatusListCreationData(Guid tenantId);
Task<(string Bpn, string? BaseUrl, WalletData WalletData, string? Did, string? DownloadUrl)> GetCallbackData(Guid tenantId);
Task<(string Bpn, Guid? CompanyId, string? BaseUrl, WalletData WalletData, string? Did, string? DownloadUrl)> GetCallbackData(Guid tenantId);
Task<(string? DownloadUrl, bool IsIssuer)> GetDownloadUrlAndIsIssuer(Guid tenantId);
Task<ProcessData?> GetWalletProcessForTenant(string bpn, string companyName);
}
5 changes: 3 additions & 2 deletions src/database/Dim.DbAccess/Repositories/TenantRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,12 @@ public Task<bool> IsTenantExisting(string companyName, string bpn) =>
)))
.SingleOrDefaultAsync();

public Task<(string Bpn, string? BaseUrl, WalletData WalletData, string? Did, string? DownloadUrl)> GetCallbackData(Guid tenantId) =>
public Task<(string Bpn, Guid? CompanyId, string? BaseUrl, WalletData WalletData, string? Did, string? DownloadUrl)> GetCallbackData(Guid tenantId) =>
dbContext.Tenants
.Where(x => x.Id == tenantId)
.Select(x => new ValueTuple<string, string?, WalletData, string?, string?>(
.Select(x => new ValueTuple<string, Guid?, string?, WalletData, string?, string?>(
x.Bpn,
x.CompanyId,
x.BaseUrl,
new WalletData(
x.TokenAddress,
Expand Down
7 changes: 6 additions & 1 deletion src/processes/DimProcess.Library/Callback/CallbackService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
using Microsoft.Extensions.Options;
using Org.Eclipse.TractusX.Portal.Backend.Framework.HttpClientExtensions;
using Org.Eclipse.TractusX.Portal.Backend.Framework.Token;
using System.Net;
using System.Net.Http.Json;
using System.Text.Json;

Expand All @@ -42,8 +43,12 @@ public async Task SendCallback(string bpn, AuthenticationDetail authenticationDe
didDocument,
authenticationDetail
);
ValueTask<(bool, string?)> CustomErrorHandling(HttpResponseMessage errorResponse) => new(
(errorResponse.StatusCode == HttpStatusCode.Conflict,
null));

await httpClient.PostAsJsonAsync($"/api/administration/registration/dim/{bpn}", data, JsonSerializerExtensions.Options, cancellationToken)
.CatchingIntoServiceExceptionFor("send-callback", HttpAsyncResponseMessageExtension.RecoverOptions.INFRASTRUCTURE)
.CatchingIntoServiceExceptionFor("send-callback", HttpAsyncResponseMessageExtension.RecoverOptions.INFRASTRUCTURE, CustomErrorHandling)
.ConfigureAwait(false);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,10 @@ public class DimHandlerSettings

[Required]
public StatusListType StatusListType { get; set; }

[Required]
public string IssuerDid { get; set; } = null!;

[Required]
public string IssuerName { get; set; } = null!;
}
19 changes: 17 additions & 2 deletions src/processes/DimProcess.Library/DimProcessHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public class DimProcessHandler(
throw new UnexpectedConditionException("DidDocumentLocation must always be set");
}

var operationId = await provisioningClient.CreateOperation(tenantId, tenantName, _settings.ApplicationName, tenantName, didDocumentLocation, isIssuer, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None);
var operationId = await provisioningClient.CreateOperation(tenantId, tenantName, _settings.ApplicationName, tenantName, didDocumentLocation, isIssuer, _settings.IssuerDid, _settings.IssuerName, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None);
tenantRepository.AttachAndModifyTenant(tenantId, t => t.OperationId = null, t => t.OperationId = operationId);

return new ValueTuple<IEnumerable<ProcessStepTypeId>?, ProcessStepStatusId, bool, string?>(
Expand Down Expand Up @@ -226,7 +226,7 @@ public class DimProcessHandler(

public async Task<(IEnumerable<ProcessStepTypeId>? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> SendCallback(Guid tenantId, CancellationToken cancellationToken)
{
var (bpn, baseUrl, walletData, did, downloadUrl) = await dimRepositories.GetInstance<ITenantRepository>().GetCallbackData(tenantId).ConfigureAwait(ConfigureAwaitOptions.None);
var (bpn, companyId, baseUrl, walletData, did, downloadUrl) = await dimRepositories.GetInstance<ITenantRepository>().GetCallbackData(tenantId).ConfigureAwait(ConfigureAwaitOptions.None);
var (tokenAddress, clientId, clientSecret, initializationVector, encryptionMode) = walletData.ValidateData();
if (baseUrl == null)
{
Expand All @@ -243,13 +243,28 @@ public class DimProcessHandler(
throw new UnexpectedConditionException("DownloadUrl must always be set");
}

if (companyId == null)
{
throw new UnexpectedConditionException("CompanyId must not be null");
Comment thread
Siegfriedk marked this conversation as resolved.
Outdated
}

var cryptoHelper = _settings.EncryptionConfigs.GetCryptoHelper(encryptionMode);
var secret = cryptoHelper.Decrypt(clientSecret, initializationVector);

var didDocument = await GetDidDocument(downloadUrl, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None);
var uaa = new AuthenticationDetail(tokenAddress, clientId, secret);
await callbackService.SendCallback(bpn, uaa, didDocument, did, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None);

var dimAuth = new BasicAuthSettings
{
TokenAddress = $"{tokenAddress}/oauth/token",
ClientId = clientId,
ClientSecret = secret
};
await dimClient
.UpdateCompanyStatus(dimAuth, baseUrl, companyId.Value, cancellationToken)
.ConfigureAwait(ConfigureAwaitOptions.None);

return new ValueTuple<IEnumerable<ProcessStepTypeId>?, ProcessStepStatusId, bool, string?>(
null,
ProcessStepStatusId.DONE,
Expand Down
4 changes: 3 additions & 1 deletion src/processes/Processes.Worker/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@
"PaddingMode": ""
}
],
"StatusListType": ""
"StatusListType": "",
"IssuerDid": "",
"IssuerName": ""
},
"Processes": {
"LockExpirySeconds": 300
Expand Down
6 changes: 3 additions & 3 deletions tests/clients/Dim.Clients.Tests/ProvisioningClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public async Task CreateOperation_WithNoContent_ThrowsServiceException()
// Arrange
_fixture.ConfigureTokenServiceFixture<ProvisioningClient>(new HttpResponseMessage(HttpStatusCode.OK));
var sut = _fixture.Create<ProvisioningClient>();
Task Act() => sut.CreateOperation(Guid.NewGuid(), "corp", "application1", "test corp", "https://example.org/did", false, CancellationToken.None);
Task Act() => sut.CreateOperation(Guid.NewGuid(), "corp", "application1", "test corp", "https://example.org/did", false, "did:web:example", "example", CancellationToken.None);

// Act
var ex = await Assert.ThrowsAsync<ServiceException>(Act);
Expand All @@ -101,7 +101,7 @@ public async Task CreateOperation_WithNoSpaceLeft_ThrowsConflictException()
// Arrange
_fixture.ConfigureTokenServiceFixture<ProvisioningClient>(new HttpResponseMessage(HttpStatusCode.BadRequest));
var sut = _fixture.Create<ProvisioningClient>();
Task Act() => sut.CreateOperation(Guid.NewGuid(), "corp", "application1", "test corp", "https://example.org/did", false, CancellationToken.None);
Task Act() => sut.CreateOperation(Guid.NewGuid(), "corp", "application1", "test corp", "https://example.org/did", false, "did:web:example", "example", CancellationToken.None);

// Act
var ex = await Assert.ThrowsAsync<ServiceException>(Act);
Expand All @@ -125,7 +125,7 @@ public async Task CreateOperation_WithValidData_ReturnsCompanyData()
var sut = _fixture.Create<ProvisioningClient>();

// Act
var result = await sut.CreateOperation(Guid.NewGuid(), "corp", "application1", "test corp", "https://example.org/did", false, CancellationToken.None);
var result = await sut.CreateOperation(Guid.NewGuid(), "corp", "application1", "test corp", "https://example.org/did", false, "did:web:example", "example", CancellationToken.None);

// Assert
result.Should().Be(operationId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ public async Task CreateOperation_WithValidData_ReturnsExpected()
initialize?.Invoke(tenant);
modify(tenant);
});
A.CallTo(() => _provisioningClient.CreateOperation(A<Guid>._, TenantName, A<string>._, A<string>._, A<string>._, A<bool>._, A<CancellationToken>._))
A.CallTo(() => _provisioningClient.CreateOperation(A<Guid>._, TenantName, A<string>._, A<string>._, A<string>._, A<bool>._, A<string>._, A<string>._, A<CancellationToken>._))
.Returns(operationId);

// Act
Expand Down Expand Up @@ -402,7 +402,7 @@ public async Task SendCallback_WithoutBaseUrl_ReturnsExpected()
{
// Arrange
A.CallTo(() => _tenantRepositories.GetCallbackData(_tenantId))
.Returns(("bpn123", null, _fixture.Create<WalletData>(), null, null));
.Returns(("bpn123", Guid.NewGuid(), null, _fixture.Create<WalletData>(), null, null));
async Task Act() => await _sut.SendCallback(_tenantId, CancellationToken.None);

// Act
Expand All @@ -417,7 +417,7 @@ public async Task SendCallback_WithoutDid_ReturnsExpected()
{
// Arrange
A.CallTo(() => _tenantRepositories.GetCallbackData(_tenantId))
.Returns(("bpn123", "https://example.org/base", _fixture.Create<WalletData>(), null, null));
.Returns(("bpn123", Guid.NewGuid(), "https://example.org/base", _fixture.Create<WalletData>(), null, null));
async Task Act() => await _sut.SendCallback(_tenantId, CancellationToken.None);

// Act
Expand All @@ -432,7 +432,7 @@ public async Task SendCallback_WithoutDownloadUrl_ReturnsExpected()
{
// Arrange
A.CallTo(() => _tenantRepositories.GetCallbackData(_tenantId))
.Returns(("bpn123", "https://example.org/base", _fixture.Create<WalletData>(), "did:web:example:org:base", null));
.Returns(("bpn123", Guid.NewGuid(), "https://example.org/base", _fixture.Create<WalletData>(), "did:web:example:org:base", null));
async Task Act() => await _sut.SendCallback(_tenantId, CancellationToken.None);

// Act
Expand All @@ -456,7 +456,7 @@ public async Task SendCallback_WithValidData_ReturnsExpected()
var (encryptSecret, initializationVector) = cryptoHelper.Encrypt("test123");
var walletData = new WalletData("https://example.org/token", "cl1", encryptSecret, initializationVector, _settings.EncryptionConfigIndex);
A.CallTo(() => _tenantRepositories.GetCallbackData(_tenantId))
.Returns(("bpn123", "https://example.org/base", walletData, tenant.Did, tenant.DidDownloadUrl));
.Returns(("bpn123", Guid.NewGuid(), "https://example.org/base", walletData, tenant.Did, tenant.DidDownloadUrl));

// Act
var result = await _sut.SendCallback(_tenantId, CancellationToken.None);
Expand Down
Loading