Skip to content

Commit

Permalink
feat(statusList): add statuslist endpoints (#22)
Browse files Browse the repository at this point in the history
* add endpoint to get statusList
* add endpoint to create statusList
* add statuslist creation for issuer wallets
  • Loading branch information
Phil91 authored Apr 3, 2024
1 parent 9027356 commit 167ff48
Show file tree
Hide file tree
Showing 28 changed files with 449 additions and 54 deletions.
15 changes: 15 additions & 0 deletions charts/dim/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ spec:
value: "{{ .Values.dim.swaggerEnabled }}"
- name: "DIM__ROOTDIRECTORYID"
value: "{{ .Values.dim.rootDirectoryId }}"
- name: "DIM__OPERATORID"
value: "{{ .Values.dim.operatorId }}"
- name: "JWTBEAREROPTIONS__METADATAADDRESS"
value: "{{ .Values.idp.address }}{{ .Values.idp.jwtBearerOptions.metadataPath }}"
- name: "JWTBEAREROPTIONS__REQUIREHTTPSMETADATA"
Expand All @@ -91,6 +93,19 @@ spec:
value: "{{ .Values.idp.address }}{{ .Values.idp.jwtBearerOptions.tokenValidationParameters.validIssuerPath }}"
- name: "JWTBEAREROPTIONS__REFRESHINTERVAL"
value: "{{ .Values.idp.jwtBearerOptions.refreshInterval }}"
- name: "CF__CLIENTID"
value: "{{ .Values.processesworker.cf.clientId }}"
- name: "CF__CLIENTSECRET"
valueFrom:
secretKeyRef:
name: "{{ template "dim.secretName" . }}"
key: "client-secret-cf"
- name: "CF__TOKENADDRESS"
value: "{{ .Values.processesworker.cf.tokenAddress }}"
- name: "CF__BASEURL"
value: "{{ .Values.processesworker.cf.baseUrl }}"
- name: "CF__GRANTTYPE"
value: "{{ .Values.processesworker.cf.grantType }}"
ports:
- name: http
containerPort: {{ .Values.portContainer }}
Expand Down
1 change: 1 addition & 0 deletions charts/dim/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ dim:
path: "/ready"
swaggerEnabled: false
rootDirectoryId: "00000000-0000-0000-0000-000000000000"
operatorId: "00000000-0000-0000-0000-000000000000"

migrations:
name: "migrations"
Expand Down
1 change: 1 addition & 0 deletions consortia/environments/values-dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ dim:
imagePullPolicy: "Always"
swaggerEnabled: true
rootDirectoryId: "27fee02a-e265-4cfc-af70-4f217a33840b"
operatorId: "27fee02a-e265-4cfc-af70-4f217a33840b"

migrations:
image:
Expand Down
1 change: 1 addition & 0 deletions consortia/environments/values-int.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ ingress:
dim:
swaggerEnabled: true
rootDirectoryId: "27fee02a-e265-4cfc-af70-4f217a33840b"
operatorId: "27fee02a-e265-4cfc-af70-4f217a33840b"

migrations:
logging:
Expand Down
56 changes: 56 additions & 0 deletions src/clients/Dim.Clients/Api/Dim/DimClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,4 +155,60 @@ await client.PatchAsJsonAsync($"{dimBaseUrl}/api/v2.0.0/companyIdentities/{compa
return (false, message);
}).ConfigureAwait(false);
}

public async Task<string> GetStatusList(BasicAuthSettings dimAuth, string dimBaseUrl, Guid companyId, CancellationToken cancellationToken)
{
var client = await _basicAuthTokenService.GetBasicAuthorizedClient<DimClient>(dimAuth, cancellationToken).ConfigureAwait(false);
var result = await client.GetAsync($"{dimBaseUrl}/api/v2.0.0/companyIdentities/{companyId}/revocationLists", cancellationToken);
try
{
var response = await result.Content
.ReadFromJsonAsync<StatusListListResponse>(JsonSerializerExtensions.Options, cancellationToken)
.ConfigureAwait(false);
if (response == null)
{
throw new ServiceException("Response must not be null");
}

if (!response.Data.Any(x => x.RemainingSpace > 0))
{
throw new ConflictException("There is no status list with remaining space, please create a new one.");
}

return response.Data.First(x => x.RemainingSpace > 0).StatusListCredential;
}
catch (JsonException je)
{
throw new ServiceException(je.Message);
}
}

public async Task<string> CreateStatusList(BasicAuthSettings dimAuth, string dimBaseUrl, Guid companyId, CancellationToken cancellationToken)
{
var client = await _basicAuthTokenService.GetBasicAuthorizedClient<DimClient>(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,
async m =>
{
var message = await m.Content.ReadAsStringAsync().ConfigureAwait(false);
return (false, message);
}).ConfigureAwait(false);
try
{
var response = await result.Content
.ReadFromJsonAsync<CreateStatusListResponse>(JsonSerializerExtensions.Options, cancellationToken)
.ConfigureAwait(false);
if (response == null)
{
throw new ServiceException("Response must not be null");
}

return response.RevocationVc.Id;
}
catch (JsonException je)
{
throw new ServiceException(je.Message);
}
}
}
2 changes: 2 additions & 0 deletions src/clients/Dim.Clients/Api/Dim/IDimClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,6 @@ public interface IDimClient

Task<string> GetApplication(BasicAuthSettings dimAuth, string dimBaseUrl, string applicationId, CancellationToken cancellationToken);
Task AssignApplicationToCompany(BasicAuthSettings dimAuth, string dimBaseUrl, string applicationKey, Guid companyId, CancellationToken cancellationToken);
Task<string> GetStatusList(BasicAuthSettings dimAuth, string dimBaseUrl, Guid companyId, CancellationToken cancellationToken);
Task<string> CreateStatusList(BasicAuthSettings dimAuth, string dimBaseUrl, Guid companyId, CancellationToken cancellationToken);
}
60 changes: 60 additions & 0 deletions src/clients/Dim.Clients/Api/Dim/StatusListResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/********************************************************************************
* Copyright (c) 2024 BMW Group AG
*
* 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 System.Text.Json.Serialization;

namespace Dim.Clients.Api.Dim;

public record CreateStatusListRequest(
[property: JsonPropertyName("payload")] CreateStatusListPaypload Payload
);

public record CreateStatusListPaypload(
[property: JsonPropertyName("create")] CreateStatusList Create
);

public record CreateStatusList(
[property: JsonPropertyName("type")] string Type,
[property: JsonPropertyName("name")] string Name,
[property: JsonPropertyName("description")] string Description,
[property: JsonPropertyName("length")] int Length
);

public record CreateStatusListResponse(
[property: JsonPropertyName("id")] Guid Id,
[property: JsonPropertyName("revocationVc")] RevocationVc RevocationVc
);

public record RevocationVc(
[property: JsonPropertyName("id")] string Id
);

public record StatusListListResponse(
[property: JsonPropertyName("count")] int Count,
[property: JsonPropertyName("data")] IEnumerable<StatusListResponse> Data
);

public record StatusListResponse(
[property: JsonPropertyName("id")] string Id,
[property: JsonPropertyName("name")] string Name,
[property: JsonPropertyName("statusListCredential")] string StatusListCredential,
[property: JsonPropertyName("type")] string Type,
[property: JsonPropertyName("length")] int Length,
[property: JsonPropertyName("remainingSpace")] int RemainingSpace
);
5 changes: 2 additions & 3 deletions src/clients/Dim.Clients/Dim.Clients.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,11 @@

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.4" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.1" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.1" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0" />
<PackageReference Include="Org.Eclipse.TractusX.Portal.Backend.Framework.DependencyInjection" Version="1.3.0" />
<PackageReference Include="Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling" Version="1.3.0" />
<PackageReference Include="Org.Eclipse.TractusX.Portal.Backend.Framework.HttpClientExtensions" Version="1.3.0" />
Expand Down
3 changes: 2 additions & 1 deletion src/database/Dim.DbAccess/Repositories/ITenantRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,6 @@ public interface ITenantRepository
Task<Guid?> GetDimInstanceId(Guid tenantId);
Task<(string bpn, string? DownloadUrl, string? Did, Guid? DimInstanceId)> GetCallbackData(Guid tenantId);
Task<(Guid? DimInstanceId, string HostingUrl, bool IsIssuer)> GetDimInstanceIdAndHostingUrl(Guid tenantId);
Task<(string? ApplicationId, Guid? CompanyId, Guid? DimInstanceId)> GetApplicationAndCompanyId(Guid tenantId);
Task<(string? ApplicationId, Guid? CompanyId, Guid? DimInstanceId, bool IsIssuer)> GetApplicationAndCompanyId(Guid tenantId);
Task<(bool Exists, Guid? CompanyId, Guid? InstanceId)> GetCompanyAndInstanceIdForBpn(string bpn);
}
12 changes: 9 additions & 3 deletions src/database/Dim.DbAccess/Repositories/TenantRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,18 @@ public void AttachAndModifyTenant(Guid tenantId, Action<Tenant>? initialize, Act
.Select(x => new ValueTuple<Guid?, string, bool>(x.DimInstanceId, x.DidDocumentLocation, x.IsIssuer))
.SingleOrDefaultAsync();

public Task<(string? ApplicationId, Guid? CompanyId, Guid? DimInstanceId)> GetApplicationAndCompanyId(Guid tenantId) =>
public Task<(string? ApplicationId, Guid? CompanyId, Guid? DimInstanceId, bool IsIssuer)> GetApplicationAndCompanyId(Guid tenantId) =>
_context.Tenants
.Where(x => x.Id == tenantId)
.Select(x => new ValueTuple<string?, Guid?, Guid?>(
.Select(x => new ValueTuple<string?, Guid?, Guid?, bool>(
x.ApplicationId,
x.CompanyId,
x.DimInstanceId))
x.DimInstanceId,
x.IsIssuer))
.SingleOrDefaultAsync();

public Task<(bool Exists, Guid? CompanyId, Guid? InstanceId)> GetCompanyAndInstanceIdForBpn(string bpn) =>
_context.Tenants.Where(x => x.Bpn == bpn)
.Select(x => new ValueTuple<bool, Guid?, Guid?>(true, x.CompanyId, x.DimInstanceId))
.SingleOrDefaultAsync();
}
3 changes: 2 additions & 1 deletion src/database/Dim.Entities/Enums/ProcessStepTypeId.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,6 @@ public enum ProcessStepTypeId
CREATE_APPLICATION = 14,
CREATE_COMPANY_IDENTITY = 15,
ASSIGN_COMPANY_APPLICATION = 16,
SEND_CALLBACK = 17
CREATE_STATUS_LIST = 17,
SEND_CALLBACK = 18
}
4 changes: 4 additions & 0 deletions src/database/Dim.Migrations/Dim.Migrations.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,8 @@
<ProjectReference Include="..\Dim.Entities\Dim.Entities.csproj" />
</ItemGroup>

<ItemGroup>
<Folder Include="Migrations\" />
</ItemGroup>

</Project>

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
namespace Dim.Migrations.Migrations
{
/// <inheritdoc />
public partial class initial : Migration
public partial class Initial : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
Expand Down Expand Up @@ -202,7 +202,8 @@ protected override void Up(MigrationBuilder migrationBuilder)
{ 14, "CREATE_APPLICATION" },
{ 15, "CREATE_COMPANY_IDENTITY" },
{ 16, "ASSIGN_COMPANY_APPLICATION" },
{ 17, "SEND_CALLBACK" }
{ 17, "CREATE_STATUS_LIST" },
{ 18, "SEND_CALLBACK" }
});

migrationBuilder.InsertData(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,10 @@
********************************************************************************/

// <auto-generated />
using System;

using Dim.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;

#nullable disable

namespace Dim.Migrations.Migrations
{
Expand Down Expand Up @@ -264,6 +260,11 @@ protected override void BuildModel(ModelBuilder modelBuilder)
new
{
Id = 17,
Label = "CREATE_STATUS_LIST"
},
new
{
Id = 18,
Label = "SEND_CALLBACK"
});
});
Expand Down
7 changes: 5 additions & 2 deletions src/processes/DimProcess.Executor/DimProcessTypeExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public class DimProcessTypeExecutor : IProcessTypeExecutor
ProcessStepTypeId.GET_DIM_DETAILS,
ProcessStepTypeId.CREATE_APPLICATION,
ProcessStepTypeId.CREATE_COMPANY_IDENTITY,
ProcessStepTypeId.CREATE_STATUS_LIST,
ProcessStepTypeId.ASSIGN_COMPANY_APPLICATION,
ProcessStepTypeId.SEND_CALLBACK);

Expand Down Expand Up @@ -126,9 +127,11 @@ public DimProcessTypeExecutor(
.ConfigureAwait(false),
ProcessStepTypeId.CREATE_COMPANY_IDENTITY => await _dimProcessHandler.CreateCompanyIdentity(_tenantId, _tenantName, cancellationToken)
.ConfigureAwait(false),
ProcessStepTypeId.ASSIGN_COMPANY_APPLICATION => await _dimProcessHandler.AssignCompanyApplication(_tenantId, _tenantName, cancellationToken)
ProcessStepTypeId.CREATE_STATUS_LIST => await _dimProcessHandler.CreateStatusList(_tenantId, cancellationToken)
.ConfigureAwait(false),
ProcessStepTypeId.SEND_CALLBACK => await _dimProcessHandler.SendCallback(_tenantId, _tenantName, cancellationToken)
ProcessStepTypeId.ASSIGN_COMPANY_APPLICATION => await _dimProcessHandler.AssignCompanyApplication(_tenantId, cancellationToken)
.ConfigureAwait(false),
ProcessStepTypeId.SEND_CALLBACK => await _dimProcessHandler.SendCallback(_tenantId, cancellationToken)
.ConfigureAwait(false),
_ => (null, ProcessStepStatusId.TODO, false, null)
};
Expand Down
36 changes: 33 additions & 3 deletions src/processes/DimProcess.Library/DimProcessHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -459,9 +459,9 @@ await _provisioningClient.CreateCloudFoundryEnvironment(saBinding.Url, bindingRe
null);
}

public async Task<(IEnumerable<ProcessStepTypeId>? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> AssignCompanyApplication(Guid tenantId, string tenantName, CancellationToken cancellationToken)
public async Task<(IEnumerable<ProcessStepTypeId>? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> AssignCompanyApplication(Guid tenantId, CancellationToken cancellationToken)
{
var (applicationId, companyId, dimInstanceId) = await _dimRepositories.GetInstance<ITenantRepository>().GetApplicationAndCompanyId(tenantId).ConfigureAwait(false);
var (applicationId, companyId, dimInstanceId, isIssuer) = await _dimRepositories.GetInstance<ITenantRepository>().GetApplicationAndCompanyId(tenantId).ConfigureAwait(false);
if (applicationId == null)
{
throw new ConflictException("ApplicationId must always be set here");
Expand Down Expand Up @@ -496,14 +496,44 @@ await _provisioningClient.CreateCloudFoundryEnvironment(saBinding.Url, bindingRe
{
tenant.ApplicationKey = applicationKey;
});
return new ValueTuple<IEnumerable<ProcessStepTypeId>?, ProcessStepStatusId, bool, string?>(
Enumerable.Repeat(isIssuer ? ProcessStepTypeId.CREATE_STATUS_LIST : ProcessStepTypeId.SEND_CALLBACK, 1),
ProcessStepStatusId.DONE,
false,
null);
}

public async Task<(IEnumerable<ProcessStepTypeId>? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CreateStatusList(Guid tenantId, CancellationToken cancellationToken)
{
var (_, companyId, dimInstanceId, _) = await _dimRepositories.GetInstance<ITenantRepository>().GetApplicationAndCompanyId(tenantId).ConfigureAwait(false);
if (companyId == null)
{
throw new ConflictException("CompanyId must always be set here");
}

if (dimInstanceId == null)
{
throw new ConflictException("DimInstanceId must not be null.");
}

var dimDetails = await _cfClient.GetServiceBindingDetails(dimInstanceId.Value, cancellationToken).ConfigureAwait(false);
var dimAuth = new BasicAuthSettings
{
TokenAddress = $"{dimDetails.Credentials.Uaa.Url}/oauth/token",
ClientId = dimDetails.Credentials.Uaa.ClientId,
ClientSecret = dimDetails.Credentials.Uaa.ClientSecret
};
var dimBaseUrl = dimDetails.Credentials.Url;
await _dimClient.CreateStatusList(dimAuth, dimBaseUrl, companyId.Value, cancellationToken).ConfigureAwait(false);

return new ValueTuple<IEnumerable<ProcessStepTypeId>?, ProcessStepStatusId, bool, string?>(
Enumerable.Repeat(ProcessStepTypeId.SEND_CALLBACK, 1),
ProcessStepStatusId.DONE,
false,
null);
}

public async Task<(IEnumerable<ProcessStepTypeId>? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> SendCallback(Guid tenantId, string tenantName, CancellationToken cancellationToken)
public async Task<(IEnumerable<ProcessStepTypeId>? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> SendCallback(Guid tenantId, CancellationToken cancellationToken)
{
var (bpn, downloadUrl, did, dimInstanceId) = await _dimRepositories.GetInstance<ITenantRepository>().GetCallbackData(tenantId).ConfigureAwait(false);
if (downloadUrl == null)
Expand Down
Loading

0 comments on commit 167ff48

Please sign in to comment.