Skip to content

Commit 175484e

Browse files
committed
feat: replace edc dataplane with siglet
1 parent 159653c commit 175484e

37 files changed

Lines changed: 136 additions & 1375 deletions

File tree

extensions/data-plane-certs/build.gradle.kts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,26 @@ dependencies {
2121
api(libs.edc.spi.http)
2222
api(libs.edc.spi.transaction)
2323
api(libs.edc.spi.web)
24-
api(libs.edc.spi.dataplane)
24+
implementation(libs.edc.core.boot)
25+
implementation(libs.edc.core.runtime)
26+
implementation(libs.edc.core.token)
27+
implementation(libs.edc.core.connector)
28+
implementation(libs.edc.core.api)
29+
implementation(libs.edc.core.participantcontext.config)
2530
implementation(libs.jersey.multipart)
2631
implementation(libs.edc.lib.util)
2732
implementation(libs.edc.lib.sql)
33+
implementation(libs.edc.lib.token)
34+
implementation(libs.edc.lib.oauth2.authn)
2835
implementation(libs.edc.core.sql.bootstrapper)
29-
implementation(libs.edc.lib.util.dataplane)
30-
implementation(libs.edc.dataplane.iam)
36+
implementation(libs.edc.core.sql)
37+
implementation(libs.edc.core.http)
38+
implementation(libs.edc.transaction.local)
39+
implementation(libs.edc.transaction.pool)
40+
implementation(libs.edc.api.control.configuration)
41+
implementation(libs.edc.api.observability)
3142
implementation(libs.jakarta.rsApi)
43+
implementation(libs.postgres)
3244

3345
testImplementation(libs.edc.lib.http)
3446
testImplementation(libs.edc.junit)

extensions/data-plane-certs/src/main/java/org/eclipse/edc/virtualized/dataplane/cert/CertExchangeExtension.java

Lines changed: 55 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,23 @@
1414

1515
package org.eclipse.edc.virtualized.dataplane.cert;
1616

17-
import org.eclipse.edc.connector.dataplane.iam.service.DataPlaneAuthorizationServiceImpl;
18-
import org.eclipse.edc.connector.dataplane.spi.Endpoint;
19-
import org.eclipse.edc.connector.dataplane.spi.edr.EndpointDataReferenceServiceRegistry;
20-
import org.eclipse.edc.connector.dataplane.spi.iam.DataPlaneAuthorizationService;
21-
import org.eclipse.edc.connector.dataplane.spi.iam.PublicEndpointGeneratorService;
17+
import org.eclipse.edc.api.authentication.JwksResolver;
18+
import org.eclipse.edc.api.authentication.filter.JwtValidatorFilter;
19+
import org.eclipse.edc.keys.spi.KeyParserRegistry;
2220
import org.eclipse.edc.runtime.metamodel.annotation.Configuration;
2321
import org.eclipse.edc.runtime.metamodel.annotation.Extension;
2422
import org.eclipse.edc.runtime.metamodel.annotation.Inject;
2523
import org.eclipse.edc.runtime.metamodel.annotation.Setting;
2624
import org.eclipse.edc.runtime.metamodel.annotation.Settings;
25+
import org.eclipse.edc.spi.EdcException;
2726
import org.eclipse.edc.spi.system.Hostname;
2827
import org.eclipse.edc.spi.system.ServiceExtension;
2928
import org.eclipse.edc.spi.system.ServiceExtensionContext;
29+
import org.eclipse.edc.token.rules.ExpirationIssuedAtValidationRule;
30+
import org.eclipse.edc.token.rules.IssuerEqualsValidationRule;
31+
import org.eclipse.edc.token.rules.NotBeforeValidationRule;
32+
import org.eclipse.edc.token.spi.TokenValidationRule;
33+
import org.eclipse.edc.token.spi.TokenValidationService;
3034
import org.eclipse.edc.transaction.spi.TransactionContext;
3135
import org.eclipse.edc.virtualized.dataplane.cert.api.CertExchangePublicController;
3236
import org.eclipse.edc.virtualized.dataplane.cert.api.CertInternalExchangeController;
@@ -35,6 +39,11 @@
3539
import org.eclipse.edc.web.spi.configuration.PortMapping;
3640
import org.eclipse.edc.web.spi.configuration.PortMappingRegistry;
3741

42+
import java.net.MalformedURLException;
43+
import java.net.URL;
44+
import java.time.Clock;
45+
import java.util.List;
46+
3847
import static org.eclipse.edc.virtualized.dataplane.cert.CertExchangeExtension.NAME;
3948

4049
@Extension(NAME)
@@ -43,12 +52,7 @@ public class CertExchangeExtension implements ServiceExtension {
4352
public static final String API_CONTEXT = "certs";
4453
private static final int DEFAULT_CERTS_PORT = 8186;
4554
private static final String DEFAULT_CERTS_PATH = "/api/data";
46-
47-
48-
@Setting(description = "Base url of the public public API endpoint without the trailing slash. This should point to the public certs endpoint configured.",
49-
required = false,
50-
key = "edc.dataplane.api.certs.baseurl", warnOnMissingConfig = true)
51-
private String publicBaseUrl;
55+
private static final long FIVE_MINUTES = 1000 * 60 * 5;
5256

5357
@Configuration
5458
private CertApiConfiguration apiConfiguration;
@@ -60,41 +64,54 @@ public class CertExchangeExtension implements ServiceExtension {
6064
private PortMappingRegistry portMappingRegistry;
6165

6266
@Inject
63-
private DataPlaneAuthorizationService authorizationService;
67+
private WebService webService;
68+
6469
@Inject
65-
private PublicEndpointGeneratorService generatorService;
70+
private CertStore certStore;
6671

6772
@Inject
68-
private EndpointDataReferenceServiceRegistry endpointDataReferenceServiceRegistry;
73+
private TransactionContext transactionContext;
6974

7075
@Inject
71-
private WebService webService;
76+
private TokenValidationService tokenValidationService;
77+
78+
@Configuration
79+
private SigletConfig sigletConfig;
7280

7381
@Inject
74-
private CertStore certStore;
82+
private KeyParserRegistry keyParserRegistry;
7583

7684
@Inject
77-
private TransactionContext transactionContext;
85+
private Clock clock;
7886

7987
@Override
8088
public void initialize(ServiceExtensionContext context) {
8189
var portMapping = new PortMapping(API_CONTEXT, apiConfiguration.port(), apiConfiguration.path());
8290
portMappingRegistry.register(portMapping);
8391

84-
if (publicBaseUrl == null) {
85-
publicBaseUrl = "http://%s:%d%s".formatted(hostname.get(), portMapping.port(), portMapping.path());
86-
context.getMonitor().warning("The public API endpoint was not explicitly configured, the default '%s' will be used.".formatted(publicBaseUrl));
92+
URL url;
93+
try {
94+
url = new URL(sigletConfig.jwksUrl());
95+
} catch (MalformedURLException e) {
96+
throw new EdcException(e);
8797
}
88-
var endpoint = Endpoint.url(publicBaseUrl);
89-
generatorService.addGeneratorFunction("HttpCertData", dataAddress -> endpoint);
90-
webService.registerResource(API_CONTEXT, new CertExchangePublicController(authorizationService, certStore, transactionContext));
98+
99+
webService.registerResource(API_CONTEXT, new CertExchangePublicController(certStore, transactionContext));
100+
webService.registerResource(API_CONTEXT, new JwtValidatorFilter(tokenValidationService, new JwksResolver(url, keyParserRegistry, sigletConfig.cacheValidityInMillis), getRules()));
101+
91102
webService.registerResource("control", new CertInternalExchangeController(certStore, transactionContext));
92103

93-
if (authorizationService instanceof DataPlaneAuthorizationServiceImpl dpAuthService) {
94-
endpointDataReferenceServiceRegistry.register("HttpCertData", dpAuthService);
95-
}
96104
}
97105

106+
private List<TokenValidationRule> getRules() {
107+
return List.of(
108+
new IssuerEqualsValidationRule(sigletConfig.expectedIssuer),
109+
new NotBeforeValidationRule(clock, 0, true),
110+
new ExpirationIssuedAtValidationRule(clock, 0, false)
111+
);
112+
}
113+
114+
98115
@Settings
99116
record CertApiConfiguration(
100117
@Setting(key = "web.http." + API_CONTEXT + ".port", description = "Port for " + API_CONTEXT + " api context", defaultValue = DEFAULT_CERTS_PORT + "")
@@ -104,4 +121,16 @@ record CertApiConfiguration(
104121
) {
105122

106123
}
124+
125+
@Settings
126+
record SigletConfig(
127+
@Setting(key = "edc.iam.siglet.issuer", description = "Issuer of the Siglet server", required = false)
128+
String expectedIssuer,
129+
@Setting(key = "edc.iam.siglet.jwks.url", description = "Absolute URL where the JWKS of the Siglet server is hosted")
130+
String jwksUrl,
131+
@Setting(key = "edc.iam.siglet.jwks.cache.validity", description = "Time (in ms) that cached JWKS are cached", defaultValue = "" + FIVE_MINUTES)
132+
long cacheValidityInMillis
133+
) {
134+
135+
}
107136
}

extensions/data-plane-certs/src/main/java/org/eclipse/edc/virtualized/dataplane/cert/api/CertExchangePublicController.java

Lines changed: 32 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,16 @@
1515
package org.eclipse.edc.virtualized.dataplane.cert.api;
1616

1717
import com.fasterxml.jackson.core.type.TypeReference;
18-
import com.nimbusds.jwt.SignedJWT;
1918
import jakarta.ws.rs.GET;
20-
import jakarta.ws.rs.HeaderParam;
2119
import jakarta.ws.rs.POST;
2220
import jakarta.ws.rs.Path;
2321
import jakarta.ws.rs.PathParam;
2422
import jakarta.ws.rs.WebApplicationException;
23+
import jakarta.ws.rs.container.ContainerRequestContext;
24+
import jakarta.ws.rs.core.Context;
2525
import jakarta.ws.rs.core.Response;
2626
import jakarta.ws.rs.core.StreamingOutput;
27-
import org.eclipse.edc.connector.dataplane.spi.iam.DataPlaneAuthorizationService;
27+
import org.eclipse.edc.spi.iam.ClaimToken;
2828
import org.eclipse.edc.spi.query.QuerySpec;
2929
import org.eclipse.edc.transaction.spi.TransactionContext;
3030
import org.eclipse.edc.virtualized.dataplane.cert.model.ActivityItem;
@@ -35,30 +35,27 @@
3535
import java.io.InputStream;
3636
import java.time.Instant;
3737
import java.util.List;
38-
import java.util.Map;
3938

40-
import static jakarta.ws.rs.core.HttpHeaders.AUTHORIZATION;
4139
import static jakarta.ws.rs.core.Response.Status.FORBIDDEN;
4240
import static jakarta.ws.rs.core.Response.Status.UNAUTHORIZED;
41+
import static org.eclipse.edc.api.authentication.filter.Constants.REQUEST_PROPERTY_CLAIMS;
4342

4443
@Path("certs")
4544
public class CertExchangePublicController {
4645

47-
private final DataPlaneAuthorizationService authorizationService;
4846
private final CertStore certStore;
4947
private final TransactionContext transactionContext;
5048

51-
public CertExchangePublicController(DataPlaneAuthorizationService authorizationService, CertStore certStore, TransactionContext transactionContext) {
52-
this.authorizationService = authorizationService;
49+
public CertExchangePublicController(CertStore certStore, TransactionContext transactionContext) {
5350
this.certStore = certStore;
5451
this.transactionContext = transactionContext;
5552
}
5653

5754
@POST
5855
@Path("/request")
59-
public List<CertMetadata> queryCertificates(@HeaderParam(AUTHORIZATION) String token, QuerySpec querySpec) {
56+
public List<CertMetadata> queryCertificates(@Context ContainerRequestContext requestContext, QuerySpec querySpec) {
6057
return transactionContext.execute(() -> {
61-
checkAuth(token);
58+
checkAuth(requestContext);
6259
return certStore.queryMetadata(querySpec)
6360
// strip out the history for public API
6461
.stream().map(ct -> new CertMetadata(ct.id(), ct.contentType(), ct.properties())).toList();
@@ -67,54 +64,46 @@ public List<CertMetadata> queryCertificates(@HeaderParam(AUTHORIZATION) String t
6764

6865
@GET
6966
@Path("/{id}")
70-
public Response certificateDownload(@HeaderParam(AUTHORIZATION) String token, @PathParam("id") String id) {
67+
public Response certificateDownload(@Context ContainerRequestContext requestContext, @PathParam("id") String id) {
7168
return transactionContext.execute(() -> {
72-
var subject = checkAuth(token);
69+
var claims = checkAuth(requestContext);
7370
var metadata = certStore.getMetadata(id);
7471
if (metadata == null) {
7572
return Response.status(Response.Status.NOT_FOUND).build();
7673
}
77-
metadata.history().add(new ActivityItem(subject, Instant.now().getEpochSecond(), "DOWNLOAD"));
78-
certStore.updateMetadata(id, metadata);
79-
StreamingOutput stream = output -> {
80-
try (InputStream is = certStore.retrieve(id)) {
81-
is.transferTo(output);
82-
}
83-
};
84-
74+
var subject = claims.getClaim("sub");
75+
if (subject instanceof String s) {
76+
metadata.history().add(new ActivityItem(s, Instant.now().getEpochSecond(), "DOWNLOAD"));
77+
certStore.updateMetadata(id, metadata);
78+
StreamingOutput stream = output -> {
79+
try (InputStream is = certStore.retrieve(id)) {
80+
is.transferTo(output);
81+
}
82+
};
83+
84+
85+
return Response.ok(stream)
86+
.header("Content-Type", metadata.contentType())
87+
.build();
88+
} else {
89+
throw new WebApplicationException(FORBIDDEN);
90+
}
8591

86-
return Response.ok(stream)
87-
.header("Content-Type", metadata.contentType())
88-
.build();
8992
});
9093
}
9194

92-
private String checkAuth(String token) {
93-
if (token == null) {
95+
private ClaimToken checkAuth(ContainerRequestContext requestContext) {
96+
if (requestContext == null) {
9497
throw new WebApplicationException(UNAUTHORIZED);
9598
}
96-
97-
// assuming "token" is a JWT, lets parse it and extract the `sub` claim
98-
var subject = parseJwt(token);
99-
100-
101-
var sourceDataAddress = authorizationService.authorize(token, Map.of());
102-
if (sourceDataAddress.failed()) {
99+
var claims = requestContext.getProperty(REQUEST_PROPERTY_CLAIMS);
100+
if (claims instanceof ClaimToken claimToken) {
101+
return claimToken;
102+
} else {
103103
throw new WebApplicationException(FORBIDDEN);
104-
105104
}
106-
107-
return subject;
108105
}
109106

110-
private String parseJwt(String token) {
111-
try {
112-
var signedJwt = SignedJWT.parse(token);
113-
return String.join(",", signedJwt.getJWTClaimsSet().getAudience());
114-
} catch (Exception e) {
115-
return null;
116-
}
117-
}
118107

119108
@NotNull
120109
protected <T> TypeReference<T> getTypeRef() {

extensions/data-plane-public-api-v2/build.gradle.kts

Lines changed: 0 additions & 47 deletions
This file was deleted.

0 commit comments

Comments
 (0)