Skip to content

Commit

Permalink
feat(Identity): add identity extractor from referringConnector
Browse files Browse the repository at this point in the history
  • Loading branch information
wolf4ood committed May 28, 2023
1 parent 24ac3a5 commit 962c23a
Show file tree
Hide file tree
Showing 20 changed files with 331 additions and 136 deletions.
30 changes: 10 additions & 20 deletions charts/tractusx-connector/templates/deployment-controlplane.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,12 @@ spec:
{{- end }}
{{- end }}

########################
## ID CONFIGURATION ##
########################
- name: EDC_PARTICIPANT_ID
value: {{ .Values.participant.id | required ".Values.participant.id is required" | quote }}

########################
## DAPS CONFIGURATION ##
########################
Expand Down Expand Up @@ -154,31 +160,15 @@ spec:
value: {{ .Values.controlplane.endpoints.observability.insecure | quote }}

#########
## IDS ##
## DSP ##
#########
- name: "IDS_WEBHOOK_ADDRESS"
value: {{ include "txdc.controlplane.url.protocol" . | quote }}
- name: "EDC_IDS_ENDPOINT"

- name: "EDC_DSP_CALLBACK_ADDRESS"
value: {{ printf "%s%s" (include "txdc.controlplane.url.protocol" .) .Values.controlplane.endpoints.protocol.path | quote }}
- name: "EDC_IDS_ID"
value: {{ printf "urn:connector:%s" (lower .Values.controlplane.internationalDataSpaces.id) | quote }}
- name: "EDC_IDS_DESCRIPTION"
value: {{ .Values.controlplane.internationalDataSpaces.description | quote }}
- name: "EDC_IDS_TITLE"
value: {{ .Values.controlplane.internationalDataSpaces.title | quote }}
- name: "EDC_IDS_MAINTAINER"
value: {{ .Values.controlplane.internationalDataSpaces.maintainer | quote }}
- name: "EDC_IDS_CURATOR"
value: {{ .Values.controlplane.internationalDataSpaces.curator | quote }}
- name: "EDC_IDS_CATALOG_ID"
value: {{ printf "urn:catalog:%s" (lower .Values.controlplane.internationalDataSpaces.catalogId) | quote }}
- name: "EDC_OAUTH_PROVIDER_AUDIENCE"
value: "idsc:IDS_CONNECTORS_ALL"
- name: "EDC_OAUTH_ENDPOINT_AUDIENCE"
value: {{ printf "%s%s%s" (include "txdc.controlplane.url.protocol" . ) .Values.controlplane.endpoints.protocol.path "/data" | quote }}
# this is the old setting name for 'EDC_OAUTH_ENDPOINT_AUDIENCE' and is mandatory for Produce EDC v0.1.2 and older
- name: "EDC_IDS_ENDPOINT_AUDIENCE"
value: {{ printf "%s%s%s" (include "txdc.controlplane.url.protocol" . ) .Values.controlplane.endpoints.protocol.path "/data" | quote }}
value: {{ printf "%s%s" (include "txdc.controlplane.url.protocol" . ) .Values.controlplane.endpoints.protocol.path | quote }}

################
## POSTGRESQL ##
Expand Down
3 changes: 3 additions & 0 deletions charts/tractusx-connector/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ imagePullSecrets: []

customLabels: {}

participant:
id: ""

controlplane:
image:
# -- Which derivate of the control plane to use. when left empty the deployment will select the correct image automatically
Expand Down
2 changes: 2 additions & 0 deletions edc-extensions/cx-oauth2/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,6 @@ dependencies {
implementation(libs.slf4j.api)
implementation(libs.nimbus.jwt)
implementation(libs.okhttp)

testImplementation(libs.edc.junit)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
*
* 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
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation
*
*/

package org.eclipse.tractusx.edc.oauth2;

import org.eclipse.edc.runtime.metamodel.annotation.Inject;
import org.eclipse.edc.runtime.metamodel.annotation.Setting;
import org.eclipse.edc.spi.agent.ParticipantAgentService;
import org.eclipse.edc.spi.agent.ParticipantAgentServiceExtension;
import org.eclipse.edc.spi.iam.ClaimToken;
import org.eclipse.edc.spi.monitor.Monitor;
import org.eclipse.edc.spi.system.ServiceExtension;
import org.eclipse.edc.spi.system.ServiceExtensionContext;
import org.jetbrains.annotations.NotNull;

import java.util.Map;
import java.util.regex.Pattern;

import static org.eclipse.edc.spi.agent.ParticipantAgent.PARTICIPANT_IDENTITY;

public class CxParticipantExtension implements ServiceExtension, ParticipantAgentServiceExtension {

public static final String REFERRING_CONNECTOR_CLAIM = "referringConnector";

private static final String DEFAULT_PARTICIPANT_ID_REGEX = "[^/]+(?=/$|$)";
private static final int DEFAULT_PARTICIPANT_ID_REGEX_GROUP = 0;

@Setting(value = "Data plane proxy API consumer port", defaultValue = CxParticipantExtension.PARTICIPANT_ID_REGEX)
private static final String PARTICIPANT_ID_REGEX = "tx.participant.id.regex";

@Setting(value = "Data plane proxy API consumer port", defaultValue = CxParticipantExtension.PARTICIPANT_ID_REGEX)
private static final String PARTICIPANT_ID_REGEX_GROUP = "tx.participant.id.regex";
@Inject
ParticipantAgentService agentService;
private Pattern participantRegex;

private int participantRegexGroup;

@Inject
private Monitor monitor;

@Override
public void initialize(ServiceExtensionContext context) {
this.participantRegex = Pattern.compile(context.getConfig().getString(PARTICIPANT_ID_REGEX, DEFAULT_PARTICIPANT_ID_REGEX));
this.participantRegexGroup = context.getConfig().getInteger(PARTICIPANT_ID_REGEX_GROUP, DEFAULT_PARTICIPANT_ID_REGEX_GROUP);

agentService.register(this);
}

@Override
public @NotNull Map<String, String> attributesFor(ClaimToken token) {
var referringConnector = token.getClaim(REFERRING_CONNECTOR_CLAIM);
if (referringConnector instanceof String referringConnectorUrl) {
var matcher = participantRegex.matcher(referringConnectorUrl);
if (matcher.find()) {
var id = matcher.group(participantRegexGroup);
return Map.of(PARTICIPANT_IDENTITY, id);
}
monitor.warning("Unable to extract the participant id from the referring connector claim");
}
return Map.of();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@
#

org.eclipse.tractusx.edc.oauth2.CxOauth2Extension
org.eclipse.tractusx.edc.oauth2.CxParticipantExtension
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
*
* 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
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation
*
*/

package org.eclipse.tractusx.edc.oauth2;

import org.eclipse.edc.junit.extensions.DependencyInjectionExtension;
import org.eclipse.edc.spi.agent.ParticipantAgentService;
import org.eclipse.edc.spi.iam.ClaimToken;
import org.eclipse.edc.spi.system.ServiceExtensionContext;
import org.eclipse.edc.spi.system.injection.ObjectFactory;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;
import org.junit.jupiter.params.provider.ArgumentsSource;

import java.util.Map;
import java.util.stream.Stream;

import static org.assertj.core.api.Assertions.assertThat;
import static org.eclipse.edc.spi.agent.ParticipantAgent.PARTICIPANT_IDENTITY;
import static org.eclipse.tractusx.edc.oauth2.CxParticipantExtension.REFERRING_CONNECTOR_CLAIM;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;

@ExtendWith(DependencyInjectionExtension.class)
public class CxParticipantExtensionTest {

CxParticipantExtension extension;

ParticipantAgentService agentService = mock(ParticipantAgentService.class);

ServiceExtensionContext context;

@BeforeEach
void setUp(ObjectFactory factory, ServiceExtensionContext context) {
this.context = spy(context);
context.registerService(ParticipantAgentService.class, agentService);
extension = factory.constructInstance(CxParticipantExtension.class);
}

@Test
void initialize() {
extension.initialize(context);
var attributes = Map.of(PARTICIPANT_IDENTITY, "BPNSOKRATES");
verify(agentService).register(isA(CxParticipantExtension.class));
var claims = ClaimToken.Builder.newInstance().claim(REFERRING_CONNECTOR_CLAIM, "http://sokrates-controlplane/BPNSOKRATES").build();


assertThat(extension.attributesFor(claims)).containsExactlyEntriesOf(attributes);

claims = ClaimToken.Builder.newInstance().claim(REFERRING_CONNECTOR_CLAIM, "http://sokrates-controlplane/BPNSOKRATES/").build();
assertThat(extension.attributesFor(claims)).containsExactlyEntriesOf(attributes);

claims = ClaimToken.Builder.newInstance().claim(REFERRING_CONNECTOR_CLAIM, "http://sokrates-controlplane/test/path/BPNSOKRATES/").build();
assertThat(extension.attributesFor(claims)).containsExactlyEntriesOf(attributes);
}


@ParameterizedTest
@ArgumentsSource(ClaimProvider.class)
void attributesFor_shouldMatchTheId(Map<String, Object> claims) {
var attributes = Map.of(PARTICIPANT_IDENTITY, "BPNSOKRATES");
extension.initialize(context);
var claimToken = ClaimToken.Builder.newInstance().claims(claims).build();
assertThat(extension.attributesFor(claimToken)).containsExactlyEntriesOf(attributes);
}

static class ClaimProvider implements ArgumentsProvider {
ClaimProvider() {
}

@Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
return Stream.of(
Map.of(REFERRING_CONNECTOR_CLAIM, "http://sokrates-controlplane/BPNSOKRATES"),
Map.of(REFERRING_CONNECTOR_CLAIM, "http://sokrates-controlplane/BPNSOKRATES/"),
Map.of(REFERRING_CONNECTOR_CLAIM, "http://sokrates-controlplane/test/path/BPNSOKRATES"),
Map.of(REFERRING_CONNECTOR_CLAIM, "https://sokrates-controlplane/test/path/BPNSOKRATES"),
Map.of(REFERRING_CONNECTOR_CLAIM, "BPNSOKRATES")
).map(Arguments::arguments);
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

package org.eclipse.tractusx.edc.hashicorpvault;

import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.OkHttpClient;
import org.eclipse.edc.spi.system.ServiceExtensionContext;

Expand Down Expand Up @@ -50,6 +51,14 @@ public class AbstractHashicorpVaultExtension {

private static final String VAULT_TIMEOUT_SECONDS = "edc.vault.hashicorp.timeout.seconds";

public HashicorpVaultClient createVaultClient(ServiceExtensionContext context, ObjectMapper mapper) {
var config = loadHashicorpVaultClientConfig(context);

final OkHttpClient okHttpClient = createOkHttpClient(config);

return new HashicorpVaultClient(config, okHttpClient, mapper);
}

protected OkHttpClient createOkHttpClient(HashicorpVaultClientConfig config) {
OkHttpClient.Builder builder =
new OkHttpClient.Builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@

package org.eclipse.tractusx.edc.hashicorpvault;

import okhttp3.OkHttpClient;
import org.eclipse.edc.runtime.metamodel.annotation.Inject;
import org.eclipse.edc.runtime.metamodel.annotation.Requires;
import org.eclipse.edc.spi.system.ServiceExtension;
import org.eclipse.edc.spi.system.ServiceExtensionContext;
import org.eclipse.edc.spi.system.health.HealthCheckService;
import org.eclipse.edc.spi.types.TypeManager;

@Requires(HealthCheckService.class)
public class HashicorpVaultHealthExtension extends AbstractHashicorpVaultExtension
Expand All @@ -34,19 +35,21 @@ public class HashicorpVaultHealthExtension extends AbstractHashicorpVaultExtensi

public static final boolean VAULT_HEALTH_CHECK_DEFAULT = true;

@Inject
private HealthCheckService healthCheckService;

@Inject
private TypeManager typeManager;

@Override
public String name() {
return "Hashicorp Vault Health Check";
}


@Override
public void initialize(ServiceExtensionContext context) {
final HashicorpVaultClientConfig config = loadHashicorpVaultClientConfig(context);

final OkHttpClient okHttpClient = createOkHttpClient(config);

final HashicorpVaultClient client =
new HashicorpVaultClient(config, okHttpClient, context.getTypeManager().getMapper());
var client = createVaultClient(context, typeManager.getMapper());

configureHealthCheck(client, context);

Expand All @@ -61,7 +64,6 @@ private void configureHealthCheck(HashicorpVaultClient client, ServiceExtensionC
final HashicorpVaultHealthCheck healthCheck =
new HashicorpVaultHealthCheck(client, context.getMonitor());

final HealthCheckService healthCheckService = context.getService(HealthCheckService.class);
healthCheckService.addLivenessProvider(healthCheck);
healthCheckService.addReadinessProvider(healthCheck);
healthCheckService.addStartupStatusProvider(healthCheck);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,31 +20,31 @@

package org.eclipse.tractusx.edc.hashicorpvault;

import okhttp3.OkHttpClient;
import org.eclipse.edc.runtime.metamodel.annotation.Inject;
import org.eclipse.edc.runtime.metamodel.annotation.Provides;
import org.eclipse.edc.spi.security.CertificateResolver;
import org.eclipse.edc.spi.security.PrivateKeyResolver;
import org.eclipse.edc.spi.security.Vault;
import org.eclipse.edc.spi.security.VaultPrivateKeyResolver;
import org.eclipse.edc.spi.system.ServiceExtension;
import org.eclipse.edc.spi.system.ServiceExtensionContext;
import org.eclipse.edc.spi.types.TypeManager;

@Provides({Vault.class, CertificateResolver.class, PrivateKeyResolver.class})
@Provides({ Vault.class, CertificateResolver.class, PrivateKeyResolver.class })
public class HashicorpVaultVaultExtension extends AbstractHashicorpVaultExtension
implements ServiceExtension {

@Inject
private TypeManager typeManager;

@Override
public String name() {
return "Hashicorp Vault";
}

@Override
public void initialize(ServiceExtensionContext context) {
final HashicorpVaultClientConfig config = loadHashicorpVaultClientConfig(context);

final OkHttpClient okHttpClient = createOkHttpClient(config);

final HashicorpVaultClient client = new HashicorpVaultClient(config, okHttpClient, context.getTypeManager().getMapper());
var client = createVaultClient(context, typeManager.getMapper());

final HashicorpVault vault = new HashicorpVault(client);
final CertificateResolver certificateResolver =
Expand Down
Loading

0 comments on commit 962c23a

Please sign in to comment.