Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 3 additions & 3 deletions charts/cx-ve/Chart.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
dependencies:
- name: core-platform-distribution
repository: oci://ghcr.io/eclipse-cfm/charts
version: 0.0.38
version: 0.0.41
- name: dx-onboarding-api
repository: file://../dx-onboarding-api
version: 0.1.0
Expand All @@ -17,5 +17,5 @@ dependencies:
- name: certo
repository: oci://ghcr.io/metaform/charts
version: 0.0.8
digest: sha256:572fa35cd63d6ffa916f89bdd100e2507fdc3a5afe6e32fb715450a459a57204
generated: "2026-11-06T11:53:18.747977922Z"
digest: sha256:4e7fd7061dfd44ca0ddc362c048d3f50b838724fa8986ede6ffa2acca1848169
generated: "2026-11-11T13:03:30.05584+01:00"
2 changes: 1 addition & 1 deletion charts/cx-ve/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ dependencies:
# infra (NATS/Vault/Postgres/telemetry) + generic platform seeding
- name: core-platform-distribution
repository: oci://ghcr.io/eclipse-cfm/charts
version: 0.0.38
version: 0.0.41
# The DECADE-X onboarding API (the TSP onboarding intake) from this repository
- name: dx-onboarding-api
repository: file://../dx-onboarding-api
Expand Down
2 changes: 1 addition & 1 deletion charts/cx-ve/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ certoAgent:
replicas: 1
image:
repository: ghcr.io/eclipse-cfm/cfm/certoagent
tag: 0.0.6
tag: 0.0.8
pullPolicy: IfNotPresent
# The agent runs under the platform's "cfm-agents" ServiceAccount; the certo-jwtlet-seed job
# maps that SA to the "sudo" participant context with the certo-mgmt-api scopes, authorizing
Expand Down
2 changes: 1 addition & 1 deletion charts/dx-onboarding-api/templates/jwtlet-seed-job.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
## Registers this app's ServiceAccount in JWTlet so the pod's projected SA token can be exchanged
## (RFC 8693) for the token its IssuerService calls need: on approval, the TSP registers the
## participant as credential holder and has the issuer offer it its credentials, through the
## issuer's admin API (`sudo` with issuer-admin-api:admin). The scope mappings themselves are seeded by the platform; this job only
## issuer's admin API (the `issuer` context with the holders and credentials:write scopes). The scope mappings themselves are seeded by the platform; this job only
## registers the SA. Creating a mapping is idempotent (409 = already exists).
{{- if .Values.jwtlet.seed.enabled }}
apiVersion: batch/v1
Expand Down
12 changes: 7 additions & 5 deletions charts/dx-onboarding-api/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,13 @@ jwtlet:
enabled: true
mappings:
# IssuerService admin API (holder registration, credential offers) — the app's only exchange.
# Must agree with config issuer-service.token-resource; `sudo` is no participant context the
# issuer knows, which is why the admin scope is what lets the calls through.
- participantContext: sudo
# Must agree with config issuer-service.token-resource. Exchanging under the issuer's own
# context makes the token's sub own it, so the resource scopes suffice and no admin is needed.
- participantContext: issuer
scopes:
- issuer-admin-api:admin
- issuer-admin-api:holders:read
- issuer-admin-api:holders:write
- issuer-admin-api:credentials:write
audiences:
- edcv
image: curlimages/curl:latest
Expand All @@ -81,7 +83,7 @@ config:
issuer-service:
url: http://issuerservice.edc-v.svc.cluster.local:10013/api/admin
issuer-context-id: issuer
token-resource: sudo
token-resource: issuer
credential-definitions:
legal-entity: dx-legal-entity-credential-def
site: dx-site-credential-def
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,16 @@

/**
* Has the IssuerService offer the holder its DECADE-X credentials, authorized like the holder
* registration: the Legal Entity VC, and the Site VC when the participant declared a business site
* registration but with {@code issuer-admin-api:credentials:write}: the Legal Entity VC, and the Site VC when the participant declared a business site
* ({@code issuer-service.credential-definitions}). The IssuerService resolves the holder's DID
* document and pushes a DCP CredentialOffer to the CredentialService it advertises; an unknown
* definition id fails the whole offer.
*/
@Component
public class IssuerServiceCredentialOfferService implements CredentialOfferService {

static final String SCOPE = "issuer-admin-api:credentials:write";

private static final Logger log = LoggerFactory.getLogger(IssuerServiceCredentialOfferService.class);

private final RestClient restClient;
Expand All @@ -46,8 +48,7 @@ public void offerCredentials(OnboardingRequest request) {
DecadeXCredentialClaims.firstSite(request).ifPresent(site -> offered.add(definitions.site()));
restClient.post()
.uri("/v1/participants/{issuerContextId}/credentials/offer", properties.issuerContextId())
.header("Authorization", "Bearer " + tokenProvider.getToken(properties.tokenResource(),
IssuerServiceHolderRegistrationService.SCOPE))
.header("Authorization", "Bearer " + tokenProvider.getToken(properties.tokenResource(), SCOPE))
.body(Map.of("holderId", holderId, "credentials", offered))
.retrieve()
.toBodilessEntity();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@
* properties — instead of being taken as success, which would leave the holder without the
* DECADE-X claims and fail the credentials' generation later, out of sight.
*
* <p>Authorized by a workload token exchanged under the
* {@code issuer-service.token-resource} mapping (the platform's {@code sudo}) for
* {@code issuer-admin-api:admin} — the scope the admin API accepts from a caller that is not one of
* its participants.
* <p>Authorized by a workload token exchanged under the {@code issuer-service.token-resource}
* mapping, which must be the issuer's participant context: the token's {@code sub} then owns the
* addressed context, so the holder scopes suffice and no {@code issuer-admin-api:admin} elevation is
* needed. The GET of the 409 merge needs {@code holders:read}, the POST and PUT {@code holders:write}.
*/
@Component
public class IssuerServiceHolderRegistrationService implements HolderRegistrationService {

static final String SCOPE = "issuer-admin-api:admin";
static final String SCOPE = "issuer-admin-api:holders:read issuer-admin-api:holders:write";

private static final Logger log = LoggerFactory.getLogger(IssuerServiceHolderRegistrationService.class);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
*
* @param url the admin API's base URL (in-cluster: the issuer's admin port)
* @param issuerContextId the issuer's participant context, which holds the credential definitions
* @param tokenResource the jwtlet mapping the workload token is exchanged under — it must
* grant {@code issuer-admin-api:admin}
* @param tokenResource the jwtlet mapping the workload token is exchanged under — the
* issuer's participant context, granting {@code issuer-admin-api:holders:read},
* {@code :holders:write} and {@code :credentials:write}
* @param credentialDefinitions the credential definitions a DECADE-X member is offered
*/
@ConfigurationProperties(prefix = "issuer-service")
Expand Down
7 changes: 4 additions & 3 deletions dx-onboarding-api/src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,13 @@ spring:

# The IssuerService an approved participant is registered with as credential holder, and offered
# its DECADE-X credentials through. The admin API is called
# with a token exchanged under the token-resource jwtlet mapping, which must grant
# issuer-admin-api:admin (the chart's jwtlet seed registers it for this service's account).
# with a token exchanged under the token-resource jwtlet mapping: the issuer's participant context,
# granting issuer-admin-api:holders:read, :holders:write and :credentials:write (the chart's jwtlet
# seed registers it for this service's account).
issuer-service:
url: http://issuerservice.edc-v.svc.cluster.local:10013/api/admin # ISSUERSERVICE_URL
issuer-context-id: issuer # ISSUERSERVICE_ISSUERCONTEXTID
token-resource: sudo # ISSUERSERVICE_TOKENRESOURCE
token-resource: issuer # ISSUERSERVICE_TOKENRESOURCE
# Must exist at the issuer — the umbrella chart's DECADE-X issuer seed creates them; an unknown id
# fails the whole offer. Every member is offered the Legal Entity VC; the Site VC only a member
# that declared a business site (it is issued for the first one).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class IssuerServiceAdaptersTest {
private final RestClient.Builder builder = RestClient.builder().baseUrl(API);
private final MockRestServiceServer server = MockRestServiceServer.bindTo(builder).build();
private final RestClient restClient = builder.build();
private final IssuerServiceProperties properties = new IssuerServiceProperties(API, "issuer", "sudo",
private final IssuerServiceProperties properties = new IssuerServiceProperties(API, "issuer", "issuer",
new IssuerServiceProperties.CredentialDefinitions("dx-legal-entity-credential-def",
"dx-site-credential-def"));
private final IssuerServiceHolderRegistrationService holders = new IssuerServiceHolderRegistrationService(
Expand Down Expand Up @@ -88,7 +88,7 @@ private static OnboardingRequestData withSites() {
void registerHolder_registersTheDidWithItsLegalEntity() {
server.expect(requestTo(API + "/v1/participants/issuer/holders"))
.andExpect(method(HttpMethod.POST))
.andExpect(header("Authorization", "Bearer token-for-sudo-issuer-admin-api:admin"))
.andExpect(header("Authorization", "Bearer token-for-issuer-issuer-admin-api:holders:read issuer-admin-api:holders:write"))
.andExpect(content().json("""
{"holderId": "%s", "did": "%s", "name": "Acme Corp",
"properties": {"id": "%s", "dxSubjectType": "dx:Wallet", "dxLegalEntity": %s}}"""
Expand Down Expand Up @@ -174,7 +174,7 @@ void registerHolder_mergesIntoAHolderAnotherDataspaceRegistered() {
void offerCredentials_offersBothCredentialsToAHolderWithASite() {
server.expect(requestTo(API + "/v1/participants/issuer/credentials/offer"))
.andExpect(method(HttpMethod.POST))
.andExpect(header("Authorization", "Bearer token-for-sudo-issuer-admin-api:admin"))
.andExpect(header("Authorization", "Bearer token-for-issuer-issuer-admin-api:credentials:write"))
.andExpect(content().json("""
{"holderId": "%s", "credentials": ["dx-legal-entity-credential-def", "dx-site-credential-def"]}"""
.formatted(DID), true))
Expand Down
6 changes: 3 additions & 3 deletions vendor-stack/chart/Chart.lock
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
dependencies:
- name: core-platform-distribution
repository: oci://ghcr.io/eclipse-cfm/charts
version: 0.0.38
version: 0.0.41
- name: decadex-profile
repository: file://../../charts/decadex-profile
version: 0.1.0
- name: certo
repository: oci://ghcr.io/metaform/charts
version: 0.0.8
digest: sha256:522471d02af7e32ef93bfa7ddd0a97d1d71f67759ac79acb60a343d1410d2c90
generated: "2026-11-06T11:53:20.902352907Z"
digest: sha256:8637883c863e420a3560204e4bc3fb253d724b12a94d58abca913db0908f349f
generated: "2026-11-11T13:03:33.496487+01:00"
2 changes: 1 addition & 1 deletion vendor-stack/chart/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ dependencies:
# infra (NATS/Vault/Postgres/telemetry) + generic platform seeding
- name: core-platform-distribution
repository: oci://ghcr.io/eclipse-cfm/charts
version: 0.0.38
version: 0.0.41
# DECADE-X dataspace seeding from this repository: the EDC dataspace profile + DCP scope, and the
# tenant-manager dataspace profile participant provisioning needs
- name: decadex-profile
Expand Down
2 changes: 1 addition & 1 deletion vendor-stack/chart/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ certoAgent:
replicas: 1
image:
repository: ghcr.io/eclipse-cfm/cfm/certoagent
tag: 0.0.6
tag: 0.0.8
pullPolicy: IfNotPresent
resources: {}
natsAuth:
Expand Down
Loading