Skip to content

Commit

Permalink
chore: deprecate resolution api context (#432)
Browse files Browse the repository at this point in the history
  • Loading branch information
paullatzelsperger authored Aug 23, 2024
1 parent 77a2434 commit df5e38b
Show file tree
Hide file tree
Showing 11 changed files with 59 additions and 41 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/verify.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ jobs:
docker run -d --rm --name identity-hub \
-e "WEB_HTTP_IDENTITY_PORT=8182" \
-e "WEB_HTTP_IDENTITY_PATH=/api/identity" \
-e "WEB_HTTP_RESOLUTION_PORT=10001" \
-e "WEB_HTTP_RESOLUTION_PATH=/api/resolution" \
-e "WEB_HTTP_PRESENTATION_PORT=10001" \
-e "WEB_HTTP_PRESENTATION_PATH=/api/presentation" \
identity-hub:latest
- name: 'Wait for Identity Hub to be healthy'
Expand Down
19 changes: 11 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@ extensibility mechanism.
Here, developers find everything necessary to build and run a basic "vanilla" version of IdentityHub.

## Security Warning
Older versions of IdentityHub (in particular <= 0.3.1 ) **must not be used anymore**, as they were intended for proof-of-concept
purposes only and may contain **significant security vulnerabilities** (for example missing authn/authz on the API) and possibly
others.

Older versions of IdentityHub (in particular <= 0.3.1 ) **must not be used anymore**, as they were intended for
proof-of-concept
purposes only and may contain **significant security vulnerabilities** (for example missing authn/authz on the API) and
possibly
others.
**Please always use the latest version of IdentityHub.**

## Quick start
Expand All @@ -39,8 +42,8 @@ two ways of running IdentityHub:
Once the jar file is built, IdentityHub can be launched using this shell command:

```bash
java -Dweb.http.resolution.port=10001 \
-Dweb.http.resolution.path="/api/resolution" \
java -Dweb.http.presentation.port=10001 \
-Dweb.http.presentation.path="/api/presentation" \
-Dweb.http.port=8181 \
-Dweb.http.path="/api" \
-Dweb.http.identity.port=8182 \
Expand All @@ -49,7 +52,7 @@ java -Dweb.http.resolution.port=10001 \
-jar launcher/build/libs/identity-hub.jar
```

this will expose the Presentation API at `http://localhost:10001/api/resolution` and the Identity API
this will expose the Presentation API at `http://localhost:10001/api/presentation` and the Identity API
at `http://localhost:8191/api/identity`. More information about IdentityHub's APIs can be
found [here](docs/developer/architecture/identityhub-apis.md)

Expand All @@ -63,8 +66,8 @@ docker build -t identity-hub ./launcher

```bash
docker run --rm --name identity-hub \
-e "WEB_HTTP_RESOLUTION_PORT=10001" \
-e "WEB_HTTP_RESOLUTION_PATH=/api/resolution/" \
-e "WEB_HTTP_PRESENTATION_PORT=10001" \
-e "WEB_HTTP_PRESENTATION_PATH=/api/presentation/" \
-e "WEB_HTTP_PATH=/api" \
-e "WEB_HTTP_PORT=8181" \
-e "WEB_HTTP_IDENTITY_PORT=8182" \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,14 @@
import static org.eclipse.edc.iam.identitytrust.spi.DcpConstants.DCP_CONTEXT_URL;
import static org.eclipse.edc.identityhub.api.PresentationApiExtension.NAME;
import static org.eclipse.edc.identityhub.spi.IdentityHubApiContext.PRESENTATION;
import static org.eclipse.edc.identityhub.spi.IdentityHubApiContext.RESOLUTION;
import static org.eclipse.edc.spi.constants.CoreConstants.JSON_LD;

@Extension(value = NAME)
public class PresentationApiExtension implements ServiceExtension {

public static final String NAME = "Presentation API Extension";
public static final String RESOLUTION_SCOPE = "resolution-scope";
public static final String PRESENTATION_SCOPE = "presentation-scope";
private static final String API_VERSION_JSON_FILE = "presentation-api-version.json";
@Inject
private TypeTransformerRegistry typeTransformer;
Expand Down Expand Up @@ -89,11 +90,14 @@ public void initialize(ServiceExtensionContext context) {
var controller = new PresentationApiController(validatorRegistry, typeTransformer, credentialResolver, accessTokenVerifier, verifiablePresentationService, context.getMonitor(), participantContextService);

var jsonLdMapper = typeManager.getMapper(JSON_LD);
webService.registerResource(PRESENTATION, new ObjectMapperProvider(jsonLdMapper));
webService.registerResource(PRESENTATION, new JerseyJsonLdInterceptor(jsonLd, jsonLdMapper, RESOLUTION_SCOPE));
webService.registerResource(PRESENTATION, controller);

jsonLd.registerContext(DCP_CONTEXT_URL, RESOLUTION_SCOPE);
var contextString = determineApiContext(context);

webService.registerResource(contextString, new ObjectMapperProvider(jsonLdMapper));
webService.registerResource(contextString, new JerseyJsonLdInterceptor(jsonLd, jsonLdMapper, PRESENTATION_SCOPE));
webService.registerResource(contextString, controller);

jsonLd.registerContext(DCP_CONTEXT_URL, PRESENTATION_SCOPE);

// register transformer -- remove once registration is handled in EDC
typeTransformer.register(new JsonObjectToPresentationQueryTransformer(jsonLdMapper));
Expand All @@ -103,6 +107,15 @@ public void initialize(ServiceExtensionContext context) {
registerVersionInfo(getClass().getClassLoader());
}

private String determineApiContext(ServiceExtensionContext context) {

if (context.getConfig("web.http").getRelativeEntries(PRESENTATION).isEmpty() && !context.getConfig("web.http").getRelativeEntries(RESOLUTION).isEmpty()) {
context.getMonitor().warning("Deprecated config: 'web.http.%s.* was replaced by 'web.http.%s.*', please update at your earliest convenience.".formatted(RESOLUTION, PRESENTATION));
return RESOLUTION;
}
return PRESENTATION;
}

private void registerVersionInfo(ClassLoader resourceClassLoader) {
try (var versionContent = resourceClassLoader.getResourceAsStream(API_VERSION_JSON_FILE)) {
if (versionContent == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
import jakarta.ws.rs.core.Response;

@OpenAPIDefinition(
info = @Info(description = "This represents the Presentation API as per DCP specification. It serves endpoints to query for specific VerifiablePresentations.", title = "Resolution API",
info = @Info(description = "This represents the Presentation API as per DCP specification. It serves endpoints to query for specific VerifiablePresentations.", title = "Presentation API",
version = "1"))
@SecurityScheme(name = "Authentication",
description = "Self-Issued ID token containing an access_token",
Expand All @@ -39,7 +39,7 @@
bearerFormat = "JWT")
public interface PresentationApi {

@Tag(name = "Resolution API")
@Tag(name = "Presentation API")
@Operation(description = "Issues a new presentation query, that contains either a DIF presentation definition, or a list of scopes",
requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = ApiSchema.PresentationQuerySchema.class))),
responses = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
{
"version": "1.0.0",
"urlPath": "/v1",
"lastUpdated": "2024-08-22T09:20:00Z",
"lastUpdated": "2024-08-22T12:00:00Z",
"maturity": "stable"
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ all SPIs that are relevant here.
## Hub API (`:core:presentation-api`)

This module contains implementations for
the [Resolution API](https://github.com/eclipse-tractusx/identity-trust/blob/main/specifications/M1/verifiable.presentation.protocol.md#4-resolution-api)
the [Presentation API](https://github.com/eclipse-tractusx/identity-trust/blob/main/specifications/M1/verifiable.presentation.protocol.md#4-resolution-api)
and
the [Storage API](https://github.com/eclipse-tractusx/identity-trust/blob/main/specifications/M1/verifiable.presentation.protocol.md#5-storage-api).
Is
Expand Down
2 changes: 1 addition & 1 deletion docs/developer/architecture/identityhub-apis.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ These APIs is intended to be exposed to the internet.
This API allows clients to request credentials in the form of a VerifiablePresentation. It is part of the Verifiable
Credential Presentation protocol of the DCP specification.

Please refer to the [API documentation](https://eclipse-edc.github.io/IdentityHub/openapi/ih-resolution-api) for more
Please refer to the [API documentation](https://eclipse-edc.github.io/IdentityHub/openapi/presentation-api) for more
details.

### Storage API
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ void teardown(ParticipantContextService contextService, CredentialStore store) {
@Test
void query_tokenNotPresent_shouldReturn401(IdentityHubEndToEndTestContext context) {

context.getResolutionEndpoint().baseRequest()
context.getPresentationEndpoint().baseRequest()
.contentType("application/json")
.post("/v1/participants/%s/presentations/query".formatted(TEST_PARTICIPANT_CONTEXT_ID_ENCODED))
.then()
Expand All @@ -166,7 +166,7 @@ void query_validationError_shouldReturn400(IdentityHubEndToEndTestContext contex
"@type": "PresentationQueryMessage"
}
""";
context.getResolutionEndpoint().baseRequest()
context.getPresentationEndpoint().baseRequest()
.contentType(JSON)
.header(AUTHORIZATION, generateSiToken())
.body(query)
Expand All @@ -191,7 +191,7 @@ void query_withPresentationDefinition_shouldReturn503(IdentityHubEndToEndTestCon
}
}
""";
context.getResolutionEndpoint().baseRequest()
context.getPresentationEndpoint().baseRequest()
.contentType(JSON)
.header(AUTHORIZATION, generateSiToken())
.body(query)
Expand All @@ -209,7 +209,7 @@ void query_tokenVerificationFails_shouldReturn401(IdentityHubEndToEndTestContext
when(DID_PUBLIC_KEY_RESOLVER.resolveKey(eq("did:web:provider#key1"))).thenReturn(Result.success(spoofedKey.toPublicKey()));

var token = generateSiToken();
context.getResolutionEndpoint().baseRequest()
context.getPresentationEndpoint().baseRequest()
.contentType(JSON)
.header(AUTHORIZATION, token)
.body(VALID_QUERY_WITH_SCOPE)
Expand All @@ -231,7 +231,7 @@ void query_proofOfPossessionFails_shouldReturn401(IdentityHubEndToEndTestContext
when(DID_PUBLIC_KEY_RESOLVER.resolveKey(eq("did:web:consumer#key1"))).thenReturn(Result.success(CONSUMER_KEY.toPublicKey()));
when(DID_PUBLIC_KEY_RESOLVER.resolveKey(eq("did:web:provider#key1"))).thenReturn(Result.success(PROVIDER_KEY.toPublicKey()));

context.getResolutionEndpoint().baseRequest()
context.getPresentationEndpoint().baseRequest()
.contentType(JSON)
.header(AUTHORIZATION, token)
.body(VALID_QUERY_WITH_SCOPE)
Expand Down Expand Up @@ -275,7 +275,7 @@ void query_credentialQueryResolverFails_shouldReturn403(IdentityHubEndToEndTestC
store.create(res2);


context.getResolutionEndpoint().baseRequest()
context.getPresentationEndpoint().baseRequest()
.contentType(JSON)
.header(AUTHORIZATION, token)
.body(VALID_QUERY_WITH_ADDITIONAL_SCOPE)
Expand All @@ -295,7 +295,7 @@ void query_success_noCredentials(IdentityHubEndToEndTestContext context) throws
when(DID_PUBLIC_KEY_RESOLVER.resolveKey(eq("did:web:consumer#key1"))).thenReturn(Result.success(CONSUMER_KEY.toPublicKey()));
when(DID_PUBLIC_KEY_RESOLVER.resolveKey(eq("did:web:provider#key1"))).thenReturn(Result.success(PROVIDER_KEY.toPublicKey()));

var response = context.getResolutionEndpoint().baseRequest()
var response = context.getPresentationEndpoint().baseRequest()
.contentType(JSON)
.header(AUTHORIZATION, token)
.body(VALID_QUERY_WITH_SCOPE)
Expand Down Expand Up @@ -329,7 +329,7 @@ void query_success_containsCredential(IdentityHubEndToEndTestContext context, Cr
when(DID_PUBLIC_KEY_RESOLVER.resolveKey(eq("did:web:consumer#key1"))).thenReturn(Result.success(CONSUMER_KEY.toPublicKey()));
when(DID_PUBLIC_KEY_RESOLVER.resolveKey(eq("did:web:provider#key1"))).thenReturn(Result.success(PROVIDER_KEY.toPublicKey()));

var response = context.getResolutionEndpoint().baseRequest()
var response = context.getPresentationEndpoint().baseRequest()
.contentType(JSON)
.header(AUTHORIZATION, token)
.body(VALID_QUERY_WITH_SCOPE)
Expand Down Expand Up @@ -390,7 +390,7 @@ void query_shouldFilterOutInvalidCreds(int vcStateCode, IdentityHubEndToEndTestC
when(DID_PUBLIC_KEY_RESOLVER.resolveKey(eq("did:web:provider#key1"))).thenReturn(Result.success(PROVIDER_KEY.toPublicKey()));

var token = generateSiToken();
var response = context.getResolutionEndpoint().baseRequest()
var response = context.getPresentationEndpoint().baseRequest()
.contentType(JSON)
.header(AUTHORIZATION, token)
.body(VALID_QUERY_WITH_SCOPE)
Expand Down Expand Up @@ -431,7 +431,7 @@ void query_accessTokenKeyIdDoesNotBelongToParticipant_shouldReturn401(IdentityHu
when(DID_PUBLIC_KEY_RESOLVER.resolveKey(eq("did:web:consumer#key1"))).thenReturn(Result.success(CONSUMER_KEY.toPublicKey()));
when(DID_PUBLIC_KEY_RESOLVER.resolveKey(eq("did:web:provider#key1"))).thenReturn(Result.success(PROVIDER_KEY.toPublicKey()));

context.getResolutionEndpoint().baseRequest()
context.getPresentationEndpoint().baseRequest()
.contentType(JSON)
.header(AUTHORIZATION, token)
.body(VALID_QUERY_WITH_SCOPE)
Expand Down Expand Up @@ -463,7 +463,7 @@ void query_accessTokenAudienceDoesNotBelongToParticipant_shouldReturn401(Identit
when(DID_PUBLIC_KEY_RESOLVER.resolveKey(eq("did:web:consumer#key1"))).thenReturn(Result.success(CONSUMER_KEY.toPublicKey()));
when(DID_PUBLIC_KEY_RESOLVER.resolveKey(eq("did:web:provider#key1"))).thenReturn(Result.success(PROVIDER_KEY.toPublicKey()));

context.getResolutionEndpoint().baseRequest()
context.getPresentationEndpoint().baseRequest()
.contentType(JSON)
.header(AUTHORIZATION, token)
.body(VALID_QUERY_WITH_SCOPE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ public IdentityHubRuntimeConfiguration.Endpoint getIdentityApiEndpoint() {
return configuration.getIdentityApiEndpoint();
}

public IdentityHubRuntimeConfiguration.Endpoint getResolutionEndpoint() {
return configuration.getResolutionEndpoint();
public IdentityHubRuntimeConfiguration.Endpoint getPresentationEndpoint() {
return configuration.getPresentationEndpoint();
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@
*/
public class IdentityHubRuntimeConfiguration {

private Endpoint resolutionEndpoint;
private Endpoint presentationEndpoint;
private Endpoint identityEndpoint;
private String id;
private String name;

public Endpoint getResolutionEndpoint() {
return resolutionEndpoint;
public Endpoint getPresentationEndpoint() {
return presentationEndpoint;
}

public Map<String, String> config() {
Expand All @@ -44,8 +44,8 @@ public Map<String, String> config() {
put(PARTICIPANT_ID, id);
put("web.http.port", String.valueOf(getFreePort()));
put("web.http.path", "/api/v1");
put("web.http.resolution.port", String.valueOf(resolutionEndpoint.getUrl().getPort()));
put("web.http.resolution.path", resolutionEndpoint.getUrl().getPath());
put("web.http.presentation.port", String.valueOf(presentationEndpoint.getUrl().getPort()));
put("web.http.presentation.path", presentationEndpoint.getUrl().getPath());
put("web.http.identity.port", String.valueOf(identityEndpoint.getUrl().getPort()));
put("web.http.identity.path", identityEndpoint.getUrl().getPath());
put("edc.runtime.id", name);
Expand Down Expand Up @@ -81,8 +81,8 @@ public Builder name(String name) {
}

public IdentityHubRuntimeConfiguration build() {
participant.resolutionEndpoint = new Endpoint(URI.create("http://localhost:" + getFreePort() + "/api/v1/resolution"), Map.of());
participant.identityEndpoint = new Endpoint(URI.create("http://localhost:" + getFreePort() + "/api/management"), Map.of());
participant.presentationEndpoint = new Endpoint(URI.create("http://localhost:" + getFreePort() + "/api/presentation"), Map.of());
participant.identityEndpoint = new Endpoint(URI.create("http://localhost:" + getFreePort() + "/api/identity"), Map.of());
return participant;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,7 @@
public interface IdentityHubApiContext {
String IDENTITY = "identity";
String IH_DID = "did";
String PRESENTATION = "resolution"; // should be "presentation", but this would break config
String PRESENTATION = "presentation";
@Deprecated(since = "0.9.0")
String RESOLUTION = "resolution";
}

0 comments on commit df5e38b

Please sign in to comment.