From df5e38b1cb6aaa9c26c7e01d68ce46b2bee0e6e4 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger <43503240+paullatzelsperger@users.noreply.github.com> Date: Fri, 23 Aug 2024 13:07:12 +0200 Subject: [PATCH] chore: deprecate resolution api context (#432) --- .github/workflows/verify.yaml | 4 ++-- README.md | 19 ++++++++------- .../api/PresentationApiExtension.java | 23 +++++++++++++++---- .../identityhub/api/v1/PresentationApi.java | 4 ++-- .../resources/presentation-api-version.json | 2 +- .../identity-hub-modules.md | 2 +- .../architecture/identityhub-apis.md | 2 +- .../tests/PresentationApiEndToEndTest.java | 22 +++++++++--------- .../IdentityHubEndToEndTestContext.java | 4 ++-- .../IdentityHubRuntimeConfiguration.java | 14 +++++------ .../spi/IdentityHubApiContext.java | 4 +++- 11 files changed, 59 insertions(+), 41 deletions(-) diff --git a/.github/workflows/verify.yaml b/.github/workflows/verify.yaml index e3fde087e..ed42f0cdb 100644 --- a/.github/workflows/verify.yaml +++ b/.github/workflows/verify.yaml @@ -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' diff --git a/README.md b/README.md index 3a4bf9752..401ecacd4 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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 \ @@ -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) @@ -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" \ diff --git a/core/presentation-api/src/main/java/org/eclipse/edc/identityhub/api/PresentationApiExtension.java b/core/presentation-api/src/main/java/org/eclipse/edc/identityhub/api/PresentationApiExtension.java index 72173c1ec..5f3a7511f 100644 --- a/core/presentation-api/src/main/java/org/eclipse/edc/identityhub/api/PresentationApiExtension.java +++ b/core/presentation-api/src/main/java/org/eclipse/edc/identityhub/api/PresentationApiExtension.java @@ -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; @@ -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)); @@ -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) { diff --git a/core/presentation-api/src/main/java/org/eclipse/edc/identityhub/api/v1/PresentationApi.java b/core/presentation-api/src/main/java/org/eclipse/edc/identityhub/api/v1/PresentationApi.java index 0be9e302b..d2597522a 100644 --- a/core/presentation-api/src/main/java/org/eclipse/edc/identityhub/api/v1/PresentationApi.java +++ b/core/presentation-api/src/main/java/org/eclipse/edc/identityhub/api/v1/PresentationApi.java @@ -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", @@ -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 = { diff --git a/core/presentation-api/src/main/resources/presentation-api-version.json b/core/presentation-api/src/main/resources/presentation-api-version.json index 3b1fa47fb..ac1c52ca5 100644 --- a/core/presentation-api/src/main/resources/presentation-api-version.json +++ b/core/presentation-api/src/main/resources/presentation-api-version.json @@ -2,7 +2,7 @@ { "version": "1.0.0", "urlPath": "/v1", - "lastUpdated": "2024-08-22T09:20:00Z", + "lastUpdated": "2024-08-22T12:00:00Z", "maturity": "stable" } ] \ No newline at end of file diff --git a/docs/developer/architecture/identity-trust-protocol/identity-hub-modules.md b/docs/developer/architecture/identity-trust-protocol/identity-hub-modules.md index 695fcebe6..fdcddbf5c 100644 --- a/docs/developer/architecture/identity-trust-protocol/identity-hub-modules.md +++ b/docs/developer/architecture/identity-trust-protocol/identity-hub-modules.md @@ -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 diff --git a/docs/developer/architecture/identityhub-apis.md b/docs/developer/architecture/identityhub-apis.md index f31da9e76..698969940 100644 --- a/docs/developer/architecture/identityhub-apis.md +++ b/docs/developer/architecture/identityhub-apis.md @@ -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 diff --git a/e2e-tests/api-tests/src/test/java/org/eclipse/edc/identityhub/tests/PresentationApiEndToEndTest.java b/e2e-tests/api-tests/src/test/java/org/eclipse/edc/identityhub/tests/PresentationApiEndToEndTest.java index 6c81a15c1..861ccda82 100644 --- a/e2e-tests/api-tests/src/test/java/org/eclipse/edc/identityhub/tests/PresentationApiEndToEndTest.java +++ b/e2e-tests/api-tests/src/test/java/org/eclipse/edc/identityhub/tests/PresentationApiEndToEndTest.java @@ -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() @@ -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) @@ -191,7 +191,7 @@ void query_withPresentationDefinition_shouldReturn503(IdentityHubEndToEndTestCon } } """; - context.getResolutionEndpoint().baseRequest() + context.getPresentationEndpoint().baseRequest() .contentType(JSON) .header(AUTHORIZATION, generateSiToken()) .body(query) @@ -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) @@ -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) @@ -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) @@ -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) @@ -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) @@ -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) @@ -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) @@ -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) diff --git a/e2e-tests/api-tests/src/test/java/org/eclipse/edc/identityhub/tests/fixtures/IdentityHubEndToEndTestContext.java b/e2e-tests/api-tests/src/test/java/org/eclipse/edc/identityhub/tests/fixtures/IdentityHubEndToEndTestContext.java index d0ae6f8db..04e0704ec 100644 --- a/e2e-tests/api-tests/src/test/java/org/eclipse/edc/identityhub/tests/fixtures/IdentityHubEndToEndTestContext.java +++ b/e2e-tests/api-tests/src/test/java/org/eclipse/edc/identityhub/tests/fixtures/IdentityHubEndToEndTestContext.java @@ -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(); } diff --git a/e2e-tests/api-tests/src/test/java/org/eclipse/edc/identityhub/tests/fixtures/IdentityHubRuntimeConfiguration.java b/e2e-tests/api-tests/src/test/java/org/eclipse/edc/identityhub/tests/fixtures/IdentityHubRuntimeConfiguration.java index 4ff77030d..3be2d7173 100644 --- a/e2e-tests/api-tests/src/test/java/org/eclipse/edc/identityhub/tests/fixtures/IdentityHubRuntimeConfiguration.java +++ b/e2e-tests/api-tests/src/test/java/org/eclipse/edc/identityhub/tests/fixtures/IdentityHubRuntimeConfiguration.java @@ -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 config() { @@ -44,8 +44,8 @@ public Map 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); @@ -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; } } diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/IdentityHubApiContext.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/IdentityHubApiContext.java index a205975a8..fcd609b56 100644 --- a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/IdentityHubApiContext.java +++ b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/IdentityHubApiContext.java @@ -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"; }