diff --git a/.github/actions/gradle-setup/action.yml b/.github/actions/gradle-setup/action.yml index 0e27dda1d..fe003d280 100644 --- a/.github/actions/gradle-setup/action.yml +++ b/.github/actions/gradle-setup/action.yml @@ -4,7 +4,7 @@ runs: using: "composite" steps: # Install Java and cache MVD Gradle build. - - uses: actions/setup-java@v2 + - uses: actions/setup-java@v3 with: java-version: '17' distribution: 'temurin' diff --git a/.github/workflows/verify.yaml b/.github/workflows/verify.yaml index f5d834f67..b8d5aa5cd 100644 --- a/.github/workflows/verify.yaml +++ b/.github/workflows/verify.yaml @@ -21,19 +21,9 @@ jobs: - name: Run Checkstyle run: ./gradlew checkstyleMain checkstyleTest checkstyleTestFixtures - OpenAPI-Definitions: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/gradle-setup - - - name: Generate OpenAPI definition from code - run: ./gradlew resolve - - - name: Check OpenAPI definition match code - run: git diff --exit-code - Verify-Launcher: + # disabled temporarily + if: false runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -79,7 +69,7 @@ jobs: # with: # files: "**/test-results/**/*.xml" - Component-Tests: + Integration-Tests: runs-on: ubuntu-latest env: JACOCO: true @@ -91,12 +81,12 @@ jobs: - name: Component Tests uses: ./.github/actions/run-tests with: - command: ./gradlew test -DincludeTags="ComponentTest" + command: ./gradlew compileJava compileTestJava test -DincludeTags="ComponentTest,ApiTest,EndToEndTest" Upload-Coverage-Report-To-Codecov: needs: - Test - - Component-Tests + - Integration-Tests runs-on: ubuntu-latest if: always() steps: diff --git a/DEPENDENCIES b/DEPENDENCIES index 3913b97e7..9623111ba 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -1,3 +1,7 @@ +maven/mavencentral/com.apicatalog/carbon-did/0.0.2, Apache-2.0, approved, #9239 +maven/mavencentral/com.apicatalog/iron-verifiable-credentials/0.8.1, Apache-2.0, approved, #9234 +maven/mavencentral/com.apicatalog/titanium-json-ld/1.0.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.apicatalog/titanium-json-ld/1.3.1, Apache-2.0, approved, #8912 maven/mavencentral/com.apicatalog/titanium-json-ld/1.3.2, Apache-2.0, approved, #8912 maven/mavencentral/com.ethlo.time/itu/1.7.0, Apache-2.0, approved, clearlydefined maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.10.3, Apache-2.0, approved, CQ21280 @@ -7,10 +11,12 @@ maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.14.1, Apache maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.14.2, Apache-2.0, approved, #5303 maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.15.1, Apache-2.0, approved, #7947 maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.15.2, Apache-2.0, approved, #7947 +maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.15.3, Apache-2.0, approved, #7947 maven/mavencentral/com.fasterxml.jackson.core/jackson-core/2.14.1, Apache-2.0 AND MIT, approved, #4303 maven/mavencentral/com.fasterxml.jackson.core/jackson-core/2.14.2, Apache-2.0 AND MIT, approved, #4303 maven/mavencentral/com.fasterxml.jackson.core/jackson-core/2.15.1, MIT AND Apache-2.0, approved, #7932 maven/mavencentral/com.fasterxml.jackson.core/jackson-core/2.15.2, MIT AND Apache-2.0, approved, #7932 +maven/mavencentral/com.fasterxml.jackson.core/jackson-core/2.15.3, MIT AND Apache-2.0, approved, #7932 maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.11.0, Apache-2.0, approved, CQ23093 maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.12.1, Apache-2.0, approved, CQ22967 maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.14.0, Apache-2.0, approved, #4105 @@ -18,23 +24,25 @@ maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.14.1, Apache-2. maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.14.2, Apache-2.0, approved, #4105 maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.15.1, Apache-2.0, approved, #7934 maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.15.2, Apache-2.0, approved, #7934 +maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.15.3, Apache-2.0, approved, #7934 maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-yaml/2.12.1, Apache-2.0, approved, CQ23167 maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-yaml/2.14.0, Apache-2.0, approved, #5933 maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-yaml/2.15.1, Apache-2.0, approved, #8802 -maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-yaml/2.15.2, Apache-2.0, approved, #8802 -maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jakarta-jsonp/2.15.2, Apache-2.0, approved, #9179 +maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-yaml/2.15.3, Apache-2.0, approved, #8802 +maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jakarta-jsonp/2.15.3, Apache-2.0, approved, #9179 maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jsr310/2.12.1, Apache-2.0, approved, CQ23727 maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jsr310/2.14.0, Apache-2.0, approved, #4699 maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jsr310/2.14.2, Apache-2.0, approved, #4699 maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jsr310/2.15.1, Apache-2.0, approved, #7930 maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jsr310/2.15.2, Apache-2.0, approved, #7930 -maven/mavencentral/com.fasterxml.jackson.jakarta.rs/jackson-jakarta-rs-base/2.15.2, Apache-2.0, approved, #9235 +maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jsr310/2.15.3, Apache-2.0, approved, #7930 +maven/mavencentral/com.fasterxml.jackson.jakarta.rs/jackson-jakarta-rs-base/2.15.3, Apache-2.0, approved, #9235 maven/mavencentral/com.fasterxml.jackson.jakarta.rs/jackson-jakarta-rs-json-provider/2.15.1, Apache-2.0, approved, #9236 -maven/mavencentral/com.fasterxml.jackson.jakarta.rs/jackson-jakarta-rs-json-provider/2.15.2, Apache-2.0, approved, #9236 +maven/mavencentral/com.fasterxml.jackson.jakarta.rs/jackson-jakarta-rs-json-provider/2.15.3, Apache-2.0, approved, #9236 maven/mavencentral/com.fasterxml.jackson.module/jackson-module-jakarta-xmlbind-annotations/2.14.1, Apache-2.0, approved, #5308 -maven/mavencentral/com.fasterxml.jackson.module/jackson-module-jakarta-xmlbind-annotations/2.15.2, Apache-2.0, approved, #9241 +maven/mavencentral/com.fasterxml.jackson.module/jackson-module-jakarta-xmlbind-annotations/2.15.3, Apache-2.0, approved, #9241 maven/mavencentral/com.fasterxml.jackson/jackson-bom/2.15.1, Apache-2.0, approved, #7929 -maven/mavencentral/com.fasterxml.jackson/jackson-bom/2.15.2, Apache-2.0, approved, #7929 +maven/mavencentral/com.fasterxml.jackson/jackson-bom/2.15.3, Apache-2.0, approved, #7929 maven/mavencentral/com.fasterxml.uuid/java-uuid-generator/4.1.0, Apache-2.0, approved, clearlydefined maven/mavencentral/com.github.cliftonlabs/json-simple/3.0.2, Apache-2.0, approved, clearlydefined maven/mavencentral/com.github.docker-java/docker-java-api/3.3.3, Apache-2.0, approved, #10346 @@ -68,14 +76,14 @@ maven/mavencentral/com.lmax/disruptor/3.4.4, Apache-2.0, approved, clearlydefine maven/mavencentral/com.networknt/json-schema-validator/1.0.76, Apache-2.0, approved, CQ22638 maven/mavencentral/com.nimbusds/nimbus-jose-jwt/9.25, Apache-2.0, approved, clearlydefined maven/mavencentral/com.nimbusds/nimbus-jose-jwt/9.28, Apache-2.0, approved, clearlydefined -maven/mavencentral/com.nimbusds/nimbus-jose-jwt/9.32, Apache-2.0, approved, #10561 +maven/mavencentral/com.nimbusds/nimbus-jose-jwt/9.37, Apache-2.0, approved, #11086 maven/mavencentral/com.puppycrawl.tools/checkstyle/10.0, LGPL-2.1-or-later, approved, #7936 maven/mavencentral/com.samskivert/jmustache/1.15, BSD-2-Clause, approved, clearlydefined -maven/mavencentral/com.squareup.okhttp3/okhttp-dnsoverhttps/4.11.0, Apache-2.0, approved, clearlydefined -maven/mavencentral/com.squareup.okhttp3/okhttp/4.11.0, Apache-2.0, approved, #9240 +maven/mavencentral/com.squareup.okhttp3/okhttp-dnsoverhttps/4.12.0, Apache-2.0, approved, #11159 +maven/mavencentral/com.squareup.okhttp3/okhttp/4.12.0, Apache-2.0, approved, #11156 maven/mavencentral/com.squareup.okhttp3/okhttp/4.9.3, Apache-2.0 AND MPL-2.0, approved, #3225 -maven/mavencentral/com.squareup.okio/okio-jvm/3.2.0, Apache-2.0, approved, clearlydefined -maven/mavencentral/com.squareup.okio/okio/3.2.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.squareup.okio/okio-jvm/3.6.0, Apache-2.0, approved, #11158 +maven/mavencentral/com.squareup.okio/okio/3.6.0, Apache-2.0, approved, #11155 maven/mavencentral/com.sun.activation/jakarta.activation/2.0.0, EPL-2.0 OR BSD-3-Clause OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jaf maven/mavencentral/com.sun.activation/jakarta.activation/2.0.1, EPL-2.0 OR BSD-3-Clause OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jaf maven/mavencentral/com.sun.mail/mailapi/1.6.2, CDDL-1.1 OR GPL-2.0-only WITH Classpath-exception-2.0, approved, clearlydefined @@ -91,9 +99,7 @@ maven/mavencentral/commons-logging/commons-logging/1.1.1, Apache-2.0, approved, maven/mavencentral/commons-logging/commons-logging/1.2, Apache-2.0, approved, CQ10162 maven/mavencentral/dev.failsafe/failsafe-okhttp/3.3.2, Apache-2.0, approved, #9178 maven/mavencentral/dev.failsafe/failsafe/3.3.2, Apache-2.0, approved, #9268 -maven/mavencentral/info.picocli/picocli-codegen/4.7.5, Apache-2.0, approved, #10355 maven/mavencentral/info.picocli/picocli/4.6.3, Apache-2.0, approved, clearlydefined -maven/mavencentral/info.picocli/picocli/4.7.5, Apache-2.0, approved, #4365 maven/mavencentral/io.github.classgraph/classgraph/4.8.138, MIT, approved, CQ22530 maven/mavencentral/io.github.classgraph/classgraph/4.8.154, MIT, approved, CQ22530 maven/mavencentral/io.netty/netty-buffer/4.1.86.Final, Apache-2.0, approved, CQ21842 @@ -109,8 +115,8 @@ maven/mavencentral/io.netty/netty-tcnative-boringssl-static/2.0.56.Final, Apache maven/mavencentral/io.netty/netty-tcnative-classes/2.0.56.Final, Apache-2.0, approved, clearlydefined maven/mavencentral/io.netty/netty-transport-native-unix-common/4.1.86.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 maven/mavencentral/io.netty/netty-transport/4.1.86.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.opentelemetry/opentelemetry-api/1.29.0, Apache-2.0, approved, #10088 -maven/mavencentral/io.opentelemetry/opentelemetry-context/1.29.0, Apache-2.0, approved, #10090 +maven/mavencentral/io.opentelemetry/opentelemetry-api/1.31.0, Apache-2.0, approved, #11087 +maven/mavencentral/io.opentelemetry/opentelemetry-context/1.31.0, Apache-2.0, approved, #11088 maven/mavencentral/io.prometheus/simpleclient/0.16.0, Apache-2.0, approved, clearlydefined maven/mavencentral/io.prometheus/simpleclient_common/0.16.0, Apache-2.0, approved, clearlydefined maven/mavencentral/io.prometheus/simpleclient_httpserver/0.16.0, Apache-2.0, approved, clearlydefined @@ -121,6 +127,7 @@ maven/mavencentral/io.rest-assured/json-path/5.3.2, Apache-2.0, approved, #9261 maven/mavencentral/io.rest-assured/rest-assured-common/5.3.2, Apache-2.0, approved, #9264 maven/mavencentral/io.rest-assured/rest-assured/5.3.2, Apache-2.0, approved, #9262 maven/mavencentral/io.rest-assured/xml-path/5.3.2, Apache-2.0, approved, #9267 +maven/mavencentral/io.setl/rdf-urdna/1.1, Apache-2.0, approved, clearlydefined maven/mavencentral/io.swagger.core.v3/swagger-annotations-jakarta/2.1.13, Apache-2.0, approved, clearlydefined maven/mavencentral/io.swagger.core.v3/swagger-annotations-jakarta/2.2.15, Apache-2.0, approved, #5947 maven/mavencentral/io.swagger.core.v3/swagger-annotations/2.2.15, Apache-2.0, approved, clearlydefined @@ -177,7 +184,7 @@ maven/mavencentral/net.minidev/json-smart/2.4.7, Apache-2.0, approved, #3288 maven/mavencentral/net.sf.jopt-simple/jopt-simple/5.0.4, MIT, approved, CQ13174 maven/mavencentral/net.sf.saxon/Saxon-HE/10.6, MPL-2.0 AND W3C, approved, #7945 maven/mavencentral/org.antlr/antlr4-runtime/4.9.3, BSD-3-Clause, approved, #322 -maven/mavencentral/org.apache.commons/commons-compress/1.23.0, Apache-2.0 AND BSD-3-Clause, approved, #7506 +maven/mavencentral/org.apache.commons/commons-compress/1.24.0, Apache-2.0 AND BSD-3-Clause AND bzip2-1.0.6 AND LicenseRef-Public-Domain, approved, #10368 maven/mavencentral/org.apache.commons/commons-digester3/3.2, Apache-2.0, approved, clearlydefined maven/mavencentral/org.apache.commons/commons-lang3/3.10, Apache-2.0, approved, clearlydefined maven/mavencentral/org.apache.commons/commons-lang3/3.11, Apache-2.0, approved, CQ22642 @@ -195,6 +202,7 @@ maven/mavencentral/org.apache.velocity/velocity-engine-core/2.3, Apache-2.0, app maven/mavencentral/org.apache.velocity/velocity-engine-scripting/2.3, Apache-2.0, approved, clearlydefined maven/mavencentral/org.apiguardian/apiguardian-api/1.1.2, Apache-2.0, approved, clearlydefined maven/mavencentral/org.assertj/assertj-core/3.24.2, Apache-2.0, approved, #6161 +maven/mavencentral/org.awaitility/awaitility/4.2.0, Apache-2.0, approved, clearlydefined maven/mavencentral/org.bouncycastle/bcpkix-jdk18on/1.72, MIT, approved, #3789 maven/mavencentral/org.bouncycastle/bcpkix-jdk18on/1.76, MIT, approved, #9825 maven/mavencentral/org.bouncycastle/bcprov-jdk18on/1.72, MIT AND CC0-1.0, approved, #3538 @@ -203,39 +211,39 @@ maven/mavencentral/org.bouncycastle/bcutil-jdk18on/1.72, MIT, approved, #3790 maven/mavencentral/org.bouncycastle/bcutil-jdk18on/1.76, MIT, approved, #9828 maven/mavencentral/org.ccil.cowan.tagsoup/tagsoup/1.2.1, Apache-2.0, approved, clearlydefined maven/mavencentral/org.checkerframework/checker-qual/3.12.0, MIT, approved, clearlydefined -maven/mavencentral/org.checkerframework/checker-qual/3.31.0, MIT, approved, clearlydefined maven/mavencentral/org.eclipse.angus/angus-activation/1.0.0, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.angus maven/mavencentral/org.eclipse.edc/aggregate-service-spi/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/api-observability/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/asset-spi/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/autodoc-processor/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/boot/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/catalog-spi/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/connector-core/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/contract-spi/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/control-plane-spi/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/core-spi/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/http-spi/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/http/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/identity-did-core/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/identity-did-crypto/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/identity-did-spi/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/identity-did-web/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/identity-trust-spi/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/jersey-core/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/jersey-providers/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/jetty-core/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/json-ld-spi/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/json-ld/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/junit/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/jwt-spi/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/management-api-test-fixtures/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/policy-engine-spi/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/policy-engine/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/policy-evaluator/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/policy-model/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/policy-spi/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/runtime-metamodel/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/sql-core/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/state-machine/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/transaction-datasource-spi/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/transaction-spi/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transfer-data-plane-spi/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transfer-spi/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/transform-core/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/transform-spi/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/util/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc @@ -243,26 +251,26 @@ maven/mavencentral/org.eclipse.edc/validator-spi/0.3.2-SNAPSHOT, Apache-2.0, app maven/mavencentral/org.eclipse.edc/web-spi/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.jetty.toolchain/jetty-jakarta-servlet-api/5.0.2, EPL-2.0 OR Apache-2.0, approved, rt.jetty maven/mavencentral/org.eclipse.jetty.toolchain/jetty-jakarta-websocket-api/2.0.0, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty.websocket/websocket-core-client/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty.websocket/websocket-core-common/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty.websocket/websocket-core-server/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty.websocket/websocket-jakarta-client/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty.websocket/websocket-jakarta-common/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty.websocket/websocket-jakarta-server/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty.websocket/websocket-servlet/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty/jetty-alpn-client/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty/jetty-annotations/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty/jetty-client/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty/jetty-http/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty/jetty-io/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty/jetty-jndi/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty/jetty-plus/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty/jetty-security/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty/jetty-server/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty/jetty-servlet/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty/jetty-util/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty/jetty-webapp/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.eclipse.jetty/jetty-xml/11.0.16, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty.websocket/websocket-core-client/11.0.17, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty.websocket/websocket-core-common/11.0.17, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty.websocket/websocket-core-server/11.0.17, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty.websocket/websocket-jakarta-client/11.0.17, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty.websocket/websocket-jakarta-common/11.0.17, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty.websocket/websocket-jakarta-server/11.0.17, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty.websocket/websocket-servlet/11.0.17, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-alpn-client/11.0.17, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-annotations/11.0.17, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-client/11.0.17, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-http/11.0.17, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-io/11.0.17, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-jndi/11.0.17, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-plus/11.0.17, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-security/11.0.17, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-server/11.0.17, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-servlet/11.0.17, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-util/11.0.17, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-webapp/11.0.17, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-xml/11.0.17, EPL-2.0 OR Apache-2.0, approved, rt.jetty maven/mavencentral/org.glassfish.hk2.external/aopalliance-repackaged/3.0.4, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.glassfish maven/mavencentral/org.glassfish.hk2/hk2-api/3.0.4, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.glassfish maven/mavencentral/org.glassfish.hk2/hk2-locator/3.0.4, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.glassfish @@ -280,6 +288,7 @@ maven/mavencentral/org.glassfish.jersey.media/jersey-media-multipart/3.1.3, EPL- maven/mavencentral/org.glassfish/jakarta.json/2.0.1, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jsonp maven/mavencentral/org.hamcrest/hamcrest-core/1.3, BSD-2-Clause, approved, CQ11429 maven/mavencentral/org.hamcrest/hamcrest-core/2.2, BSD-3-Clause, approved, clearlydefined +maven/mavencentral/org.hamcrest/hamcrest/2.1, BSD-3-Clause, approved, clearlydefined maven/mavencentral/org.hamcrest/hamcrest/2.2, BSD-3-Clause, approved, clearlydefined maven/mavencentral/org.jacoco/org.jacoco.agent/0.8.8, EPL-2.0, approved, CQ23285 maven/mavencentral/org.jacoco/org.jacoco.ant/0.8.8, EPL-2.0, approved, #1068 @@ -288,11 +297,11 @@ maven/mavencentral/org.jacoco/org.jacoco.report/0.8.8, EPL-2.0 AND Apache-2.0, a maven/mavencentral/org.javassist/javassist/3.25.0-GA, MPL-1.1 OR LGPL-2.1-or-later OR Apache-2.0, approved, CQ19885 maven/mavencentral/org.javassist/javassist/3.28.0-GA, Apache-2.0 OR LGPL-2.1-or-later OR MPL-1.1, approved, #327 maven/mavencentral/org.javassist/javassist/3.29.2-GA, Apache-2.0 AND LGPL-2.1-or-later AND MPL-1.1, approved, #6023 -maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-common/1.6.20, Apache-2.0, approved, clearlydefined -maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.6.20, Apache-2.0, approved, clearlydefined -maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.6.10, Apache-2.0, approved, clearlydefined -maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.6.20, Apache-2.0, approved, clearlydefined -maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib/1.6.20, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-common/1.9.10, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.9.10, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.8.21, Apache-2.0, approved, #8919 +maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.9.10, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib/1.9.10, Apache-2.0, approved, clearlydefined maven/mavencentral/org.jetbrains/annotations/13.0, Apache-2.0, approved, clearlydefined maven/mavencentral/org.jetbrains/annotations/17.0.0, Apache-2.0, approved, clearlydefined maven/mavencentral/org.jetbrains/annotations/24.0.1, Apache-2.0, approved, #7417 @@ -315,13 +324,12 @@ maven/mavencentral/org.objenesis/objenesis/3.3, Apache-2.0, approved, clearlydef maven/mavencentral/org.opentest4j/opentest4j/1.3.0, Apache-2.0, approved, #9713 maven/mavencentral/org.ow2.asm/asm-analysis/9.2, BSD-3-Clause, approved, clearlydefined maven/mavencentral/org.ow2.asm/asm-commons/9.2, BSD-3-Clause, approved, clearlydefined -maven/mavencentral/org.ow2.asm/asm-commons/9.5, BSD-3-Clause, approved, #7553 +maven/mavencentral/org.ow2.asm/asm-commons/9.6, BSD-3-Clause, approved, #10775 maven/mavencentral/org.ow2.asm/asm-tree/9.2, BSD-3-Clause, approved, clearlydefined -maven/mavencentral/org.ow2.asm/asm-tree/9.5, BSD-3-Clause, approved, #7555 +maven/mavencentral/org.ow2.asm/asm-tree/9.6, BSD-3-Clause, approved, #10773 maven/mavencentral/org.ow2.asm/asm/9.1, BSD-3-Clause, approved, CQ23029 maven/mavencentral/org.ow2.asm/asm/9.2, BSD-3-Clause, approved, CQ23635 -maven/mavencentral/org.ow2.asm/asm/9.5, BSD-3-Clause, approved, #7554 -maven/mavencentral/org.postgresql/postgresql/42.6.0, BSD-2-Clause AND Apache-2.0, approved, #9159 +maven/mavencentral/org.ow2.asm/asm/9.6, BSD-3-Clause, approved, #10776 maven/mavencentral/org.reflections/reflections/0.10.2, Apache-2.0 AND WTFPL, approved, clearlydefined maven/mavencentral/org.rnorth.duct-tape/duct-tape/1.0.8, MIT, approved, clearlydefined maven/mavencentral/org.slf4j/slf4j-api/1.7.22, MIT, approved, CQ11943 @@ -332,12 +340,10 @@ maven/mavencentral/org.slf4j/slf4j-api/1.7.35, MIT, approved, CQ13368 maven/mavencentral/org.slf4j/slf4j-api/1.7.36, MIT, approved, CQ13368 maven/mavencentral/org.slf4j/slf4j-api/2.0.5, MIT, approved, #5915 maven/mavencentral/org.slf4j/slf4j-api/2.0.6, MIT, approved, #5915 -maven/mavencentral/org.testcontainers/database-commons/1.19.0, Apache-2.0, approved, #10345 -maven/mavencentral/org.testcontainers/jdbc/1.19.0, Apache-2.0, approved, #10348 -maven/mavencentral/org.testcontainers/junit-jupiter/1.19.0, MIT, approved, #10344 -maven/mavencentral/org.testcontainers/postgresql/1.19.0, MIT, approved, #10350 -maven/mavencentral/org.testcontainers/testcontainers/1.19.0, Apache-2.0 AND MIT, approved, #10347 +maven/mavencentral/org.testcontainers/junit-jupiter/1.19.1, MIT, approved, #10344 +maven/mavencentral/org.testcontainers/testcontainers/1.19.1, Apache-2.0 AND MIT, approved, #10347 maven/mavencentral/org.xmlunit/xmlunit-core/2.9.1, Apache-2.0, approved, #6272 maven/mavencentral/org.xmlunit/xmlunit-placeholders/2.9.1, Apache-2.0, approved, clearlydefined maven/mavencentral/org.yaml/snakeyaml/1.33, Apache-2.0, approved, clearlydefined maven/mavencentral/org.yaml/snakeyaml/2.0, Apache-2.0 AND (Apache-2.0 OR BSD-3-Clause OR EPL-1.0 OR GPL-2.0-or-later OR LGPL-2.1-or-later), approved, #7275 +maven/mavencentral/org.yaml/snakeyaml/2.1, Apache-2.0, approved, #9847 diff --git a/core/identity-hub-api/build.gradle.kts b/core/identity-hub-api/build.gradle.kts new file mode 100644 index 000000000..93e182a96 --- /dev/null +++ b/core/identity-hub-api/build.gradle.kts @@ -0,0 +1,36 @@ +/* + * 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 + * + */ + +plugins { + `java-library` + `maven-publish` + id("io.swagger.core.v3.swagger-gradle-plugin") +} + +dependencies { + api(libs.edc.spi.jsonld) + api(libs.edc.spi.core) + api(project(":spi:identity-hub-spi")) + implementation(project(":core:identity-hub-transform")) + implementation(libs.edc.spi.validator) + implementation(libs.edc.spi.web) + implementation(libs.edc.core.jerseyproviders) + implementation(libs.edc.core.transform) + implementation(libs.jakarta.rsApi) + testImplementation(libs.edc.junit) + testImplementation(libs.edc.ext.jsonld) + testImplementation(testFixtures(libs.edc.core.jersey)) + testImplementation(testFixtures(project(":spi:identity-hub-spi"))) + testImplementation(libs.nimbus.jwt) +} diff --git a/core/identity-hub-api/src/main/java/org/eclipse/edc/identityservice/api/PresentationApiExtension.java b/core/identity-hub-api/src/main/java/org/eclipse/edc/identityservice/api/PresentationApiExtension.java new file mode 100644 index 000000000..5beb0a2c9 --- /dev/null +++ b/core/identity-hub-api/src/main/java/org/eclipse/edc/identityservice/api/PresentationApiExtension.java @@ -0,0 +1,102 @@ +/* + * 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.edc.identityservice.api; + +import org.eclipse.edc.core.transform.transformer.to.JsonValueToGenericTypeTransformer; +import org.eclipse.edc.identityhub.spi.generator.PresentationGenerator; +import org.eclipse.edc.identityhub.spi.model.PresentationQuery; +import org.eclipse.edc.identityhub.spi.resolution.CredentialQueryResolver; +import org.eclipse.edc.identityhub.spi.verification.AccessTokenVerifier; +import org.eclipse.edc.identityhub.transform.JsonObjectToPresentationQueryTransformer; +import org.eclipse.edc.identityservice.api.v1.PresentationApiController; +import org.eclipse.edc.identityservice.api.validation.PresentationQueryValidator; +import org.eclipse.edc.jsonld.spi.JsonLd; +import org.eclipse.edc.runtime.metamodel.annotation.Extension; +import org.eclipse.edc.runtime.metamodel.annotation.Inject; +import org.eclipse.edc.spi.system.ServiceExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.spi.types.TypeManager; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; +import org.eclipse.edc.web.jersey.jsonld.JerseyJsonLdInterceptor; +import org.eclipse.edc.web.jersey.jsonld.ObjectMapperProvider; +import org.eclipse.edc.web.spi.WebService; + +import java.net.URISyntaxException; + +import static org.eclipse.edc.identityhub.spi.model.IdentityHubConstants.IATP_CONTEXT_URL; +import static org.eclipse.edc.identityhub.spi.model.IdentityHubConstants.PRESENTATION_EXCHANGE_URL; +import static org.eclipse.edc.spi.CoreConstants.JSON_LD; + +@Extension(value = "Presentation API Extension") +public class PresentationApiExtension implements ServiceExtension { + + public static final String RESOLUTION_SCOPE = "resolution-scope"; + public static final String RESOLUTION_CONTEXT = "resolution"; + public static final String PRESENTATION_EXCHANGE_V_1_JSON = "presentation-exchange.v1.json"; + public static final String PRESENTATION_QUERY_V_08_JSON = "presentation-query.v08.json"; + @Inject + private TypeTransformerRegistry typeTransformer; + + @Inject + private JsonObjectValidatorRegistry validatorRegistry; + + @Inject + private WebService webService; + + @Inject + private AccessTokenVerifier accessTokenVerifier; + + @Inject + private CredentialQueryResolver credentialResolver; + + @Inject + private PresentationGenerator presentationGenerator; + + @Inject + private JsonLd jsonLd; + + @Inject + private TypeManager typeManager; + + @Override + public void initialize(ServiceExtensionContext context) { + // setup validator + validatorRegistry.register(PresentationQuery.PRESENTATION_QUERY_TYPE_PROPERTY, new PresentationQueryValidator()); + + + // Setup API + cacheContextDocuments(getClass().getClassLoader()); + var controller = new PresentationApiController(validatorRegistry, typeTransformer, credentialResolver, accessTokenVerifier, presentationGenerator, context.getMonitor()); + + var jsonLdMapper = typeManager.getMapper(JSON_LD); + webService.registerResource(RESOLUTION_CONTEXT, new ObjectMapperProvider(jsonLdMapper)); + webService.registerResource(RESOLUTION_CONTEXT, new JerseyJsonLdInterceptor(jsonLd, jsonLdMapper, RESOLUTION_SCOPE)); + webService.registerResource(RESOLUTION_CONTEXT, controller); + + // register transformer + typeTransformer.register(new JsonObjectToPresentationQueryTransformer(jsonLdMapper)); + typeTransformer.register(new JsonValueToGenericTypeTransformer(jsonLdMapper)); + } + + private void cacheContextDocuments(ClassLoader classLoader) { + try { + jsonLd.registerCachedDocument(PRESENTATION_EXCHANGE_URL, classLoader.getResource(PRESENTATION_EXCHANGE_V_1_JSON).toURI()); + jsonLd.registerCachedDocument(IATP_CONTEXT_URL, classLoader.getResource(PRESENTATION_QUERY_V_08_JSON).toURI()); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } +} diff --git a/core/identity-hub-api/src/main/java/org/eclipse/edc/identityservice/api/v1/ApiSchema.java b/core/identity-hub-api/src/main/java/org/eclipse/edc/identityservice/api/v1/ApiSchema.java new file mode 100644 index 000000000..55cbdfe13 --- /dev/null +++ b/core/identity-hub-api/src/main/java/org/eclipse/edc/identityservice/api/v1/ApiSchema.java @@ -0,0 +1,112 @@ +/* + * 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.edc.identityservice.api.v1; + +import io.swagger.v3.oas.annotations.media.Schema; + +import java.util.List; + +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.NOT_REQUIRED; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.CONTEXT; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; + +public interface ApiSchema { + @Schema(name = "ApiErrorDetail", example = ApiErrorDetailSchema.API_ERROR_EXAMPLE) + record ApiErrorDetailSchema( + String message, + String type, + String path, + String invalidValue + ) { + public static final String API_ERROR_EXAMPLE = """ + { + "message": "error message", + "type": "ErrorType", + "path": "object.error.path", + "invalidValue": "this value is not valid" + } + """; + } + + @Schema(name = "PresentationQuerySchema", example = PresentationQuerySchema.PRESENTATION_QUERY_EXAMPLE) + record PresentationQuerySchema( + @Schema(name = CONTEXT, requiredMode = REQUIRED) + Object context, + @Schema(name = TYPE, requiredMode = REQUIRED) + String type, + @Schema(name = "scope", requiredMode = NOT_REQUIRED) + List scope, + @Schema(name = "presentation_definition", requiredMode = NOT_REQUIRED) + PresentationDefinitionSchema presentationDefinitionSchema + ) { + + public static final String PRESENTATION_QUERY_EXAMPLE = """ + { + "@context": [ + "https://w3id.org/yourdataspace/2023/cs/v1", + "https://identity.foundation/presentation-exchange/submission/v1" + ], + "@type": "Query", + "presentation_definition": null, + "scope": [ + "org.eclipse.edc.vc.type:SomeCredential_0.3.5:write, + "org.eclipse.edc.vc.type:SomeOtherCredential:read, + "org.eclipse.edc.vc.type:ThirdCredential:*, + ] + } + """; + } + + @Schema(name = "PresentationResponse", example = PresentationResponse.RESPONSE_EXAMPLE) + record PresentationResponse() { + + public static final String RESPONSE_EXAMPLE = """ + """; + } + + @Schema(name = "PresentationDefinitionSchema", example = PresentationDefinitionSchema.EXAMPLE) + record PresentationDefinitionSchema() { + + private static final String EXAMPLE = """ + { + "comment": "taken from https://identity.foundation/presentation-exchange/spec/v2.0.0/#presentation-definition" + "presentation_definition": { + "id": "first simple example", + "input_descriptors": [ + { + "id": "A specific type of VC", + "name": "A specific type of VC", + "purpose": "We want a VC of this type", + "constraints": { + "fields": [ + { + "path": [ + "$.type" + ], + "filter": { + "type": "string", + "pattern": "" + } + } + ] + } + } + ] + } + } + """; + } +} diff --git a/core/identity-hub-api/src/main/java/org/eclipse/edc/identityservice/api/v1/PresentationApi.java b/core/identity-hub-api/src/main/java/org/eclipse/edc/identityservice/api/v1/PresentationApi.java new file mode 100644 index 000000000..56be59050 --- /dev/null +++ b/core/identity-hub-api/src/main/java/org/eclipse/edc/identityservice/api/v1/PresentationApi.java @@ -0,0 +1,64 @@ +/* + * 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.edc.identityservice.api.v1; + + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn; +import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; +import io.swagger.v3.oas.annotations.info.Info; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.security.SecurityScheme; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.json.JsonObject; +import jakarta.ws.rs.core.Response; +import org.eclipse.edc.identityhub.spi.model.PresentationResponse; +import org.eclipse.edc.identityservice.api.v1.ApiSchema.ApiErrorDetailSchema; + +@OpenAPIDefinition( + info = @Info(description = "This represents the Presentation API as per IATP specification. It serves endpoints to query for specific VerifiablePresentations.", title = "Resolution API", + version = "1")) +@SecurityScheme(name = "Authentication", + description = "Self-Issued ID token containing an access_token", + type = SecuritySchemeType.HTTP, + scheme = "Bearer", + bearerFormat = "JWT", + in = SecuritySchemeIn.HEADER) +public interface PresentationApi { + + @Tag(name = "Resolution 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), mediaType = "application/ld+json")), + responses = { + @ApiResponse(responseCode = "200", description = "The query was successfully processed, the response contains the VerifiablePresentation", + content = @Content(schema = @Schema(implementation = PresentationResponse.class), mediaType = "application/ld+json")), + @ApiResponse(responseCode = "400", description = "Request body was malformed, for example when both scope and presentation_definition are given", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetailSchema.class)), mediaType = "application/json")), + @ApiResponse(responseCode = "401", description = "No Authorization header was given.", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetailSchema.class)), mediaType = "application/json")), + @ApiResponse(responseCode = "403", description = "The given authentication token could not be validated. This can happen, when the request body " + + "calls for a broader query scope than the granted scope in the auth token", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetailSchema.class)), mediaType = "application/json")), + @ApiResponse(responseCode = "501", description = "When the request contained a presentation_definition object, but the implementation does not support it.", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetailSchema.class)), mediaType = "application/json")) + } + ) + Response queryPresentation(JsonObject query, String authHeader); +} diff --git a/core/identity-hub-api/src/main/java/org/eclipse/edc/identityservice/api/v1/PresentationApiController.java b/core/identity-hub-api/src/main/java/org/eclipse/edc/identityservice/api/v1/PresentationApiController.java new file mode 100644 index 000000000..23d3093e7 --- /dev/null +++ b/core/identity-hub-api/src/main/java/org/eclipse/edc/identityservice/api/v1/PresentationApiController.java @@ -0,0 +1,105 @@ +/* + * 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.edc.identityservice.api.v1; + +import jakarta.json.JsonObject; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.HeaderParam; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.Response; +import org.eclipse.edc.identityhub.spi.generator.PresentationGenerator; +import org.eclipse.edc.identityhub.spi.model.PresentationQuery; +import org.eclipse.edc.identityhub.spi.resolution.CredentialQueryResolver; +import org.eclipse.edc.identityhub.spi.verification.AccessTokenVerifier; +import org.eclipse.edc.spi.EdcException; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; +import org.eclipse.edc.web.spi.ApiErrorDetail; +import org.eclipse.edc.web.spi.exception.AuthenticationFailedException; +import org.eclipse.edc.web.spi.exception.InvalidRequestException; +import org.eclipse.edc.web.spi.exception.NotAuthorizedException; +import org.eclipse.edc.web.spi.exception.ValidationFailureException; + +import static jakarta.ws.rs.core.HttpHeaders.AUTHORIZATION; +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; +import static org.eclipse.edc.identityhub.spi.model.PresentationQuery.PRESENTATION_QUERY_TYPE_PROPERTY; + +@Consumes(APPLICATION_JSON) +@Produces(APPLICATION_JSON) +@Path("/presentation") +public class PresentationApiController implements PresentationApi { + + private final JsonObjectValidatorRegistry validatorRegistry; + private final TypeTransformerRegistry transformerRegistry; + private final CredentialQueryResolver queryResolver; + private final AccessTokenVerifier accessTokenVerifier; + private final PresentationGenerator presentationGenerator; + private final Monitor monitor; + + public PresentationApiController(JsonObjectValidatorRegistry validatorRegistry, TypeTransformerRegistry transformerRegistry, CredentialQueryResolver queryResolver, + AccessTokenVerifier accessTokenVerifier, PresentationGenerator presentationGenerator, Monitor monitor) { + this.validatorRegistry = validatorRegistry; + this.transformerRegistry = transformerRegistry; + this.queryResolver = queryResolver; + this.accessTokenVerifier = accessTokenVerifier; + this.presentationGenerator = presentationGenerator; + this.monitor = monitor; + } + + + @POST + @Path("/query") + @Override + public Response queryPresentation(JsonObject query, @HeaderParam(AUTHORIZATION) String token) { + if (token == null) { + throw new AuthenticationFailedException("Authorization header missing"); + } + validatorRegistry.validate(PRESENTATION_QUERY_TYPE_PROPERTY, query).orElseThrow(ValidationFailureException::new); + + var presentationQuery = transformerRegistry.transform(query, PresentationQuery.class).orElseThrow(InvalidRequestException::new); + + if (presentationQuery.getPresentationDefinition() != null) { + monitor.warning("DIF Presentation Queries are not supported yet. This will get implemented in future iterations."); + return notImplemented(); + } + + // verify and validate the requestor's SI token + var issuerScopes = accessTokenVerifier.verify(token).orElseThrow(f -> new AuthenticationFailedException("ID token verification failed: %s".formatted(f.getFailureDetail()))); + + // query the database + var credentials = queryResolver.query(presentationQuery, issuerScopes).orElseThrow(f -> new NotAuthorizedException(f.getFailureDetail())); + + // package the credentials in a VP and sign + var presentationResponse = presentationGenerator.createPresentation(credentials, presentationQuery.getPresentationDefinition()) + .orElseThrow(failure -> new EdcException("Error creating VerifiablePresentation: %s".formatted(failure.getFailureDetail()))); + return Response.ok() + .entity(presentationResponse) + .build(); + } + + private Response notImplemented() { + var error = ApiErrorDetail.Builder.newInstance() + .message("Not implemented.") + .type("Not implemented.") + .build(); + return Response.status(503) + .entity(error) + .build(); + } + +} diff --git a/core/identity-hub-api/src/main/java/org/eclipse/edc/identityservice/api/validation/PresentationQueryValidator.java b/core/identity-hub-api/src/main/java/org/eclipse/edc/identityservice/api/validation/PresentationQueryValidator.java new file mode 100644 index 000000000..67b43cbb5 --- /dev/null +++ b/core/identity-hub-api/src/main/java/org/eclipse/edc/identityservice/api/validation/PresentationQueryValidator.java @@ -0,0 +1,47 @@ +/* + * 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.edc.identityservice.api.validation; + +import jakarta.json.JsonObject; +import org.eclipse.edc.identityhub.spi.model.PresentationQuery; +import org.eclipse.edc.validator.spi.ValidationResult; +import org.eclipse.edc.validator.spi.Validator; + +import static org.eclipse.edc.validator.spi.ValidationResult.failure; +import static org.eclipse.edc.validator.spi.ValidationResult.success; +import static org.eclipse.edc.validator.spi.Violation.violation; + +/** + * Validates, that a JsonObject representing a {@link PresentationQuery} contains either a {@code scope} property, + * or a {@code presentation_definition} query. + */ +public class PresentationQueryValidator implements Validator { + @Override + public ValidationResult validate(JsonObject input) { + var scope = input.get(PresentationQuery.PRESENTATION_QUERY_SCOPE_PROPERTY); + + var presentationDef = input.get(PresentationQuery.PRESENTATION_QUERY_DEFINITION_PROPERTY); + + if (scope == null && presentationDef == null) { + return failure(violation("Must contain either a 'scope' or a 'presentation_definition' property.", null)); + } + + if (scope != null && presentationDef != null) { + return failure(violation("Must contain either a 'scope' or a 'presentation_definition', not both.", null)); + } + + return success(); + } +} diff --git a/extensions/self-description-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/core/identity-hub-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension similarity index 52% rename from extensions/self-description-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension rename to core/identity-hub-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension index 2fae77180..a8c488640 100644 --- a/extensions/self-description-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension +++ b/core/identity-hub-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -1,5 +1,5 @@ # -# Copyright (c) 2023 Amadeus +# 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 @@ -8,8 +8,8 @@ # SPDX-License-Identifier: Apache-2.0 # # Contributors: -# Amadeus - initial API and implementation +# Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation # # -org.eclipse.edc.identityhub.api.selfdescription.SelfDescriptionApiExtension +org.eclipse.edc.identityservice.api.PresentationApiExtension diff --git a/core/identity-hub-api/src/main/resources/presentation-exchange.v1.json b/core/identity-hub-api/src/main/resources/presentation-exchange.v1.json new file mode 100644 index 000000000..488925037 --- /dev/null +++ b/core/identity-hub-api/src/main/resources/presentation-exchange.v1.json @@ -0,0 +1,15 @@ +{ + "@context": { + "@version": 1.1, + "PresentationSubmission": { + "@id": "https://identity.foundation/presentation-exchange/#presentation-submission", + "@context": { + "@version": 1.1, + "presentation_submission": { + "@id": "https://identity.foundation/presentation-exchange/#presentation-submission", + "@type": "@json" + } + } + } + } +} \ No newline at end of file diff --git a/core/identity-hub-api/src/main/resources/presentation-query.v08.json b/core/identity-hub-api/src/main/resources/presentation-query.v08.json new file mode 100644 index 000000000..ee45d6f63 --- /dev/null +++ b/core/identity-hub-api/src/main/resources/presentation-query.v08.json @@ -0,0 +1,12 @@ +{ + "@context": { + "scope": { + "@id": "https://w3id.org/tractusx-trust/v0.8/scope", + "@container": "@set" + }, + "presentation_definition":{ + "@id": "https://w3id.org/tractusx-trust/v0.8/presentation_definition", + "@type": "@json" + } + } +} \ No newline at end of file diff --git a/core/identity-hub-api/src/test/java/org/eclipse/edc/identityservice/api/v1/PresentationApiControllerTest.java b/core/identity-hub-api/src/test/java/org/eclipse/edc/identityservice/api/v1/PresentationApiControllerTest.java new file mode 100644 index 000000000..58149b66e --- /dev/null +++ b/core/identity-hub-api/src/test/java/org/eclipse/edc/identityservice/api/v1/PresentationApiControllerTest.java @@ -0,0 +1,198 @@ +/* + * 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.edc.identityservice.api.v1; + +import com.nimbusds.jwt.JWTClaimsSet; +import jakarta.json.JsonObject; +import org.eclipse.edc.identityhub.spi.generator.PresentationGenerator; +import org.eclipse.edc.identityhub.spi.model.InputDescriptorMapping; +import org.eclipse.edc.identityhub.spi.model.PresentationQuery; +import org.eclipse.edc.identityhub.spi.model.PresentationResponse; +import org.eclipse.edc.identityhub.spi.model.PresentationSubmission; +import org.eclipse.edc.identityhub.spi.model.presentationdefinition.PresentationDefinition; +import org.eclipse.edc.identityhub.spi.resolution.CredentialQueryResolver; +import org.eclipse.edc.identityhub.spi.verification.AccessTokenVerifier; +import org.eclipse.edc.junit.annotations.ApiTest; +import org.eclipse.edc.spi.EdcException; +import org.eclipse.edc.spi.result.Result; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; +import org.eclipse.edc.web.jersey.testfixtures.RestControllerTestBase; +import org.eclipse.edc.web.spi.ApiErrorDetail; +import org.eclipse.edc.web.spi.exception.AuthenticationFailedException; +import org.eclipse.edc.web.spi.exception.InvalidRequestException; +import org.eclipse.edc.web.spi.exception.NotAuthorizedException; +import org.eclipse.edc.web.spi.exception.ValidationFailureException; +import org.junit.jupiter.api.Test; + +import java.sql.Date; +import java.time.Instant; +import java.util.List; +import java.util.UUID; + +import static jakarta.json.Json.createObjectBuilder; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.buildSignedJwt; +import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.generateEcKey; +import static org.eclipse.edc.identityhub.spi.model.PresentationQuery.PRESENTATION_QUERY_TYPE_PROPERTY; +import static org.eclipse.edc.validator.spi.ValidationResult.failure; +import static org.eclipse.edc.validator.spi.ValidationResult.success; +import static org.eclipse.edc.validator.spi.Violation.violation; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +@ApiTest +@SuppressWarnings("resource") +class PresentationApiControllerTest extends RestControllerTestBase { + + private final JsonObjectValidatorRegistry validatorRegistryMock = mock(); + private final TypeTransformerRegistry typeTransformerRegistry = mock(); + private final CredentialQueryResolver queryResolver = mock(); + private final AccessTokenVerifier accessTokenVerifier = mock(); + private final PresentationGenerator generator = mock(); + + + @Test + void query_tokenNotPresent_shouldReturn401() { + assertThatThrownBy(() -> controller().queryPresentation(createObjectBuilder().build(), null)) + .isInstanceOf(AuthenticationFailedException.class) + .hasMessage("Authorization header missing"); + } + + @Test + void query_validationError_shouldReturn400() { + when(validatorRegistryMock.validate(eq(PRESENTATION_QUERY_TYPE_PROPERTY), any())).thenReturn(failure(violation("foo", "bar"))); + + assertThatThrownBy(() -> controller().queryPresentation(createObjectBuilder().build(), generateJwt())) + .isInstanceOf(ValidationFailureException.class) + .hasMessage("foo"); + } + + @Test + void query_transformationError_shouldReturn400() { + when(validatorRegistryMock.validate(eq(PRESENTATION_QUERY_TYPE_PROPERTY), any())).thenReturn(success()); + when(typeTransformerRegistry.transform(isA(JsonObject.class), eq(PresentationQuery.class))).thenReturn(Result.failure("cannot transform")); + + assertThatThrownBy(() -> controller().queryPresentation(createObjectBuilder().build(), generateJwt())) + .isInstanceOf(InvalidRequestException.class) + .hasMessage("cannot transform"); + verifyNoInteractions(accessTokenVerifier, queryResolver, generator); + } + + @Test + void query_withPresentationDefinition_shouldReturn501() { + when(validatorRegistryMock.validate(eq(PRESENTATION_QUERY_TYPE_PROPERTY), any())).thenReturn(success()); + var presentationQueryBuilder = createPresentationQueryBuilder() + .presentationDefinition(PresentationDefinition.Builder.newInstance().id(UUID.randomUUID().toString()).build()); + when(typeTransformerRegistry.transform(isA(JsonObject.class), eq(PresentationQuery.class))).thenReturn(Result.success(presentationQueryBuilder.build())); + + var response = controller().queryPresentation(createObjectBuilder().build(), generateJwt()); + assertThat(response.getStatus()).isEqualTo(503); + assertThat(response.getEntity()).extracting(o -> (ApiErrorDetail) o).satisfies(ed -> { + assertThat(ed.getMessage()).isEqualTo("Not implemented."); + assertThat(ed.getType()).isEqualTo("Not implemented."); + }); + verifyNoInteractions(accessTokenVerifier, queryResolver, generator); + } + + + @Test + void query_tokenVerificationFails_shouldReturn401() { + when(validatorRegistryMock.validate(eq(PRESENTATION_QUERY_TYPE_PROPERTY), any())).thenReturn(success()); + var presentationQueryBuilder = createPresentationQueryBuilder().build(); + when(typeTransformerRegistry.transform(isA(JsonObject.class), eq(PresentationQuery.class))).thenReturn(Result.success(presentationQueryBuilder)); + when(accessTokenVerifier.verify(anyString())).thenReturn(Result.failure("test-failure")); + + assertThatThrownBy(() -> controller().queryPresentation(createObjectBuilder().build(), generateJwt())) + .isExactlyInstanceOf(AuthenticationFailedException.class) + .hasMessage("ID token verification failed: test-failure"); + verifyNoInteractions(queryResolver, generator); + } + + @Test + void query_queryResolutionFails_shouldReturn403() { + when(validatorRegistryMock.validate(eq(PRESENTATION_QUERY_TYPE_PROPERTY), any())).thenReturn(success()); + var presentationQueryBuilder = createPresentationQueryBuilder().build(); + when(typeTransformerRegistry.transform(isA(JsonObject.class), eq(PresentationQuery.class))).thenReturn(Result.success(presentationQueryBuilder)); + when(accessTokenVerifier.verify(anyString())).thenReturn(Result.success(List.of("test-scope1"))); + when(queryResolver.query(any(), eq(List.of("test-scope1")))).thenReturn(Result.failure("test-failure")); + + assertThatThrownBy(() -> controller().queryPresentation(createObjectBuilder().build(), generateJwt())) + .isInstanceOf(NotAuthorizedException.class) + .hasMessage("test-failure"); + verifyNoInteractions(generator); + } + + @Test + void query_presentationGenerationFails_shouldReturn500() { + when(validatorRegistryMock.validate(eq(PRESENTATION_QUERY_TYPE_PROPERTY), any())).thenReturn(success()); + var presentationQueryBuilder = createPresentationQueryBuilder().build(); + when(typeTransformerRegistry.transform(isA(JsonObject.class), eq(PresentationQuery.class))).thenReturn(Result.success(presentationQueryBuilder)); + when(accessTokenVerifier.verify(anyString())).thenReturn(Result.success(List.of("test-scope1"))); + when(queryResolver.query(any(), eq(List.of("test-scope1")))).thenReturn(Result.success(List.of())); + + when(generator.createPresentation(anyList(), any())).thenReturn(Result.failure("test-failure")); + + assertThatThrownBy(() -> controller().queryPresentation(createObjectBuilder().build(), generateJwt())) + .isExactlyInstanceOf(EdcException.class) + .hasMessage("Error creating VerifiablePresentation: test-failure"); + } + + @Test + void query_success() { + when(validatorRegistryMock.validate(eq(PRESENTATION_QUERY_TYPE_PROPERTY), any())).thenReturn(success()); + var presentationQueryBuilder = createPresentationQueryBuilder().build(); + when(typeTransformerRegistry.transform(isA(JsonObject.class), eq(PresentationQuery.class))).thenReturn(Result.success(presentationQueryBuilder)); + when(accessTokenVerifier.verify(anyString())).thenReturn(Result.success(List.of("test-scope1"))); + when(queryResolver.query(any(), eq(List.of("test-scope1")))).thenReturn(Result.success(List.of())); + + var pres = new PresentationResponse(generateJwt(), new PresentationSubmission("id", "def-id", List.of(new InputDescriptorMapping("id", "ldp_vp", "$.verifiableCredentials[0]")))); + when(generator.createPresentation(anyList(), any())).thenReturn(Result.success(pres)); + + var response = controller().queryPresentation(createObjectBuilder().build(), generateJwt()); + assertThat(response).isNotNull(); + assertThat(response.getStatus()).isEqualTo(200); + assertThat(response.getEntity()).isEqualTo(pres); + + } + + @Override + protected PresentationApiController controller() { + return new PresentationApiController(validatorRegistryMock, typeTransformerRegistry, queryResolver, accessTokenVerifier, generator, mock()); + } + + private String generateJwt() { + var ecKey = generateEcKey(); + var jwt = buildSignedJwt(new JWTClaimsSet.Builder().audience("test-audience") + .expirationTime(Date.from(Instant.now().plusSeconds(3600))) + .issuer("test-issuer") + .subject("test-subject") + .jwtID(UUID.randomUUID().toString()).build(), ecKey); + + return jwt.serialize(); + } + + private PresentationQuery.Builder createPresentationQueryBuilder() { + return PresentationQuery.Builder.newinstance() + .scopes(List.of("test-scope1", "test-scope2")); + } +} \ No newline at end of file diff --git a/core/identity-hub-api/src/test/java/org/eclipse/edc/identityservice/api/validation/PresentationQueryValidatorTest.java b/core/identity-hub-api/src/test/java/org/eclipse/edc/identityservice/api/validation/PresentationQueryValidatorTest.java new file mode 100644 index 000000000..1e1fde340 --- /dev/null +++ b/core/identity-hub-api/src/test/java/org/eclipse/edc/identityservice/api/validation/PresentationQueryValidatorTest.java @@ -0,0 +1,106 @@ +/* + * 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.edc.identityservice.api.validation; + + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import org.eclipse.edc.identityhub.spi.model.PresentationQuery; +import org.eclipse.edc.identityhub.spi.model.presentationdefinition.Constraints; +import org.eclipse.edc.identityhub.spi.model.presentationdefinition.Field; +import org.eclipse.edc.identityhub.spi.model.presentationdefinition.InputDescriptor; +import org.eclipse.edc.identityhub.spi.model.presentationdefinition.PresentationDefinition; +import org.eclipse.edc.jsonld.TitaniumJsonLd; +import org.eclipse.edc.jsonld.spi.JsonLd; +import org.eclipse.edc.jsonld.spi.JsonLdKeywords; +import org.eclipse.edc.jsonld.util.JacksonJsonLd; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.UUID; + +import static jakarta.json.Json.createArrayBuilder; +import static jakarta.json.Json.createObjectBuilder; +import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat; +import static org.mockito.Mockito.mock; + +class PresentationQueryValidatorTest { + public static final ObjectMapper MAPPER = JacksonJsonLd.createObjectMapper(); + private final PresentationQueryValidator validator = new PresentationQueryValidator(); + private final JsonLd jsonLd = new TitaniumJsonLd(mock()); + + + @Test + void validate_withScope_success() { + var jo = createObjectBuilder() + .add(PresentationQuery.PRESENTATION_QUERY_SCOPE_PROPERTY, createScopeArray()) + .build(); + + assertThat(validator.validate(jo)).isSucceeded(); + } + + @Test + void validate_withPresentationDefinition_success() throws JsonProcessingException { + var presDef = PresentationDefinition.Builder.newInstance() + .id(UUID.randomUUID().toString()) + .inputDescriptors(List.of(InputDescriptor.Builder.newInstance().id(UUID.randomUUID().toString()).constraints(new Constraints(List.of(Field.Builder.newInstance().build()))).build())) + .build(); + var jo = createObjectBuilder() + .add(PresentationQuery.PRESENTATION_QUERY_DEFINITION_PROPERTY, createPresentationDefArray(presDef)) + .build(); + + assertThat(validator.validate(jo)).isSucceeded(); + } + + + @Test + void validate_withNone_fails() { + var jo = createObjectBuilder().build(); + assertThat(validator.validate(jo)).isFailed().detail().contains("Must contain either a 'scope' or a 'presentation_definition' property."); + } + + @Test + void validate_withBoth_fails() throws JsonProcessingException { + var presDef = PresentationDefinition.Builder.newInstance() + .id(UUID.randomUUID().toString()) + .inputDescriptors(List.of(InputDescriptor.Builder.newInstance().id(UUID.randomUUID().toString()).constraints(new Constraints(List.of(Field.Builder.newInstance().build()))).build())) + .build(); + var jo = createObjectBuilder() + .add(PresentationQuery.PRESENTATION_QUERY_SCOPE_PROPERTY, createScopeArray()) + .add(PresentationQuery.PRESENTATION_QUERY_DEFINITION_PROPERTY, createPresentationDefArray(presDef)) + .build(); + + assertThat(validator.validate(jo)).isFailed().detail().contains("Must contain either a 'scope' or a 'presentation_definition', not both."); + } + + private JsonArray createScopeArray() { + return createArrayBuilder() + .add(createObjectBuilder().add(JsonLdKeywords.VALUE, "scope1")) + .add(createObjectBuilder().add(JsonLdKeywords.VALUE, "scope2")) + .build(); + } + + private JsonArray createPresentationDefArray(PresentationDefinition presDef) throws JsonProcessingException { + var val = MAPPER.writeValueAsString(presDef); + return createArrayBuilder() + .add(createObjectBuilder() + .add(JsonLdKeywords.TYPE, JsonLdKeywords.JSON) + .add(JsonLdKeywords.VALUE, MAPPER.readValue(val, JsonObject.class)) + .build()) + .build(); + } +} \ No newline at end of file diff --git a/core/identity-hub-client/build.gradle.kts b/core/identity-hub-client/build.gradle.kts deleted file mode 100644 index 953e41646..000000000 --- a/core/identity-hub-client/build.gradle.kts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -plugins { - `java-library` - `maven-publish` -} - -dependencies { - api(project(":spi:identity-hub-spi")) - api(project(":spi:identity-hub-client-spi")) - api(libs.edc.spi.http) - api(libs.edc.spi.core) - - testImplementation(project(":core:identity-hub")) - testImplementation(project(":extensions:identity-hub-api")) - testImplementation(project(":extensions:credentials:identity-hub-credentials-jwt")) - testImplementation(testFixtures(project(":spi:identity-hub-spi"))) - testImplementation(libs.edc.core.junit) - testImplementation(libs.edc.ext.identity.did.core) -} diff --git a/core/identity-hub-client/src/main/java/org/eclipse/edc/identityhub/client/IdentityHubClientImpl.java b/core/identity-hub-client/src/main/java/org/eclipse/edc/identityhub/client/IdentityHubClientImpl.java deleted file mode 100644 index 1f796e8c7..000000000 --- a/core/identity-hub-client/src/main/java/org/eclipse/edc/identityhub/client/IdentityHubClientImpl.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * Amadeus - add client for getting Self-Description - * - */ - -package org.eclipse.edc.identityhub.client; - -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; -import org.eclipse.edc.identityhub.client.spi.IdentityHubClient; -import org.eclipse.edc.identityhub.spi.credentials.model.CredentialEnvelope; -import org.eclipse.edc.identityhub.spi.credentials.transformer.CredentialEnvelopeTransformerRegistry; -import org.eclipse.edc.identityhub.spi.model.Descriptor; -import org.eclipse.edc.identityhub.spi.model.MessageRequestObject; -import org.eclipse.edc.identityhub.spi.model.MessageResponseObject; -import org.eclipse.edc.identityhub.spi.model.Record; -import org.eclipse.edc.identityhub.spi.model.RequestObject; -import org.eclipse.edc.identityhub.spi.model.ResponseObject; -import org.eclipse.edc.spi.http.EdcHttpClient; -import org.eclipse.edc.spi.result.AbstractResult; -import org.eclipse.edc.spi.result.Result; -import org.eclipse.edc.spi.types.TypeManager; -import org.jetbrains.annotations.NotNull; - -import java.io.IOException; -import java.time.Instant; -import java.util.Collection; -import java.util.List; -import java.util.UUID; -import java.util.stream.Collectors; - -import static java.lang.String.format; -import static org.eclipse.edc.identityhub.spi.model.WebNodeInterfaceMethod.COLLECTIONS_QUERY; -import static org.eclipse.edc.identityhub.spi.model.WebNodeInterfaceMethod.COLLECTIONS_WRITE; -import static org.eclipse.edc.spi.http.FallbackFactories.retryWhenStatusIsNot; -import static org.eclipse.edc.spi.result.Result.failure; - -public class IdentityHubClientImpl implements IdentityHubClient { - - private final EdcHttpClient httpClient; - private final TypeManager typeManager; - - private final CredentialEnvelopeTransformerRegistry transformerRegistry; - - public IdentityHubClientImpl(EdcHttpClient httpClient, TypeManager typeManager, CredentialEnvelopeTransformerRegistry transformerRegistry) { - this.httpClient = httpClient; - this.typeManager = typeManager; - this.transformerRegistry = transformerRegistry; - } - - @Override - public Result> getVerifiableCredentials(String hubBaseUrl) { - var descriptor = Descriptor.Builder.newInstance() - .method(COLLECTIONS_QUERY.getName()) - .build(); - var body = toRequestBody(descriptor); - var request = new Request.Builder() - .url(hubBaseUrl) - .post(body) - .build(); - - return httpClient.execute(request, List.of(retryWhenStatusIsNot(200)), this::extractCredentials); - } - - @Override - public Result addVerifiableCredential(String hubBaseUrl, CredentialEnvelope verifiableCredential) { - var transformer = transformerRegistry.resolve(verifiableCredential.format()); - if (transformer == null) { - return failure(format("Transformer not found for format %s", verifiableCredential.format())); - } - Result result = transformer.serialize(verifiableCredential); - if (result.failed()) { - return failure(result.getFailureDetail()); - } - - var descriptor = Descriptor.Builder.newInstance() - .method(COLLECTIONS_WRITE.getName()) - .recordId(UUID.randomUUID().toString()) - .dataFormat(verifiableCredential.format()) - .dateCreated(Instant.now().getEpochSecond()) // TODO: this should be passed from input - .build(); - - var request = new Request.Builder() - .url(hubBaseUrl) - .post(toRequestBody(descriptor, result.getContent())) - .build(); - - return httpClient.execute(request, List.of(retryWhenStatusIsNot(200)), this::handleAddResponse); - } - - private Result parse(Object entry) { - var record = typeManager.getMapper().convertValue(entry, Record.class); - var t = transformerRegistry.resolve(record.getDataFormat()); - - if (t == null) { - return failure(format("Transformer not found for format %s", record.getDataFormat())); - } - return t.parse(record.getData()); - } - - private Result handleAddResponse(Response response) { - var result = extractResponseObject(response); - if (result.failed()) { - return Result.failure(result.getFailureMessages()); - } - var responseObject = result.getContent(); - // If the status of Response object is not success return error - if (responseObject.getStatus() != null && !responseObject.getStatus().isSuccess()) { - return Result.failure(responseObject.getStatus().getDetail()); - } - - // If the status of one of the replies is not success return error - return responseObject.getReplies() - .stream() - .map(MessageResponseObject::getStatus) - .filter(status -> !status.isSuccess()) - .map(status -> Result.failure(status.getDetail())) - .findFirst() - .orElseGet(Result::success); - } - - private Result> extractCredentials(Response response) { - return extractResponseObject(response) - .map(ResponseObject::getReplies) - .map(replies -> replies.stream() - .flatMap(r -> r.getEntries().stream()) - .map(this::parse) - .filter(AbstractResult::succeeded) - .map(AbstractResult::getContent) - .collect(Collectors.toList())); - } - - private RequestBody toRequestBody(Descriptor descriptor) { - return toRequestBody(descriptor, null); - } - - private RequestBody toRequestBody(Descriptor descriptor, byte[] data) { - var requestObject = RequestObject.Builder.newInstance() - .messages(List.of(MessageRequestObject.Builder.newInstance() - .descriptor(descriptor) - .data(data) - .build()) - ) - .build(); - var payload = typeManager.writeValueAsString(requestObject); - return RequestBody.create(payload, okhttp3.MediaType.get("application/json")); - } - - @NotNull - private Result extractResponseObject(Response response) { - try (var body = response.body()) { - if (body != null) { - return Result.success(typeManager.readValue(body.string(), ResponseObject.class)); - } else { - return failure("Body is null"); - } - } catch (IOException e) { - return failure("Cannot read response body as String: " + e.getMessage()); - } - - } -} diff --git a/core/identity-hub-client/src/test/java/org/eclipse/edc/identityhub/client/IdentityHubClientImplIntegrationTest.java b/core/identity-hub-client/src/test/java/org/eclipse/edc/identityhub/client/IdentityHubClientImplIntegrationTest.java deleted file mode 100644 index 67b9c48ae..000000000 --- a/core/identity-hub-client/src/test/java/org/eclipse/edc/identityhub/client/IdentityHubClientImplIntegrationTest.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.client; - -import org.eclipse.edc.identityhub.client.spi.IdentityHubClient; -import org.eclipse.edc.identityhub.credentials.jwt.JwtCredentialEnvelope; -import org.eclipse.edc.identityhub.credentials.jwt.JwtCredentialEnvelopeTransformer; -import org.eclipse.edc.identityhub.spi.credentials.model.Credential; -import org.eclipse.edc.identityhub.spi.credentials.model.CredentialEnvelope; -import org.eclipse.edc.identityhub.spi.credentials.transformer.CredentialEnvelopeTransformerRegistry; -import org.eclipse.edc.junit.extensions.EdcExtension; -import org.eclipse.edc.junit.testfixtures.TestUtils; -import org.eclipse.edc.spi.types.TypeManager; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.buildSignedJwt; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.generateCredential; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.generateEcKey; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -@ExtendWith(EdcExtension.class) -class IdentityHubClientImplIntegrationTest { - - private static final String API_URL = "http://localhost:8181/api/identity-hub"; - private static final Credential VERIFIABLE_CREDENTIAL = generateCredential(); - - private static final TypeManager TYPE_MANAGER = new TypeManager(); - private final CredentialEnvelopeTransformerRegistry registry = mock(CredentialEnvelopeTransformerRegistry.class); - private IdentityHubClient client; - - @BeforeEach - void setUp() { - var okHttpClient = TestUtils.testHttpClient(); - when(registry.resolve(any())).thenReturn(new JwtCredentialEnvelopeTransformer(TYPE_MANAGER.getMapper())); - client = new IdentityHubClientImpl(okHttpClient, TYPE_MANAGER, registry); - } - - @Test - void addAndQueryVerifiableCredentials() { - var jws = buildSignedJwt(VERIFIABLE_CREDENTIAL, "http://test.url", "http://some.test.url", generateEcKey()); - - var jwsEnvelope = new JwtCredentialEnvelope(jws); - addVerifiableCredential(jwsEnvelope); - getVerifiableCredential(jwsEnvelope); - } - - private void addVerifiableCredential(CredentialEnvelope jws) { - var statusResult = client.addVerifiableCredential(API_URL, jws); - assertThat(statusResult.succeeded()).isTrue(); - } - - private void getVerifiableCredential(CredentialEnvelope jws) { - var statusResult = client.getVerifiableCredentials(API_URL); - assertThat(statusResult.succeeded()).isTrue(); - assertThat(statusResult.getContent()).usingRecursiveFieldByFieldElementComparator().contains(jws); - } -} diff --git a/core/identity-hub-client/src/test/java/org/eclipse/edc/identityhub/client/IdentityHubClientImplTest.java b/core/identity-hub-client/src/test/java/org/eclipse/edc/identityhub/client/IdentityHubClientImplTest.java deleted file mode 100644 index 1089945d2..000000000 --- a/core/identity-hub-client/src/test/java/org/eclipse/edc/identityhub/client/IdentityHubClientImplTest.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.client; - -import okhttp3.Interceptor; -import okhttp3.MediaType; -import okhttp3.Protocol; -import okhttp3.Response; -import okhttp3.ResponseBody; -import org.eclipse.edc.identityhub.credentials.jwt.JwtCredentialConstants; -import org.eclipse.edc.identityhub.credentials.jwt.JwtCredentialEnvelope; -import org.eclipse.edc.identityhub.credentials.jwt.JwtCredentialEnvelopeTransformer; -import org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil; -import org.eclipse.edc.identityhub.spi.credentials.transformer.CredentialEnvelopeTransformerRegistry; -import org.eclipse.edc.identityhub.spi.model.MessageResponseObject; -import org.eclipse.edc.identityhub.spi.model.MessageStatus; -import org.eclipse.edc.identityhub.spi.model.Record; -import org.eclipse.edc.identityhub.spi.model.RequestStatus; -import org.eclipse.edc.identityhub.spi.model.ResponseObject; -import org.eclipse.edc.junit.testfixtures.TestUtils; -import org.eclipse.edc.spi.response.ResponseStatus; -import org.eclipse.edc.spi.response.StatusResult; -import org.eclipse.edc.spi.types.TypeManager; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.UUID; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.buildSignedJwt; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.generateEcKey; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -class IdentityHubClientImplTest { - private static final String HUB_URL = "http://some.test.url"; - private static final TypeManager TYPE_MANAGER = new TypeManager(); - - private final CredentialEnvelopeTransformerRegistry registry = mock(CredentialEnvelopeTransformerRegistry.class); - - @BeforeEach - void setup() { - when(registry.resolve(any())).thenReturn(new JwtCredentialEnvelopeTransformer(TYPE_MANAGER.getMapper())); - } - - @Test - void getVerifiableCredentials() { - var credential = VerifiableCredentialTestUtil.generateCredential(); - var jws = buildSignedJwt(credential, "http://some.test.url", "http://some.test.url", generateEcKey()); - - var record = Record.Builder.newInstance() - .id(UUID.randomUUID().toString()) - .dataFormat(JwtCredentialConstants.VC_DATA_FORMAT) - .createdAt(System.currentTimeMillis()) - .data(jws.serialize().getBytes(StandardCharsets.UTF_8)) - .build(); - Interceptor interceptor = chain -> { - var request = chain.request(); - var replies = MessageResponseObject.Builder.newInstance() - .status(MessageStatus.OK) - .entries(List.of(record)) - .build(); - var responseObject = ResponseObject.Builder.newInstance() - .status(RequestStatus.OK) - .replies(List.of(replies)) - .build(); - var body = ResponseBody.create(TYPE_MANAGER.writeValueAsString(responseObject), MediaType.get("application/json")); - - return new Response.Builder() - .body(body) - .request(request) - .protocol(Protocol.HTTP_2) - .code(200) - .message("") - .build(); - }; - - var client = createClient(interceptor); - var statusResult = client.getVerifiableCredentials(HUB_URL); - assertThat(statusResult.succeeded()).isTrue(); - assertThat(statusResult.getContent()).usingRecursiveFieldByFieldElementComparator().containsExactly(new JwtCredentialEnvelope(jws)); - } - - @Test - void getVerifiableCredentialsServerError() { - - var errorMessage = "test-message"; - var body = "{\"foo\":\"bar\"}"; - var code = 500; - - Interceptor interceptor = chain -> { - var request = chain.request(); - return new Response.Builder() - .body(ResponseBody.create(body, MediaType.get("application/json"))) - .request(request) - .protocol(Protocol.HTTP_2) - .code(code) - .message(errorMessage) - .build(); - }; - - var client = createClient(interceptor); - var result = client.getVerifiableCredentials(HUB_URL); - assertThat(result.failed()).isTrue(); - assertThat(result.getFailureDetail()).contains(body); - } - - @Test - void getVerifiableCredentialsDeserializationError() { - Interceptor interceptor = chain -> { - var request = chain.request(); - var body = ResponseBody.create("{\"replies\": [{}]}", MediaType.get("application/json")); - - return new Response.Builder() - .body(body) - .request(request) - .protocol(Protocol.HTTP_2) - .code(200) - .message("{}") - .build(); - }; - - var client = createClient(interceptor); - var result = client.getVerifiableCredentials(HUB_URL); - assertThat(result.failed()).isTrue(); - } - - @Test - void addVerifiableCredentialsServerError() { - var credential = VerifiableCredentialTestUtil.generateCredential(); - var jws = buildSignedJwt(credential, "http://some.test.url", "http://some.test.url", generateEcKey()); - var errorMessage = "test error message"; - var body = "{}"; - int code = 500; - - Interceptor interceptor = chain -> { - var request = chain.request(); - return new Response.Builder() - .body(ResponseBody.create(body, MediaType.get("application/json"))) - .request(request) - .protocol(Protocol.HTTP_2) - .code(code) - .message(errorMessage) - .build(); - }; - - var client = createClient(interceptor); - var result = client.addVerifiableCredential(HUB_URL, new JwtCredentialEnvelope(jws)); - assertThat(result.failed()).isTrue(); - assertThat(result.getFailureDetail()).contains("500"); - } - - @Test - void addVerifiableCredentialsIoException() { - var credential = VerifiableCredentialTestUtil.generateCredential(); - var jws = buildSignedJwt(credential, "http://some.test.url", "http://some.test.url", generateEcKey()); - var exceptionMessage = "test exception message"; - Interceptor interceptor = chain -> { - throw new IOException(exceptionMessage); - }; - - var client = createClient(interceptor); - var statusResult = client.addVerifiableCredential(HUB_URL, new JwtCredentialEnvelope(jws)); - - var expectedResult = StatusResult.failure(ResponseStatus.FATAL_ERROR, exceptionMessage); - assertThat(statusResult).usingRecursiveComparison().isEqualTo(expectedResult); - } - - private IdentityHubClientImpl createClient(Interceptor interceptor) { - return new IdentityHubClientImpl(TestUtils.testHttpClient(interceptor), TYPE_MANAGER, registry); - } -} diff --git a/core/identity-hub-credentials-verifier/build.gradle.kts b/core/identity-hub-credentials-verifier/build.gradle.kts deleted file mode 100644 index e76e15607..000000000 --- a/core/identity-hub-credentials-verifier/build.gradle.kts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial implementation - * - */ - -plugins { - `java-library` -} - - -dependencies { - implementation(project(":core:identity-hub")) - implementation(project(":core:identity-hub-client")) - implementation(project(":spi:identity-hub-spi")) - - implementation(libs.edc.spi.identity.did) - implementation(libs.nimbus.jwt) - - testImplementation(testFixtures(project(":spi:identity-hub-spi"))) - testImplementation(project(":extensions:identity-hub-api")) - testImplementation(libs.edc.ext.identity.did.core) - testImplementation(libs.edc.core.junit) -} diff --git a/core/identity-hub-credentials-verifier/src/main/java/org/eclipse/edc/identityhub/verifier/AggregatedResult.java b/core/identity-hub-credentials-verifier/src/main/java/org/eclipse/edc/identityhub/verifier/AggregatedResult.java deleted file mode 100644 index 6997a152a..000000000 --- a/core/identity-hub-credentials-verifier/src/main/java/org/eclipse/edc/identityhub/verifier/AggregatedResult.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.verifier; - -import org.eclipse.edc.spi.result.AbstractResult; -import org.eclipse.edc.spi.result.Failure; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.List; - -/** - * A generic result type containing the result of processing plus the failures happening during the processing of the - * result. - * For example, when processing a List of objects, the processing can be successful for some objects, and unsuccessful - * for others. - * Using this class as a return type would let the client decide how to act about the failures. - * - * @param Result type - */ -class AggregatedResult extends AbstractResult> { - AggregatedResult(T successfulResult, List failureMessage) { - super(successfulResult, failureMessage.isEmpty() ? null : new Failure(failureMessage)); - } - - @Override - protected , C1> @NotNull R1 newInstance(@Nullable C1 content, @Nullable Failure failure) { - return (R1) new AggregatedResult<>(content, failure.getMessages()); - } -} diff --git a/core/identity-hub-credentials-verifier/src/main/java/org/eclipse/edc/identityhub/verifier/CredentialsVerifierExtension.java b/core/identity-hub-credentials-verifier/src/main/java/org/eclipse/edc/identityhub/verifier/CredentialsVerifierExtension.java deleted file mode 100644 index 6c2379df4..000000000 --- a/core/identity-hub-credentials-verifier/src/main/java/org/eclipse/edc/identityhub/verifier/CredentialsVerifierExtension.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.verifier; - -import org.eclipse.edc.iam.did.spi.credentials.CredentialsVerifier; -import org.eclipse.edc.identityhub.client.IdentityHubClientImpl; -import org.eclipse.edc.identityhub.spi.credentials.transformer.CredentialEnvelopeTransformerRegistry; -import org.eclipse.edc.identityhub.spi.credentials.verifier.CredentialEnvelopeVerifierRegistry; -import org.eclipse.edc.identityhub.spi.credentials.verifier.CredentialEnvelopeVerifierRegistryImpl; -import org.eclipse.edc.runtime.metamodel.annotation.Inject; -import org.eclipse.edc.runtime.metamodel.annotation.Provider; -import org.eclipse.edc.spi.http.EdcHttpClient; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.system.ServiceExtension; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.edc.spi.types.TypeManager; - -/** - * Extension to provide verifier for IdentityHub Verifiable Credentials. - */ -public class CredentialsVerifierExtension implements ServiceExtension { - - @Inject - private EdcHttpClient httpClient; - - @Inject - private TypeManager typeManager; - - @Inject - private Monitor monitor; - - @Inject - private CredentialEnvelopeTransformerRegistry credentialEnvelopeTransformerRegistry; - - private CredentialEnvelopeVerifierRegistry credentialEnvelopeVerifierRegistry; - - @Override - public void initialize(ServiceExtensionContext context) { - credentialEnvelopeVerifierRegistry = new CredentialEnvelopeVerifierRegistryImpl(); - } - - @Override - public String name() { - return "Credentials Verifier"; - } - - @Provider - public CredentialsVerifier createCredentialsVerifier() { - var client = new IdentityHubClientImpl(httpClient, typeManager, credentialEnvelopeTransformerRegistry); - return new IdentityHubCredentialsVerifier(client, monitor, credentialEnvelopeVerifierRegistry); - } - - @Provider - public CredentialEnvelopeVerifierRegistry credentialVerifierRegistry() { - return credentialEnvelopeVerifierRegistry; - } - -} diff --git a/core/identity-hub-credentials-verifier/src/main/java/org/eclipse/edc/identityhub/verifier/IdentityHubCredentialsVerifier.java b/core/identity-hub-credentials-verifier/src/main/java/org/eclipse/edc/identityhub/verifier/IdentityHubCredentialsVerifier.java deleted file mode 100644 index bfa3f70c3..000000000 --- a/core/identity-hub-credentials-verifier/src/main/java/org/eclipse/edc/identityhub/verifier/IdentityHubCredentialsVerifier.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.verifier; - -import org.eclipse.edc.iam.did.spi.credentials.CredentialsVerifier; -import org.eclipse.edc.iam.did.spi.document.DidDocument; -import org.eclipse.edc.iam.did.spi.document.Service; -import org.eclipse.edc.identityhub.client.spi.IdentityHubClient; -import org.eclipse.edc.identityhub.spi.credentials.model.Credential; -import org.eclipse.edc.identityhub.spi.credentials.model.CredentialEnvelope; -import org.eclipse.edc.identityhub.spi.credentials.verifier.CredentialEnvelopeVerifierRegistry; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.result.Result; - -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import static java.lang.String.format; -import static java.util.stream.Collectors.partitioningBy; - -/** - * Obtains and verifies credentials associated with a DID. - * The DID document contains an IdentityHub service, the IdentityHubCredentialsVerifier gets credentials from the - * IdentityHub instance and verifies the credentials. - */ -public class IdentityHubCredentialsVerifier implements CredentialsVerifier { - - private static final String IDENTITY_HUB_SERVICE_TYPE = "IdentityHub"; - private final IdentityHubClient identityHubClient; - private final Monitor monitor; - - private final CredentialEnvelopeVerifierRegistry credentialEnvelopeVerifierRegistry; - - /** - * Create a new credential verifier that uses an Identity Hub - * - * @param identityHubClient IdentityHubClient. - * @param credentialEnvelopeVerifierRegistry verifier registry - */ - public IdentityHubCredentialsVerifier(IdentityHubClient identityHubClient, Monitor monitor, CredentialEnvelopeVerifierRegistry credentialEnvelopeVerifierRegistry) { - this.identityHubClient = identityHubClient; - this.monitor = monitor; - this.credentialEnvelopeVerifierRegistry = credentialEnvelopeVerifierRegistry; - } - - /** - * Returns a map of successfully verified Credential such as defined in W3C specification. - * The key of each map entry is the associated credential identifier. - * - * @param didDocument of a participant. The Did Document should contain an IdentityHub service. - * @return verified credentials. - */ - @Override - public Result> getVerifiedCredentials(DidDocument didDocument) { - monitor.debug(() -> "Retrieving verified credentials for " + didDocument.getId()); - - var hubBaseUrl = getIdentityHubBaseUrl(didDocument); - if (hubBaseUrl == null) { - var errorMessage = "Could not retrieve identity hub URL from DID document"; - monitor.severe(errorMessage); - return Result.failure(errorMessage); - } - - monitor.debug(() -> format("Using identity hub URL: %s", hubBaseUrl)); - - var verifiableCredentials = identityHubClient.getVerifiableCredentials(hubBaseUrl); - if (verifiableCredentials.failed()) { - monitor.severe("Could not retrieve verifiable credentials from identity hub"); - return Result.failure(verifiableCredentials.getFailureMessages()); - } - - monitor.debug(() -> format("Retrieved %s verifiable credentials from identity hub", verifiableCredentials.getContent().size())); - - var claims = verifyCredentials(verifiableCredentials.getContent(), didDocument); - - var result = new AggregatedResult<>(claims.getContent(), claims.getFailureMessages()); - - // Fail if one verifiable credential is not valid. This is a temporary solution until the CredentialsVerifier - // contract is changed to support a result containing both successful results and failures. - if (result.failed()) { - monitor.severe(() -> format("Credentials verification failed: %s", result.getFailureDetail())); - return Result.failure(result.getFailureDetail()); - } else { - return Result.success(result.getContent()); - } - } - - private AggregatedResult> verifyCredentials(Collection verifiableCredentials, DidDocument didDocument) { - var verifiedCredentialsResult = verifiableCredentials - .stream() - .map(credentials -> extractVerifiedCredentials(credentials, didDocument)) - .collect(partitioningBy(Result::succeeded)); - - var verifiedCredentials = verifiedCredentialsResult.get(true) - .stream() - .flatMap(r -> r.getContent().stream()) - .collect(Collectors.toMap(Credential::getId, Object.class::cast)); - - var verifiedCredentialsFailure = verifiedCredentialsResult - .get(false) - .stream() - .map(Result::getFailureDetail) - .toList(); - - return new AggregatedResult<>(verifiedCredentials, verifiedCredentialsFailure); - } - - - private Result> extractVerifiedCredentials(CredentialEnvelope envelope, DidDocument didDocument) { - var verifier = credentialEnvelopeVerifierRegistry.resolve(envelope.format()); - - if (verifier == null) { - return Result.failure(format("Missing verifier for credentials with format %s", envelope.format())); - } - return verifier.verify(envelope, didDocument); - } - - - private String getIdentityHubBaseUrl(DidDocument didDocument) { - return didDocument - .getService() - .stream() - .filter(s -> s.getType().equals(IDENTITY_HUB_SERVICE_TYPE)) - .findFirst() - .map(Service::getServiceEndpoint) - .orElse(null); - } -} diff --git a/core/identity-hub-credentials-verifier/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/core/identity-hub-credentials-verifier/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension deleted file mode 100644 index 3c2a42e1b..000000000 --- a/core/identity-hub-credentials-verifier/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension +++ /dev/null @@ -1 +0,0 @@ -org.eclipse.edc.identityhub.verifier.CredentialsVerifierExtension diff --git a/core/identity-hub-credentials-verifier/src/test/java/org/eclipse/edc/identityhub/verifier/IdentityHubCredentialsVerifierTest.java b/core/identity-hub-credentials-verifier/src/test/java/org/eclipse/edc/identityhub/verifier/IdentityHubCredentialsVerifierTest.java deleted file mode 100644 index 22fb85fd0..000000000 --- a/core/identity-hub-credentials-verifier/src/test/java/org/eclipse/edc/identityhub/verifier/IdentityHubCredentialsVerifierTest.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.verifier; - -import org.eclipse.edc.iam.did.spi.credentials.CredentialsVerifier; -import org.eclipse.edc.iam.did.spi.document.DidDocument; -import org.eclipse.edc.iam.did.spi.document.Service; -import org.eclipse.edc.identityhub.client.spi.IdentityHubClient; -import org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil; -import org.eclipse.edc.identityhub.spi.credentials.model.Credential; -import org.eclipse.edc.identityhub.spi.credentials.model.CredentialEnvelope; -import org.eclipse.edc.identityhub.spi.credentials.verifier.CredentialEnvelopeVerifier; -import org.eclipse.edc.identityhub.spi.credentials.verifier.CredentialEnvelopeVerifierRegistry; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.result.Result; -import org.junit.jupiter.api.Test; - -import java.util.Collections; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -class IdentityHubCredentialsVerifierTest { - - private static final String HUB_BASE_URL = "https://" + "http://some.test.url"; - private static final DidDocument DID_DOCUMENT = DidDocument.Builder.newInstance() - .service(List.of(new Service("IdentityHub", "IdentityHub", HUB_BASE_URL))).build(); - - private final Monitor monitorMock = mock(Monitor.class); - private final IdentityHubClient identityHubClientMock = mock(IdentityHubClient.class); - private final CredentialEnvelopeVerifierRegistry verifierRegistry = mock(CredentialEnvelopeVerifierRegistry.class); - private final CredentialsVerifier credentialsVerifier = new IdentityHubCredentialsVerifier(identityHubClientMock, monitorMock, verifierRegistry); - - - @Test - void getVerifiableCredentials_shouldReturnError_withEmptyRegistry() { - - var envelope = mock(CredentialEnvelope.class); - when(identityHubClientMock.getVerifiableCredentials(HUB_BASE_URL)).thenReturn(Result.success(List.of(envelope))); - var result = credentialsVerifier.getVerifiedCredentials(DID_DOCUMENT); - - assertThat(result.failed()).isTrue(); - } - - @Test - void getVerifiableCredentials_shouldReturnError_FailedRequest() { - - when(identityHubClientMock.getVerifiableCredentials(HUB_BASE_URL)).thenReturn(Result.failure("Failure")); - var result = credentialsVerifier.getVerifiedCredentials(DID_DOCUMENT); - - assertThat(result.failed()).isTrue(); - } - - @Test - void getVerifiableCredentials_shouldReturnEmptyCredentials() { - - when(identityHubClientMock.getVerifiableCredentials(HUB_BASE_URL)).thenReturn(Result.success(Collections.emptyList())); - var result = credentialsVerifier.getVerifiedCredentials(DID_DOCUMENT); - - assertThat(result.succeeded()).isTrue(); - - assertThat(result.getContent()).isEmpty(); - } - - @Test - void getVerifiableCredentials_shouldReturnValidCredentials() { - - var envelope = mock(CredentialEnvelope.class); - var verifier = mock(CredentialEnvelopeVerifier.class); - - when(identityHubClientMock.getVerifiableCredentials(HUB_BASE_URL)).thenReturn(Result.success(List.of(envelope))); - when(verifierRegistry.resolve(any())).thenReturn(verifier); - var credential = VerifiableCredentialTestUtil.generateCredential(); - when(verifier.verify(envelope, DID_DOCUMENT)).thenReturn(Result.success(List.of(credential))); - - var result = credentialsVerifier.getVerifiedCredentials(DID_DOCUMENT); - - assertThat(result.succeeded()).isTrue(); - - var content = result.getContent(); - - assertThat(content).hasSize(1) - .extractingByKey(credential.getId()).satisfies(obj -> { - assertThat(obj).isInstanceOf(Credential.class); - var cred = (Credential) obj; - assertThat(cred).usingRecursiveComparison().isEqualTo(credential); - }); - } - - -} diff --git a/spi/identity-hub-client-spi/build.gradle.kts b/core/identity-hub-transform/build.gradle.kts similarity index 50% rename from spi/identity-hub-client-spi/build.gradle.kts rename to core/identity-hub-transform/build.gradle.kts index 6d97d412e..c18e15bbb 100644 --- a/spi/identity-hub-client-spi/build.gradle.kts +++ b/core/identity-hub-transform/build.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Microsoft Corporation + * 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 @@ -8,18 +8,21 @@ * SPDX-License-Identifier: Apache-2.0 * * Contributors: - * Microsoft Corporation - initial API and implementation + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation * */ plugins { `java-library` - `java-test-fixtures` `maven-publish` } dependencies { - api(libs.edc.spi.core) api(project(":spi:identity-hub-spi")) - implementation(libs.nimbus.jwt) -} + api(libs.edc.spi.jsonld) + api(libs.edc.spi.transform) + + testImplementation(libs.edc.ext.jsonld) + testImplementation(libs.edc.core.transform) + testImplementation(libs.edc.junit) +} \ No newline at end of file diff --git a/core/identity-hub-transform/src/main/java/org/eclipse/edc/identityhub/transform/JsonObjectToPresentationQueryTransformer.java b/core/identity-hub-transform/src/main/java/org/eclipse/edc/identityhub/transform/JsonObjectToPresentationQueryTransformer.java new file mode 100644 index 000000000..94ea74b6e --- /dev/null +++ b/core/identity-hub-transform/src/main/java/org/eclipse/edc/identityhub/transform/JsonObjectToPresentationQueryTransformer.java @@ -0,0 +1,71 @@ +/* + * 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.edc.identityhub.transform; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.json.JsonValue; +import org.eclipse.edc.identityhub.spi.model.PresentationQuery; +import org.eclipse.edc.identityhub.spi.model.presentationdefinition.PresentationDefinition; +import org.eclipse.edc.jsonld.spi.JsonLdKeywords; +import org.eclipse.edc.jsonld.spi.transformer.AbstractJsonLdTransformer; +import org.eclipse.edc.transform.spi.TransformerContext; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Transforms a JsonObject into a PresentationQuery object. + */ +public class JsonObjectToPresentationQueryTransformer extends AbstractJsonLdTransformer { + + private final ObjectMapper mapper; + + public JsonObjectToPresentationQueryTransformer(ObjectMapper mapper) { + super(JsonObject.class, PresentationQuery.class); + this.mapper = mapper; + } + + @Override + public @Nullable PresentationQuery transform(@NotNull JsonObject jsonObject, @NotNull TransformerContext context) { + var bldr = PresentationQuery.Builder.newinstance(); + visitProperties(jsonObject, (k, v) -> { + switch (k) { + case PresentationQuery.PRESENTATION_QUERY_DEFINITION_PROPERTY -> bldr.presentationDefinition(readPresentationDefinition(v, context)); + case PresentationQuery.PRESENTATION_QUERY_SCOPE_PROPERTY -> transformArrayOrObject(v, Object.class, o -> bldr.scope(o.toString()), context); + default -> context.reportProblem("Unknown property '%s'".formatted(k)); + } + }); + + return bldr.build(); + } + + private PresentationDefinition readPresentationDefinition(JsonValue v, TransformerContext context) { + JsonObject jo; + if (v.getValueType() == JsonValue.ValueType.ARRAY && !((JsonArray) v).isEmpty()) { + jo = v.asJsonArray().getJsonObject(0); + } else { + jo = v.asJsonObject(); + } + var rawJson = jo.getJsonObject(JsonLdKeywords.VALUE); + try { + return mapper.readValue(rawJson.toString(), PresentationDefinition.class); + } catch (JsonProcessingException e) { + context.reportProblem("Error reading JSON literal: %s".formatted(e.getMessage())); + return null; + } + } +} diff --git a/core/identity-hub-transform/src/test/java/org/eclipse/edc/identityhub/transform/JsonObjectToPresentationQueryTransformerTest.java b/core/identity-hub-transform/src/test/java/org/eclipse/edc/identityhub/transform/JsonObjectToPresentationQueryTransformerTest.java new file mode 100644 index 000000000..8a88daa29 --- /dev/null +++ b/core/identity-hub-transform/src/test/java/org/eclipse/edc/identityhub/transform/JsonObjectToPresentationQueryTransformerTest.java @@ -0,0 +1,175 @@ +/* + * 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.edc.identityhub.transform; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.json.JsonObject; +import org.eclipse.edc.core.transform.TransformerContextImpl; +import org.eclipse.edc.core.transform.TypeTransformerRegistryImpl; +import org.eclipse.edc.core.transform.transformer.to.JsonValueToGenericTypeTransformer; +import org.eclipse.edc.jsonld.TitaniumJsonLd; +import org.eclipse.edc.jsonld.spi.JsonLd; +import org.eclipse.edc.jsonld.util.JacksonJsonLd; +import org.eclipse.edc.junit.testfixtures.TestUtils; +import org.eclipse.edc.transform.spi.TransformerContext; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +class JsonObjectToPresentationQueryTransformerTest { + private final ObjectMapper mapper = JacksonJsonLd.createObjectMapper(); + private final JsonObjectToPresentationQueryTransformer transformer = new JsonObjectToPresentationQueryTransformer(mapper); + private final JsonLd jsonLd = new TitaniumJsonLd(mock()); + private final TypeTransformerRegistry trr = new TypeTransformerRegistryImpl(); + private final TransformerContext context = new TransformerContextImpl(trr); + + + @BeforeEach + void setUp() { + jsonLd.registerCachedDocument("https://identity.foundation/presentation-exchange/submission/v1", TestUtils.getFileFromResourceName("presentation_ex.json").toURI()); + jsonLd.registerCachedDocument("https://w3id.org/tractusx-trust/v0.8", TestUtils.getFileFromResourceName("presentation_query.json").toURI()); + // delegate to the generic transformer + + trr.register(new JsonValueToGenericTypeTransformer(mapper)); + } + + @Test + void transform_withScopes() throws JsonProcessingException { + var obj = """ + { + "@context": [ + "https://identity.foundation/presentation-exchange/submission/v1", + "https://w3id.org/tractusx-trust/v0.8" + ], + "@type": "Query", + "scope": [ + "org.eclipse.edc.vc.type:TestCredential:read", + "org.eclipse.edc.vc.type:AnotherCredential:all" + ] + } + """; + var json = mapper.readValue(obj, JsonObject.class); + var jo = jsonLd.expand(json); + assertThat(jo.succeeded()).withFailMessage(jo::getFailureDetail).isTrue(); + + var query = transformer.transform(jo.getContent(), context); + assertThat(query).isNotNull(); + assertThat(query.getScopes()).hasSize(2) + .containsExactlyInAnyOrder( + "org.eclipse.edc.vc.type:TestCredential:read", + "org.eclipse.edc.vc.type:AnotherCredential:all"); + assertThat(query.getPresentationDefinition()).isNull(); + } + + @Test + void transform_withPresentationDefinition() throws JsonProcessingException { + var json = """ + { + "@context": [ + "https://identity.foundation/presentation-exchange/submission/v1", + "https://w3id.org/tractusx-trust/v0.8" + ], + "@type": "Query", + "presentation_definition": { + "id": "first simple example", + "input_descriptors": [ + { + "id": "descriptor-id-1", + "name": "A specific type of VC", + "purpose": "We want a VC of this type", + "constraints": { + "fields": [ + { + "path": [ + "$.type" + ], + "filter": { + "type": "string", + "pattern": "" + } + } + ] + } + } + ] + } + } + """; + var jobj = mapper.readValue(json, JsonObject.class); + + var expansion = jsonLd.expand(jobj); + assertThat(expansion.succeeded()).withFailMessage(expansion::getFailureDetail).isTrue(); + + var query = transformer.transform(expansion.getContent(), context); + assertThat(query).isNotNull(); + assertThat(query.getScopes()).isNotNull().isEmpty(); + assertThat(query.getPresentationDefinition()).isNotNull(); + assertThat(query.getPresentationDefinition().getInputDescriptors()).isNotEmpty() + .allSatisfy(id -> assertThat(id.getId()).isEqualTo("descriptor-id-1")); + + } + + @Test + void transform_withScopesAndPresDef() throws JsonProcessingException { + var json = """ + { + "@context": [ + "https://identity.foundation/presentation-exchange/submission/v1", + "https://w3id.org/tractusx-trust/v0.8" + ], + "@type": "Query", + "scope": ["test-scope1"], + "presentation_definition": { + "id": "first simple example", + "input_descriptors": [ + { + "id": "descriptor-id-1", + "name": "A specific type of VC", + "purpose": "We want a VC of this type", + "constraints": { + "fields": [ + { + "path": [ + "$.type" + ], + "filter": { + "type": "string", + "pattern": "" + } + } + ] + } + } + ] + } + } + """; + var jobj = mapper.readValue(json, JsonObject.class); + + var expansion = jsonLd.expand(jobj); + assertThat(expansion.succeeded()).withFailMessage(expansion::getFailureDetail).isTrue(); + + var query = transformer.transform(expansion.getContent(), context); + assertThat(query).isNotNull(); + assertThat(query.getScopes()).isNotNull().containsExactly("test-scope1"); + assertThat(query.getPresentationDefinition()).isNotNull(); + assertThat(query.getPresentationDefinition().getInputDescriptors()).isNotEmpty() + .allSatisfy(id -> assertThat(id.getId()).isEqualTo("descriptor-id-1")); + } +} \ No newline at end of file diff --git a/core/identity-hub-transform/src/test/resources/presentation_ex.json b/core/identity-hub-transform/src/test/resources/presentation_ex.json new file mode 100644 index 000000000..488925037 --- /dev/null +++ b/core/identity-hub-transform/src/test/resources/presentation_ex.json @@ -0,0 +1,15 @@ +{ + "@context": { + "@version": 1.1, + "PresentationSubmission": { + "@id": "https://identity.foundation/presentation-exchange/#presentation-submission", + "@context": { + "@version": 1.1, + "presentation_submission": { + "@id": "https://identity.foundation/presentation-exchange/#presentation-submission", + "@type": "@json" + } + } + } + } +} \ No newline at end of file diff --git a/core/identity-hub-transform/src/test/resources/presentation_query.json b/core/identity-hub-transform/src/test/resources/presentation_query.json new file mode 100644 index 000000000..ee45d6f63 --- /dev/null +++ b/core/identity-hub-transform/src/test/resources/presentation_query.json @@ -0,0 +1,12 @@ +{ + "@context": { + "scope": { + "@id": "https://w3id.org/tractusx-trust/v0.8/scope", + "@container": "@set" + }, + "presentation_definition":{ + "@id": "https://w3id.org/tractusx-trust/v0.8/presentation_definition", + "@type": "@json" + } + } +} \ No newline at end of file diff --git a/core/identity-hub/build.gradle.kts b/core/identity-hub/build.gradle.kts deleted file mode 100644 index e7ff63663..000000000 --- a/core/identity-hub/build.gradle.kts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial implementation - * - */ - -plugins { - `java-library` - `maven-publish` -} - -dependencies { - api(project(":spi:identity-hub-spi")) - implementation(project(":spi:identity-hub-store-spi")) - - implementation(libs.nimbus.jwt) - implementation(libs.edc.spi.transaction) - - testImplementation(libs.edc.core.junit) - testImplementation(testFixtures(project(":spi:identity-hub-spi"))) - testImplementation(testFixtures(project(":spi:identity-hub-store-spi"))) -} diff --git a/core/identity-hub/src/main/java/org/eclipse/edc/identityhub/IdentityHubExtension.java b/core/identity-hub/src/main/java/org/eclipse/edc/identityhub/IdentityHubExtension.java deleted file mode 100644 index 1bf1bdd6e..000000000 --- a/core/identity-hub/src/main/java/org/eclipse/edc/identityhub/IdentityHubExtension.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub; - -import org.eclipse.edc.identityhub.processor.CollectionsQueryProcessor; -import org.eclipse.edc.identityhub.processor.CollectionsWriteProcessor; -import org.eclipse.edc.identityhub.processor.FeatureDetectionReadProcessor; -import org.eclipse.edc.identityhub.processor.MessageProcessorRegistryImpl; -import org.eclipse.edc.identityhub.spi.credentials.transformer.CredentialEnvelopeTransformerRegistry; -import org.eclipse.edc.identityhub.spi.credentials.transformer.CredentialEnvelopeTransformerRegistryImpl; -import org.eclipse.edc.identityhub.spi.processor.MessageProcessorRegistry; -import org.eclipse.edc.identityhub.store.InMemoryIdentityHubStore; -import org.eclipse.edc.identityhub.store.spi.IdentityHubStore; -import org.eclipse.edc.runtime.metamodel.annotation.Inject; -import org.eclipse.edc.runtime.metamodel.annotation.Provider; -import org.eclipse.edc.runtime.metamodel.annotation.Provides; -import org.eclipse.edc.spi.system.ServiceExtension; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.edc.transaction.spi.TransactionContext; - -import static org.eclipse.edc.identityhub.spi.model.WebNodeInterfaceMethod.COLLECTIONS_QUERY; -import static org.eclipse.edc.identityhub.spi.model.WebNodeInterfaceMethod.COLLECTIONS_WRITE; -import static org.eclipse.edc.identityhub.spi.model.WebNodeInterfaceMethod.FEATURE_DETECTION_READ; - -/** - * EDC extension to boot the services used by the Identity Hub - */ - -@Provides({ CredentialEnvelopeTransformerRegistry.class }) -public class IdentityHubExtension implements ServiceExtension { - - - @Inject - private IdentityHubStore identityHubStore; - - @Inject - private TransactionContext transactionContext; - - private CredentialEnvelopeTransformerRegistry credentialEnvelopeTransformerRegistry; - - - @Override - public void initialize(ServiceExtensionContext context) { - credentialEnvelopeTransformerRegistry = new CredentialEnvelopeTransformerRegistryImpl(); - context.registerService(CredentialEnvelopeTransformerRegistry.class, credentialEnvelopeTransformerRegistry); - } - - @Provider(isDefault = true) - public MessageProcessorRegistry messageProcessorRegistry(ServiceExtensionContext context) { - var methodProcessorFactory = new MessageProcessorRegistryImpl(); - - methodProcessorFactory.register(COLLECTIONS_QUERY, new CollectionsQueryProcessor(identityHubStore, transactionContext)); - methodProcessorFactory.register(COLLECTIONS_WRITE, new CollectionsWriteProcessor(identityHubStore, context.getMonitor(), transactionContext, credentialEnvelopeTransformerRegistry)); - methodProcessorFactory.register(FEATURE_DETECTION_READ, new FeatureDetectionReadProcessor()); - - return methodProcessorFactory; - } - - @Provider(isDefault = true) - public IdentityHubStore identityHubStore() { - return new InMemoryIdentityHubStore(); - } - -} diff --git a/core/identity-hub/src/main/java/org/eclipse/edc/identityhub/processor/CollectionsQueryProcessor.java b/core/identity-hub/src/main/java/org/eclipse/edc/identityhub/processor/CollectionsQueryProcessor.java deleted file mode 100644 index f62428ca5..000000000 --- a/core/identity-hub/src/main/java/org/eclipse/edc/identityhub/processor/CollectionsQueryProcessor.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.processor; - -import org.eclipse.edc.identityhub.spi.model.MessageRequestObject; -import org.eclipse.edc.identityhub.spi.model.MessageResponseObject; -import org.eclipse.edc.identityhub.spi.model.MessageStatus; -import org.eclipse.edc.identityhub.spi.model.Record; -import org.eclipse.edc.identityhub.spi.processor.MessageProcessor; -import org.eclipse.edc.identityhub.store.spi.IdentityHubRecord; -import org.eclipse.edc.identityhub.store.spi.IdentityHubStore; -import org.eclipse.edc.transaction.spi.TransactionContext; - -import java.util.stream.Collectors; - -/** - * Processor of "CollectionsQuery" messages, returning the list of objects available in the {@link IdentityHubStore} - */ -public class CollectionsQueryProcessor implements MessageProcessor { - - private final IdentityHubStore identityHubStore; - - private final TransactionContext transactionContext; - - public CollectionsQueryProcessor(IdentityHubStore identityHubStore, TransactionContext transactionContext) { - this.identityHubStore = identityHubStore; - this.transactionContext = transactionContext; - } - - @Override - public MessageResponseObject process(MessageRequestObject requestObject) { - try (var stream = transactionContext.execute(identityHubStore::getAll)) { - var entries = stream.map(this::toRecord).collect(Collectors.toList()); - return MessageResponseObject.Builder.newInstance() - .status(MessageStatus.OK) - .entries(entries) - .build(); - } - } - - private Record toRecord(IdentityHubRecord identityHubRecord) { - return Record.Builder.newInstance() - .id(identityHubRecord.getId()) - .createdAt(identityHubRecord.getCreatedAt()) - .dataFormat(identityHubRecord.getPayloadFormat()) - .data(identityHubRecord.getPayload()) - .build(); - } -} diff --git a/core/identity-hub/src/main/java/org/eclipse/edc/identityhub/processor/CollectionsWriteProcessor.java b/core/identity-hub/src/main/java/org/eclipse/edc/identityhub/processor/CollectionsWriteProcessor.java deleted file mode 100644 index a03534c15..000000000 --- a/core/identity-hub/src/main/java/org/eclipse/edc/identityhub/processor/CollectionsWriteProcessor.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.processor; - -import org.eclipse.edc.identityhub.spi.credentials.transformer.CredentialEnvelopeTransformerRegistry; -import org.eclipse.edc.identityhub.spi.model.MessageRequestObject; -import org.eclipse.edc.identityhub.spi.model.MessageResponseObject; -import org.eclipse.edc.identityhub.spi.model.MessageStatus; -import org.eclipse.edc.identityhub.spi.processor.MessageProcessor; -import org.eclipse.edc.identityhub.store.spi.IdentityHubRecord; -import org.eclipse.edc.identityhub.store.spi.IdentityHubStore; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.result.Result; -import org.eclipse.edc.transaction.spi.TransactionContext; - -import static java.lang.String.format; - -/** - * Processor of "CollectionsWrite" messages, in order to write objects into the {@link IdentityHubStore}. - */ -public class CollectionsWriteProcessor implements MessageProcessor { - - private final IdentityHubStore identityHubStore; - private final Monitor monitor; - private final TransactionContext transactionContext; - - private final CredentialEnvelopeTransformerRegistry transformerRegistry; - - public CollectionsWriteProcessor(IdentityHubStore identityHubStore, Monitor monitor, TransactionContext transactionContext, CredentialEnvelopeTransformerRegistry transformerRegistry) { - this.identityHubStore = identityHubStore; - this.monitor = monitor; - this.transactionContext = transactionContext; - this.transformerRegistry = transformerRegistry; - } - - @Override - public MessageResponseObject process(MessageRequestObject requestObject) { - var record = createRecord(requestObject); - if (record.failed()) { - monitor.warning(format("Failed to create record %s", record.getFailureDetail())); - return MessageResponseObject.Builder.newInstance().status(MessageStatus.MALFORMED_MESSAGE).build(); - } - - try { - transactionContext.execute(() -> identityHubStore.add(record.getContent())); - } catch (Exception e) { - monitor.warning("Failed to add Verifiable Credential to Identity Hub", e); - return MessageResponseObject.Builder.newInstance().status(MessageStatus.UNHANDLED_ERROR).build(); - } - - return MessageResponseObject.Builder.newInstance().status(MessageStatus.OK).build(); - } - - private Result createRecord(MessageRequestObject requestObject) { - var descriptor = requestObject.getDescriptor(); - if (descriptor.getRecordId() == null) { - return Result.failure("Missing mandatory `recordId` in descriptor"); - } - if (descriptor.getDateCreated() == 0) { - return Result.failure("Missing mandatory `dateCreated` in descriptor"); - } - if (descriptor.getDataFormat() == null) { - return Result.failure("Missing mandatory `dataFormat` in descriptor"); - } - - var transformer = transformerRegistry.resolve(descriptor.getDataFormat()); - - if (transformer == null) { - return Result.failure(format("No registered transformer for `dataFormat` %s", descriptor.getDataFormat())); - } - var parsing = transformer.parse(requestObject.getData()); - - if (parsing.failed()) { - return Result.failure(parsing.getFailureMessages()); - } - return Result.success(IdentityHubRecord.Builder.newInstance() - .id(descriptor.getRecordId()) - .payload(requestObject.getData()) - .payloadFormat(descriptor.getDataFormat()) - .createdAt(descriptor.getDateCreated()) - .build()); - } -} diff --git a/core/identity-hub/src/main/java/org/eclipse/edc/identityhub/processor/FeatureDetectionReadProcessor.java b/core/identity-hub/src/main/java/org/eclipse/edc/identityhub/processor/FeatureDetectionReadProcessor.java deleted file mode 100644 index 45433d54a..000000000 --- a/core/identity-hub/src/main/java/org/eclipse/edc/identityhub/processor/FeatureDetectionReadProcessor.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.processor; - -import org.eclipse.edc.identityhub.spi.model.FeatureDetection; -import org.eclipse.edc.identityhub.spi.model.MessageRequestObject; -import org.eclipse.edc.identityhub.spi.model.MessageResponseObject; -import org.eclipse.edc.identityhub.spi.model.MessageStatus; -import org.eclipse.edc.identityhub.spi.model.WebNodeInterfaces; -import org.eclipse.edc.identityhub.spi.processor.MessageProcessor; - -import java.util.List; - -import static org.eclipse.edc.identityhub.spi.model.WebNodeInterfaceMethod.COLLECTIONS_QUERY; -import static org.eclipse.edc.identityhub.spi.model.WebNodeInterfaceMethod.COLLECTIONS_WRITE; - -/** - * Processor of "FeatureDetectionRead" messages - */ -public class FeatureDetectionReadProcessor implements MessageProcessor { - - @Override - public MessageResponseObject process(MessageRequestObject requestObject) { - return MessageResponseObject.Builder.newInstance() - .status(MessageStatus.OK) - .entries(List.of( - FeatureDetection.Builder.newInstance().interfaces( - WebNodeInterfaces.Builder.newInstance() - .supportedCollection(COLLECTIONS_QUERY.getName()) - .supportedCollection(COLLECTIONS_WRITE.getName()) - .build() - ).build() - )) - .build(); - } -} diff --git a/core/identity-hub/src/main/java/org/eclipse/edc/identityhub/processor/InterfaceNotImplementedProcessor.java b/core/identity-hub/src/main/java/org/eclipse/edc/identityhub/processor/InterfaceNotImplementedProcessor.java deleted file mode 100644 index 2ecc0997d..000000000 --- a/core/identity-hub/src/main/java/org/eclipse/edc/identityhub/processor/InterfaceNotImplementedProcessor.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.processor; - -import org.eclipse.edc.identityhub.spi.model.MessageRequestObject; -import org.eclipse.edc.identityhub.spi.model.MessageResponseObject; -import org.eclipse.edc.identityhub.spi.processor.MessageProcessor; - -import java.util.List; - -import static org.eclipse.edc.identityhub.spi.model.MessageStatus.INTERFACE_NOT_IMPLEMENTED; - -/** - * Default message processor when a non-supported interface is provided - */ -public class InterfaceNotImplementedProcessor implements MessageProcessor { - - @Override - public MessageResponseObject process(MessageRequestObject requestObject) { - return MessageResponseObject.Builder.newInstance() - .status(INTERFACE_NOT_IMPLEMENTED) - .entries(List.of()) - .build(); - } -} diff --git a/core/identity-hub/src/main/java/org/eclipse/edc/identityhub/processor/MessageProcessorRegistryImpl.java b/core/identity-hub/src/main/java/org/eclipse/edc/identityhub/processor/MessageProcessorRegistryImpl.java deleted file mode 100644 index cab41df1f..000000000 --- a/core/identity-hub/src/main/java/org/eclipse/edc/identityhub/processor/MessageProcessorRegistryImpl.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.processor; - -import org.eclipse.edc.identityhub.spi.model.WebNodeInterfaceMethod; -import org.eclipse.edc.identityhub.spi.processor.MessageProcessor; -import org.eclipse.edc.identityhub.spi.processor.MessageProcessorRegistry; - -import java.util.HashMap; -import java.util.Map; - -/** - * Registry used to provide the right MessageProcessor according to the message method. - */ -public class MessageProcessorRegistryImpl implements MessageProcessorRegistry { - - private static final InterfaceNotImplementedProcessor DEFAULT_PROCESSOR = new InterfaceNotImplementedProcessor(); - - private final Map messageProcessorsByMethod; - - public MessageProcessorRegistryImpl() { - messageProcessorsByMethod = new HashMap<>(); - } - - @Override - public void register(WebNodeInterfaceMethod method, MessageProcessor messageProcessor) { - messageProcessorsByMethod.put(method, messageProcessor); - } - - @Override - public MessageProcessor resolve(WebNodeInterfaceMethod method) { - return messageProcessorsByMethod.getOrDefault(method, DEFAULT_PROCESSOR); - } -} diff --git a/core/identity-hub/src/main/java/org/eclipse/edc/identityhub/store/InMemoryIdentityHubStore.java b/core/identity-hub/src/main/java/org/eclipse/edc/identityhub/store/InMemoryIdentityHubStore.java deleted file mode 100644 index a8c80a963..000000000 --- a/core/identity-hub/src/main/java/org/eclipse/edc/identityhub/store/InMemoryIdentityHubStore.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.store; - -import org.eclipse.edc.identityhub.store.spi.IdentityHubRecord; -import org.eclipse.edc.identityhub.store.spi.IdentityHubStore; -import org.eclipse.edc.spi.persistence.EdcPersistenceException; -import org.jetbrains.annotations.NotNull; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Stream; - -/** - * In memory store of Hub Objects. - */ -public class InMemoryIdentityHubStore implements IdentityHubStore { - - private final Map cache = new ConcurrentHashMap<>(); - - @Override - public @NotNull Stream getAll() { - return cache.values().stream(); - } - - @Override - public void add(IdentityHubRecord record) { - if (cache.containsKey(record.getId())) { - throw new EdcPersistenceException("Identity Hub already contains a record with id: " + record.getId()); - } - cache.put(record.getId(), record); - } -} diff --git a/core/identity-hub/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/core/identity-hub/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension deleted file mode 100644 index dba95d603..000000000 --- a/core/identity-hub/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension +++ /dev/null @@ -1 +0,0 @@ -org.eclipse.edc.identityhub.IdentityHubExtension diff --git a/core/identity-hub/src/test/java/org/eclipse/edc/identityhub/processor/CollectionsWriteProcessorTest.java b/core/identity-hub/src/test/java/org/eclipse/edc/identityhub/processor/CollectionsWriteProcessorTest.java deleted file mode 100644 index f9ad5846b..000000000 --- a/core/identity-hub/src/test/java/org/eclipse/edc/identityhub/processor/CollectionsWriteProcessorTest.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.processor; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.nimbusds.jose.JWSAlgorithm; -import com.nimbusds.jose.JWSHeader; -import com.nimbusds.jose.crypto.ECDSASigner; -import com.nimbusds.jwt.JWTClaimsSet; -import com.nimbusds.jwt.SignedJWT; -import org.eclipse.edc.identityhub.spi.credentials.model.CredentialEnvelope; -import org.eclipse.edc.identityhub.spi.credentials.transformer.CredentialEnvelopeTransformer; -import org.eclipse.edc.identityhub.spi.credentials.transformer.CredentialEnvelopeTransformerRegistry; -import org.eclipse.edc.identityhub.spi.model.Descriptor; -import org.eclipse.edc.identityhub.spi.model.MessageRequestObject; -import org.eclipse.edc.identityhub.spi.model.MessageResponseObject; -import org.eclipse.edc.identityhub.spi.model.MessageStatus; -import org.eclipse.edc.identityhub.store.spi.IdentityHubRecord; -import org.eclipse.edc.identityhub.store.spi.IdentityHubStore; -import org.eclipse.edc.spi.EdcException; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.result.Result; -import org.eclipse.edc.transaction.spi.NoopTransactionContext; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -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 org.mockito.ArgumentCaptor; - -import java.nio.charset.StandardCharsets; -import java.time.Instant; -import java.util.UUID; -import java.util.stream.Stream; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.buildSignedJwt; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.generateCredential; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.generateEcKey; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - - -class CollectionsWriteProcessorTest { - - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - private static final String ISSUER = "http://some.test.url"; - private static final String SUBJECT = "http://some.test.url"; - - private CredentialEnvelopeTransformerRegistry tranformerRegistry; - - private IdentityHubStore identityHubStore; - private CollectionsWriteProcessor writeProcessor; - - - private static Descriptor.Builder descriptorBuilder() { - return Descriptor.Builder.newInstance(); - } - - private static MessageRequestObject getValidMessageRequestObject() { - return MessageRequestObject.Builder.newInstance() - .descriptor(getValidDescriptor()) - .data(getValidData()) - .build(); - } - - private static Descriptor getValidDescriptor() { - return descriptorBuilder() - .method("test") - .dataFormat("validDateFormat") - .dateCreated(Instant.now().getEpochSecond()) - .recordId(UUID.randomUUID().toString()) - .build(); - } - - private static byte[] getValidData() { - var verifiableCredential = generateCredential(); - return buildSignedJwt(verifiableCredential, ISSUER, SUBJECT, generateEcKey()).serialize().getBytes(StandardCharsets.UTF_8); - } - - @BeforeEach - void setUp() { - identityHubStore = mock(IdentityHubStore.class); - tranformerRegistry = mock(CredentialEnvelopeTransformerRegistry.class); - - writeProcessor = new CollectionsWriteProcessor(identityHubStore, mock(Monitor.class), new NoopTransactionContext(), tranformerRegistry); - } - - @ParameterizedTest - @ArgumentsSource(InvalidArgumentProvider.class) - void writeCredentials_invalidInput(MessageRequestObject requestObject) { - // Arrange - var expectedResult = MessageResponseObject.Builder.newInstance().status(MessageStatus.MALFORMED_MESSAGE).build(); - - // Act - var result = writeProcessor.process(requestObject); - - // Assert - verify(identityHubStore, never()).add(any()); - assertThat(result).usingRecursiveComparison().isEqualTo(expectedResult); - } - - @Test - void writeCredentials_addStoreFailure() { - // Arrange - doThrow(new EdcException("store error")).when(identityHubStore).add(any()); - var transformer = mock(CredentialEnvelopeTransformer.class); - var carrier = mock(CredentialEnvelope.class); - when(transformer.parse(any())).thenReturn(Result.success(carrier)); - when(tranformerRegistry.resolve(any())).thenReturn(transformer); - var requestObject = getValidMessageRequestObject(); - var expectedResult = MessageResponseObject.Builder.newInstance().status(MessageStatus.UNHANDLED_ERROR).build(); - - // Act - var result = writeProcessor.process(requestObject); - - // Assert - assertThat(result).usingRecursiveComparison().isEqualTo(expectedResult); - } - - @Test - void writeCredentials() { - // Arrange - var requestObject = getValidMessageRequestObject(); - var transformer = mock(CredentialEnvelopeTransformer.class); - var carrier = mock(CredentialEnvelope.class); - when(transformer.parse(any())).thenReturn(Result.success(carrier)); - when(tranformerRegistry.resolve(any())).thenReturn(transformer); - - // Act - var result = writeProcessor.process(requestObject); - - // Assert - var expectedResult = MessageResponseObject.Builder.newInstance().status(MessageStatus.OK).build(); - - var captor = ArgumentCaptor.forClass(IdentityHubRecord.class); - verify(identityHubStore).add(captor.capture()); - assertThat(result).usingRecursiveComparison().isEqualTo(expectedResult); - var record = captor.getValue(); - assertThat(record).isNotNull(); - assertThat(record.getId()).isEqualTo(requestObject.getDescriptor().getRecordId()); - assertThat(record.getCreatedAt()).isEqualTo(requestObject.getDescriptor().getDateCreated()); - assertThat(record.getPayload()).isEqualTo(requestObject.getData()); - } - - private static class InvalidArgumentProvider implements ArgumentsProvider { - @Override - public Stream provideArguments(ExtensionContext extensionContext) throws Exception { - var missingRecordIdDescriptor = descriptorBuilder() - .method("test") - .dateCreated(Instant.now().getEpochSecond()) - .build(); - - var missingDateCreatedDescriptor = descriptorBuilder() - .method("test") - .recordId(UUID.randomUUID().toString()) - .build(); - - var verifiableCredentialWithoutId = new JWTClaimsSet.Builder() - .claim("vc", "{ \"credentialSubject\": { \"foo\": \"bar\" }}") - .issuer(ISSUER) - .subject(SUBJECT) - .expirationTime(null) - .notBeforeTime(null) - .build(); - var dataWithInvalidVc = buildSignedJwt(verifiableCredentialWithoutId, generateEcKey()).serialize().getBytes(StandardCharsets.UTF_8); - - var jws = new SignedJWT(new JWSHeader.Builder(JWSAlgorithm.ES256).build(), new JWTClaimsSet.Builder().build()); - jws.sign(new ECDSASigner(generateEcKey().toECPrivateKey())); - var dataWithoutMandatoryVcField = jws.serialize().getBytes(StandardCharsets.UTF_8); - - return Stream.of( - // valid descriptor but invalid data - Arguments.of(MessageRequestObject.Builder.newInstance().descriptor(getValidDescriptor()).data(dataWithInvalidVc).build()), - Arguments.of(MessageRequestObject.Builder.newInstance().descriptor(getValidDescriptor()).data(dataWithoutMandatoryVcField).build()), - Arguments.of(MessageRequestObject.Builder.newInstance().descriptor(getValidDescriptor()).data("{".getBytes(StandardCharsets.UTF_8)).build()), - Arguments.of(MessageRequestObject.Builder.newInstance().descriptor(getValidDescriptor()).data("invalid base64".getBytes(StandardCharsets.UTF_8)).build()), - // valid date but invalid descriptor - Arguments.of(MessageRequestObject.Builder.newInstance().descriptor(missingRecordIdDescriptor).data(getValidData()).build()), - Arguments.of(MessageRequestObject.Builder.newInstance().descriptor(missingDateCreatedDescriptor).data(getValidData()).build()) - ); - } - } -} diff --git a/core/identity-hub/src/test/java/org/eclipse/edc/identityhub/store/InMemoryIdentityHubStoreTest.java b/core/identity-hub/src/test/java/org/eclipse/edc/identityhub/store/InMemoryIdentityHubStoreTest.java deleted file mode 100644 index 2d6e805de..000000000 --- a/core/identity-hub/src/test/java/org/eclipse/edc/identityhub/store/InMemoryIdentityHubStoreTest.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.store; - -import org.eclipse.edc.identityhub.store.spi.IdentityHubStore; -import org.eclipse.edc.identityhub.store.spi.IdentityHubStoreTestBase; -import org.junit.jupiter.api.BeforeEach; - -class InMemoryIdentityHubStoreTest extends IdentityHubStoreTestBase { - - private InMemoryIdentityHubStore store; - - @BeforeEach - void setup() { - store = new InMemoryIdentityHubStore(); - } - - @Override - protected IdentityHubStore getStore() { - return store; - } -} diff --git a/docs/developer/openapi.md b/docs/developer/openapi.md deleted file mode 100644 index 94cf5c32c..000000000 --- a/docs/developer/openapi.md +++ /dev/null @@ -1,13 +0,0 @@ -# Generating the OpenApi Spec - -It is possible to generate an OpenAPI spec in the form of a `*.yaml` file by invoking a Gradle -task. - -The file is at `resources/openapi/yaml/identity-hub.yaml`. - -## Generate OpenAPI `yaml` file - -To re-generate the YAML file, invoke -```shell -./gradlew clean resolve -``` diff --git a/docs/templates/README.md b/docs/templates/README.md deleted file mode 100644 index 1cf75e8be..000000000 --- a/docs/templates/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# Templates - -Find all provided documentation templates in this folder. Please note that the _italic text -and sentences_ should be removed. Feel free to add additional sections and subsections, however, make sure -that at least the sections of the templates marked as "mandatory" are filled. - -## Decision Records - -[Link](decision-record.md) to template. - -Each decision record should be put in an appropriate folder that is following a naming pattern: -`YYYY-MM-DD-title-of-decision-record`. This should be located at the [decision record folder](../developer/decision-records) -and contain all relevant files, at least a filled-out template named `README.md` and any additional images. - -As of now, every merged decision record is in state `accepted`. Please make sure to add a comment to -a decision record that replaces a previous one with adding a hint: `superseded by [...]`. \ No newline at end of file diff --git a/docs/templates/decision-record.md b/docs/templates/decision-record.md deleted file mode 100644 index 68d30c57a..000000000 --- a/docs/templates/decision-record.md +++ /dev/null @@ -1,15 +0,0 @@ -# Title of the Decision Record - -## Decision - -_Briefly and clearly describe the topic this record covers, what decision was made and why._ - -## Rationale - -_Briefly describe the relevance of this topic and why it is important to have a decision. If applicable, -add an evaluation of several options (e.g., different libs)._ - -## Approach - -_Clearly describe the approach. Feel free to add subsections, graphics, or example code and please -make sure every relevant detail is explained._ diff --git a/docs/templates/puml-colors.txt b/docs/templates/puml-colors.txt deleted file mode 100644 index d3bd9c6cd..000000000 --- a/docs/templates/puml-colors.txt +++ /dev/null @@ -1,17 +0,0 @@ - -Insert this at the beginning of each PlantUML file to keep the style consistent. - ---- - -skinParam NoteBackgroundColor WhiteSmoke -skinParam NoteFontColor Black -skinParam ParticipantBackgroundColor WhiteSmoke -skinParam ActorBackgroundColor WhiteSmoke -skinParam AgentBackgroundColor White -skinParam AgentBorderColor SkyBlue -skinparam shadowing false - -!define ConsumerColor f8f2ff -!define ProviderColor d9edff -!define WarningColor Business -!define LeadColor Technology \ No newline at end of file diff --git a/system-tests/README.md b/e2e-tests/README.md similarity index 100% rename from system-tests/README.md rename to e2e-tests/README.md diff --git a/e2e-tests/api-tests/build.gradle.kts b/e2e-tests/api-tests/build.gradle.kts new file mode 100644 index 000000000..20267538d --- /dev/null +++ b/e2e-tests/api-tests/build.gradle.kts @@ -0,0 +1,20 @@ +plugins { + `java-library` +} + + +dependencies { + testImplementation(project(":spi:identity-hub-spi")) + testImplementation(libs.edc.junit) + testImplementation(libs.restAssured) + testImplementation(libs.awaitility) + testImplementation(libs.testcontainers.junit) + // needed for the Participant + testImplementation(testFixtures(libs.edc.testfixtures.managementapi)) + testImplementation(testFixtures(project(":spi:identity-hub-spi"))) + testImplementation(libs.nimbus.jwt) +} + +edcBuild { + publish.set(false) +} \ No newline at end of file diff --git a/e2e-tests/api-tests/src/test/java/org/eclipse/edc/identityhub/tests/ResolutionApiEndToEndTest.java b/e2e-tests/api-tests/src/test/java/org/eclipse/edc/identityhub/tests/ResolutionApiEndToEndTest.java new file mode 100644 index 000000000..3cdcc45d0 --- /dev/null +++ b/e2e-tests/api-tests/src/test/java/org/eclipse/edc/identityhub/tests/ResolutionApiEndToEndTest.java @@ -0,0 +1,225 @@ +/* + * 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.edc.identityhub.tests; + +import com.nimbusds.jwt.JWTClaimsSet; +import org.eclipse.edc.identityhub.spi.generator.PresentationGenerator; +import org.eclipse.edc.identityhub.spi.model.InputDescriptorMapping; +import org.eclipse.edc.identityhub.spi.model.PresentationResponse; +import org.eclipse.edc.identityhub.spi.model.PresentationSubmission; +import org.eclipse.edc.identityhub.spi.resolution.CredentialQueryResolver; +import org.eclipse.edc.identityhub.spi.verification.AccessTokenVerifier; +import org.eclipse.edc.identityhub.tests.fixtures.IdentityHubRuntimeConfiguration; +import org.eclipse.edc.identityhub.tests.fixtures.TestData; +import org.eclipse.edc.junit.annotations.EndToEndTest; +import org.eclipse.edc.junit.extensions.EdcRuntimeExtension; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mockito.ArgumentMatchers; + +import java.sql.Date; +import java.time.Instant; +import java.util.List; +import java.util.UUID; + +import static io.restassured.http.ContentType.JSON; +import static jakarta.ws.rs.core.HttpHeaders.AUTHORIZATION; +import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.buildSignedJwt; +import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.generateEcKey; +import static org.eclipse.edc.spi.result.Result.failure; +import static org.eclipse.edc.spi.result.Result.success; +import static org.hamcrest.Matchers.equalTo; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@EndToEndTest +public class ResolutionApiEndToEndTest { + public static final String VALID_QUERY_WITH_SCOPE = """ + { + "@context": [ + "https://identity.foundation/presentation-exchange/submission/v1", + "https://w3id.org/tractusx-trust/v0.8" + ], + "@type": "Query", + "scope":[ + "test-scope1" + ] + } + """; + protected static final IdentityHubRuntimeConfiguration IDENTITY_HUB_PARTICIPANT = IdentityHubRuntimeConfiguration.Builder.newInstance() + .name("identity-hub") + .id("identity-hub") + .build(); + // todo: these mocks should be replaced, once their respective implementations exist! + private static final CredentialQueryResolver CREDENTIAL_QUERY_RESOLVER = mock(); + private static final PresentationGenerator PRESENTATION_GENERATOR = mock(); + private static final AccessTokenVerifier ACCESS_TOKEN_VERIFIER = mock(); + + @RegisterExtension + static EdcRuntimeExtension runtime; + + static { + runtime = new EdcRuntimeExtension(":launcher", "identity-hub", IDENTITY_HUB_PARTICIPANT.controlPlaneConfiguration()); + runtime.registerServiceMock(CredentialQueryResolver.class, CREDENTIAL_QUERY_RESOLVER); + runtime.registerServiceMock(PresentationGenerator.class, PRESENTATION_GENERATOR); + runtime.registerServiceMock(AccessTokenVerifier.class, ACCESS_TOKEN_VERIFIER); + } + + @Test + void query_tokenNotPresent_shouldReturn401() { + IDENTITY_HUB_PARTICIPANT.getResolutionEndpoint().baseRequest() + .contentType("application/json") + .post("/presentation/query") + .then() + .statusCode(401) + .extract().body().asString(); + } + + @Test + void query_validationError_shouldReturn400() { + var query = """ + { + "@context": [ + "https://identity.foundation/presentation-exchange/submission/v1", + "https://w3id.org/tractusx-trust/v0.8" + ], + "@type": "Query" + } + """; + IDENTITY_HUB_PARTICIPANT.getResolutionEndpoint().baseRequest() + .contentType(JSON) + .header(AUTHORIZATION, generateJwt()) + .body(query) + .post("/presentation/query") + .then() + .statusCode(400) + .extract().body().asString(); + + } + + @Test + void query_withPresentationDefinition_shouldReturn503() { + var query = """ + { + "@context": [ + "https://identity.foundation/presentation-exchange/submission/v1", + "https://w3id.org/tractusx-trust/v0.8" + ], + "@type": "Query", + "presentation_definition":{ + } + } + """; + IDENTITY_HUB_PARTICIPANT.getResolutionEndpoint().baseRequest() + .contentType(JSON) + .header(AUTHORIZATION, generateJwt()) + .body(query) + .post("/presentation/query") + .then() + .statusCode(503) + .extract().body().asString(); + } + + + @Test + void query_tokenVerificationFails_shouldReturn401() { + var token = generateJwt(); + when(ACCESS_TOKEN_VERIFIER.verify(eq(token))).thenReturn(failure("token not verified")); + IDENTITY_HUB_PARTICIPANT.getResolutionEndpoint().baseRequest() + .contentType(JSON) + .header(AUTHORIZATION, token) + .body(VALID_QUERY_WITH_SCOPE) + .post("/presentation/query") + .then() + .statusCode(401) + .log().ifValidationFails() + .body("[0].type", equalTo("AuthenticationFailed")) + .body("[0].message", equalTo("ID token verification failed: token not verified")); + } + + @Test + void query_queryResolutionFails_shouldReturn403() { + var token = generateJwt(); + when(ACCESS_TOKEN_VERIFIER.verify(eq(token))).thenReturn(success(List.of("test-scope1"))); + when(CREDENTIAL_QUERY_RESOLVER.query(any(), ArgumentMatchers.anyList())).thenReturn(failure("scope mismatch!")); + + IDENTITY_HUB_PARTICIPANT.getResolutionEndpoint().baseRequest() + .contentType(JSON) + .header(AUTHORIZATION, token) + .body(VALID_QUERY_WITH_SCOPE) + .post("/presentation/query") + .then() + .statusCode(403) + .log().ifValidationFails() + .body("[0].type", equalTo("NotAuthorized")) + .body("[0].message", equalTo("scope mismatch!")); + } + + @Test + void query_presentationGenerationFails_shouldReturn500() { + var token = generateJwt(); + when(ACCESS_TOKEN_VERIFIER.verify(eq(token))).thenReturn(success(List.of("test-scope1"))); + when(CREDENTIAL_QUERY_RESOLVER.query(any(), ArgumentMatchers.anyList())).thenReturn(success(List.of())); + when(PRESENTATION_GENERATOR.createPresentation(anyList(), eq(null))).thenReturn(failure("generator test error")); + + IDENTITY_HUB_PARTICIPANT.getResolutionEndpoint().baseRequest() + .contentType(JSON) + .header(AUTHORIZATION, token) + .body(VALID_QUERY_WITH_SCOPE) + .post("/presentation/query") + .then() + .statusCode(500) + .log().ifValidationFails(); + } + + @Test + void query_success() { + var token = generateJwt(); + when(ACCESS_TOKEN_VERIFIER.verify(eq(token))).thenReturn(success(List.of("test-scope1"))); + when(CREDENTIAL_QUERY_RESOLVER.query(any(), ArgumentMatchers.anyList())).thenReturn(success(List.of())); + when(PRESENTATION_GENERATOR.createPresentation(anyList(), eq(null))).thenReturn(success(createPresentationResponse())); + + var resp = IDENTITY_HUB_PARTICIPANT.getResolutionEndpoint().baseRequest() + .contentType(JSON) + .header(AUTHORIZATION, token) + .body(VALID_QUERY_WITH_SCOPE) + .post("/presentation/query") + .then() + .statusCode(200) + .log().ifValidationFails() + .extract().body().as(PresentationResponse.class); + + } + + private PresentationResponse createPresentationResponse() { + var submission = new PresentationSubmission("id", "def-id", List.of(new InputDescriptorMapping("input-id", "ldp-vp", "foo"))); + return new PresentationResponse(TestData.VP_EXAMPLE, submission); + } + + private String generateJwt() { + var ecKey = generateEcKey(); + var jwt = buildSignedJwt(new JWTClaimsSet.Builder().audience("test-audience") + .expirationTime(Date.from(Instant.now().plusSeconds(3600))) + .issuer("test-issuer") + .subject("test-subject") + .jwtID(UUID.randomUUID().toString()).build(), ecKey); + + return jwt.serialize(); + } + +} 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 new file mode 100644 index 000000000..f8d833bc9 --- /dev/null +++ b/e2e-tests/api-tests/src/test/java/org/eclipse/edc/identityhub/tests/fixtures/IdentityHubRuntimeConfiguration.java @@ -0,0 +1,102 @@ +/* + * 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.edc.identityhub.tests.fixtures; + +import io.restassured.specification.RequestSpecification; + +import java.net.URI; +import java.util.HashMap; +import java.util.Map; + +import static io.restassured.RestAssured.given; +import static org.eclipse.edc.junit.testfixtures.TestUtils.getFreePort; +import static org.eclipse.edc.spi.system.ServiceExtensionContext.PARTICIPANT_ID; + +/** + * The IdentityHubRuntimeConfiguration class represents an IdentityHub Runtime configuration and provides various information, such as API endpoints + */ +public class IdentityHubRuntimeConfiguration { + + private Endpoint resolutionEndpoint; + private String id; + private String name; + + public Endpoint getResolutionEndpoint() { + return resolutionEndpoint; + } + + public Map controlPlaneConfiguration() { + return new HashMap<>() { + { + 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("edc.connector.name", name); + } + }; + } + + public static final class Builder { + private final IdentityHubRuntimeConfiguration participant; + + private Builder() { + participant = new IdentityHubRuntimeConfiguration(); + } + + public static Builder newInstance() { + return new Builder(); + } + + public Builder id(String id) { + this.participant.id = id; + return this; + } + + public Builder name(String name) { + this.participant.name = name; + return this; + } + + public IdentityHubRuntimeConfiguration build() { + participant.resolutionEndpoint = new Endpoint(URI.create("http://localhost:" + getFreePort() + "/api/v1/resolution"), Map.of()); + return participant; + } + } + + public static class Endpoint { + private final URI url; + private final Map headers; + + public Endpoint(URI url) { + this.url = url; + this.headers = new HashMap(); + } + + public Endpoint(URI url, Map headers) { + this.url = url; + this.headers = headers; + } + + public RequestSpecification baseRequest() { + return given().baseUri(this.url.toString()).headers(this.headers); + } + + public URI getUrl() { + return this.url; + } + } +} diff --git a/e2e-tests/api-tests/src/test/java/org/eclipse/edc/identityhub/tests/fixtures/TestData.java b/e2e-tests/api-tests/src/test/java/org/eclipse/edc/identityhub/tests/fixtures/TestData.java new file mode 100644 index 000000000..1a818f6b6 --- /dev/null +++ b/e2e-tests/api-tests/src/test/java/org/eclipse/edc/identityhub/tests/fixtures/TestData.java @@ -0,0 +1,58 @@ +/* + * 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.edc.identityhub.tests.fixtures; + +public interface TestData { + // taken from https://www.w3.org/TR/vc-data-model/#example-a-simple-example-of-a-verifiable-credential + String VP_EXAMPLE = """ + { + + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/2018/credentials/examples/v1" + ], + + "id": "http://example.edu/credentials/1872", + "type": ["VerifiableCredential", "AlumniCredential"], + "issuer": "https://example.edu/issuers/565049", + "issuanceDate": "2010-01-01T19:23:24Z", + "credentialSubject": { + "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", + "alumniOf": { + "id": "did:example:c276e12ec21ebfeb1f712ebc6f1", + "name": [{ + "value": "Example University", + "lang": "en" + }, { + "value": "Exemple d'Université", + "lang": "fr" + }] + } + }, + + + "proof": { + "type": "RsaSignature2018", + "created": "2017-06-18T21:19:10Z", + "proofPurpose": "assertionMethod", + "verificationMethod": "https://example.edu/issuers/565049#key-1", + "jws": "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..TCYt5X + sITJX1CxPCT8yAV-TVkIEq_PbChOMqsLfRoPsnsgw5WEuts01mq-pQy7UJiN5mgRxD-WUc + X16dUEMGlv50aqzpqh4Qktb3rk-BuQy72IFLOqV0G_zS245-kronKb78cPN25DGlcTwLtj + PAYuNzVBAh4vGHSrQyHUdBBPM" + } + } + """; +} diff --git a/system-tests/build.gradle.kts b/e2e-tests/build.gradle.kts similarity index 81% rename from system-tests/build.gradle.kts rename to e2e-tests/build.gradle.kts index 04df562c2..2c4831a51 100644 --- a/system-tests/build.gradle.kts +++ b/e2e-tests/build.gradle.kts @@ -19,14 +19,10 @@ plugins { dependencies { api(project(":spi:identity-hub-spi")) api(libs.edc.spi.identity.did) - testImplementation(project(":launcher")) - testImplementation(project(":identity-hub-cli")) testImplementation(libs.edc.core.junit) testImplementation(testFixtures(project(":spi:identity-hub-spi"))) - testImplementation(libs.picocli.core) - testImplementation(libs.picocli.codegen) testImplementation(libs.jackson.databind) testImplementation(libs.mockserver.client) testImplementation(libs.mockserver.netty) diff --git a/system-tests/resources/jwt/authority/private-key.pem b/e2e-tests/resources/jwt/authority/private-key.pem similarity index 100% rename from system-tests/resources/jwt/authority/private-key.pem rename to e2e-tests/resources/jwt/authority/private-key.pem diff --git a/system-tests/resources/jwt/authority/public-key.pem b/e2e-tests/resources/jwt/authority/public-key.pem similarity index 100% rename from system-tests/resources/jwt/authority/public-key.pem rename to e2e-tests/resources/jwt/authority/public-key.pem diff --git a/system-tests/resources/jwt/participant/private-key.pem b/e2e-tests/resources/jwt/participant/private-key.pem similarity index 100% rename from system-tests/resources/jwt/participant/private-key.pem rename to e2e-tests/resources/jwt/participant/private-key.pem diff --git a/system-tests/resources/jwt/participant/public-key.pem b/e2e-tests/resources/jwt/participant/public-key.pem similarity index 100% rename from system-tests/resources/jwt/participant/public-key.pem rename to e2e-tests/resources/jwt/participant/public-key.pem diff --git a/system-tests/src/test/resources/webdid/authority/did.json b/e2e-tests/src/test/resources/webdid/authority/did.json similarity index 100% rename from system-tests/src/test/resources/webdid/authority/did.json rename to e2e-tests/src/test/resources/webdid/authority/did.json diff --git a/system-tests/src/test/resources/webdid/participant/did.json b/e2e-tests/src/test/resources/webdid/participant/did.json similarity index 100% rename from system-tests/src/test/resources/webdid/participant/did.json rename to e2e-tests/src/test/resources/webdid/participant/did.json diff --git a/extensions/credentials/identity-hub-credentials-jwt/build.gradle.kts b/extensions/credentials/identity-hub-credentials-jwt/build.gradle.kts deleted file mode 100644 index 886824f50..000000000 --- a/extensions/credentials/identity-hub-credentials-jwt/build.gradle.kts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial implementation - * - */ - -plugins { - `java-library` -} - - -dependencies { - implementation(project(":spi:identity-hub-spi")) - - implementation(libs.edc.spi.identity.did) - implementation(libs.nimbus.jwt) - - testImplementation(testFixtures(project(":spi:identity-hub-spi"))) - testImplementation(project(":extensions:identity-hub-api")) - testImplementation(libs.edc.ext.identity.did.core) - testImplementation(libs.edc.ext.identity.did.crypto) - testImplementation(libs.edc.core.junit) -} diff --git a/extensions/credentials/identity-hub-credentials-jwt/src/main/java/org/eclipse/edc/identityhub/credentials/jwt/JwtCredentialConstants.java b/extensions/credentials/identity-hub-credentials-jwt/src/main/java/org/eclipse/edc/identityhub/credentials/jwt/JwtCredentialConstants.java deleted file mode 100644 index c9c0e4e09..000000000 --- a/extensions/credentials/identity-hub-credentials-jwt/src/main/java/org/eclipse/edc/identityhub/credentials/jwt/JwtCredentialConstants.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2023 Amadeus - * - * 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: - * Amadeus - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.credentials.jwt; - -public interface JwtCredentialConstants { - - String VC_DATA_FORMAT = "application/vc+jwt"; - - String VP_DATA_FORMAT = "application/vp+ld+jwt"; - String VERIFIABLE_CREDENTIALS_KEY = "vc"; - - String VERIFIABLE_PRESENTATION_KEY = "vp"; -} diff --git a/extensions/credentials/identity-hub-credentials-jwt/src/main/java/org/eclipse/edc/identityhub/credentials/jwt/JwtCredentialEnvelope.java b/extensions/credentials/identity-hub-credentials-jwt/src/main/java/org/eclipse/edc/identityhub/credentials/jwt/JwtCredentialEnvelope.java deleted file mode 100644 index 3d56063a3..000000000 --- a/extensions/credentials/identity-hub-credentials-jwt/src/main/java/org/eclipse/edc/identityhub/credentials/jwt/JwtCredentialEnvelope.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.credentials.jwt; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.nimbusds.jwt.SignedJWT; -import org.eclipse.edc.identityhub.spi.credentials.model.Credential; -import org.eclipse.edc.identityhub.spi.credentials.model.CredentialEnvelope; -import org.eclipse.edc.identityhub.spi.credentials.model.VerifiableCredential; -import org.eclipse.edc.spi.result.Result; - -import java.util.List; -import java.util.Objects; - -import static org.eclipse.edc.identityhub.credentials.jwt.JwtCredentialConstants.VC_DATA_FORMAT; -import static org.eclipse.edc.identityhub.credentials.jwt.JwtCredentialConstants.VERIFIABLE_CREDENTIALS_KEY; - -public class JwtCredentialEnvelope implements CredentialEnvelope { - - private final SignedJWT jwt; - - public JwtCredentialEnvelope(SignedJWT jwt) { - this.jwt = jwt; - } - - @Override - public String format() { - return VC_DATA_FORMAT; - } - - @Override - public Result> toVerifiableCredentials(ObjectMapper mapper) { - try { - var payload = jwt.getJWTClaimsSet().getClaims(); - var vcObject = payload.get(VERIFIABLE_CREDENTIALS_KEY); - if (vcObject == null) { - return Result.failure(String.format("Missing `%s` claim", VERIFIABLE_CREDENTIALS_KEY)); - } - var credential = mapper.convertValue(vcObject, Credential.class); - // JWT Verifiable Credentials do not have embedded proof. - return Result.success(List.of(new VerifiableCredential(credential, null))); - } catch (Exception e) { - return Result.failure(Objects.requireNonNullElseGet(e.getMessage(), e::toString)); - } - } - - public SignedJWT getJwt() { - return jwt; - } -} diff --git a/extensions/credentials/identity-hub-credentials-jwt/src/main/java/org/eclipse/edc/identityhub/credentials/jwt/JwtCredentialEnvelopeTransformer.java b/extensions/credentials/identity-hub-credentials-jwt/src/main/java/org/eclipse/edc/identityhub/credentials/jwt/JwtCredentialEnvelopeTransformer.java deleted file mode 100644 index ac5280290..000000000 --- a/extensions/credentials/identity-hub-credentials-jwt/src/main/java/org/eclipse/edc/identityhub/credentials/jwt/JwtCredentialEnvelopeTransformer.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.credentials.jwt; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.nimbusds.jwt.SignedJWT; -import org.eclipse.edc.identityhub.spi.credentials.transformer.CredentialEnvelopeTransformer; -import org.eclipse.edc.spi.result.Result; - -import java.nio.charset.StandardCharsets; - -import static org.eclipse.edc.identityhub.credentials.jwt.JwtCredentialConstants.VC_DATA_FORMAT; - -public class JwtCredentialEnvelopeTransformer implements CredentialEnvelopeTransformer { - - private final ObjectMapper mapper; - - public JwtCredentialEnvelopeTransformer(ObjectMapper mapper) { - this.mapper = mapper; - } - - @Override - public Result parse(byte[] data) { - try { - var jwt = SignedJWT.parse(new String(data)); - var envelope = new JwtCredentialEnvelope(jwt); - // verify that the VC can be properly build from the signed JWT. - envelope.toVerifiableCredentials(mapper); - return Result.success(new JwtCredentialEnvelope(jwt)); - } catch (Exception e) { - return Result.failure("Failed to parse Verifiable Credential: " + e.getMessage()); - } - } - - @Override - public Result serialize(JwtCredentialEnvelope envelope) { - return Result.success(envelope.getJwt().serialize().getBytes(StandardCharsets.UTF_8)); - } - - - @Override - public String dataFormat() { - return VC_DATA_FORMAT; - } -} diff --git a/extensions/credentials/identity-hub-credentials-jwt/src/main/java/org/eclipse/edc/identityhub/credentials/jwt/JwtCredentialFactory.java b/extensions/credentials/identity-hub-credentials-jwt/src/main/java/org/eclipse/edc/identityhub/credentials/jwt/JwtCredentialFactory.java deleted file mode 100644 index d991d86b7..000000000 --- a/extensions/credentials/identity-hub-credentials-jwt/src/main/java/org/eclipse/edc/identityhub/credentials/jwt/JwtCredentialFactory.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.credentials.jwt; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.nimbusds.jose.JOSEException; -import com.nimbusds.jose.JWSAlgorithm; -import com.nimbusds.jose.JWSHeader; -import com.nimbusds.jwt.JWTClaimsSet; -import com.nimbusds.jwt.SignedJWT; -import org.eclipse.edc.iam.did.spi.key.PrivateKeyWrapper; -import org.eclipse.edc.identityhub.spi.credentials.model.Credential; - -import java.text.ParseException; -import java.util.Map; - -import static org.eclipse.edc.identityhub.credentials.jwt.JwtCredentialConstants.VERIFIABLE_CREDENTIALS_KEY; - -public class JwtCredentialFactory { - - private final ObjectMapper mapper; - - public JwtCredentialFactory(ObjectMapper mapper) { - this.mapper = mapper; - } - - /** - * Build Verifiable Credential in JWT format such as defined in W3C specification. - * The credential is defined in the `vc` claim of the token, such as: - *
{@code
-     * "credentialId" : {
-     *     "vc": {
-     *         "credentialSubject": {
-     *             // some claims about the subject.
-     *         }
-     *     }
-     *     "iss": "issuer-value",
-     *     "sub": "subject-value",
-     *     // other JWT claims
-     * }
-     * }
- * - * @param credential the credential - * @param privateKey private key for signing the JWT - * @return the verifiable credential in JWT format. - */ - public SignedJWT buildSignedJwt(Credential credential, PrivateKeyWrapper privateKey) throws JOSEException, ParseException { - var jwsHeader = new JWSHeader.Builder(JWSAlgorithm.ES256).build(); - // this step of preparatory mapping is required as nimbus relies on Gson which will not respect the format annotation defined in the @Credential object. - var mapped = mapper.convertValue(credential, Map.class); - var claims = new JWTClaimsSet.Builder() - .claim(VERIFIABLE_CREDENTIALS_KEY, mapped) - .issuer(credential.getIssuer()) - .issueTime(credential.getIssuanceDate()) - .expirationTime(credential.getExpirationDate()) - .subject(credential.getCredentialSubject().getId()) - .build(); - - var jws = new SignedJWT(jwsHeader, claims); - - jws.sign(privateKey.signer()); - - return SignedJWT.parse(jws.serialize()); - } -} diff --git a/extensions/credentials/identity-hub-credentials-jwt/src/main/java/org/eclipse/edc/identityhub/credentials/jwt/JwtCredentialsExtension.java b/extensions/credentials/identity-hub-credentials-jwt/src/main/java/org/eclipse/edc/identityhub/credentials/jwt/JwtCredentialsExtension.java deleted file mode 100644 index 8f2cb62ea..000000000 --- a/extensions/credentials/identity-hub-credentials-jwt/src/main/java/org/eclipse/edc/identityhub/credentials/jwt/JwtCredentialsExtension.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.credentials.jwt; - -import org.eclipse.edc.identityhub.spi.credentials.transformer.CredentialEnvelopeTransformerRegistry; -import org.eclipse.edc.runtime.metamodel.annotation.Extension; -import org.eclipse.edc.runtime.metamodel.annotation.Inject; -import org.eclipse.edc.spi.system.ServiceExtension; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.edc.spi.types.TypeManager; - -/** - * Extension for handling parsing and encoding verifiable credential in JWT format. - */ -@Extension(value = JwtCredentialsExtension.NAME) -public class JwtCredentialsExtension implements ServiceExtension { - - public static final String NAME = "Verifiable credential in JWT format"; - - @Inject - private CredentialEnvelopeTransformerRegistry transformerRegistry; - - @Inject - private TypeManager typeManager; - - @Override - public String name() { - return NAME; - } - - @Override - public void initialize(ServiceExtensionContext context) { - transformerRegistry.register(new JwtCredentialEnvelopeTransformer(typeManager.getMapper())); - transformerRegistry.register(new JwtPresentationEnvelopeTransformer(typeManager.getMapper())); - } -} diff --git a/extensions/credentials/identity-hub-credentials-jwt/src/main/java/org/eclipse/edc/identityhub/credentials/jwt/JwtPresentation.java b/extensions/credentials/identity-hub-credentials-jwt/src/main/java/org/eclipse/edc/identityhub/credentials/jwt/JwtPresentation.java deleted file mode 100644 index b9579737a..000000000 --- a/extensions/credentials/identity-hub-credentials-jwt/src/main/java/org/eclipse/edc/identityhub/credentials/jwt/JwtPresentation.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (c) 2023 GAIA-X - * - * 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: - * GAIA-X - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.credentials.jwt; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonFormat; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; -import org.eclipse.edc.spi.EdcException; -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.List; - -import static com.fasterxml.jackson.annotation.JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY; - -/** - * Represents a presentation in JWT format defined at W3C specification. - */ -@JsonInclude(JsonInclude.Include.NON_EMPTY) -@JsonDeserialize(builder = JwtPresentation.Builder.class) -public class JwtPresentation { - - private static final String JSON_PROP_TYPES = "type"; - public static final String DEFAULT_TYPE = "VerifiablePresentation"; - private static final String JSON_PROP_VERIFIABLE_CREDENTIAL = "verifiableCredential"; - private static final String JSON_PROP_CONTEXTS = "@context"; - - private String id; - - @JsonProperty(JSON_PROP_CONTEXTS) - @JsonFormat(with = ACCEPT_SINGLE_VALUE_AS_ARRAY) - private final List contexts = new ArrayList<>(); - - @JsonProperty(JSON_PROP_TYPES) - @JsonFormat(with = ACCEPT_SINGLE_VALUE_AS_ARRAY) - private final List types = new ArrayList<>(); - - @JsonProperty(JSON_PROP_VERIFIABLE_CREDENTIAL) - @JsonFormat(with = ACCEPT_SINGLE_VALUE_AS_ARRAY) - private List signedCredentials = new ArrayList<>(); - - protected JwtPresentation() { - } - - /** - * Returns the credential id. - * - * @return id - * @see Identifier - */ - @NotNull - public String getId() { - return id; - } - - /** - * Returns the contexts. - * - * @return contexts - * @see Contexts - */ - @NotNull - public List getContexts() { - return contexts; - } - - /** - * Returns the types. - * - * @return types - * @see Types - */ - @NotNull - public List getTypes() { - return types; - } - - /** - * Returns the list of verifiable credentials in JWT format - * - * @return verifiable credentials in JWT format - * @see Verifiable Credentials - */ - @NotNull - public List getSignedCredentials() { - return signedCredentials; - } - - @JsonPOJOBuilder(withPrefix = "") - public static class Builder { - private final JwtPresentation presentation; - - private Builder(JwtPresentation presentation) { - this.presentation = presentation; - } - - @JsonCreator - public static Builder newInstance() { - return new Builder(new JwtPresentation()); - } - - - public Builder id(String id) { - presentation.id = id; - return this; - } - - public Builder context(String context) { - presentation.contexts.add(context); - return this; - } - - @JsonProperty(JSON_PROP_CONTEXTS) - public Builder contexts(List contexts) { - presentation.contexts.addAll(contexts); - return this; - } - - @JsonProperty(JSON_PROP_TYPES) - public Builder types(List types) { - presentation.types.addAll(types); - return this; - } - - public Builder type(String type) { - presentation.types.add(type); - return this; - } - - @JsonProperty(JSON_PROP_VERIFIABLE_CREDENTIAL) - @JsonFormat(with = ACCEPT_SINGLE_VALUE_AS_ARRAY) - public Builder signedCredentials(List signedCredentials) { - presentation.signedCredentials.addAll(signedCredentials); - return this; - } - - public JwtPresentation build() { - if (presentation.types.isEmpty()) { - throw new EdcException("Presentation must contain `type` property."); - } - - if (presentation.signedCredentials.isEmpty()) { - throw new EdcException("Presentation must contain `verifiableCredential` property."); - } - return presentation; - } - } -} diff --git a/extensions/credentials/identity-hub-credentials-jwt/src/main/java/org/eclipse/edc/identityhub/credentials/jwt/JwtPresentationEnvelope.java b/extensions/credentials/identity-hub-credentials-jwt/src/main/java/org/eclipse/edc/identityhub/credentials/jwt/JwtPresentationEnvelope.java deleted file mode 100644 index 1e0cf733b..000000000 --- a/extensions/credentials/identity-hub-credentials-jwt/src/main/java/org/eclipse/edc/identityhub/credentials/jwt/JwtPresentationEnvelope.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2023 GAIA-X - * - * 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: - * GAIA-X - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.credentials.jwt; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.nimbusds.jwt.SignedJWT; -import org.eclipse.edc.identityhub.spi.credentials.model.CredentialEnvelope; -import org.eclipse.edc.identityhub.spi.credentials.model.VerifiableCredential; -import org.eclipse.edc.spi.EdcException; -import org.eclipse.edc.spi.result.Result; - -import java.text.ParseException; -import java.util.Collection; -import java.util.List; -import java.util.Objects; - -import static org.eclipse.edc.identityhub.credentials.jwt.JwtCredentialConstants.VERIFIABLE_PRESENTATION_KEY; -import static org.eclipse.edc.identityhub.credentials.jwt.JwtCredentialConstants.VP_DATA_FORMAT; - -public class JwtPresentationEnvelope implements CredentialEnvelope { - - private final SignedJWT jwt; - - public JwtPresentationEnvelope(SignedJWT jwt) { - this.jwt = jwt; - } - - @Override - public String format() { - return VP_DATA_FORMAT; - } - - @Override - public Result> toVerifiableCredentials(ObjectMapper mapper) { - try { - var payload = jwt.getJWTClaimsSet().getClaims(); - var vpObject = payload.get(VERIFIABLE_PRESENTATION_KEY); - if (vpObject == null) { - return Result.failure(String.format("Missing `%s` claim", VERIFIABLE_PRESENTATION_KEY)); - } - var jwtPresentation = mapper.convertValue(vpObject, JwtPresentation.class); - var verifiableCredentials = jwtPresentation.getSignedCredentials().stream().map(jwtVC -> { - try { - return new JwtCredentialEnvelope(SignedJWT.parse(jwtVC)) - .toVerifiableCredentials(mapper) - .getContent(); - } catch (ParseException e) { - throw new EdcException("Presentation's verifiableCredential value should be a JWT"); - } - }).flatMap(Collection::stream).toList(); - - return Result.success(verifiableCredentials); - } catch (Exception e) { - return Result.failure(Objects.requireNonNullElseGet(e.getMessage(), e::toString)); - } - } - - public Result> toJwtVerifiableCredentials(ObjectMapper mapper) { - try { - var payload = jwt.getJWTClaimsSet().getClaims(); - var vpObject = payload.get(VERIFIABLE_PRESENTATION_KEY); - if (vpObject == null) { - return Result.failure(String.format("Missing `%s` claim", VERIFIABLE_PRESENTATION_KEY)); - } - var jwtPresentation = mapper.convertValue(vpObject, JwtPresentation.class); - var signedCredentials = jwtPresentation.getSignedCredentials().stream().map(jwtVC -> { - try { - return SignedJWT.parse(jwtVC); - } catch (ParseException e) { - throw new EdcException("Presentation's verifiableCredential value should be a JWT"); - } - }).toList(); - - return Result.success(signedCredentials); - } catch (Exception e) { - return Result.failure(Objects.requireNonNullElseGet(e.getMessage(), e::toString)); - } - } - - public SignedJWT getJwt() { - return jwt; - } -} diff --git a/extensions/credentials/identity-hub-credentials-jwt/src/main/java/org/eclipse/edc/identityhub/credentials/jwt/JwtPresentationEnvelopeTransformer.java b/extensions/credentials/identity-hub-credentials-jwt/src/main/java/org/eclipse/edc/identityhub/credentials/jwt/JwtPresentationEnvelopeTransformer.java deleted file mode 100644 index 979ff35a7..000000000 --- a/extensions/credentials/identity-hub-credentials-jwt/src/main/java/org/eclipse/edc/identityhub/credentials/jwt/JwtPresentationEnvelopeTransformer.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2023 GAIA-X - * - * 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: - * GAIA-X - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.credentials.jwt; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.nimbusds.jwt.SignedJWT; -import org.eclipse.edc.identityhub.spi.credentials.transformer.CredentialEnvelopeTransformer; -import org.eclipse.edc.spi.result.Result; - -import java.nio.charset.StandardCharsets; - -import static org.eclipse.edc.identityhub.credentials.jwt.JwtCredentialConstants.VP_DATA_FORMAT; - -public class JwtPresentationEnvelopeTransformer implements CredentialEnvelopeTransformer { - - private final ObjectMapper mapper; - - public JwtPresentationEnvelopeTransformer(ObjectMapper mapper) { - this.mapper = mapper; - } - - @Override - public Result parse(byte[] data) { - try { - var jwt = SignedJWT.parse(new String(data)); - var envelope = new JwtPresentationEnvelope(jwt); - envelope.toVerifiableCredentials(mapper); - return Result.success(envelope); - } catch (Exception e) { - return Result.failure("Failed to parse Verifiable Presentation: " + e.getMessage()); - } - } - - @Override - public Result serialize(JwtPresentationEnvelope envelope) { - return Result.success(envelope.getJwt().serialize().getBytes(StandardCharsets.UTF_8)); - } - - - @Override - public String dataFormat() { - return VP_DATA_FORMAT; - } -} diff --git a/extensions/credentials/identity-hub-credentials-jwt/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/extensions/credentials/identity-hub-credentials-jwt/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension deleted file mode 100644 index 43d574928..000000000 --- a/extensions/credentials/identity-hub-credentials-jwt/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension +++ /dev/null @@ -1 +0,0 @@ -org.eclipse.edc.identityhub.credentials.jwt.JwtCredentialsExtension diff --git a/extensions/credentials/identity-hub-credentials-jwt/src/test/java/org/eclipse/edc/identityhub/credentials/jwt/JwtCredentialEnvelopeTest.java b/extensions/credentials/identity-hub-credentials-jwt/src/test/java/org/eclipse/edc/identityhub/credentials/jwt/JwtCredentialEnvelopeTest.java deleted file mode 100644 index 705edaf39..000000000 --- a/extensions/credentials/identity-hub-credentials-jwt/src/test/java/org/eclipse/edc/identityhub/credentials/jwt/JwtCredentialEnvelopeTest.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.credentials.jwt; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.nimbusds.jose.JWSAlgorithm; -import com.nimbusds.jose.JWSHeader; -import com.nimbusds.jose.jwk.ECKey; -import com.nimbusds.jwt.JWTClaimsSet; -import com.nimbusds.jwt.SignedJWT; -import org.eclipse.edc.identityhub.spi.credentials.model.Credential; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.edc.identityhub.credentials.jwt.JwtCredentialConstants.VERIFIABLE_CREDENTIALS_KEY; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.buildSignedJwt; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.generateCredential; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.generateEcKey; - -class JwtCredentialEnvelopeTest { - - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - private static final Credential CREDENTIAL = generateCredential(); - private static final JWSHeader JWS_HEADER = new JWSHeader.Builder(JWSAlgorithm.ES256).build(); - private ECKey privateKey; - - @BeforeEach - public void setUp() { - privateKey = generateEcKey(); - } - - @Test - void verifyToVerifiableCredential() { - // Arrange - var issuer = "test-issuer"; - var subject = "test-subject"; - var jwt = buildSignedJwt(CREDENTIAL, issuer, subject, privateKey); - var envelope = new JwtCredentialEnvelope(jwt); - - // Act - var result = envelope.toVerifiableCredentials(OBJECT_MAPPER); - - // Assert - assertThat(result.succeeded()).isTrue(); - assertThat(result.getContent()).hasSize(1).anySatisfy(verifiableCredential -> { - assertThat(verifiableCredential.getProof()).isNull(); - assertThat(verifiableCredential.getItem()).usingRecursiveComparison().isEqualTo(CREDENTIAL); - }); - } - - @Test - void verifyToVerifiableCredential_OnJwtWithMissingVcField() { - // Arrange - var claims = new JWTClaimsSet.Builder().claim("test-name", "test-value").build(); - var jws = new SignedJWT(JWS_HEADER, claims); - var envelope = new JwtCredentialEnvelope(jws); - - // Act - var result = envelope.toVerifiableCredentials(OBJECT_MAPPER); - - // Assert - assertThat(result.failed()).isTrue(); - assertThat(result.getFailureDetail()).contains(VERIFIABLE_CREDENTIALS_KEY); - } - - @Test - void verifyToVerifiableCredential_OnJwtWithWrongFormat() { - // Arrange - var claims = new JWTClaimsSet.Builder().claim(VERIFIABLE_CREDENTIALS_KEY, "test-value").build(); - var jws = new SignedJWT(JWS_HEADER, claims); - var envelope = new JwtCredentialEnvelope(jws); - - // Act - var result = envelope.toVerifiableCredentials(OBJECT_MAPPER); - - // Assert - assertThat(result.failed()).isTrue(); - } -} diff --git a/extensions/credentials/identity-hub-credentials-jwt/src/test/java/org/eclipse/edc/identityhub/credentials/jwt/JwtCredentialFactoryTest.java b/extensions/credentials/identity-hub-credentials-jwt/src/test/java/org/eclipse/edc/identityhub/credentials/jwt/JwtCredentialFactoryTest.java deleted file mode 100644 index eaa91300b..000000000 --- a/extensions/credentials/identity-hub-credentials-jwt/src/test/java/org/eclipse/edc/identityhub/credentials/jwt/JwtCredentialFactoryTest.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.credentials.jwt; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.eclipse.edc.iam.did.crypto.key.EcPrivateKeyWrapper; -import org.eclipse.edc.iam.did.crypto.key.EcPublicKeyWrapper; -import org.eclipse.edc.identityhub.spi.credentials.model.Credential; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.edc.identityhub.credentials.jwt.JwtCredentialConstants.VERIFIABLE_CREDENTIALS_KEY; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.generateCredential; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.generateEcKey; - -class JwtCredentialFactoryTest { - - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - private static final Credential CREDENTIAL = generateCredential(); - - private JwtCredentialFactory jwtCredentialFactory; - private EcPrivateKeyWrapper privateKey; - private EcPublicKeyWrapper publicKey; - - @BeforeEach - public void setUp() { - var key = generateEcKey(); - jwtCredentialFactory = new JwtCredentialFactory(OBJECT_MAPPER); - privateKey = new EcPrivateKeyWrapper(key); - publicKey = new EcPublicKeyWrapper(key); - } - - @Test - void buildSignedJwt_success() throws Exception { - var signedJwt = jwtCredentialFactory.buildSignedJwt(CREDENTIAL, privateKey); - - boolean result = signedJwt.verify(publicKey.verifier()); - assertThat(result).isTrue(); - - assertThat(signedJwt.getJWTClaimsSet().getClaims()) - .containsEntry("iss", CREDENTIAL.getIssuer()) - .containsEntry("sub", CREDENTIAL.getCredentialSubject().getId()) - .extractingByKey(VERIFIABLE_CREDENTIALS_KEY) - .satisfies(c -> assertThat(OBJECT_MAPPER.convertValue(c, Credential.class)) - .usingRecursiveComparison() - .isEqualTo(CREDENTIAL)); - assertThat(signedJwt.getJWTClaimsSet().getIssueTime()).isEqualTo(CREDENTIAL.getIssuanceDate()); - } -} diff --git a/extensions/credentials/identity-hub-credentials-jwt/src/test/java/org/eclipse/edc/identityhub/credentials/jwt/JwtPresentationEnvelopeTest.java b/extensions/credentials/identity-hub-credentials-jwt/src/test/java/org/eclipse/edc/identityhub/credentials/jwt/JwtPresentationEnvelopeTest.java deleted file mode 100644 index f9e4b49f3..000000000 --- a/extensions/credentials/identity-hub-credentials-jwt/src/test/java/org/eclipse/edc/identityhub/credentials/jwt/JwtPresentationEnvelopeTest.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (c) 2023 GAIA-X - * - * 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: - * GAIA-X - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.credentials.jwt; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.nimbusds.jose.JWSAlgorithm; -import com.nimbusds.jose.JWSHeader; -import com.nimbusds.jose.jwk.ECKey; -import com.nimbusds.jwt.JWTClaimsSet; -import com.nimbusds.jwt.SignedJWT; -import org.eclipse.edc.identityhub.spi.credentials.model.VerifiableCredential; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.edc.identityhub.credentials.jwt.JwtCredentialConstants.VERIFIABLE_PRESENTATION_KEY; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.buildSignedJwt; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.generateCredential; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.generateEcKey; - -class JwtPresentationEnvelopeTest { - - private static final String ISSUER = "issuer"; - private static final String SUBJECT = "subject"; - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - private static final JWSHeader JWS_HEADER = new JWSHeader.Builder(JWSAlgorithm.ES256).build(); - private ECKey privateKey; - - @BeforeEach - public void setUp() { - privateKey = generateEcKey(); - } - - @Test - void verifyToVerifiablePresentation() { - var verifiableCredentials = generateCredentials(); - var presentation = generateJwtPresentation(verifiableCredentials); - var jwtPresentation = buildPresentationJwt(presentation); - var envelope = new JwtPresentationEnvelope(jwtPresentation); - - var result = envelope.toVerifiableCredentials(OBJECT_MAPPER); - - assertThat(result.succeeded()).isTrue(); - assertThat(result.getContent()).usingRecursiveComparison().isEqualTo(verifiableCredentials); - } - - @Test - void verifyVerifiablePresentation_OnMissingMandatoryClaim() { - var claims = new JWTClaimsSet.Builder().claim("wrong-claim", "value").build(); - var jws = new SignedJWT(JWS_HEADER, claims); - var envelope = new JwtPresentationEnvelope(jws); - - var result = envelope.toVerifiableCredentials(OBJECT_MAPPER); - - assertThat(result.failed()).isTrue(); - assertThat(result.getFailureDetail()).contains(VERIFIABLE_PRESENTATION_KEY); - } - - @Test - void verifyVerifiablePresentation_OnWrongJwt() { - var claims = new JWTClaimsSet.Builder().claim(VERIFIABLE_PRESENTATION_KEY, "wrong-value").build(); - var jws = new SignedJWT(JWS_HEADER, claims); - var envelope = new JwtPresentationEnvelope(jws); - - var result = envelope.toVerifiableCredentials(OBJECT_MAPPER); - - assertThat(result.failed()).isTrue(); - } - - @Test - void shouldReturnCorrectFormatAndJwt() { - var verifiableCredentials = generateCredentials(); - var presentation = generateJwtPresentation(verifiableCredentials); - var jwtPresentation = buildPresentationJwt(presentation); - var envelope = new JwtPresentationEnvelope(jwtPresentation); - - var format = envelope.format(); - var resultJwt = envelope.getJwt(); - - assertThat(format).isEqualTo("application/vp+ld+jwt"); - assertThat(resultJwt).isEqualTo(jwtPresentation); - } - - private JwtPresentation generateJwtPresentation(List credentials) { - var verifiableCredentials = credentials.stream() - .map(vc -> buildSignedJwt(vc.getItem(), ISSUER, SUBJECT, privateKey).serialize()) - .toList(); - return JwtPresentation.Builder.newInstance() - .id(UUID.randomUUID().toString()) - .type(JwtPresentation.DEFAULT_TYPE) - .signedCredentials(verifiableCredentials) - .build(); - } - - private List generateCredentials() { - return List.of(new VerifiableCredential(generateCredential(), null)); - } - - private SignedJWT buildPresentationJwt(JwtPresentation jwtPresentation) { - var claims = new JWTClaimsSet.Builder() - .claim(VERIFIABLE_PRESENTATION_KEY, OBJECT_MAPPER.convertValue(jwtPresentation, Map.class)) - .issuer(JwtPresentationEnvelopeTest.ISSUER) - .subject(JwtPresentationEnvelopeTest.SUBJECT) - .expirationTime(null) - .notBeforeTime(null) - .build(); - - return buildSignedJwt(claims, privateKey); - } -} diff --git a/extensions/credentials/identity-hub-credentials-jwt/src/test/java/org/eclipse/edc/identityhub/credentials/jwt/JwtPresentationEnvelopeTransformerTest.java b/extensions/credentials/identity-hub-credentials-jwt/src/test/java/org/eclipse/edc/identityhub/credentials/jwt/JwtPresentationEnvelopeTransformerTest.java deleted file mode 100644 index 41278a06e..000000000 --- a/extensions/credentials/identity-hub-credentials-jwt/src/test/java/org/eclipse/edc/identityhub/credentials/jwt/JwtPresentationEnvelopeTransformerTest.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2023 GAIA-X - * - * 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: - * GAIA-X - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.credentials.jwt; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.nimbusds.jose.jwk.ECKey; -import com.nimbusds.jwt.JWTClaimsSet; -import com.nimbusds.jwt.SignedJWT; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.buildSignedJwt; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.generateEcKey; - -class JwtPresentationEnvelopeTransformerTest { - - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - - private ECKey privateKey; - - @BeforeEach - public void setUp() { - privateKey = generateEcKey(); - } - - @Test - void shouldParseJwtPresentationEnvelope() { - var transformer = new JwtPresentationEnvelopeTransformer(OBJECT_MAPPER); - var jwt = generateJwt(); - var envelope = new JwtPresentationEnvelope(jwt); - var serializedEnvelope = jwt.serialize().getBytes(StandardCharsets.UTF_8); - - var result = transformer.parse(serializedEnvelope); - - assertThat(result.succeeded()).isTrue(); - assertThat(result.getContent()).usingRecursiveComparison().isEqualTo(envelope); - } - - @Test - void shouldFailResult_OnInvalidEnvelope() { - var transformer = new JwtPresentationEnvelopeTransformer(OBJECT_MAPPER); - var jwt = "wrong-value"; - var serializedEnvelope = jwt.getBytes(StandardCharsets.UTF_8); - - var result = transformer.parse(serializedEnvelope); - - assertThat(result.failed()).isTrue(); - assertThat(result.getFailureDetail()).contains("Failed to parse Verifiable Presentation"); - } - - @Test - void shouldSerializeJwt() { - var transformer = new JwtPresentationEnvelopeTransformer(OBJECT_MAPPER); - var jwt = generateJwt(); - var envelope = new JwtPresentationEnvelope(jwt); - - var result = transformer.serialize(envelope); - - var expectedSerializedEnvelope = jwt.serialize().getBytes(StandardCharsets.UTF_8); - assertThat(result.succeeded()).isTrue(); - assertThat(result.getContent()).isEqualTo(expectedSerializedEnvelope); - } - - @Test - void shouldReturnCorrectDataFormat() { - var transformer = new JwtPresentationEnvelopeTransformer(OBJECT_MAPPER); - - var format = transformer.dataFormat(); - - assertThat(format).isEqualTo("application/vp+ld+jwt"); - } - - - private SignedJWT generateJwt() { - var claims = new JWTClaimsSet.Builder() - .claim("test-claim", new Object()) - .issuer("issuer") - .subject("subject") - .expirationTime(null) - .notBeforeTime(null) - .build(); - - return buildSignedJwt(claims, privateKey); - } -} diff --git a/extensions/credentials/identity-hub-credentials-jwt/src/test/java/org/eclipse/edc/identityhub/credentials/jwt/JwtPresentationTest.java b/extensions/credentials/identity-hub-credentials-jwt/src/test/java/org/eclipse/edc/identityhub/credentials/jwt/JwtPresentationTest.java deleted file mode 100644 index c057ac841..000000000 --- a/extensions/credentials/identity-hub-credentials-jwt/src/test/java/org/eclipse/edc/identityhub/credentials/jwt/JwtPresentationTest.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (c) 2023 GAIA-X - * - * 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: - * GAIA-X - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.credentials.jwt; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.nimbusds.jose.jwk.ECKey; -import org.assertj.core.api.Assertions; -import org.eclipse.edc.spi.EdcException; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.List; -import java.util.UUID; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.buildSignedJwt; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.generateCredential; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.generateEcKey; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -class JwtPresentationTest { - - private static final String ISSUER = "issuer"; - private static final String SUBJECT = "subject"; - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - private ECKey privateKey; - - @BeforeEach - public void setUp() { - privateKey = generateEcKey(); - } - - @Test - void verifySerialize() throws JsonProcessingException { - var presentation = generateJwtPresentation(); - - var json = OBJECT_MAPPER.writeValueAsString(presentation); - - assertNotNull(json); - - var result = OBJECT_MAPPER.readValue(json, JwtPresentation.class); - assertThat(result).usingRecursiveComparison().isEqualTo(presentation); - } - - @Test - void verifyDeserialize() throws JsonProcessingException { - var json = """ - { - "@context": ["https://www.w3.org/2018/credentials/v1"], - "type": ["VerifiablePresentation"], - "verifiableCredential": ["eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9"] - } - """; - var expectedPresentation = JwtPresentation.Builder.newInstance() - .type(JwtPresentation.DEFAULT_TYPE) - .context("https://www.w3.org/2018/credentials/v1") - .signedCredentials(List.of("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9")) - .build(); - - var result = OBJECT_MAPPER.readValue(json, JwtPresentation.class); - - assertThat(result).usingRecursiveComparison().isEqualTo(expectedPresentation); - } - - @Test - void verifyNullFieldNotSerialized() throws JsonProcessingException { - var presentation = JwtPresentation.Builder.newInstance() - .type(JwtPresentation.DEFAULT_TYPE) - .signedCredentials(generateSignedCredentials()) - .build(); - - var json = OBJECT_MAPPER.writeValueAsString(presentation); - - Assertions.assertThat(json) - .doesNotContain("`id`") - .doesNotContain("`context`"); - } - - @Test - void verifyTypeMandatory() { - assertThatExceptionOfType(EdcException.class).isThrownBy(() -> JwtPresentation.Builder.newInstance() - .id(UUID.randomUUID().toString()) - .signedCredentials(generateSignedCredentials()) - .build()) - .withMessageContaining("`type`"); - } - - @Test - void verifySignedCredentialsMandatory() { - assertThatExceptionOfType(EdcException.class).isThrownBy(() -> JwtPresentation.Builder.newInstance() - .id(UUID.randomUUID().toString()) - .type(JwtPresentation.DEFAULT_TYPE) - .build()) - .withMessageContaining("`verifiableCredential`"); - } - - private JwtPresentation generateJwtPresentation() { - return JwtPresentation.Builder.newInstance() - .id(UUID.randomUUID().toString()) - .context("https://www.w3.org/2018/credentials/v1") - .type(JwtPresentation.DEFAULT_TYPE) - .signedCredentials(generateSignedCredentials()) - .build(); - } - - private List generateSignedCredentials() { - return List.of(buildSignedJwt(generateCredential(), ISSUER, SUBJECT, privateKey).serialize()); - } -} diff --git a/extensions/identity-hub-api/build.gradle.kts b/extensions/identity-hub-api/build.gradle.kts deleted file mode 100644 index 1a90ea339..000000000 --- a/extensions/identity-hub-api/build.gradle.kts +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -plugins { - `java-library` - id("io.swagger.core.v3.swagger-gradle-plugin") - `maven-publish` -} - -dependencies { - api(project(":spi:identity-hub-spi")) - implementation(project(":spi:identity-hub-store-spi")) - implementation(project(":core:identity-hub")) - implementation(libs.edc.ext.http) - implementation(libs.edc.spi.transaction) - - - testImplementation(libs.edc.core.junit) - testImplementation(libs.nimbus.jwt) - testImplementation(libs.restAssured) - testImplementation(project(":spi:identity-hub-spi")) - testImplementation(project(":extensions:credentials:identity-hub-credentials-jwt")) - - testImplementation(testFixtures(project(":spi:identity-hub-spi"))) - testImplementation(testFixtures(project(":spi:identity-hub-store-spi"))) -} diff --git a/extensions/identity-hub-api/src/main/java/org/eclipse/edc/identityhub/api/IdentityHubApiConfiguration.java b/extensions/identity-hub-api/src/main/java/org/eclipse/edc/identityhub/api/IdentityHubApiConfiguration.java deleted file mode 100644 index b570b88a0..000000000 --- a/extensions/identity-hub-api/src/main/java/org/eclipse/edc/identityhub/api/IdentityHubApiConfiguration.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.api; - -import org.eclipse.edc.web.spi.configuration.WebServiceConfiguration; - -public class IdentityHubApiConfiguration extends WebServiceConfiguration { - public IdentityHubApiConfiguration(WebServiceConfiguration webServiceConfig) { - contextAlias = webServiceConfig.getContextAlias(); - path = webServiceConfig.getPath(); - port = webServiceConfig.getPort(); - } -} diff --git a/extensions/identity-hub-api/src/main/java/org/eclipse/edc/identityhub/api/IdentityHubApiExtension.java b/extensions/identity-hub-api/src/main/java/org/eclipse/edc/identityhub/api/IdentityHubApiExtension.java deleted file mode 100644 index 7c2aa0164..000000000 --- a/extensions/identity-hub-api/src/main/java/org/eclipse/edc/identityhub/api/IdentityHubApiExtension.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.api; - -import org.eclipse.edc.identityhub.api.controller.IdentityHubController; -import org.eclipse.edc.identityhub.spi.processor.MessageProcessorRegistry; -import org.eclipse.edc.runtime.metamodel.annotation.Extension; -import org.eclipse.edc.runtime.metamodel.annotation.Inject; -import org.eclipse.edc.runtime.metamodel.annotation.Provider; -import org.eclipse.edc.spi.system.ServiceExtension; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.edc.web.spi.WebServer; -import org.eclipse.edc.web.spi.WebService; -import org.eclipse.edc.web.spi.configuration.WebServiceConfigurer; -import org.eclipse.edc.web.spi.configuration.WebServiceSettings; - -/** - * EDC extension for Identity Hub API - */ -@Extension(value = IdentityHubApiExtension.NAME) -public class IdentityHubApiExtension implements ServiceExtension { - - public static final String NAME = "Identity Hub API"; - - private static final String IDENTITY_CONTEXT_ALIAS = "identity"; - private static final String DEFAULT_IDENTITY_API_CONTEXT_PATH = "/api/v1/identity"; - private static final int DEFAULT_IDENTITY_API_PORT = 8188; - public static final WebServiceSettings SETTINGS = WebServiceSettings.Builder.newInstance() - .apiConfigKey("web.http." + IDENTITY_CONTEXT_ALIAS) - .contextAlias(IDENTITY_CONTEXT_ALIAS) - .defaultPath(DEFAULT_IDENTITY_API_CONTEXT_PATH) - .defaultPort(DEFAULT_IDENTITY_API_PORT) - .useDefaultContext(true) - .name("Identity API") - .build(); - @Inject - private WebService webService; - @Inject - private MessageProcessorRegistry messageProcessorRegistry; - @Inject - private WebServiceConfigurer configurer; - @Inject - private WebServer webServer; - - private IdentityHubApiConfiguration configuration; - - @Override - public String name() { - return NAME; - } - - @Override - public void initialize(ServiceExtensionContext context) { - var identityHubController = new IdentityHubController(messageProcessorRegistry); - var webServiceConfig = configurer.configure(context, webServer, SETTINGS); - configuration = new IdentityHubApiConfiguration(webServiceConfig); - webService.registerResource(webServiceConfig.getContextAlias(), identityHubController); - } - - @Provider - public IdentityHubApiConfiguration identityHubApiConfiguration() { - return configuration; - } -} diff --git a/extensions/identity-hub-api/src/main/java/org/eclipse/edc/identityhub/api/controller/IdentityHubController.java b/extensions/identity-hub-api/src/main/java/org/eclipse/edc/identityhub/api/controller/IdentityHubController.java deleted file mode 100644 index d618c0407..000000000 --- a/extensions/identity-hub-api/src/main/java/org/eclipse/edc/identityhub/api/controller/IdentityHubController.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.api.controller; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.ws.rs.Consumes; -import jakarta.ws.rs.POST; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.core.MediaType; -import org.eclipse.edc.identityhub.spi.model.MessageRequestObject; -import org.eclipse.edc.identityhub.spi.model.MessageResponseObject; -import org.eclipse.edc.identityhub.spi.model.RequestObject; -import org.eclipse.edc.identityhub.spi.model.RequestStatus; -import org.eclipse.edc.identityhub.spi.model.ResponseObject; -import org.eclipse.edc.identityhub.spi.model.WebNodeInterfaceMethod; -import org.eclipse.edc.identityhub.spi.model.WebNodeInterfaces; -import org.eclipse.edc.identityhub.spi.processor.MessageProcessorRegistry; - -import java.util.stream.Collectors; - -/** - * Identity Hub controller, exposing a Decentralized Web Node compatible endpoint. - *

- * See {@link WebNodeInterfaces} for a list of currently supported DWN interfaces. - */ -@Tag(name = "IdentityHub") -@Produces(MediaType.APPLICATION_JSON) -@Consumes(MediaType.APPLICATION_JSON) -@Path("/identity-hub") -public class IdentityHubController { - - private final MessageProcessorRegistry messageProcessorRegistry; - - public IdentityHubController(MessageProcessorRegistry messageProcessorRegistry) { - this.messageProcessorRegistry = messageProcessorRegistry; - } - - @Operation(description = "A Decentralized Web Node (https://identity.foundation/decentralized-web-node/spec) compatible endpoint supporting operations to read and write Verifiable Credentials into an Identity Hub") - @POST - public ResponseObject handleRequest(RequestObject requestObject) { - var replies = requestObject.getMessages() - .stream() - .map(this::processMessage) - .collect(Collectors.toList()); - - return ResponseObject.Builder.newInstance() - .status(RequestStatus.OK) - .replies(replies) - .build(); - } - - private MessageResponseObject processMessage(MessageRequestObject messageRequestObject) { - var method = WebNodeInterfaceMethod.fromName(messageRequestObject.getDescriptor().getMethod()); - var processor = messageProcessorRegistry.resolve(method); - return processor.process(messageRequestObject); - } - -} - diff --git a/extensions/identity-hub-api/src/main/java/org/eclipse/edc/identityhub/api/controller/OpenApiCommon.java b/extensions/identity-hub-api/src/main/java/org/eclipse/edc/identityhub/api/controller/OpenApiCommon.java deleted file mode 100644 index 6bc708051..000000000 --- a/extensions/identity-hub-api/src/main/java/org/eclipse/edc/identityhub/api/controller/OpenApiCommon.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.api.controller; - -import io.swagger.v3.oas.annotations.OpenAPIDefinition; -import io.swagger.v3.oas.annotations.info.Info; - -@OpenAPIDefinition( - info = @Info( - title = "Eclipse Dataspace Connector Identity Hub", - version = "0.0.1" - ) -) -public class OpenApiCommon { - private OpenApiCommon() { - } -} diff --git a/extensions/identity-hub-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/extensions/identity-hub-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension deleted file mode 100644 index 69f4c39ed..000000000 --- a/extensions/identity-hub-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension +++ /dev/null @@ -1,15 +0,0 @@ -# -# Copyright (c) 2022 Microsoft Corporation -# -# 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: -# Microsoft Corporation - initial API and implementation -# -# - -org.eclipse.edc.identityhub.api.IdentityHubApiExtension diff --git a/extensions/identity-hub-api/src/test/java/org/eclipse/edc/identityhub/api/IdentityHubApiTest.java b/extensions/identity-hub-api/src/test/java/org/eclipse/edc/identityhub/api/IdentityHubApiTest.java deleted file mode 100644 index 0ddc56aad..000000000 --- a/extensions/identity-hub-api/src/test/java/org/eclipse/edc/identityhub/api/IdentityHubApiTest.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.api; - -import com.nimbusds.jwt.SignedJWT; -import io.restassured.http.ContentType; -import io.restassured.specification.RequestSpecification; -import org.eclipse.edc.identityhub.spi.model.Descriptor; -import org.eclipse.edc.identityhub.spi.model.MessageRequestObject; -import org.eclipse.edc.identityhub.spi.model.Record; -import org.eclipse.edc.identityhub.spi.model.RequestObject; -import org.eclipse.edc.junit.extensions.EdcExtension; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import java.time.Instant; -import java.util.List; -import java.util.UUID; -import java.util.stream.Collectors; - -import static io.restassured.RestAssured.given; -import static java.nio.charset.StandardCharsets.UTF_8; -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.buildSignedJwt; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.generateCredential; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.generateEcKey; -import static org.eclipse.edc.identityhub.spi.model.WebNodeInterfaceMethod.COLLECTIONS_QUERY; -import static org.eclipse.edc.identityhub.spi.model.WebNodeInterfaceMethod.COLLECTIONS_WRITE; -import static org.eclipse.edc.identityhub.spi.model.WebNodeInterfaceMethod.FEATURE_DETECTION_READ; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.is; - -@ExtendWith(EdcExtension.class) -abstract class IdentityHubApiTest { - - public static final String DATA_FORMAT = "application/vc+jwt"; - private static final String IDENTITY_HUB_PATH = "/identity-hub"; - private String apiBasePath; - - @BeforeEach - void setUp(EdcExtension extension) { - apiBasePath = configureApi(extension); - } - - @Test - void writeAndQueryObject() { - // Arrange - var issuer = "http://some.test.url"; - var subject = "http://some.test.url"; - var credential = generateCredential(); - var jwt = buildSignedJwt(credential, issuer, subject, generateEcKey()); - - // Act - collectionsWrite(jwt); - var credentials = collectionsQuery(); - - // Assert - assertThat(credentials).usingRecursiveFieldByFieldElementComparator().containsExactly(jwt.serialize().getBytes(UTF_8)); - } - - @Test - void detectFeatures() { - baseRequest() - .body(createRequestObject(FEATURE_DETECTION_READ.getName())) - .post() - .then() - .statusCode(200) - .body("replies", hasSize(1)) - .body("replies[0].entries", hasSize(1)) - .body("replies[0].entries[0].interfaces.collections['CollectionsQuery']", is(true)) - .body("replies[0].entries[0].interfaces.collections['CollectionsWrite']", is(true)); - } - - @Test - void useUnsupportedMethod() { - baseRequest() - .body(createRequestObject("Not supported method")) - .post() - .then() - .statusCode(200) - .body("replies", hasSize(1)) - .body("replies[0].status.code", equalTo(501)) - .body("replies[0].status.detail", equalTo("The interface method is not implemented")); - } - - @Test - void writeMalformedMessage() { - byte[] data = "invalid base64".getBytes(UTF_8); - baseRequest() - .body(createRequestObject(COLLECTIONS_WRITE.getName(), data, DATA_FORMAT)) - .post() - .then() - .statusCode(200) - .body("replies", hasSize(1)) - .body("replies[0].status.code", equalTo(400)) - .body("replies[0].status.detail", equalTo("The message was malformed or improperly constructed")); - } - - protected abstract String configureApi(EdcExtension extension); - - private RequestSpecification baseRequest() { - return given() - .baseUri(apiBasePath) - .basePath(IDENTITY_HUB_PATH) - .contentType(ContentType.JSON) - .when(); - } - - private RequestObject createRequestObject(String method) { - return createRequestObject(method, null); - } - - private RequestObject createRequestObject(String method, byte[] data) { - return createRequestObject(method, data, null); - } - - private RequestObject createRequestObject(String method, byte[] data, String dataFormat) { - return RequestObject.Builder.newInstance() - .messages(List.of( - MessageRequestObject.Builder.newInstance() - .descriptor(Descriptor.Builder.newInstance() - .method(method) - .recordId(UUID.randomUUID().toString()) - .dataFormat(dataFormat) - .dateCreated(Instant.now().getEpochSecond()) - .build()) - .data(data) - .build())) - .build(); - } - - private void collectionsWrite(SignedJWT verifiableCredential) { - byte[] data = verifiableCredential.serialize().getBytes(UTF_8); - baseRequest() - .body(createRequestObject(COLLECTIONS_WRITE.getName(), data, DATA_FORMAT)) - .post() - .then() - .statusCode(200) - .body("replies", hasSize(1)) - .body("replies[0].status.code", equalTo(200)) - .body("replies[0].status.detail", equalTo("The message was successfully processed")); - } - - private List collectionsQuery() { - return baseRequest() - .body(createRequestObject(COLLECTIONS_QUERY.getName())) - .post() - .then() - .statusCode(200) - .body("replies", hasSize(1)) - .body("replies[0].status.code", equalTo(200)) - .body("replies[0].status.detail", equalTo("The message was successfully processed")) - .extract().body().jsonPath().getList("replies[0].entries", Record.class) - .stream().map(s -> s.getData()) - .collect(Collectors.toList()); - } -} diff --git a/extensions/identity-hub-api/src/test/java/org/eclipse/edc/identityhub/api/IdentityHubApiWithConfigTest.java b/extensions/identity-hub-api/src/test/java/org/eclipse/edc/identityhub/api/IdentityHubApiWithConfigTest.java deleted file mode 100644 index 547538aa6..000000000 --- a/extensions/identity-hub-api/src/test/java/org/eclipse/edc/identityhub/api/IdentityHubApiWithConfigTest.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.api; - -import org.eclipse.edc.junit.extensions.EdcExtension; -import org.junit.jupiter.api.BeforeAll; - -import java.util.Map; - -import static org.eclipse.edc.junit.testfixtures.TestUtils.getFreePort; - -public class IdentityHubApiWithConfigTest extends IdentityHubApiTest { - - private static int port; - - @BeforeAll - static void prepare() { - port = getFreePort(); - } - - @Override - protected String configureApi(EdcExtension extension) { - extension.setConfiguration(Map.of( - "web.http.identity.port", String.valueOf(port), - "web.http.identity.path", "/api/v1/identity/testpath")); - return String.format("http://localhost:%s/api/v1/identity/testpath", port); - } -} diff --git a/extensions/identity-hub-api/src/test/java/org/eclipse/edc/identityhub/api/IdentityHubDefaultApiTest.java b/extensions/identity-hub-api/src/test/java/org/eclipse/edc/identityhub/api/IdentityHubDefaultApiTest.java deleted file mode 100644 index dc5fbb1c7..000000000 --- a/extensions/identity-hub-api/src/test/java/org/eclipse/edc/identityhub/api/IdentityHubDefaultApiTest.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.api; - -import org.eclipse.edc.junit.extensions.EdcExtension; -import org.junit.jupiter.api.BeforeAll; - -import java.util.Map; - -import static org.eclipse.edc.junit.testfixtures.TestUtils.getFreePort; - -public class IdentityHubDefaultApiTest extends IdentityHubApiTest { - private static int port; - - @BeforeAll - static void prepare() { - port = getFreePort(); - } - - @Override - protected String configureApi(EdcExtension extension) { - extension.setConfiguration(Map.of("web.http.port", String.valueOf(port))); - return String.format("http://localhost:%s/api", port); - } -} diff --git a/extensions/identity-hub-verifier-jwt/build.gradle.kts b/extensions/identity-hub-verifier-jwt/build.gradle.kts deleted file mode 100644 index 3a717c266..000000000 --- a/extensions/identity-hub-verifier-jwt/build.gradle.kts +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial implementation - * - */ - -plugins { - `java-library` -} - - -dependencies { - api(project(":extensions:credentials:identity-hub-credentials-jwt")); - - implementation(project(":core:identity-hub")) - implementation(project(":core:identity-hub-client")) - implementation(project(":spi:identity-hub-spi")) - - implementation(libs.edc.spi.identity.did) - implementation(libs.nimbus.jwt) - - testImplementation(testFixtures(project(":spi:identity-hub-spi"))) - testImplementation(project(":extensions:identity-hub-api")) - testImplementation(project(":core:identity-hub-credentials-verifier")) - - testImplementation(libs.edc.ext.identity.did.crypto) - testImplementation(libs.edc.ext.identity.did.core) - testImplementation(libs.edc.core.junit) -} - - diff --git a/extensions/identity-hub-verifier-jwt/src/main/java/org/eclipse/edc/identityhub/verifier/jwt/DidJwtCredentialsVerifier.java b/extensions/identity-hub-verifier-jwt/src/main/java/org/eclipse/edc/identityhub/verifier/jwt/DidJwtCredentialsVerifier.java deleted file mode 100644 index ba59c13cd..000000000 --- a/extensions/identity-hub-verifier-jwt/src/main/java/org/eclipse/edc/identityhub/verifier/jwt/DidJwtCredentialsVerifier.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.verifier.jwt; - -import com.nimbusds.jose.JOSEException; -import com.nimbusds.jwt.JWTClaimsSet; -import com.nimbusds.jwt.SignedJWT; -import com.nimbusds.jwt.proc.BadJWTException; -import com.nimbusds.jwt.proc.DefaultJWTClaimsVerifier; -import org.eclipse.edc.iam.did.spi.key.PublicKeyWrapper; -import org.eclipse.edc.iam.did.spi.resolution.DidPublicKeyResolver; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.result.Result; - -import java.text.ParseException; -import java.util.Set; - -/** - * Verifies verifiable credentials in JWT format with a DID issuer. - */ -class DidJwtCredentialsVerifier implements JwtCredentialsVerifier { - - // RFC 7519 Registered (standard) claim - private static final String ISSUER_CLAIM = "iss"; - private final DidPublicKeyResolver didPublicKeyResolver; - private final Monitor monitor; - - DidJwtCredentialsVerifier(DidPublicKeyResolver didPublicKeyResolver, Monitor monitor) { - this.didPublicKeyResolver = didPublicKeyResolver; - this.monitor = monitor; - } - - @Override - public Result isSignedByIssuer(SignedJWT jwt) { - String issuer; - try { - issuer = jwt.getJWTClaimsSet().getIssuer(); - } catch (ParseException e) { - var failureMessage = "Error parsing issuer from JWT"; - monitor.warning(failureMessage, e); - return Result.failure(String.format("%s: %s", failureMessage, e.getMessage())); - } - var issuerPublicKey = didPublicKeyResolver.resolvePublicKey(issuer); - if (issuerPublicKey.failed()) { - var failureMessage = String.format("Failed finding publicKey of issuer: %s", issuer); - monitor.warning(failureMessage); - return Result.failure(failureMessage); - } - return verifySignature(jwt, issuerPublicKey.getContent()); - } - - @Override - public Result verifyClaims(SignedJWT jwt, String expectedSubject) { - JWTClaimsSet jwtClaimsSet; - try { - jwtClaimsSet = jwt.getJWTClaimsSet(); - } catch (ParseException e) { - var failureMessage = "Error parsing issuer from JWT"; - monitor.warning(failureMessage, e); - return Result.failure(String.format("%s: %s", failureMessage, e.getMessage())); - } - - // verify claims - var exactMatchClaims = new JWTClaimsSet.Builder() - .subject(expectedSubject) - .build(); - var requiredClaims = Set.of(ISSUER_CLAIM); - - var claimsVerifier = new DefaultJWTClaimsVerifier<>(exactMatchClaims, requiredClaims); - - try { - claimsVerifier.verify(jwtClaimsSet, null); - } catch (BadJWTException e) { - var failureMessage = "Failure verifying JWT token"; - monitor.warning(failureMessage, e); - return Result.failure(String.format("%s: %s", failureMessage, e.getMessage())); - } - - monitor.debug(() -> "JWT claims verification successful"); - return Result.success(); - } - - private Result verifySignature(SignedJWT jwt, PublicKeyWrapper issuerPublicKey) { - try { - var verified = jwt.verify(issuerPublicKey.verifier()); - if (!verified) { - return Result.failure("Invalid JWT signature"); - } - monitor.debug(() -> "JWT signature verification successful"); - return Result.success(); - } catch (JOSEException e) { - var failureMessage = "Unable to verify JWT token"; - monitor.warning(failureMessage, e); - return Result.failure(String.format("%s: %s", failureMessage, e.getMessage())); - } - } -} diff --git a/extensions/identity-hub-verifier-jwt/src/main/java/org/eclipse/edc/identityhub/verifier/jwt/JwtCredentialEnvelopeVerifier.java b/extensions/identity-hub-verifier-jwt/src/main/java/org/eclipse/edc/identityhub/verifier/jwt/JwtCredentialEnvelopeVerifier.java deleted file mode 100644 index d03c30af7..000000000 --- a/extensions/identity-hub-verifier-jwt/src/main/java/org/eclipse/edc/identityhub/verifier/jwt/JwtCredentialEnvelopeVerifier.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.verifier.jwt; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.eclipse.edc.iam.did.spi.document.DidDocument; -import org.eclipse.edc.identityhub.credentials.jwt.JwtCredentialEnvelope; -import org.eclipse.edc.identityhub.spi.credentials.model.Credential; -import org.eclipse.edc.identityhub.spi.credentials.model.Verifiable; -import org.eclipse.edc.identityhub.spi.credentials.verifier.CredentialEnvelopeVerifier; -import org.eclipse.edc.spi.result.Result; - -import java.util.List; - -/** - * Implementation of a Verifiable Credentials verifier working with JWT format - * - * @see vc-data-model - */ -public class JwtCredentialEnvelopeVerifier extends JwtEnvelopeVerifier implements CredentialEnvelopeVerifier { - - private final ObjectMapper mapper; - - public JwtCredentialEnvelopeVerifier(JwtCredentialsVerifier jwtCredentialsVerifier, ObjectMapper mapper) { - super(jwtCredentialsVerifier); - this.mapper = mapper; - } - - @Override - public Result> verify(JwtCredentialEnvelope vc, DidDocument didDocument) { - var jwt = vc.getJwt(); - var result = verifyJwtClaims(jwt, didDocument); - if (result.failed()) { - return Result.failure(result.getFailureMessages()); - } - var signatureResult = verifySignature(jwt); - - if (signatureResult.failed()) { - return Result.failure(signatureResult.getFailureMessages()); - } - var verifiableCredentialResult = vc.toVerifiableCredentials(mapper); - if (verifiableCredentialResult.failed()) { - return Result.failure(verifiableCredentialResult.getFailureMessages()); - } - - return Result.success(verifiableCredentialResult.getContent().stream().map(Verifiable::getItem).toList()); - } -} diff --git a/extensions/identity-hub-verifier-jwt/src/main/java/org/eclipse/edc/identityhub/verifier/jwt/JwtCredentialsVerifier.java b/extensions/identity-hub-verifier-jwt/src/main/java/org/eclipse/edc/identityhub/verifier/jwt/JwtCredentialsVerifier.java deleted file mode 100644 index 7559286a6..000000000 --- a/extensions/identity-hub-verifier-jwt/src/main/java/org/eclipse/edc/identityhub/verifier/jwt/JwtCredentialsVerifier.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.verifier.jwt; - -import com.nimbusds.jwt.SignedJWT; -import org.eclipse.edc.spi.result.Result; - -/** - * Verifies verifiable credentials in JWT format. - */ -public interface JwtCredentialsVerifier { - - /** - * Verifies if a JWT is really signed by the claimed issuer (iss field). - * - * @param jwt to be verified. - * @return if the JWT is signed by the claimed issuer. - */ - Result isSignedByIssuer(SignedJWT jwt); - - /** - * Verifies if a JWT targets the given subject, and checks for the presence of the issuer ("iss") claim. The expiration ("exp") and not-before ("nbf") claims are verified if present as well. - * - * @param jwt to be verified. - * @param expectedSubject subject claim to verify. - * @return if the JWT is valid and for the given subject - */ - Result verifyClaims(SignedJWT jwt, String expectedSubject); -} diff --git a/extensions/identity-hub-verifier-jwt/src/main/java/org/eclipse/edc/identityhub/verifier/jwt/JwtCredentialsVerifierExtension.java b/extensions/identity-hub-verifier-jwt/src/main/java/org/eclipse/edc/identityhub/verifier/jwt/JwtCredentialsVerifierExtension.java deleted file mode 100644 index 483038e1a..000000000 --- a/extensions/identity-hub-verifier-jwt/src/main/java/org/eclipse/edc/identityhub/verifier/jwt/JwtCredentialsVerifierExtension.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.verifier.jwt; - -import org.eclipse.edc.iam.did.spi.resolution.DidPublicKeyResolver; -import org.eclipse.edc.identityhub.spi.credentials.verifier.CredentialEnvelopeVerifierRegistry; -import org.eclipse.edc.runtime.metamodel.annotation.Inject; -import org.eclipse.edc.runtime.metamodel.annotation.Provides; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.system.ServiceExtension; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.edc.spi.types.TypeManager; - -import static org.eclipse.edc.identityhub.credentials.jwt.JwtCredentialConstants.VC_DATA_FORMAT; -import static org.eclipse.edc.identityhub.credentials.jwt.JwtCredentialConstants.VP_DATA_FORMAT; - -/** - * Extension to provide verifier for IdentityHub Verifiable Credentials in JWT format. - */ -@Provides(JwtCredentialsVerifier.class) -public class JwtCredentialsVerifierExtension implements ServiceExtension { - - @Inject - private Monitor monitor; - - @Inject - private DidPublicKeyResolver didPublicKeyResolver; - - @Inject - private CredentialEnvelopeVerifierRegistry verifierRegistry; - - @Inject - private TypeManager typeManager; - - @Override - public String name() { - return "JWT Credentials Verifier"; - } - - @Override - public void initialize(ServiceExtensionContext context) { - var jwtVerifier = new DidJwtCredentialsVerifier(didPublicKeyResolver, monitor); - context.registerService(JwtCredentialsVerifier.class, jwtVerifier); - verifierRegistry.register(VC_DATA_FORMAT, new JwtCredentialEnvelopeVerifier(jwtVerifier, typeManager.getMapper())); - verifierRegistry.register(VP_DATA_FORMAT, new JwtPresentationEnvelopeVerifier(jwtVerifier, typeManager.getMapper())); - } -} diff --git a/extensions/identity-hub-verifier-jwt/src/main/java/org/eclipse/edc/identityhub/verifier/jwt/JwtEnvelopeVerifier.java b/extensions/identity-hub-verifier-jwt/src/main/java/org/eclipse/edc/identityhub/verifier/jwt/JwtEnvelopeVerifier.java deleted file mode 100644 index aaa8c6a08..000000000 --- a/extensions/identity-hub-verifier-jwt/src/main/java/org/eclipse/edc/identityhub/verifier/jwt/JwtEnvelopeVerifier.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2023 GAIA-X - * - * 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: - * GAIA-X - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.verifier.jwt; - -import com.nimbusds.jwt.SignedJWT; -import org.eclipse.edc.iam.did.spi.document.DidDocument; -import org.eclipse.edc.spi.result.Result; -import org.jetbrains.annotations.NotNull; - -/** - * Implementation of an abstract Verifier for an envelope with a JWT - */ -public abstract class JwtEnvelopeVerifier { - - private final JwtCredentialsVerifier jwtCredentialsVerifier; - - protected JwtEnvelopeVerifier(JwtCredentialsVerifier jwtCredentialsVerifier) { - this.jwtCredentialsVerifier = jwtCredentialsVerifier; - } - - @NotNull Result verifyJwtClaims(SignedJWT jwt, DidDocument didDocument) { - var result = jwtCredentialsVerifier.verifyClaims(jwt, didDocument.getId()); - return result.succeeded() ? Result.success(jwt) : Result.failure(result.getFailureMessages()); - } - - @NotNull Result verifySignature(SignedJWT jwt) { - var result = jwtCredentialsVerifier.isSignedByIssuer(jwt); - return result.succeeded() ? Result.success(jwt) : Result.failure(result.getFailureMessages()); - } -} diff --git a/extensions/identity-hub-verifier-jwt/src/main/java/org/eclipse/edc/identityhub/verifier/jwt/JwtPresentationEnvelopeVerifier.java b/extensions/identity-hub-verifier-jwt/src/main/java/org/eclipse/edc/identityhub/verifier/jwt/JwtPresentationEnvelopeVerifier.java deleted file mode 100644 index 669b51c9f..000000000 --- a/extensions/identity-hub-verifier-jwt/src/main/java/org/eclipse/edc/identityhub/verifier/jwt/JwtPresentationEnvelopeVerifier.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2023 GAIA-X - * - * 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: - * GAIA-X - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.verifier.jwt; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.eclipse.edc.iam.did.spi.document.DidDocument; -import org.eclipse.edc.identityhub.credentials.jwt.JwtPresentationEnvelope; -import org.eclipse.edc.identityhub.spi.credentials.model.Credential; -import org.eclipse.edc.identityhub.spi.credentials.model.Verifiable; -import org.eclipse.edc.identityhub.spi.credentials.verifier.CredentialEnvelopeVerifier; -import org.eclipse.edc.spi.result.Result; - -import java.util.List; - -/** - * Implementation of a Verifiable Presentation verifier working with JWT format - * - * @see W3C specification - */ -public class JwtPresentationEnvelopeVerifier extends JwtEnvelopeVerifier implements CredentialEnvelopeVerifier { - - private final ObjectMapper mapper; - - public JwtPresentationEnvelopeVerifier(JwtCredentialsVerifier jwtCredentialsVerifier, ObjectMapper mapper) { - super(jwtCredentialsVerifier); - this.mapper = mapper; - } - - @Override - public Result> verify(JwtPresentationEnvelope vp, DidDocument didDocument) { - var jwt = vp.getJwt(); - var result = verifyJwtClaims(jwt, didDocument); - if (result.failed()) { - return Result.failure(result.getFailureMessages()); - } - var signatureResult = verifySignature(jwt); - - if (signatureResult.failed()) { - return Result.failure(signatureResult.getFailureMessages()); - } - var verifiableCredentialResult = vp.toVerifiableCredentials(mapper); - if (verifiableCredentialResult.failed()) { - return Result.failure(verifiableCredentialResult.getFailureMessages()); - } - - var jwtVerifyResult = Result.success(verifiableCredentialResult.getContent().stream().map(Verifiable::getItem).toList()); - - if (jwtVerifyResult.failed()) { - return Result.failure(jwtVerifyResult.getFailureMessages()); - } - - var jwtVerifiableCredentials = vp.toJwtVerifiableCredentials(mapper); - if (jwtVerifiableCredentials.failed()) { - return Result.failure(jwtVerifiableCredentials.getFailureMessages()); - } - var vcResult = jwtVerifiableCredentials.getContent().stream().map(jwtVc -> { - var verifyJwtClaimsResult = verifyJwtClaims(jwtVc, didDocument); - if (verifyJwtClaimsResult.failed()) { - return Result.failure(verifyJwtClaimsResult.getFailureMessages()); - } - - return verifySignature(jwtVc); - }).filter(Result::failed).findFirst().orElse(Result.success()); - - if (vcResult.failed()) { - return Result.failure(vcResult.getFailureMessages()); - } - - return jwtVerifyResult; - } - -} diff --git a/extensions/identity-hub-verifier-jwt/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/extensions/identity-hub-verifier-jwt/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension deleted file mode 100644 index dbb31eb5e..000000000 --- a/extensions/identity-hub-verifier-jwt/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension +++ /dev/null @@ -1 +0,0 @@ -org.eclipse.edc.identityhub.verifier.jwt.JwtCredentialsVerifierExtension diff --git a/extensions/identity-hub-verifier-jwt/src/test/java/org/eclipse/edc/identityhub/verifier/jwt/CredentialsVerifierExtensionTest.java b/extensions/identity-hub-verifier-jwt/src/test/java/org/eclipse/edc/identityhub/verifier/jwt/CredentialsVerifierExtensionTest.java deleted file mode 100644 index 9a695d9c2..000000000 --- a/extensions/identity-hub-verifier-jwt/src/test/java/org/eclipse/edc/identityhub/verifier/jwt/CredentialsVerifierExtensionTest.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.verifier.jwt; - -import com.nimbusds.jose.jwk.ECKey; -import org.eclipse.edc.iam.did.spi.credentials.CredentialsVerifier; -import org.eclipse.edc.iam.did.spi.document.DidConstants; -import org.eclipse.edc.iam.did.spi.document.DidDocument; -import org.eclipse.edc.iam.did.spi.document.Service; -import org.eclipse.edc.iam.did.spi.document.VerificationMethod; -import org.eclipse.edc.iam.did.spi.resolution.DidResolver; -import org.eclipse.edc.iam.did.spi.resolution.DidResolverRegistry; -import org.eclipse.edc.identityhub.client.IdentityHubClientImpl; -import org.eclipse.edc.identityhub.client.spi.IdentityHubClient; -import org.eclipse.edc.identityhub.credentials.jwt.JwtCredentialEnvelope; -import org.eclipse.edc.identityhub.credentials.jwt.JwtCredentialEnvelopeTransformer; -import org.eclipse.edc.identityhub.spi.credentials.model.Credential; -import org.eclipse.edc.identityhub.spi.credentials.transformer.CredentialEnvelopeTransformerRegistry; -import org.eclipse.edc.junit.extensions.EdcExtension; -import org.eclipse.edc.junit.testfixtures.TestUtils; -import org.eclipse.edc.spi.result.Result; -import org.eclipse.edc.spi.types.TypeManager; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import static java.lang.String.format; -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.buildSignedJwt; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.generateCredential; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.generateEcKey; -import static org.eclipse.edc.junit.testfixtures.TestUtils.getFreePort; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -@ExtendWith(EdcExtension.class) -class CredentialsVerifierExtensionTest { - - private static final TypeManager TYPE_MANAGER = new TypeManager(); - private static final int PORT = getFreePort(); - private static final String API_URL = format("http://localhost:%d/api/identity-hub", PORT); - private static final String DID_METHOD = "test-method"; - private static final String CREDENTIAL_ISSUER = format("did:%s:%s", DID_METHOD, "http://some.test.url"); - private static final String SUBJECT = "http://some.test.url"; - - private final CredentialEnvelopeTransformerRegistry registry = mock(CredentialEnvelopeTransformerRegistry.class); - - private IdentityHubClient identityHubClient; - - private static DidDocument createDidDocument(ECKey jwk) { - var vm = VerificationMethod.Builder.create() - .id(UUID.randomUUID().toString()) - .type(DidConstants.ECDSA_SECP_256_K_1_VERIFICATION_KEY_2019) - .publicKeyJwk(jwk.toPublicJWK().toJSONObject()) - .build(); - return DidDocument.Builder.newInstance() - .id(SUBJECT) - .service(List.of(new Service("IdentityHub", "IdentityHub", API_URL))) - .verificationMethod(Collections.singletonList(vm)) - .build(); - } - - @BeforeEach - void setUp(EdcExtension extension) { - when(registry.resolve(any())).thenReturn(new JwtCredentialEnvelopeTransformer(TYPE_MANAGER.getMapper())); - identityHubClient = new IdentityHubClientImpl(TestUtils.testHttpClient(), TYPE_MANAGER, registry); - extension.setConfiguration(Map.of("web.http.port", String.valueOf(PORT))); - } - - @Test - void getVerifiedClaims_getValidClaims(CredentialsVerifier verifier, DidResolverRegistry registry) { - // Arrange - var jwk = generateEcKey(); - var didDocument = createDidDocument(jwk); - var credential = generateCredential(); - var jwt = buildSignedJwt(credential, CREDENTIAL_ISSUER, SUBJECT, jwk); - var didResolverMock = mock(DidResolver.class); - when(didResolverMock.getMethod()).thenReturn(DID_METHOD); - when(didResolverMock.resolve(anyString())).thenReturn(Result.success(didDocument)); - - registry.register(didResolverMock); - - // Act - var addResult = identityHubClient.addVerifiableCredential(API_URL, new JwtCredentialEnvelope(jwt)); - assertThat(addResult.succeeded()).isTrue(); - var credentials = verifier.getVerifiedCredentials(didDocument); - - // Assert - assertThat(credentials.succeeded()).isTrue(); - assertThat(credentials.getContent()) - .hasSize(1) - .extractingByKey(credential.getId()) - .satisfies(o -> { - assertThat(o).isNotNull().isInstanceOf(Credential.class); - var cred = (Credential) o; - assertThat(cred).usingRecursiveComparison().isEqualTo(credential); - }); - } -} diff --git a/extensions/identity-hub-verifier-jwt/src/test/java/org/eclipse/edc/identityhub/verifier/jwt/DidJwtCredentialsVerifierTest.java b/extensions/identity-hub-verifier-jwt/src/test/java/org/eclipse/edc/identityhub/verifier/jwt/DidJwtCredentialsVerifierTest.java deleted file mode 100644 index 72ed207c8..000000000 --- a/extensions/identity-hub-verifier-jwt/src/test/java/org/eclipse/edc/identityhub/verifier/jwt/DidJwtCredentialsVerifierTest.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.verifier.jwt; - -import com.nimbusds.jose.JOSEException; -import com.nimbusds.jose.jwk.ECKey; -import com.nimbusds.jwt.JWTClaimsSet; -import com.nimbusds.jwt.SignedJWT; -import org.eclipse.edc.iam.did.crypto.key.EcPublicKeyWrapper; -import org.eclipse.edc.iam.did.spi.key.PublicKeyWrapper; -import org.eclipse.edc.iam.did.spi.resolution.DidPublicKeyResolver; -import org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.result.Result; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.text.ParseException; - -import static java.time.Instant.now; -import static java.time.temporal.ChronoUnit.DAYS; -import static java.util.Date.from; -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.buildSignedJwt; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.generateCredential; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.generateEcKey; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.when; - -class DidJwtCredentialsVerifierTest { - - private static final ECKey JWK = generateEcKey(); - private static final ECKey ANOTHER_JWK = generateEcKey(); - private static final String ISSUER = "http://some.test.url"; - private static final String SUBJECT = "http://some.test.url"; - private static final String OTHER_SUBJECT = "http://some.test.url" + "other"; - private static final SignedJWT JWT = buildSignedJwt(generateCredential(), ISSUER, SUBJECT, JWK); - private DidPublicKeyResolver didPublicKeyResolver; - private DidJwtCredentialsVerifier didJwtCredentialsVerifier; - - @BeforeEach - public void setUp() { - didPublicKeyResolver = mock(DidPublicKeyResolver.class); - didJwtCredentialsVerifier = new DidJwtCredentialsVerifier(didPublicKeyResolver, mock(Monitor.class)); - } - - - @Test - void isSignedByIssuer_jwtSignedByIssuer() { - - // Arrange - when(didPublicKeyResolver.resolvePublicKey(ISSUER)).thenReturn(Result.success(toPublicKeyWrapper(JWK))); - - // Assert - assertThat(didJwtCredentialsVerifier.isSignedByIssuer(JWT).succeeded()).isTrue(); - } - - @Test - void isSignedByIssuer_jwtSignedByWrongIssuer() { - - // Arrange - when(didPublicKeyResolver.resolvePublicKey(ISSUER)).thenReturn(Result.success(toPublicKeyWrapper(ANOTHER_JWK))); - - // Assert - assertThat(didJwtCredentialsVerifier.isSignedByIssuer(JWT).failed()).isTrue(); - } - - @Test - void isSignedByIssuer_PublicKeyCantBeResolved() { - - // Arrange - when(didPublicKeyResolver.resolvePublicKey(ISSUER)).thenReturn(Result.failure("Failed resolving public key")); - - // Assert - assertThat(didJwtCredentialsVerifier.isSignedByIssuer(JWT).failed()).isTrue(); - } - - @Test - void isSignedByIssuer_issuerDidCantBeResolved() throws ParseException { - - // Arrange - when(didPublicKeyResolver.resolvePublicKey(JWT.getJWTClaimsSet().getIssuer())).thenReturn(Result.failure("test failure")); - - // Assert - assertThat(didJwtCredentialsVerifier.isSignedByIssuer(JWT).failed()).isTrue(); - } - - @Test - void isSignedByIssuer_cantParsePayload() throws Exception { - - // Arrange - var jws = mock(SignedJWT.class); - when(jws.getJWTClaimsSet()).thenThrow(new ParseException("Failed parsing JWT payload", 0)); - - // Assert - assertThat(didJwtCredentialsVerifier.isSignedByIssuer(jws).failed()).isTrue(); - } - - @Test - void verifyClaims_success() { - assertThat(didJwtCredentialsVerifier.verifyClaims(JWT, SUBJECT).succeeded()).isTrue(); - } - - @Test - void verifyClaims_OnInvalidSubject() { - assertThat(didJwtCredentialsVerifier.verifyClaims(JWT, OTHER_SUBJECT).failed()).isTrue(); - } - - @Test - void verifyClaims_OnEmptySubject() { - var jwt = buildSignedJwt(generateCredential(), ISSUER, null, JWK); - assertThat(didJwtCredentialsVerifier.verifyClaims(jwt, OTHER_SUBJECT).failed()).isTrue(); - } - - @Test - void verifyClaims_OnEmptyIssuer() { - var jwt = buildSignedJwt(generateCredential(), null, SUBJECT, JWK); - assertThat(didJwtCredentialsVerifier.verifyClaims(jwt, SUBJECT).failed()).isTrue(); - } - - @Test - void verifyClaims_OnInvalidJwt() throws Exception { - // Arrange - var jwt = mock(SignedJWT.class); - var message = "Test Message"; - when(jwt.getJWTClaimsSet()).thenThrow(new ParseException(message, 0)); - - // Act - assertThat(didJwtCredentialsVerifier.verifyClaims(jwt, SUBJECT).failed()).isTrue(); - } - - @Test - void verifyClaims_OnValidExp() { - var claims = new JWTClaimsSet.Builder() - .issuer(ISSUER) - .subject(SUBJECT) - .expirationTime(from(now().plus(1, DAYS))) - .build(); - - var jwt = VerifiableCredentialTestUtil.buildSignedJwt(claims, JWK); - - assertThat(didJwtCredentialsVerifier.verifyClaims(jwt, SUBJECT).succeeded()).isTrue(); - } - - @Test - void verifyClaims_OnInvalidExp() { - var claims = new JWTClaimsSet.Builder() - .issuer(ISSUER) - .subject(SUBJECT) - .expirationTime(from(now().minus(1, DAYS))) - .build(); - - var jwt = VerifiableCredentialTestUtil.buildSignedJwt(claims, JWK); - - assertThat(didJwtCredentialsVerifier.verifyClaims(jwt, SUBJECT).failed()).isTrue(); - } - - @Test - void verifyClaims_OnValidNotBefore() { - var claims = new JWTClaimsSet.Builder() - .issuer(ISSUER) - .subject(SUBJECT) - .notBeforeTime(from(now().minus(1, DAYS))) - .build(); - - var jwt = VerifiableCredentialTestUtil.buildSignedJwt(claims, JWK); - - assertThat(didJwtCredentialsVerifier.verifyClaims(jwt, SUBJECT).succeeded()).isTrue(); - } - - @Test - void verifyClaims_OnInvalidNotBefore() { - var claims = new JWTClaimsSet.Builder() - .issuer(ISSUER) - .subject(SUBJECT) - .notBeforeTime(from(now().plus(1, DAYS))) - .build(); - - var jwt = VerifiableCredentialTestUtil.buildSignedJwt(claims, JWK); - - assertThat(didJwtCredentialsVerifier.verifyClaims(jwt, SUBJECT).failed()).isTrue(); - } - - @Test - void verifyClaims_JwsCantBeVerified() throws Exception { - // Arrange - var jwt = spy(JWT); - when(didPublicKeyResolver.resolvePublicKey(ISSUER)).thenReturn(Result.success(toPublicKeyWrapper(JWK))); - // JOSEException can occur if JWS algorithm is not supported, or if signature verification failed for some - // other internal reason - doThrow(new JOSEException("JWS algorithm is not supported")).when(jwt).verify(any()); - - // Act & Assert - assertThat(didJwtCredentialsVerifier.isSignedByIssuer(jwt).failed()).isTrue(); - } - - public static PublicKeyWrapper toPublicKeyWrapper(ECKey jwk) { - return new EcPublicKeyWrapper(jwk); - } -} diff --git a/extensions/identity-hub-verifier-jwt/src/test/java/org/eclipse/edc/identityhub/verifier/jwt/IdentityHubJwtCredentialsVerifierTest.java b/extensions/identity-hub-verifier-jwt/src/test/java/org/eclipse/edc/identityhub/verifier/jwt/IdentityHubJwtCredentialsVerifierTest.java deleted file mode 100644 index 2ed325129..000000000 --- a/extensions/identity-hub-verifier-jwt/src/test/java/org/eclipse/edc/identityhub/verifier/jwt/IdentityHubJwtCredentialsVerifierTest.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.verifier.jwt; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.nimbusds.jose.JWSAlgorithm; -import com.nimbusds.jose.JWSHeader; -import com.nimbusds.jwt.JWTClaimsSet; -import com.nimbusds.jwt.SignedJWT; -import org.eclipse.edc.iam.did.spi.credentials.CredentialsVerifier; -import org.eclipse.edc.iam.did.spi.document.DidDocument; -import org.eclipse.edc.iam.did.spi.document.Service; -import org.eclipse.edc.identityhub.client.spi.IdentityHubClient; -import org.eclipse.edc.identityhub.credentials.jwt.JwtCredentialEnvelope; -import org.eclipse.edc.identityhub.spi.credentials.model.Credential; -import org.eclipse.edc.identityhub.spi.credentials.verifier.CredentialEnvelopeVerifierRegistry; -import org.eclipse.edc.identityhub.verifier.IdentityHubCredentialsVerifier; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.result.Result; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentMatchers; - -import java.util.List; -import java.util.Map; -import java.util.function.Supplier; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.buildSignedJwt; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.generateCredential; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.generateEcKey; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -class IdentityHubJwtCredentialsVerifierTest { - - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - private static final String HUB_BASE_URL = "https://" + "http://some.test.url"; - private static final DidDocument DID_DOCUMENT = DidDocument.Builder.newInstance() - .service(List.of(new Service("IdentityHub", "IdentityHub", HUB_BASE_URL))).build(); - private static final String ISSUER = "http://some.test.url"; - private static final String SUBJECT = "http://some.test.url"; - private final Monitor monitorMock = mock(Monitor.class); - private final IdentityHubClient identityHubClientMock = mock(IdentityHubClient.class); - private final JwtCredentialsVerifier jwtCredentialsVerifierMock = mock(JwtCredentialsVerifier.class); - private final CredentialEnvelopeVerifierRegistry credentialsVerifierRegistry = mock(CredentialEnvelopeVerifierRegistry.class); - private final CredentialsVerifier credentialsVerifier = new IdentityHubCredentialsVerifier(identityHubClientMock, monitorMock, credentialsVerifierRegistry); - - @Test - void getVerifiedClaims_getValidClaims() { - // Arrange - var credential = generateCredential(); - var jws = buildSignedJwt(credential, ISSUER, SUBJECT, generateEcKey()); - setUpMocks(jws, true, true); - - // Act - var credentials = credentialsVerifier.getVerifiedCredentials(DID_DOCUMENT); - - // Assert - assertThat(credentials.succeeded()).isTrue(); - assertThat(credentials.getContent()) - .hasSize(1) - .extractingByKey(credential.getId()) - .satisfies(o -> { - assertThat(o).isInstanceOf(Credential.class); - var cred = (Credential) o; - assertThat(cred).usingRecursiveComparison().isEqualTo(credential); - }); - } - - @Test - void getVerifiedClaims_filtersSignedByWrongIssuer() { - - // Arrange - var credential = generateCredential(); - var jws = buildSignedJwt(credential, ISSUER, SUBJECT, generateEcKey()); - setUpMocks(jws, true, false); - - // Act - var credentials = credentialsVerifier.getVerifiedCredentials(DID_DOCUMENT); - - // Assert - assertThat(credentials.failed()).isTrue(); - } - - @Test - void getVerifiedClaims_hubUrlNotResolved() { - // Arrange - var didDocument = DidDocument.Builder.newInstance().build(); - - // Act - var credentials = credentialsVerifier.getVerifiedCredentials(didDocument); - - // Assert - assertThat(credentials.failed()).isTrue(); - assertThat(credentials.getFailureMessages()).containsExactly("Could not retrieve identity hub URL from DID document"); - } - - @Test - void getVerifiedClaims_idHubCallFails() { - - // Arrange - when(identityHubClientMock.getVerifiableCredentials(HUB_BASE_URL)).thenReturn(Result.failure("error")); - - // Act - var credentials = credentialsVerifier.getVerifiedCredentials(DID_DOCUMENT); - - // Assert - assertThat(credentials.failed()).isTrue(); - } - - @Test - void getVerifiedClaims_verifiableCredentialsWithWrongFormat() { - - // Arrange - var jws = new SignedJWT(new JWSHeader.Builder(JWSAlgorithm.ES256).build(), new JWTClaimsSet.Builder().build()); - setUpMocks(jws, true, true); - - // Act - var credentials = credentialsVerifier.getVerifiedCredentials(DID_DOCUMENT); - - // Assert - assertThat(credentials.failed()).isTrue(); - verify(monitorMock).severe(ArgumentMatchers.>any()); - } - - @Test - void getVerifiedClaims_verifiableCredentialsWithMissingId() { - - // Arrange - var jwsHeader = new JWSHeader.Builder(JWSAlgorithm.ES256).build(); - var jwtClaims = new JWTClaimsSet.Builder() - .claim("vc", Map.of("key1", "value1")) - .issuer("test issuer") - .subject(SUBJECT) - .build(); - var jws = new SignedJWT(jwsHeader, jwtClaims); - setUpMocks(jws, true, true); - - // Act - var credentials = credentialsVerifier.getVerifiedCredentials(DID_DOCUMENT); - - // Assert - assertThat(credentials.failed()).isTrue(); - verify(monitorMock).severe(ArgumentMatchers.>any()); - } - - private void setUpMocks(SignedJWT jws, boolean isSigned, boolean claimsValid) { - when(credentialsVerifierRegistry.resolve("application/vc+jwt")).thenReturn(new JwtCredentialEnvelopeVerifier(jwtCredentialsVerifierMock, OBJECT_MAPPER)); - when(identityHubClientMock.getVerifiableCredentials(HUB_BASE_URL)).thenReturn(Result.success(List.of(new JwtCredentialEnvelope(jws)))); - when(jwtCredentialsVerifierMock.isSignedByIssuer(jws)).thenReturn(isSigned ? Result.success() : Result.failure("JWT not signed")); - when(jwtCredentialsVerifierMock.verifyClaims(eq(jws), any())).thenReturn(claimsValid ? Result.success() : Result.failure("VC not valid")); - } - -} diff --git a/extensions/identity-hub-verifier-jwt/src/test/java/org/eclipse/edc/identityhub/verifier/jwt/JwtCredentialEnvelopeVerifierTest.java b/extensions/identity-hub-verifier-jwt/src/test/java/org/eclipse/edc/identityhub/verifier/jwt/JwtCredentialEnvelopeVerifierTest.java deleted file mode 100644 index b19fd0485..000000000 --- a/extensions/identity-hub-verifier-jwt/src/test/java/org/eclipse/edc/identityhub/verifier/jwt/JwtCredentialEnvelopeVerifierTest.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (c) 2023 GAIA-X - * - * 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: - * GAIA-X - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.verifier.jwt; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.eclipse.edc.iam.did.spi.document.DidDocument; -import org.eclipse.edc.iam.did.spi.document.Service; -import org.eclipse.edc.identityhub.credentials.jwt.JwtCredentialEnvelope; -import org.eclipse.edc.identityhub.spi.credentials.model.Credential; -import org.eclipse.edc.spi.result.Result; -import org.junit.jupiter.api.Test; - -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.buildSignedJwt; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.generateCredential; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.generateEcKey; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -class JwtCredentialEnvelopeVerifierTest { - - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - private final JwtCredentialsVerifier jwtCredentialsVerifierMock = mock(JwtCredentialsVerifier.class); - private final JwtCredentialEnvelope jwtCredentialEnvelopeMock = mock(JwtCredentialEnvelope.class); - private static final String ISSUER = "http://some.test.url"; - private static final String SUBJECT = "http://some.test.url"; - private static final String HUB_BASE_URL = "https://" + "http://some.test.url"; - private static final DidDocument DID_DOCUMENT = DidDocument.Builder.newInstance() - .service(List.of(new Service("IdentityHub", "IdentityHub", HUB_BASE_URL))).build(); - - @Test - void shouldVerify_ValidJwt() { - var credential = generateCredential(); - var jws = buildSignedJwt(credential, ISSUER, SUBJECT, generateEcKey()); - setUpMocks(true, true); - var jwtCredentialEnvelopeVerifier = new JwtCredentialEnvelopeVerifier(jwtCredentialsVerifierMock, OBJECT_MAPPER); - var jwtCredentialEnvelope = new JwtCredentialEnvelope(jws); - - var credentialVerification = jwtCredentialEnvelopeVerifier.verify(jwtCredentialEnvelope, DID_DOCUMENT); - - assertThat(credentialVerification.succeeded()).isTrue(); - assertThat(credentialVerification.getContent()) - .hasSize(1) - .anySatisfy(c -> { - assertThat(c).isInstanceOf(Credential.class); - assertThat(c).usingRecursiveComparison().isEqualTo(credential); - }); - } - - @Test - void shouldVerifyJwt_UnsignedJwt() { - var credential = generateCredential(); - var jws = buildSignedJwt(credential, ISSUER, SUBJECT, generateEcKey()); - setUpMocks(false, true); - var jwtCredentialEnvelopeVerifier = new JwtCredentialEnvelopeVerifier(jwtCredentialsVerifierMock, OBJECT_MAPPER); - var jwtCredentialEnvelope = new JwtCredentialEnvelope(jws); - - var credentialVerification = jwtCredentialEnvelopeVerifier.verify(jwtCredentialEnvelope, DID_DOCUMENT); - - assertThat(credentialVerification.failed()).isTrue(); - } - - @Test - void shouldVerifyJwt_InvalidClaims() { - var credential = generateCredential(); - var jws = buildSignedJwt(credential, ISSUER, SUBJECT, generateEcKey()); - setUpMocks(true, false); - var jwtCredentialEnvelopeVerifier = new JwtCredentialEnvelopeVerifier(jwtCredentialsVerifierMock, OBJECT_MAPPER); - var jwtCredentialEnvelope = new JwtCredentialEnvelope(jws); - - var credentialVerification = jwtCredentialEnvelopeVerifier.verify(jwtCredentialEnvelope, DID_DOCUMENT); - - assertThat(credentialVerification.failed()).isTrue(); - } - - @Test - void shouldVerifyJwt_FailedCredential() { - setUpMocks(true, true); - when(jwtCredentialEnvelopeMock.toVerifiableCredentials(any())).thenReturn(Result.failure("Missing vc claim")); - - var jwtCredentialEnvelopeVerifier = new JwtCredentialEnvelopeVerifier(jwtCredentialsVerifierMock, OBJECT_MAPPER); - var credentialVerification = jwtCredentialEnvelopeVerifier.verify(jwtCredentialEnvelopeMock, DID_DOCUMENT); - - assertThat(credentialVerification.failed()).isTrue(); - } - - private void setUpMocks(boolean isSigned, boolean claimsValid) { - when(jwtCredentialsVerifierMock.isSignedByIssuer(any())).thenReturn(isSigned ? Result.success() : Result.failure("JWT not signed")); - when(jwtCredentialsVerifierMock.verifyClaims(any(), any())).thenReturn(claimsValid ? Result.success() : Result.failure("VC not valid")); - } - -} diff --git a/extensions/identity-hub-verifier-jwt/src/test/java/org/eclipse/edc/identityhub/verifier/jwt/JwtPresentationEnvelopeVerifierTest.java b/extensions/identity-hub-verifier-jwt/src/test/java/org/eclipse/edc/identityhub/verifier/jwt/JwtPresentationEnvelopeVerifierTest.java deleted file mode 100644 index 710d23886..000000000 --- a/extensions/identity-hub-verifier-jwt/src/test/java/org/eclipse/edc/identityhub/verifier/jwt/JwtPresentationEnvelopeVerifierTest.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (c) 2023 GAIA-X - * - * 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: - * GAIA-X - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.verifier.jwt; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.nimbusds.jwt.JWTClaimsSet; -import com.nimbusds.jwt.SignedJWT; -import org.eclipse.edc.iam.did.spi.document.DidDocument; -import org.eclipse.edc.iam.did.spi.document.Service; -import org.eclipse.edc.identityhub.credentials.jwt.JwtPresentation; -import org.eclipse.edc.identityhub.credentials.jwt.JwtPresentationEnvelope; -import org.eclipse.edc.identityhub.spi.credentials.model.Credential; -import org.eclipse.edc.spi.result.Result; -import org.junit.jupiter.api.Test; - -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.edc.identityhub.credentials.jwt.JwtCredentialConstants.VERIFIABLE_PRESENTATION_KEY; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.buildSignedJwt; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.generateCredential; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.generateEcKey; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -class JwtPresentationEnvelopeVerifierTest { - - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - private final JwtCredentialsVerifier jwtCredentialsVerifierMock = mock(JwtCredentialsVerifier.class); - private final JwtPresentationEnvelope jwtPresentationEnvelopeMock = mock(JwtPresentationEnvelope.class); - private static final String ISSUER = "http://some.test.url"; - private static final String SUBJECT = "http://some.test.url"; - private static final String HUB_BASE_URL = "https://" + "http://some.test.url"; - private static final DidDocument DID_DOCUMENT = DidDocument.Builder.newInstance() - .service(List.of(new Service("IdentityHub", "IdentityHub", HUB_BASE_URL))).build(); - - @Test - void shouldVerify_ValidJwt() { - var credentials = generateCredentials(); - var presentation = generateJwtPresentation(credentials); - var jws = buildPresentationJwt(presentation); - setUpMocks(true, true); - var jwtPresentationEnvelopeVerifier = new JwtPresentationEnvelopeVerifier(jwtCredentialsVerifierMock, OBJECT_MAPPER); - var jwtPresentationEnvelope = new JwtPresentationEnvelope(jws); - - var credentialVerification = jwtPresentationEnvelopeVerifier.verify(jwtPresentationEnvelope, DID_DOCUMENT); - - assertThat(credentialVerification.succeeded()).isTrue(); - assertThat(credentialVerification.getContent()) - .hasSize(1) - .satisfies(c -> { - assertThat(c).isInstanceOf(List.class); - assertThat(c).usingRecursiveComparison().isEqualTo(credentials); - }); - } - - @Test - void shouldVerify_InvalidCredentialSignature() { - var credentialJwt = buildSignedJwt(generateCredential(), ISSUER, SUBJECT, generateEcKey()); - var presentation = JwtPresentation.Builder.newInstance() - .id(UUID.randomUUID().toString()) - .type(JwtPresentation.DEFAULT_TYPE) - .signedCredentials(List.of(credentialJwt.serialize())) - .build(); - var jws = buildPresentationJwt(presentation); - when(jwtCredentialsVerifierMock.verifyClaims(any(), any())).thenReturn(Result.success()); - when(jwtCredentialsVerifierMock.isSignedByIssuer(any())).thenReturn(Result.success()).thenReturn(Result.failure("VC JWT not signed")); - var jwtPresentationEnvelopeVerifier = new JwtPresentationEnvelopeVerifier(jwtCredentialsVerifierMock, OBJECT_MAPPER); - var jwtPresentationEnvelope = new JwtPresentationEnvelope(jws); - - var credentialVerification = jwtPresentationEnvelopeVerifier.verify(jwtPresentationEnvelope, DID_DOCUMENT); - - assertThat(credentialVerification.failed()).isTrue(); - assertThat(credentialVerification.getFailureDetail()).contains("VC JWT not signed"); - } - - @Test - void shouldVerify_InvalidCredentialClaims() { - var credentialJwt = buildSignedJwt(generateCredential(), ISSUER, SUBJECT, generateEcKey()); - var presentation = JwtPresentation.Builder.newInstance() - .id(UUID.randomUUID().toString()) - .type(JwtPresentation.DEFAULT_TYPE) - .signedCredentials(List.of(credentialJwt.serialize())) - .build(); - var jws = buildPresentationJwt(presentation); - when(jwtCredentialsVerifierMock.verifyClaims(any(), any())).thenReturn(Result.success()).thenReturn(Result.failure("VC Claim not valid")); - when(jwtCredentialsVerifierMock.isSignedByIssuer(any())).thenReturn(Result.success()); - var jwtPresentationEnvelopeVerifier = new JwtPresentationEnvelopeVerifier(jwtCredentialsVerifierMock, OBJECT_MAPPER); - var jwtPresentationEnvelope = new JwtPresentationEnvelope(jws); - - var credentialVerification = jwtPresentationEnvelopeVerifier.verify(jwtPresentationEnvelope, DID_DOCUMENT); - - assertThat(credentialVerification.failed()).isTrue(); - assertThat(credentialVerification.getFailureDetail()).contains("VC Claim not valid"); - } - - @Test - void shouldVerifyJwt_UnsignedJwt() { - var credentials = generateCredentials(); - var presentation = generateJwtPresentation(credentials); - var jws = buildPresentationJwt(presentation); - setUpMocks(false, true); - var jwtPresentationEnvelopeVerifier = new JwtPresentationEnvelopeVerifier(jwtCredentialsVerifierMock, OBJECT_MAPPER); - var jwtPresentationEnvelope = new JwtPresentationEnvelope(jws); - - var credentialVerification = jwtPresentationEnvelopeVerifier.verify(jwtPresentationEnvelope, DID_DOCUMENT); - - assertThat(credentialVerification.failed()).isTrue(); - } - - @Test - void shouldVerifyJwt_InvalidClaims() { - var credentials = generateCredentials(); - var presentation = generateJwtPresentation(credentials); - var jws = buildPresentationJwt(presentation); - setUpMocks(true, false); - var jwtPresentationEnvelopeVerifier = new JwtPresentationEnvelopeVerifier(jwtCredentialsVerifierMock, OBJECT_MAPPER); - var jwtPresentationEnvelope = new JwtPresentationEnvelope(jws); - - var credentialVerification = jwtPresentationEnvelopeVerifier.verify(jwtPresentationEnvelope, DID_DOCUMENT); - - assertThat(credentialVerification.failed()).isTrue(); - } - - @Test - void shouldVerifyJwt_FailedCredential() { - setUpMocks(true, true); - when(jwtPresentationEnvelopeMock.toVerifiableCredentials(any())).thenReturn(Result.failure("Missing vp claim")); - - var jwtPresentationEnvelopeVerifier = new JwtPresentationEnvelopeVerifier(jwtCredentialsVerifierMock, OBJECT_MAPPER); - var credentialVerification = jwtPresentationEnvelopeVerifier.verify(jwtPresentationEnvelopeMock, DID_DOCUMENT); - - assertThat(credentialVerification.failed()).isTrue(); - } - - private void setUpMocks(boolean isSigned, boolean claimsValid) { - when(jwtCredentialsVerifierMock.isSignedByIssuer(any())).thenReturn(isSigned ? Result.success() : Result.failure("JWT not signed")); - when(jwtCredentialsVerifierMock.verifyClaims(any(), any())).thenReturn(claimsValid ? Result.success() : Result.failure("VC not valid")); - } - - private SignedJWT buildPresentationJwt(JwtPresentation presentation) { - var claims = new JWTClaimsSet.Builder() - .claim(VERIFIABLE_PRESENTATION_KEY, OBJECT_MAPPER.convertValue(presentation, Map.class)) - .issuer(ISSUER) - .subject(SUBJECT) - .expirationTime(null) - .notBeforeTime(null) - .build(); - - return buildSignedJwt(claims, generateEcKey()); - } - - private JwtPresentation generateJwtPresentation(List credentials) { - var verifiableCredentials = credentials.stream() - .map(vc -> buildSignedJwt(vc, ISSUER, SUBJECT, generateEcKey()).serialize()) - .toList(); - return JwtPresentation.Builder.newInstance() - .id(UUID.randomUUID().toString()) - .type(JwtPresentation.DEFAULT_TYPE) - .signedCredentials(verifiableCredentials) - .build(); - } - - private List generateCredentials() { - return List.of(generateCredential()); - } - -} diff --git a/extensions/self-description-api/build.gradle.kts b/extensions/self-description-api/build.gradle.kts deleted file mode 100644 index f47c0df63..000000000 --- a/extensions/self-description-api/build.gradle.kts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2023 Amadeus - * - * 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: - * Amadeus - initial API and implementation - * - */ - -plugins { - `java-library` - id("io.swagger.core.v3.swagger-gradle-plugin") - `maven-publish` -} - -dependencies { - implementation(project(":extensions:identity-hub-api")) - - implementation(libs.edc.ext.http) - testImplementation(libs.edc.core.junit) - testImplementation(libs.restAssured) -} diff --git a/extensions/self-description-api/src/main/java/org/eclipse/edc/identityhub/api/selfdescription/SelfDescriptionApiExtension.java b/extensions/self-description-api/src/main/java/org/eclipse/edc/identityhub/api/selfdescription/SelfDescriptionApiExtension.java deleted file mode 100644 index 8e435e668..000000000 --- a/extensions/self-description-api/src/main/java/org/eclipse/edc/identityhub/api/selfdescription/SelfDescriptionApiExtension.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.api.selfdescription; - -import org.eclipse.edc.identityhub.api.IdentityHubApiConfiguration; -import org.eclipse.edc.identityhub.api.selfdescription.controller.SelfDescriptionController; -import org.eclipse.edc.identityhub.api.selfdescription.loader.SelfDescriptionLoader; -import org.eclipse.edc.runtime.metamodel.annotation.Extension; -import org.eclipse.edc.runtime.metamodel.annotation.Inject; -import org.eclipse.edc.runtime.metamodel.annotation.Setting; -import org.eclipse.edc.spi.system.ServiceExtension; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.edc.spi.types.TypeManager; -import org.eclipse.edc.web.spi.WebService; - -import java.util.Optional; - -/** - * Temporary extension for serving a static self-description document. - */ -@Extension(value = SelfDescriptionApiExtension.NAME) -public class SelfDescriptionApiExtension implements ServiceExtension { - - public static final String NAME = "Self Description API"; - - @Setting - private static final String SELF_DESCRIPTION_DOCUMENT_PATH_SETTING = "edc.self.description.document.path"; - private static final String DEFAULT_SELF_DESCRIPTION_FILE_NAME = "default-self-description.json"; - - @Inject - private TypeManager typeManager; - - @Inject - private WebService webService; - - @Inject - private IdentityHubApiConfiguration configuration; - - @Override - public String name() { - return NAME; - } - - @Override - public void initialize(ServiceExtensionContext context) { - var mapper = typeManager.getMapper(); - var loader = new SelfDescriptionLoader(mapper); - var selfDescription = Optional.ofNullable(context.getSetting(SELF_DESCRIPTION_DOCUMENT_PATH_SETTING, null)) - .map(loader::fromFile) - .orElse(loader.fromClasspath(DEFAULT_SELF_DESCRIPTION_FILE_NAME)); - var controller = new SelfDescriptionController(selfDescription); - webService.registerResource(configuration.getContextAlias(), controller); - } -} diff --git a/extensions/self-description-api/src/main/java/org/eclipse/edc/identityhub/api/selfdescription/controller/SelfDescriptionApi.java b/extensions/self-description-api/src/main/java/org/eclipse/edc/identityhub/api/selfdescription/controller/SelfDescriptionApi.java deleted file mode 100644 index f2545c851..000000000 --- a/extensions/self-description-api/src/main/java/org/eclipse/edc/identityhub/api/selfdescription/controller/SelfDescriptionApi.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2023 Amadeus - * - * 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: - * Amadeus - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.api.selfdescription.controller; - -import com.fasterxml.jackson.databind.JsonNode; -import io.swagger.v3.oas.annotations.OpenAPIDefinition; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; - -@OpenAPIDefinition -@Tag(name = "SelfDescription") -public interface SelfDescriptionApi { - - @Operation(description = "Serve Self-Description document.") - JsonNode getSelfDescription(); -} - diff --git a/extensions/self-description-api/src/main/java/org/eclipse/edc/identityhub/api/selfdescription/controller/SelfDescriptionController.java b/extensions/self-description-api/src/main/java/org/eclipse/edc/identityhub/api/selfdescription/controller/SelfDescriptionController.java deleted file mode 100644 index c162cbd57..000000000 --- a/extensions/self-description-api/src/main/java/org/eclipse/edc/identityhub/api/selfdescription/controller/SelfDescriptionController.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2023 Amadeus - * - * 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: - * Amadeus - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.api.selfdescription.controller; - -import com.fasterxml.jackson.databind.JsonNode; -import jakarta.ws.rs.Consumes; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.core.MediaType; - -@Produces(MediaType.APPLICATION_JSON) -@Consumes(MediaType.APPLICATION_JSON) -@Path("/identity-hub") -public class SelfDescriptionController implements SelfDescriptionApi { - - private final JsonNode selfDescription; - - public SelfDescriptionController(JsonNode selfDescription) { - this.selfDescription = selfDescription; - } - - @GET - @Override - @Path("/self-description") - public JsonNode getSelfDescription() { - return selfDescription; - } -} - diff --git a/extensions/self-description-api/src/main/java/org/eclipse/edc/identityhub/api/selfdescription/loader/SelfDescriptionLoader.java b/extensions/self-description-api/src/main/java/org/eclipse/edc/identityhub/api/selfdescription/loader/SelfDescriptionLoader.java deleted file mode 100644 index a1c55f92f..000000000 --- a/extensions/self-description-api/src/main/java/org/eclipse/edc/identityhub/api/selfdescription/loader/SelfDescriptionLoader.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2022 Amadeus - * - * 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: - * Amadeus - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.api.selfdescription.loader; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.eclipse.edc.spi.EdcException; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.NoSuchFileException; -import java.nio.file.Path; - -import static java.lang.String.format; - -public class SelfDescriptionLoader { - - private final ObjectMapper mapper; - - public SelfDescriptionLoader(ObjectMapper mapper) { - this.mapper = mapper; - } - - /** - * Load Self-Description from a provided file path. - * - * @param path Path to the Self-Description to be loaded. - * @return JSON representation of the Self-Description. - */ - public JsonNode fromFile(String path) { - try (var is = Files.newInputStream(Path.of(path))) { - return mapper.readTree(is); - } catch (IOException e) { - throw new EdcException(e); - } - } - - /** - * Load Self-Description from the classpath. - * - * @param fileName Name of the classpath file from which Self-Description will be loaded. - * @return JSON representation of the Self-Description. - */ - public JsonNode fromClasspath(String fileName) { - try (var is = getClass().getClassLoader().getResourceAsStream(fileName)) { - if (is == null) { - throw new NoSuchFileException(format("Cannot find file `%s` from classpath", fileName)); - } - return mapper.readTree(is); - } catch (IOException e) { - throw new EdcException(e); - } - } -} diff --git a/extensions/self-description-api/src/main/resources/default-self-description.json b/extensions/self-description-api/src/main/resources/default-self-description.json deleted file mode 100644 index ee504fc63..000000000 --- a/extensions/self-description-api/src/main/resources/default-self-description.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "selfDescriptionCredential": { - "@context": [ - "http://www.w3.org/ns/shacl#", - "http://www.w3.org/2001/XMLSchema#", - "http://w3id.org/gaia-x/participant#" - ], - "@id": "http://example.org/participant-dp6gtq7i75lmk9p4j2tfg", - "@type": [ - "VerifiableCredential", - "LegalPerson" - ], - "credentialSubject": { - "id": "did:web:example.com", - "gx-participant:registrationNumber": { - "@type": "xsd:string", - "@value": "DEANY1234NUMBER" - }, - "gx-participant:headquarterAddress": { - "@type": "gx-participant:Address", - "gx-participant:country": { - "@type": "xsd:string", - "@value": "FR" - } - }, - "gx-participant:legalAddress": { - "@type": "gx-participant:Address", - "gx-participant:country": { - "@type": "xsd:string", - "@value": "FR" - } - }, - "gx-participant:note": { - "@value": "Test Self Description signed by deltaDAO for the Gaia-X Hackathon #4", - "@type": "xsd:string" - } - }, - "proof": { - "type": "JsonWebKey2020", - "created": "2022-06-21T11:47:23.802Z", - "proofPurpose": "assertionMethod", - "verificationMethod": "did:web:test.delta-dao.com", - "jws": "eyJhbGciOiJQUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..2bSfloq1nDSLL45R-RTx_dYzwrE7C2XRCelTG78rORNXueTgrI1wchuUAI18R4_dIJgVpYiwMU_xnSAYgPXvfREZAK9-qbQc0lpzpVgDaVwhoI7N_TPIeSPd_3zwDSy4W8LTGAXt9nTAwQJfH-qwsV6kRqN1To0p-VqdnZda2IWf_2Mxdu2cYFm9mGT9RCoACCqwdkBR44PIP3lt-pGM68_pxfj3TNiAAcYj8fWucSkoeA_tSZV7Z5YNxa-Ge6j4DbpNADOBUYgmKeU0zIvPEi4UGYiFgeiM7F9u3-v_0VQ3ODuBcprQYIbgL0_7YN6QwUlZBfpeXXXvG7LmQGbibw" - } - }, - "complianceCredential": { - "@context": [ - "https://www.w3.org/2018/credentials/v1" - ], - "@type": [ - "VerifiableCredential", - "ParticipantCredential" - ], - "id": "https://catalogue.gaia-x.eu/credentials/ParticipantCredential/1655812045129", - "issuer": "did:web:compliance.gaia-x.eu", - "issuanceDate": "2022-06-21T11:47:25.129Z", - "credentialSubject": { - "id": "did:web:example.com", - "hash": "f5b874c2091b79b0536aa1afb12b599ee0b0b48c90667e9bf6161846f29eb765" - }, - "proof": { - "type": "JsonWebKey2020", - "created": "2022-06-21T11:47:25.129Z", - "proofPurpose": "assertionMethod", - "jws": "eyJhbGciOiJQUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..YQndX4X4oUE2_dU0dkIU1CA36Y9PlIaB5-duDj6QXRCNTa7aZ_sLVmsaHetj7xk4ghHztuiSONaDfg_JV-EYQrf3OFlKxxcrUzUcpaswekIgAAvoWei-eS0DZ2tVbA3aCaDxUS5CGoKRNdv5yive_oWcPtlrPnflpOSHXspBnrry1BKLrkfBzt45LPDvt56E_UGjAcPnMe4ANT8GWbwb5hujLkZcnhFSKnGv8OlcRbFWNLz9QM8nBSXc2PM-Yrbz1odbrJ7DnX_wpOwapW-ZKu6xPLT-OV--gsZU__jscgxSGdrci_qX3TYc8_Ahs2YCI2aFfOEKlDl4nLYcA51BtQ", - "verificationMethod": "did:web:compliance.gaia-x.eu" - } - } -} \ No newline at end of file diff --git a/extensions/self-description-api/src/test/java/org/eclipse/edc/identityhub/api/selfdescription/controller/SelfDescriptionApiTest.java b/extensions/self-description-api/src/test/java/org/eclipse/edc/identityhub/api/selfdescription/controller/SelfDescriptionApiTest.java deleted file mode 100644 index b61deecfd..000000000 --- a/extensions/self-description-api/src/test/java/org/eclipse/edc/identityhub/api/selfdescription/controller/SelfDescriptionApiTest.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.api.selfdescription.controller; - -import org.eclipse.edc.junit.extensions.EdcExtension; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import static io.restassured.RestAssured.given; -import static org.hamcrest.Matchers.equalTo; - -@ExtendWith(EdcExtension.class) -abstract class SelfDescriptionApiTest { - - private static final String IDENTITY_HUB_PATH = "/identity-hub"; - - private String apiBasePath; - - @BeforeEach - void setUp(EdcExtension extension) { - apiBasePath = configureApi(extension); - } - - @Test - void getSelfDescription() { - given() - .baseUri(apiBasePath) - .basePath(IDENTITY_HUB_PATH) - .get("/self-description") - .then() - .assertThat() - .statusCode(200) - .body("selfDescriptionCredential.credentialSubject.gx-participant:headquarterAddress.gx-participant:country.@value", equalTo("FR")); - } - - protected abstract String configureApi(EdcExtension extension); -} diff --git a/extensions/self-description-api/src/test/java/org/eclipse/edc/identityhub/api/selfdescription/controller/SelfDescriptionApiWithConfigTest.java b/extensions/self-description-api/src/test/java/org/eclipse/edc/identityhub/api/selfdescription/controller/SelfDescriptionApiWithConfigTest.java deleted file mode 100644 index 05beded65..000000000 --- a/extensions/self-description-api/src/test/java/org/eclipse/edc/identityhub/api/selfdescription/controller/SelfDescriptionApiWithConfigTest.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.api.selfdescription.controller; - -import org.eclipse.edc.junit.extensions.EdcExtension; -import org.junit.jupiter.api.BeforeAll; - -import java.util.Map; - -import static org.eclipse.edc.junit.testfixtures.TestUtils.getFreePort; - -public class SelfDescriptionApiWithConfigTest extends SelfDescriptionApiTest { - - private static int port; - - @BeforeAll - static void prepare() { - port = getFreePort(); - } - - @Override - protected String configureApi(EdcExtension extension) { - extension.setConfiguration(Map.of( - "web.http.identity.port", String.valueOf(port), - "web.http.identity.path", "/api/v1/identity/testpath")); - return String.format("http://localhost:%s/api/v1/identity/testpath", port); - } -} diff --git a/extensions/self-description-api/src/test/java/org/eclipse/edc/identityhub/api/selfdescription/controller/SelfDescriptionDefaultApiTest.java b/extensions/self-description-api/src/test/java/org/eclipse/edc/identityhub/api/selfdescription/controller/SelfDescriptionDefaultApiTest.java deleted file mode 100644 index 4761cd49a..000000000 --- a/extensions/self-description-api/src/test/java/org/eclipse/edc/identityhub/api/selfdescription/controller/SelfDescriptionDefaultApiTest.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.api.selfdescription.controller; - -import org.eclipse.edc.junit.extensions.EdcExtension; -import org.junit.jupiter.api.BeforeAll; - -import java.util.Map; - -import static org.eclipse.edc.junit.testfixtures.TestUtils.getFreePort; - -public class SelfDescriptionDefaultApiTest extends SelfDescriptionApiTest { - private static int port; - - @BeforeAll - static void prepare() { - port = getFreePort(); - } - - @Override - protected String configureApi(EdcExtension extension) { - extension.setConfiguration(Map.of("web.http.port", String.valueOf(port))); - return String.format("http://localhost:%s/api", port); - } -} diff --git a/extensions/self-description-api/src/test/java/org/eclipse/edc/identityhub/api/selfdescription/loader/SelfDescriptionLoaderTest.java b/extensions/self-description-api/src/test/java/org/eclipse/edc/identityhub/api/selfdescription/loader/SelfDescriptionLoaderTest.java deleted file mode 100644 index 4558ffab4..000000000 --- a/extensions/self-description-api/src/test/java/org/eclipse/edc/identityhub/api/selfdescription/loader/SelfDescriptionLoaderTest.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2022 Amadeus - * - * 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: - * Amadeus - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.api.selfdescription.loader; - -import com.fasterxml.jackson.core.JsonParseException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.eclipse.edc.spi.EdcException; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.NoSuchFileException; -import java.nio.file.Path; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -class SelfDescriptionLoaderTest { - - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - - private final SelfDescriptionLoader loader = new SelfDescriptionLoader(OBJECT_MAPPER); - - private static JsonNode loadJsonFile(String path) throws IOException { - var content = Files.readString(Path.of(path)); - return OBJECT_MAPPER.readTree(content); - } - - @Test - void fromFile() throws IOException { - var path = "src/test/resources/self-description.json"; - var expected = loadJsonFile(path); - - var result = loader.fromFile(path); - - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo(expected); - } - - @Test - void fromFile_fileNotFound() { - assertThatExceptionOfType(EdcException.class).isThrownBy(() -> loader.fromFile("src/test/resources/invalid-self-description.json")) - .withRootCauseExactlyInstanceOf(NoSuchFileException.class); - } - - @Test - void fromFile_invalidJson() { - assertThatExceptionOfType(EdcException.class).isThrownBy(() -> loader.fromFile("src/test/resources/invalid-self-description.txt")) - .withRootCauseExactlyInstanceOf(JsonParseException.class); - } - - @Test - void fromClasspath() throws IOException { - var expected = loadJsonFile("src/main/resources/default-self-description.json"); - - var result = loader.fromClasspath("default-self-description.json"); - - assertThat(result).usingRecursiveFieldByFieldElementComparator().isEqualTo(expected); - } - - @Test - void fromClasspath_fileNotFound() { - assertThatExceptionOfType(EdcException.class).isThrownBy(() -> loader.fromClasspath("default-self-descriptionxx.json")) - .withRootCauseExactlyInstanceOf(NoSuchFileException.class); - } -} diff --git a/extensions/self-description-api/src/test/resources/invalid-self-description.txt b/extensions/self-description-api/src/test/resources/invalid-self-description.txt deleted file mode 100644 index bdd51cc27..000000000 --- a/extensions/self-description-api/src/test/resources/invalid-self-description.txt +++ /dev/null @@ -1 +0,0 @@ -hello-world \ No newline at end of file diff --git a/extensions/self-description-api/src/test/resources/self-description.json b/extensions/self-description-api/src/test/resources/self-description.json deleted file mode 100644 index db0225819..000000000 --- a/extensions/self-description-api/src/test/resources/self-description.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "selfDescriptionCredential": { - "@context": [ - "http://www.w3.org/ns/shacl#", - "http://www.w3.org/2001/XMLSchema#", - "http://w3id.org/gaia-x/participant#" - ], - "@id": "http://example.org/participant-dp6gtq7i75lmk9p4j2tfg", - "@type": [ - "VerifiableCredential", - "LegalPerson" - ], - "credentialSubject": { - "id": "did:web:example.com", - "gx-participant:registrationNumber": { - "@type": "xsd:string", - "@value": "HELLOWORLD" - }, - "gx-participant:headquarterAddress": { - "@type": "gx-participant:Address", - "gx-participant:country": { - "@type": "xsd:string", - "@value": "DE" - } - }, - "gx-participant:legalAddress": { - "@type": "gx-participant:Address", - "gx-participant:country": { - "@type": "xsd:string", - "@value": "DE" - } - }, - "gx-participant:note": { - "@value": "A random self-description", - "@type": "xsd:string" - } - }, - "proof": { - "type": "JsonWebKey2020", - "created": "2022-06-21T11:47:23.802Z", - "proofPurpose": "assertionMethod", - "verificationMethod": "did:web:test.delta-dao.com", - "jws": "eyJhbGciOiJQUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..2bSfloq1nDSLL45R-RTx_dYzwrE7C2XRCelTG78rORNXueTgrI1wchuUAI18R4_dIJgVpYiwMU_xnSAYgPXvfREZAK9-qbQc0lpzpVgDaVwhoI7N_TPIeSPd_3zwDSy4W8LTGAXt9nTAwQJfH-qwsV6kRqN1To0p-VqdnZda2IWf_2Mxdu2cYFm9mGT9RCoACCqwdkBR44PIP3lt-pGM68_pxfj3TNiAAcYj8fWucSkoeA_tSZV7Z5YNxa-Ge6j4DbpNADOBUYgmKeU0zIvPEi4UGYiFgeiM7F9u3-v_0VQ3ODuBcprQYIbgL0_7YN6QwUlZBfpeXXXvG7LmQGbibw" - } - } -} \ No newline at end of file diff --git a/extensions/store/sql/identity-hub-store-sql/README.md b/extensions/store/sql/identity-hub-store-sql/README.md deleted file mode 100644 index ae05a39a1..000000000 --- a/extensions/store/sql/identity-hub-store-sql/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# SQL Identity Hub store - -Provides SQL persistence for Identity Hub. - -## Prerequisites - -Please apply this [schema](docs/schema.sql) to your SQL database. - -## Entity Diagram - -![ER Diagram](docs/er.png) - -## Test - -```bash -./gradlew test -DincludeTags="ComponentTest" -``` \ No newline at end of file diff --git a/extensions/store/sql/identity-hub-store-sql/build.gradle.kts b/extensions/store/sql/identity-hub-store-sql/build.gradle.kts deleted file mode 100644 index 2a96a111e..000000000 --- a/extensions/store/sql/identity-hub-store-sql/build.gradle.kts +++ /dev/null @@ -1,15 +0,0 @@ -plugins { - `java-library` -} - -dependencies { - api(project(":spi:identity-hub-store-spi")) - implementation(libs.edc.spi.transaction) - implementation(libs.edc.spi.transaction.datasource) - implementation(libs.edc.core.sql) - - testImplementation(libs.edc.core.junit) - testImplementation(testFixtures(libs.edc.core.sql)) - testImplementation(testFixtures(project(":spi:identity-hub-store-spi"))) - testImplementation(libs.postgres) -} diff --git a/extensions/store/sql/identity-hub-store-sql/docs/er.png b/extensions/store/sql/identity-hub-store-sql/docs/er.png deleted file mode 100644 index 81d91973e..000000000 Binary files a/extensions/store/sql/identity-hub-store-sql/docs/er.png and /dev/null differ diff --git a/extensions/store/sql/identity-hub-store-sql/docs/er.puml b/extensions/store/sql/identity-hub-store-sql/docs/er.puml deleted file mode 100644 index c2a3a844f..000000000 --- a/extensions/store/sql/identity-hub-store-sql/docs/er.puml +++ /dev/null @@ -1,8 +0,0 @@ -@startuml -entity edc_identityhub { - * id: string <> - * payload: string - * created_at: long - -- -} -@enduml \ No newline at end of file diff --git a/extensions/store/sql/identity-hub-store-sql/docs/schema.sql b/extensions/store/sql/identity-hub-store-sql/docs/schema.sql deleted file mode 100644 index bc5ea2d66..000000000 --- a/extensions/store/sql/identity-hub-store-sql/docs/schema.sql +++ /dev/null @@ -1,7 +0,0 @@ -CREATE TABLE IF NOT EXISTS edc_identityhub -( - id VARCHAR NOT NULL PRIMARY KEY, - payload VARCHAR NOT NULL UNIQUE, - payloadFormat VARCHAR NOT NULL, - created_at BIGINT NOT NULL -); diff --git a/extensions/store/sql/identity-hub-store-sql/src/main/java/org/eclipse/edc/identityhub/store/sql/SqlIdentityHubStore.java b/extensions/store/sql/identity-hub-store-sql/src/main/java/org/eclipse/edc/identityhub/store/sql/SqlIdentityHubStore.java deleted file mode 100644 index 88407b529..000000000 --- a/extensions/store/sql/identity-hub-store-sql/src/main/java/org/eclipse/edc/identityhub/store/sql/SqlIdentityHubStore.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2020 - 2023 Amadeus - * - * 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: - * Amadeus - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.store.sql; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.eclipse.edc.identityhub.store.spi.IdentityHubRecord; -import org.eclipse.edc.identityhub.store.spi.IdentityHubStore; -import org.eclipse.edc.identityhub.store.sql.schema.IdentityHubStatements; -import org.eclipse.edc.spi.persistence.EdcPersistenceException; -import org.eclipse.edc.sql.QueryExecutor; -import org.eclipse.edc.sql.store.AbstractSqlStore; -import org.eclipse.edc.transaction.datasource.spi.DataSourceRegistry; -import org.eclipse.edc.transaction.spi.TransactionContext; -import org.jetbrains.annotations.NotNull; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.Objects; -import java.util.stream.Stream; - -/** - * SQL implementation for {@link IdentityHubStore}. - */ -public class SqlIdentityHubStore extends AbstractSqlStore implements IdentityHubStore { - private final IdentityHubStatements statements; - - public SqlIdentityHubStore(DataSourceRegistry dataSourceRegistry, String dataSourceName, TransactionContext transactionContext, - IdentityHubStatements identityHubStatements, ObjectMapper mapper, QueryExecutor queryExecutor) { - super(dataSourceRegistry, dataSourceName, transactionContext, mapper, queryExecutor); - statements = Objects.requireNonNull(identityHubStatements); - } - - @Override - public @NotNull Stream getAll() { - return transactionContext.execute(() -> { - try (var connection = getConnection()) { - return queryExecutor.query(connection, true, this::parse, statements.getFindAllTemplate()); - } catch (Exception e) { - throw new EdcPersistenceException(e.getMessage(), e); - } - }); - } - - @Override - public void add(IdentityHubRecord record) { - transactionContext.execute(() -> { - try (var connection = getConnection()) { - var payload = new String(record.getPayload()); - queryExecutor.execute(connection, statements.getInsertTemplate(), record.getId(), payload, record.getPayloadFormat(), record.getCreatedAt()); - } catch (Exception e) { - throw new EdcPersistenceException(e.getMessage(), e); - } - }); - } - - private IdentityHubRecord parse(ResultSet resultSet) throws SQLException { - return IdentityHubRecord.Builder.newInstance() - .id(resultSet.getString(statements.getIdColumn())) - .payload(resultSet.getString(statements.getPayloadColumn()).getBytes()) - .payloadFormat(resultSet.getString(statements.getPayloadFormatColumn())) - .createdAt(resultSet.getLong(statements.getCreatedAtColumn())) - .build(); - } -} diff --git a/extensions/store/sql/identity-hub-store-sql/src/main/java/org/eclipse/edc/identityhub/store/sql/SqlIdentityHubStoreExtension.java b/extensions/store/sql/identity-hub-store-sql/src/main/java/org/eclipse/edc/identityhub/store/sql/SqlIdentityHubStoreExtension.java deleted file mode 100644 index f74195fbc..000000000 --- a/extensions/store/sql/identity-hub-store-sql/src/main/java/org/eclipse/edc/identityhub/store/sql/SqlIdentityHubStoreExtension.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2020 - 2023 Amadeus - * - * 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: - * Amadeus - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.store.sql; - -import org.eclipse.edc.identityhub.store.spi.IdentityHubStore; -import org.eclipse.edc.identityhub.store.sql.schema.BaseSqlIdentityHubStatements; -import org.eclipse.edc.identityhub.store.sql.schema.IdentityHubStatements; -import org.eclipse.edc.runtime.metamodel.annotation.Extension; -import org.eclipse.edc.runtime.metamodel.annotation.Inject; -import org.eclipse.edc.runtime.metamodel.annotation.Provider; -import org.eclipse.edc.runtime.metamodel.annotation.Setting; -import org.eclipse.edc.spi.system.ServiceExtension; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.edc.spi.types.TypeManager; -import org.eclipse.edc.sql.QueryExecutor; -import org.eclipse.edc.transaction.datasource.spi.DataSourceRegistry; -import org.eclipse.edc.transaction.spi.TransactionContext; - -import java.util.Objects; - -/** - * Extension that provides a {@link IdentityHubStore} with SQL as backend storage. - */ -@Extension(value = SqlIdentityHubStoreExtension.NAME) -public class SqlIdentityHubStoreExtension implements ServiceExtension { - - public static final String NAME = "SQL Identity Hub Store"; - - @Setting(value = "Name of the datasource in which store items are persisted") - private static final String DATASOURCE_NAME_SETTING = "edc.datasource.identityhub.name"; - private static final String DEFAULT_DATASOURCE_NAME = "identityhub"; - - @Inject(required = false) - private IdentityHubStatements statements; - @Inject - private DataSourceRegistry dataSourceRegistry; - @Inject - private TransactionContext trxContext; - @Inject - private TypeManager typeManager; - @Inject - private QueryExecutor queryExecutor; - - @Override - public String name() { - return NAME; - } - - - @Provider - public IdentityHubStore identityHubStore(ServiceExtensionContext context) { - var s = Objects.requireNonNullElse(statements, new BaseSqlIdentityHubStatements()); - var dataSource = context.getSetting(DATASOURCE_NAME_SETTING, DEFAULT_DATASOURCE_NAME); - return new SqlIdentityHubStore(dataSourceRegistry, dataSource, trxContext, s, typeManager.getMapper(), queryExecutor); - } -} diff --git a/extensions/store/sql/identity-hub-store-sql/src/main/java/org/eclipse/edc/identityhub/store/sql/schema/BaseSqlIdentityHubStatements.java b/extensions/store/sql/identity-hub-store-sql/src/main/java/org/eclipse/edc/identityhub/store/sql/schema/BaseSqlIdentityHubStatements.java deleted file mode 100644 index 7107dbcbd..000000000 --- a/extensions/store/sql/identity-hub-store-sql/src/main/java/org/eclipse/edc/identityhub/store/sql/schema/BaseSqlIdentityHubStatements.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2020 - 2022 Amadeus - * - * 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: - * Amadeus - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.store.sql.schema; - -import static java.lang.String.format; - -/** - * Provide an agnostic SQL implementation of {@link IdentityHubStatements} which is not tied to - * a particular SQL storage. - */ -public class BaseSqlIdentityHubStatements implements IdentityHubStatements { - - @Override - public String getInsertTemplate() { - return format("INSERT INTO %s (%s, %s, %s, %s) VALUES (?,?,?,?)", - getTable(), getIdColumn(), getPayloadColumn(), getPayloadFormatColumn(), getCreatedAtColumn()); - } - - @Override - public String getFindAllTemplate() { - return format("SELECT * FROM %s", getTable()); - } -} diff --git a/extensions/store/sql/identity-hub-store-sql/src/main/java/org/eclipse/edc/identityhub/store/sql/schema/IdentityHubStatements.java b/extensions/store/sql/identity-hub-store-sql/src/main/java/org/eclipse/edc/identityhub/store/sql/schema/IdentityHubStatements.java deleted file mode 100644 index 018ae3795..000000000 --- a/extensions/store/sql/identity-hub-store-sql/src/main/java/org/eclipse/edc/identityhub/store/sql/schema/IdentityHubStatements.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2020 - 2022 Amadeus - * - * 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: - * Amadeus - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.store.sql.schema; - -/** - * Provides the mapping with columns, statements with the underlying SQL storage system. - */ -public interface IdentityHubStatements { - - default String getTable() { - return "edc_identityhub"; - } - - default String getIdColumn() { - return "id"; - } - - default String getPayloadColumn() { - return "payload"; - } - - default String getPayloadFormatColumn() { - return "payloadFormat"; - } - - - default String getCreatedAtColumn() { - return "created_at"; - } - - String getInsertTemplate(); - - String getFindAllTemplate(); - -} diff --git a/extensions/store/sql/identity-hub-store-sql/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/extensions/store/sql/identity-hub-store-sql/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension deleted file mode 100644 index 3884048e4..000000000 --- a/extensions/store/sql/identity-hub-store-sql/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension +++ /dev/null @@ -1,15 +0,0 @@ -# -# Copyright (c) 2020 - 2022 Amadeus -# -# 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: -# Amadeus - initial API and implementation -# -# - -org.eclipse.edc.identityhub.store.sql.SqlIdentityHubStoreExtension diff --git a/extensions/store/sql/identity-hub-store-sql/src/test/java/org/eclipse/edc/identityhub/store/sql/PostgresIdentityHubStoreTest.java b/extensions/store/sql/identity-hub-store-sql/src/test/java/org/eclipse/edc/identityhub/store/sql/PostgresIdentityHubStoreTest.java deleted file mode 100644 index d30ee48b8..000000000 --- a/extensions/store/sql/identity-hub-store-sql/src/test/java/org/eclipse/edc/identityhub/store/sql/PostgresIdentityHubStoreTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2020 - 2023 Amadeus - * - * 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: - * Amadeus - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.store.sql; - -import org.eclipse.edc.identityhub.store.spi.IdentityHubStore; -import org.eclipse.edc.identityhub.store.spi.IdentityHubStoreTestBase; -import org.eclipse.edc.identityhub.store.sql.schema.BaseSqlIdentityHubStatements; -import org.eclipse.edc.junit.annotations.ComponentTest; -import org.eclipse.edc.spi.types.TypeManager; -import org.eclipse.edc.sql.QueryExecutor; -import org.eclipse.edc.sql.testfixtures.PostgresqlStoreSetupExtension; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; - -@ComponentTest -@ExtendWith(PostgresqlStoreSetupExtension.class) -public class PostgresIdentityHubStoreTest extends IdentityHubStoreTestBase { - private SqlIdentityHubStore store; - - @BeforeEach - void setUp(PostgresqlStoreSetupExtension extension, QueryExecutor queryExecutor) throws IOException { - var statements = new BaseSqlIdentityHubStatements(); - var typeManager = new TypeManager(); - - store = new SqlIdentityHubStore(extension.getDataSourceRegistry(), extension.getDatasourceName(), - extension.getTransactionContext(), statements, typeManager.getMapper(), queryExecutor); - - var schema = Files.readString(Paths.get("docs/schema.sql")); - extension.runQuery(schema); - } - - @AfterEach - void tearDown(PostgresqlStoreSetupExtension extension) { - var dialect = new BaseSqlIdentityHubStatements(); - extension.runQuery("DROP TABLE " + dialect.getTable()); - } - - @Override - protected IdentityHubStore getStore() { - return store; - } -} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 36ac96179..6c90af494 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,19 +3,20 @@ format.version = "1.1" [versions] assertj = "3.24.2" +awaitility = "4.2.0" edc = "0.3.2-SNAPSHOT" failsafe = "3.3.2" jackson = "2.14.2" jupiter = "5.10.0" mockserver = "5.15.0" nimbus = "9.25" -picocli = "4.7.5" postgres = "42.6.0" restAssured = "5.3.2" swagger = "2.1.13" +rsApi = "3.1.0" +testcontainers = "1.19.1" [libraries] -assertj = { module = "org.assertj:assertj-core", version.ref = "assertj" } edc-util = { module = "org.eclipse.edc:util", version.ref = "edc" } edc-junit = { module = "org.eclipse.edc:junit", version.ref = "edc" } edc-boot = { module = "org.eclipse.edc:boot", version.ref = "edc" } @@ -23,41 +24,52 @@ edc-spi-core = { module = "org.eclipse.edc:core-spi", version.ref = "edc" } edc-spi-http = { module = "org.eclipse.edc:http-spi", version.ref = "edc" } edc-spi-policy-engine = { module = "org.eclipse.edc:policy-engine-spi", version.ref = "edc" } edc-spi-transaction = { module = "org.eclipse.edc:transaction-spi", version.ref = "edc" } +edc-spi-transform = { module = "org.eclipse.edc:transform-spi", version.ref = "edc" } edc-spi-transaction-datasource = { module = "org.eclipse.edc:transaction-datasource-spi", version.ref = "edc" } edc-spi-identity-did = { module = "org.eclipse.edc:identity-did-spi", version.ref = "edc" } edc-spi-aggregate-service = { module = "org.eclipse.edc:aggregate-service-spi", version.ref = "edc" } +edc-spi-jsonld = { module = "org.eclipse.edc:json-ld-spi", version.ref = "edc" } +edc-spi-validator = { module = "org.eclipse.edc:validator-spi", version.ref = "edc" } +edc-spi-web = { module = "org.eclipse.edc:web-spi", version.ref = "edc" } +edc-spi-identitytrust = { module = "org.eclipse.edc:identity-trust-spi", version.ref = "edc" } edc-core-connector = { module = "org.eclipse.edc:connector-core", version.ref = "edc" } edc-core-controlPlane = { module = "org.eclipse.edc:control-plane-core", version.ref = "edc" } edc-core-micrometer = { module = "org.eclipse.edc:micrometer-core", version.ref = "edc" } edc-core-api = { module = "org.eclipse.edc:api-core", version.ref = "edc" } edc-core-stateMachine = { module = "org.eclipse.edc:state-machine", version.ref = "edc" } edc-core-sql = { module = "org.eclipse.edc:sql-core", version.ref = "edc" } +edc-core-jersey = { module = "org.eclipse.edc:jersey-core", version.ref = "edc" } +edc-core-jerseyproviders = { module = "org.eclipse.edc:jersey-providers", version.ref = "edc" } edc-core-junit = { module = "org.eclipse.edc:junit", version.ref = "edc" } +edc-core-transform = { module = "org.eclipse.edc:transform-core", version.ref = "edc" } edc-ext-identity-did-crypto = { module = "org.eclipse.edc:identity-did-crypto", version.ref = "edc" } edc-ext-identity-did-core = { module = "org.eclipse.edc:identity-did-core", version.ref = "edc" } edc-ext-identity-did-web = { module = "org.eclipse.edc:identity-did-web", version.ref = "edc" } edc-ext-http = { module = "org.eclipse.edc:http", version.ref = "edc" } -edc-ext-json-ld = { module = "org.eclipse.edc:json-ld", version.ref = "edc" } -edc-ext-micrometer-jetty = { module = "org.eclipse.edc:jetty-micrometer", version.ref = "edc" } -edc-ext-micrometer-jersey = { module = "org.eclipse.edc:jersey-micrometer", version.ref = "edc" } +edc-ext-jsonld = { module = "org.eclipse.edc:json-ld", version.ref = "edc" } edc-ext-observability = { module = "org.eclipse.edc:api-observability", version.ref = "edc" } edc-ext-configuration-filesystem = { module = "org.eclipse.edc:configuration-filesystem", version.ref = "edc" } edc-ext-vault-filesystem = { module = "org.eclipse.edc:vault-filesystem", version.ref = "edc" } -edc-ext-jdklogger = { module = "org.eclipse.edc:monitor-jdk-logger", version.ref = "edc" } +edc-testfixtures-managementapi = { module = "org.eclipse.edc:management-api-test-fixtures", version.ref = "edc" } + + +# Third party libs +assertj = { module = "org.assertj:assertj-core", version.ref = "assertj" } +awaitility = { module = "org.awaitility:awaitility", version.ref = "awaitility" } failsafe-core = { module = "dev.failsafe:failsafe", version.ref = "failsafe" } jackson-databind = { module = "com.fasterxml.jackson.core:jackson-databind", version.ref = "jackson" } +jakarta-rsApi = { module = "jakarta.ws.rs:jakarta.ws.rs-api", version.ref = "rsApi" } junit-jupiter-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "jupiter" } mockserver-client = { module = "org.mock-server:mockserver-client-java", version.ref = "mockserver" } mockserver-netty = { module = "org.mock-server:mockserver-netty", version.ref = "mockserver" } nimbus-jwt = { module = "com.nimbusds:nimbus-jose-jwt", version.ref = "nimbus" } -picocli-core = { module = "info.picocli:picocli", version.ref = "picocli" } -picocli-codegen = { module = "info.picocli:picocli-codegen", version.ref = "picocli" } postgres = { module = "org.postgresql:postgresql", version.ref = "postgres" } restAssured = { module = "io.rest-assured:rest-assured", version.ref = "restAssured" } swagger-jaxrs = { module = "io.swagger.core.v3:swagger-jaxrs2-jakarta", version.ref = "swagger" } +testcontainers-junit = { module = "org.testcontainers:junit-jupiter", version.ref = "testcontainers" } [bundles] -connector = [ "edc.boot", "edc.core-connector", "edc.ext.http", "edc-ext-observability", "edc-ext-json-ld" ] +connector = ["edc-boot", "edc-core-connector", "edc-ext-http", "edc-ext-observability", "edc-ext-jsonld"] [plugins] shadow = { id = "com.github.johnrengelman.shadow", version = "8.1.1" } diff --git a/identity-hub-cli/README.md b/identity-hub-cli/README.md deleted file mode 100644 index 708873430..000000000 --- a/identity-hub-cli/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# Command-line client - -The client is a Java JAR that provides access to an identity hub service via REST. - -## Running the client - -To run the command line client, and list available options and commands: - -```bash -cd IdentityHub -./gradlew build -java -jar client-cli/build/libs/identity-hub-cli.jar --help -``` - -For example, to get verifiable credentials: - -``` -java -jar client-cli/build/libs/identity-hub-cli.jar \ - -s=http://localhost:8181/api \ - vc list -``` - -The client can also be run from a local Maven repository: - -``` -cd IdentityHub -./gradlew publishToMavenLocal -``` - -``` -cd OtherDirectory -mvn dependency:copy -Dartifact=org.eclipse.edc:identity-hub-cli:-SNAPSHOT:jar:all -DoutputDirectory=. -java -jar identity-hub-cli-0.3.2-SNAPSHOT-all.jar --help -``` - diff --git a/identity-hub-cli/build.gradle.kts b/identity-hub-cli/build.gradle.kts deleted file mode 100644 index 4d0816b94..000000000 --- a/identity-hub-cli/build.gradle.kts +++ /dev/null @@ -1,36 +0,0 @@ -plugins { - `java-library` - id("application") - alias(libs.plugins.shadow) - `maven-publish` -} - -dependencies { - api(libs.picocli.core) - annotationProcessor(libs.picocli.codegen) - - implementation(project(":core:identity-hub-client")) - implementation(project(":extensions:credentials:identity-hub-credentials-jwt")) - implementation(project(":extensions:identity-hub-verifier-jwt")) - - implementation(libs.edc.core.connector) - implementation(libs.edc.ext.identity.did.crypto) - implementation(libs.edc.spi.identity.did) - implementation(libs.jackson.databind) - implementation(libs.nimbus.jwt) - - testImplementation(testFixtures(project(":spi:identity-hub-spi"))) -} - -repositories { - mavenCentral() -} - -application { - mainClass.set("org.eclipse.edc.identityhub.cli.IdentityHubCli") -} - -tasks.withType { - mergeServiceFiles() - archiveFileName.set("identity-hub-cli.jar") -} diff --git a/identity-hub-cli/src/main/java/org/eclipse/edc/identityhub/cli/AddVerifiableCredentialCommand.java b/identity-hub-cli/src/main/java/org/eclipse/edc/identityhub/cli/AddVerifiableCredentialCommand.java deleted file mode 100644 index f1c0b79d8..000000000 --- a/identity-hub-cli/src/main/java/org/eclipse/edc/identityhub/cli/AddVerifiableCredentialCommand.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.cli; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.nimbusds.jwt.SignedJWT; -import org.eclipse.edc.iam.did.crypto.key.EcPrivateKeyWrapper; -import org.eclipse.edc.identityhub.credentials.jwt.JwtCredentialEnvelope; -import org.eclipse.edc.identityhub.credentials.jwt.JwtCredentialFactory; -import org.eclipse.edc.identityhub.spi.credentials.model.Credential; -import org.eclipse.edc.identityhub.spi.credentials.model.CredentialSubject; -import org.eclipse.edc.identityhub.spi.credentials.model.VerifiableCredential; -import picocli.CommandLine; -import picocli.CommandLine.Command; -import picocli.CommandLine.ParentCommand; - -import java.sql.Date; -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.Callable; - -import static org.eclipse.edc.identityhub.cli.CryptoUtils.readEcKeyPemFile; - - -@Command(name = "add", description = "Adds a verifiable credential to identity hub") -class AddVerifiableCredentialCommand implements Callable { - - private static final ObjectMapper MAPPER = new ObjectMapper(); - private static final JwtCredentialFactory JWT_CREDENTIAL_FACTORY = new JwtCredentialFactory(MAPPER); - - @ParentCommand - private VerifiableCredentialsCommand command; - - @CommandLine.Spec - private CommandLine.Model.CommandSpec spec; - - @CommandLine.Option(names = { "-c", "--claims" }, required = true, description = "Claims of the Verifiable Credential") - private String claims; - - @CommandLine.Option(names = { "-i", "--issuer" }, required = true, description = "DID of the Verifiable Credential issuer") - private String issuer; - - @CommandLine.Option(names = { "-b", "--subject" }, required = true, description = "DID of the Verifiable Credential subject") - private String subject; - - @CommandLine.Option(names = { "-k", "--private-key" }, required = true, description = "PEM file with EC private key for signing Verifiable Credentials") - private String privateKeyPemFile; - - @Override - public Integer call() throws Exception { - var out = spec.commandLine().getOut(); - var credentialSubject = createCredentialSubject(); - var credential = toCredential(credentialSubject); - var jwt = toJwt(credential); - - command.cli.identityHubClient.addVerifiableCredential(command.cli.hubUrl, new JwtCredentialEnvelope(jwt)) - .orElseThrow(responseFailure -> new CliException("Error while adding the Verifiable credential to the Identity Hub")); - - out.println("Verifiable Credential added successfully"); - - return 0; - } - - private CredentialSubject createCredentialSubject() { - Map claimsMap; - try { - claimsMap = MAPPER.readValue(claims, Map.class); - } catch (JsonProcessingException e) { - throw new CliException("Error while processing request json."); - } - var builder = CredentialSubject.Builder.newInstance() - .id(subject); - claimsMap.forEach(builder::claim); - return builder.build(); - } - - private Credential toCredential(CredentialSubject credentialSubject) { - return Credential.Builder.newInstance() - .id(UUID.randomUUID().toString()) - .issuer(issuer) - .issuanceDate(Date.from(Instant.now().truncatedTo(ChronoUnit.SECONDS))) - .context(VerifiableCredential.DEFAULT_CONTEXT) - .type(VerifiableCredential.DEFAULT_TYPE) - .credentialSubject(credentialSubject) - .build(); - } - - private SignedJWT toJwt(Credential credential) { - try { - var privateKey = readEcKeyPemFile(privateKeyPemFile); - return JWT_CREDENTIAL_FACTORY.buildSignedJwt(credential, new EcPrivateKeyWrapper(privateKey)); - } catch (Exception e) { - throw new CliException("Error while signing Verifiable Credential", e); - } - } -} diff --git a/identity-hub-cli/src/main/java/org/eclipse/edc/identityhub/cli/CliException.java b/identity-hub-cli/src/main/java/org/eclipse/edc/identityhub/cli/CliException.java deleted file mode 100644 index 90be6793f..000000000 --- a/identity-hub-cli/src/main/java/org/eclipse/edc/identityhub/cli/CliException.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.cli; - -/** - * Base exception for the CLI client. - * The client should use unchecked exceptions when appropriate (e.g., non-recoverable errors) and may extend this exception. - */ -public class CliException extends RuntimeException { - public CliException(String message) { - super(message); - } - - public CliException(String message, Throwable cause) { - super(message, cause); - } - -} diff --git a/identity-hub-cli/src/main/java/org/eclipse/edc/identityhub/cli/CryptoUtils.java b/identity-hub-cli/src/main/java/org/eclipse/edc/identityhub/cli/CryptoUtils.java deleted file mode 100644 index ebf6f3e07..000000000 --- a/identity-hub-cli/src/main/java/org/eclipse/edc/identityhub/cli/CryptoUtils.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.cli; - -import com.nimbusds.jose.JOSEException; -import com.nimbusds.jose.jwk.ECKey; - -import java.io.IOException; -import java.nio.file.Path; - -import static java.nio.file.Files.readString; - -public class CryptoUtils { - - private CryptoUtils() { - } - - /** - * Read {@link ECKey} from a PEM file. - * - * @throws IOException if file cannot be read. - * @throws JOSEException if {@link ECKey} cannot be parsed from PEM. - */ - public static ECKey readEcKeyPemFile(String file) throws IOException, JOSEException { - var contents = readString(Path.of(file)); - return (ECKey) ECKey.parseFromPEMEncodedObjects(contents); - } -} diff --git a/identity-hub-cli/src/main/java/org/eclipse/edc/identityhub/cli/IdentityHubCli.java b/identity-hub-cli/src/main/java/org/eclipse/edc/identityhub/cli/IdentityHubCli.java deleted file mode 100644 index faf120951..000000000 --- a/identity-hub-cli/src/main/java/org/eclipse/edc/identityhub/cli/IdentityHubCli.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.cli; - -import dev.failsafe.RetryPolicy; -import okhttp3.OkHttpClient; -import okhttp3.Response; -import org.eclipse.edc.connector.core.base.EdcHttpClientImpl; -import org.eclipse.edc.identityhub.client.IdentityHubClientImpl; -import org.eclipse.edc.identityhub.client.spi.IdentityHubClient; -import org.eclipse.edc.identityhub.credentials.jwt.JwtCredentialEnvelopeTransformer; -import org.eclipse.edc.identityhub.credentials.jwt.JwtPresentationEnvelopeTransformer; -import org.eclipse.edc.identityhub.spi.credentials.transformer.CredentialEnvelopeTransformerRegistryImpl; -import org.eclipse.edc.spi.monitor.ConsoleMonitor; -import org.eclipse.edc.spi.types.TypeManager; -import picocli.CommandLine; -import picocli.CommandLine.Command; - -import java.time.temporal.ChronoUnit; -import java.util.concurrent.TimeUnit; - -@Command(name = "identity-hub-cli", mixinStandardHelpOptions = true, - description = "Client utility for MVD identity hub.", - subcommands = { - VerifiableCredentialsCommand.class, - }) -public class IdentityHubCli { - @CommandLine.Option(names = { "-s", "--identity-hub-url" }, required = true, description = "Identity Hub URL", defaultValue = "http://localhost:8181/api/identity-hub") - String hubUrl; - - private static final int RETRIES = 3; - private static final int CONNECT_TIMEOUT_SECONDS = 30; - private static final int READ_TIMEOUT_SECONDS = 30; - private static final int MIN_BACKOFF_MILLIS = 500; - private static final int MAX_BACKOFF_MILLIS = 10000; - - IdentityHubClient identityHubClient; - - public static void main(String... args) { - CommandLine commandLine = getCommandLine(); - var exitCode = commandLine.execute(args); - System.exit(exitCode); - } - - public static CommandLine getCommandLine() { - var command = new IdentityHubCli(); - return new CommandLine(command).setExecutionStrategy(command::executionStrategy); - } - - private int executionStrategy(CommandLine.ParseResult parseResult) { - init(); // custom initialization to be done before executing any command or subcommand - return new CommandLine.RunLast().execute(parseResult); - } - - private void init() { - var typeManager = new TypeManager(); - var monitor = new ConsoleMonitor(); - - var okHttpClient = new OkHttpClient.Builder() - .connectTimeout(CONNECT_TIMEOUT_SECONDS, TimeUnit.SECONDS) - .readTimeout(READ_TIMEOUT_SECONDS, TimeUnit.SECONDS) - .build(); - - var retryPolicy = RetryPolicy.builder() - .withMaxRetries(RETRIES) - .withBackoff(MIN_BACKOFF_MILLIS, MAX_BACKOFF_MILLIS, ChronoUnit.MILLIS) - .build(); - - var client = new EdcHttpClientImpl(okHttpClient, retryPolicy, monitor); - - var registry = new CredentialEnvelopeTransformerRegistryImpl(); - registry.register(new JwtCredentialEnvelopeTransformer(typeManager.getMapper())); - registry.register(new JwtPresentationEnvelopeTransformer(typeManager.getMapper())); - - identityHubClient = new IdentityHubClientImpl(client, typeManager, registry); - } -} diff --git a/identity-hub-cli/src/main/java/org/eclipse/edc/identityhub/cli/ListVerifiableCredentialsCommand.java b/identity-hub-cli/src/main/java/org/eclipse/edc/identityhub/cli/ListVerifiableCredentialsCommand.java deleted file mode 100644 index b4b1d1564..000000000 --- a/identity-hub-cli/src/main/java/org/eclipse/edc/identityhub/cli/ListVerifiableCredentialsCommand.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.cli; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; -import org.eclipse.edc.spi.result.Result; -import picocli.CommandLine.Command; -import picocli.CommandLine.Model.CommandSpec; -import picocli.CommandLine.ParentCommand; -import picocli.CommandLine.Spec; - -import java.util.Collection; -import java.util.concurrent.Callable; - -import static java.util.stream.Collectors.toList; - -@Command(name = "list", description = "Lists verifiable credentials") -class ListVerifiableCredentialsCommand implements Callable { - - private static final ObjectMapper MAPPER = new ObjectMapper() - .enable(SerializationFeature.INDENT_OUTPUT); - - @ParentCommand - private VerifiableCredentialsCommand command; - - @Spec - private CommandSpec spec; - - @Override - public Integer call() throws Exception { - var out = spec.commandLine().getOut(); - var result = command.cli.identityHubClient.getVerifiableCredentials(command.cli.hubUrl); - if (result.failed()) { - throw new CliException("Failed to get verifiable credentials: " + result.getFailureDetail()); - } - var vcs = result.getContent().stream() - .map(envelope -> envelope.toVerifiableCredentials(MAPPER)) - .map(Result::getContent) - .flatMap(Collection::stream) - .collect(toList()); - MAPPER.writeValue(out, vcs); - out.println(); - return 0; - } -} diff --git a/identity-hub-cli/src/main/java/org/eclipse/edc/identityhub/cli/VerifiableCredentialsCommand.java b/identity-hub-cli/src/main/java/org/eclipse/edc/identityhub/cli/VerifiableCredentialsCommand.java deleted file mode 100644 index 1b75b53c4..000000000 --- a/identity-hub-cli/src/main/java/org/eclipse/edc/identityhub/cli/VerifiableCredentialsCommand.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.cli; - -import picocli.CommandLine.Command; -import picocli.CommandLine.ParentCommand; - -@Command(name = "vc", mixinStandardHelpOptions = true, - description = "Manage verifiable credentials.", - subcommands = { - ListVerifiableCredentialsCommand.class, - AddVerifiableCredentialCommand.class - }) -class VerifiableCredentialsCommand { - @ParentCommand - IdentityHubCli cli; -} diff --git a/identity-hub-cli/src/test/java/org/eclipse/edc/identityhub/cli/CliTestUtils.java b/identity-hub-cli/src/test/java/org/eclipse/edc/identityhub/cli/CliTestUtils.java deleted file mode 100644 index 6ab484eb3..000000000 --- a/identity-hub-cli/src/test/java/org/eclipse/edc/identityhub/cli/CliTestUtils.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.cli; - -import com.nimbusds.jose.JOSEException; -import com.nimbusds.jose.jwk.ECKey; -import com.nimbusds.jwt.SignedJWT; -import org.eclipse.edc.iam.did.crypto.key.EcPublicKeyWrapper; -import org.eclipse.edc.identityhub.spi.credentials.model.Credential; - -import static org.eclipse.edc.identityhub.cli.CryptoUtils.readEcKeyPemFile; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.buildSignedJwt; - -class CliTestUtils { - - public static final String PUBLIC_KEY_PATH = "src/test/resources/test-public-key.pem"; - public static final String PRIVATE_KEY_PATH = "src/test/resources/test-private-key.pem"; - public static final ECKey PUBLIC_KEY; - public static final ECKey PRIVATE_KEY; - - static { - try { - PUBLIC_KEY = readEcKeyPemFile(PUBLIC_KEY_PATH); - PRIVATE_KEY = readEcKeyPemFile(PRIVATE_KEY_PATH); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private CliTestUtils() { - } - - public static SignedJWT toJwtVerifiableCredential(Credential vc) { - try { - return buildSignedJwt(vc, - vc.getIssuer(), - vc.getCredentialSubject().getId(), - PRIVATE_KEY); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - public static boolean verifyVerifiableCredentialSignature(SignedJWT jwt) { - try { - var wrapper = new EcPublicKeyWrapper(PUBLIC_KEY); - return jwt.verify(wrapper.verifier()); - } catch (JOSEException e) { - throw new RuntimeException(e); - } - } - -} diff --git a/identity-hub-cli/src/test/java/org/eclipse/edc/identityhub/cli/VerifiableCredentialsCommandTest.java b/identity-hub-cli/src/test/java/org/eclipse/edc/identityhub/cli/VerifiableCredentialsCommandTest.java deleted file mode 100644 index 4bbee4a56..000000000 --- a/identity-hub-cli/src/test/java/org/eclipse/edc/identityhub/cli/VerifiableCredentialsCommandTest.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.cli; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.eclipse.edc.identityhub.client.spi.IdentityHubClient; -import org.eclipse.edc.identityhub.credentials.jwt.JwtCredentialEnvelope; -import org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil; -import org.eclipse.edc.identityhub.spi.credentials.model.Credential; -import org.eclipse.edc.spi.result.Result; -import org.eclipse.edc.spi.types.TypeManager; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; -import picocli.CommandLine; - -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.edc.identityhub.cli.CliTestUtils.PRIVATE_KEY_PATH; -import static org.eclipse.edc.identityhub.cli.CliTestUtils.toJwtVerifiableCredential; -import static org.eclipse.edc.identityhub.cli.CliTestUtils.verifyVerifiableCredentialSignature; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isA; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -class VerifiableCredentialsCommandTest { - - private static final ObjectMapper MAPPER = new TypeManager().getMapper(); - private static final Credential CREDENTIAL1 = VerifiableCredentialTestUtil.generateCredential(); - private static final JwtCredentialEnvelope VC1 = new JwtCredentialEnvelope(toJwtVerifiableCredential(CREDENTIAL1)); - private static final Credential CREDENTIAL2 = VerifiableCredentialTestUtil.generateCredential(); - private static final JwtCredentialEnvelope VC2 = new JwtCredentialEnvelope(toJwtVerifiableCredential(CREDENTIAL2)); - private static final String HUB_URL = "http://some.test.url"; - - private final IdentityHubCli app = new IdentityHubCli(); - private final CommandLine cmd = new CommandLine(app); - private final StringWriter out = new StringWriter(); - private final StringWriter err = new StringWriter(); - - @BeforeEach - void setUp() { - app.identityHubClient = mock(IdentityHubClient.class); - app.hubUrl = HUB_URL; - cmd.setOut(new PrintWriter(out)); - cmd.setErr(new PrintWriter(err)); - } - - @Test - void list() throws Exception { - // arrange - when(app.identityHubClient.getVerifiableCredentials(app.hubUrl)).thenReturn(Result.success(List.of(VC1, VC2))); - - // act - var exitCode = executeList(); - var outContent = out.toString(); - var errContent = err.toString(); - - // assert - assertThat(exitCode).isZero(); - assertThat(errContent).isEmpty(); - - var claims = MAPPER.readValue(outContent, new TypeReference>>() { - }); - var vcs = claims.stream() - .map(c -> MAPPER.convertValue(c, Credential.class)) - .collect(Collectors.toList()); - - assertThat(vcs) - .usingRecursiveFieldByFieldElementComparator() - .isEqualTo(List.of(CREDENTIAL1, CREDENTIAL2)); - } - - @Test - void add() throws Exception { - // arrange - var json = MAPPER.writeValueAsString(CREDENTIAL1.getCredentialSubject().getClaims()); - var vcArgCaptor = ArgumentCaptor.forClass(JwtCredentialEnvelope.class); - doReturn(Result.success()).when(app.identityHubClient).addVerifiableCredential(eq(app.hubUrl), vcArgCaptor.capture()); - - // act - var exitCode = executeAdd(json, CREDENTIAL1.getIssuer(), CREDENTIAL1.getCredentialSubject().getId(), PRIVATE_KEY_PATH); - var outContent = out.toString(); - var errContent = err.toString(); - - // assert - assertThat(exitCode).isZero(); - assertThat(outContent).isEqualTo("Verifiable Credential added successfully" + System.lineSeparator()); - assertThat(errContent).isEmpty(); - - verify(app.identityHubClient).addVerifiableCredential(eq(app.hubUrl), isA(JwtCredentialEnvelope.class)); - var envelope = vcArgCaptor.getValue(); - var signedJwt = envelope.getJwt(); - - // assert JWT signature - assertThat(verifyVerifiableCredentialSignature(signedJwt)).isTrue(); - - var result = envelope.toVerifiableCredentials(MAPPER); - - assertThat(result.succeeded()).isTrue(); - assertThat(result.getContent()).hasSize(1).anySatisfy(verifiableCredential -> - assertThat(verifiableCredential.getItem()).usingRecursiveComparison() - .ignoringFields("id") - .ignoringFields("issuanceDate") - .isEqualTo(CREDENTIAL1)); - - } - - @Test - void add_invalidJson_fails() { - // arrange - var json = "Invalid json"; - - // act - var exitCode = executeAdd(json, "issuer", "subject", PRIVATE_KEY_PATH); - var outContent = out.toString(); - var errContent = err.toString(); - - // assert - assertThat(exitCode).isNotZero(); - assertThat(outContent).isEmpty(); - assertThat(errContent).contains("Error while processing request json"); - } - - @Test - void add_invalidPrivateKey_fails() throws JsonProcessingException { - // arrange - var json = MAPPER.writeValueAsString(CREDENTIAL1.getCredentialSubject().getClaims()); - - // act - var exitCode = executeAdd(json, CREDENTIAL1.getIssuer(), CREDENTIAL2.getCredentialSubject().getId(), "non-existing-key"); - var outContent = out.toString(); - var errContent = err.toString(); - - // assert - assertThat(exitCode).isNotZero(); - assertThat(outContent).isEmpty(); - assertThat(errContent).contains("Error while signing Verifiable Credential"); - } - - private int executeList() { - return cmd.execute("-s", HUB_URL, "vc", "list"); - } - - private int executeAdd(String json, String issuer, String subject, String privateKey) { - return cmd.execute("-s", HUB_URL, "vc", "add", "-c", json, "-i", issuer, "-b", subject, "-k", privateKey); - } -} diff --git a/identity-hub-cli/src/test/resources/test-private-key.pem b/identity-hub-cli/src/test/resources/test-private-key.pem deleted file mode 100644 index d042b43cb..000000000 --- a/identity-hub-cli/src/test/resources/test-private-key.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN EC PRIVATE KEY----- -MHcCAQEEINfoZvp8OzraxNeWR5YtDOXXp9s55joSacPLpuC+g8u4oAoGCCqGSM49 -AwEHoUQDQgAEMgmADLs+uEkcS70toCTY+TDhBfmDtMtjQ2F6feomOSSVUEIrns/5 -2a1VUKoJSxzMx6SeFgxrf3w3NvDtpiVzsQ== ------END EC PRIVATE KEY----- diff --git a/identity-hub-cli/src/test/resources/test-public-key.pem b/identity-hub-cli/src/test/resources/test-public-key.pem deleted file mode 100644 index eb4fa54ca..000000000 --- a/identity-hub-cli/src/test/resources/test-public-key.pem +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PUBLIC KEY----- -MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMgmADLs+uEkcS70toCTY+TDhBfmD -tMtjQ2F6feomOSSVUEIrns/52a1VUKoJSxzMx6SeFgxrf3w3NvDtpiVzsQ== ------END PUBLIC KEY----- diff --git a/launcher/build.gradle.kts b/launcher/build.gradle.kts index 703bd5f4d..89ed0345f 100644 --- a/launcher/build.gradle.kts +++ b/launcher/build.gradle.kts @@ -19,13 +19,13 @@ plugins { } dependencies { - runtimeOnly(project(":core:identity-hub")) - runtimeOnly(project(":core:identity-hub-credentials-verifier")) - runtimeOnly(project(":extensions:identity-hub-api")) - runtimeOnly(project(":extensions:self-description-api")) + runtimeOnly(project(":core:identity-hub-api")) +// runtimeOnly(project(":core:identity-hub-credentials-verifier")) +// runtimeOnly(project(":extensions:identity-hub-api")) +// runtimeOnly(project(":extensions:self-description-api")) runtimeOnly(libs.bundles.connector) - runtimeOnly(libs.edc.ext.identity.did.core) - runtimeOnly(libs.edc.ext.identity.did.web) +// runtimeOnly(libs.edc.ext.identity.did.core) +// runtimeOnly(libs.edc.ext.identity.did.web) } application { diff --git a/resources/openapi/yaml/identity-hub-api.yaml b/resources/openapi/yaml/identity-hub-api.yaml deleted file mode 100644 index 06c2660e3..000000000 --- a/resources/openapi/yaml/identity-hub-api.yaml +++ /dev/null @@ -1,113 +0,0 @@ -openapi: 3.0.1 -info: - title: Eclipse Dataspace Connector Identity Hub - version: 0.0.1 -paths: - /identity-hub: - post: - description: A Decentralized Web Node (https://identity.foundation/decentralized-web-node/spec) - compatible endpoint supporting operations to read and write Verifiable Credentials - into an Identity Hub - operationId: handleRequest - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/RequestObject' - responses: - default: - content: - application/json: - schema: - $ref: '#/components/schemas/ResponseObject' - description: default response - tags: - - IdentityHub -components: - schemas: - Descriptor: - type: object - properties: - dataCid: - type: string - description: "[UNSUPPORTED] If data is available, this field should contain\ - \ stringified Version 1 CID of the DAG PB encoded data" - dataFormat: - type: string - description: "if data is available, this field should contain a registered\ - \ IANA Media Type data format. Use 'application/vc+ldp' for Verifiable\ - \ Credentials." - dateCreated: - type: integer - format: int64 - description: Unix epoch timestamp interpreted as the time the logical entry - was created by the DID owner or another permitted party - method: - type: string - description: A string that matches a Decentralized Web Node Interface method - recordId: - type: string - description: Unique identifier of the record - MessageRequestObject: - type: object - properties: - data: - type: array - description: Optional base64Url encoded string of the message data - items: - type: string - format: byte - description: Optional base64Url encoded string of the message data - descriptor: - $ref: '#/components/schemas/Descriptor' - MessageResponseObject: - type: object - properties: - entries: - type: array - description: Resulting message entries returned from the invocation of the - corresponding message as free form objects - items: - type: object - description: Resulting message entries returned from the invocation of - the corresponding message as free form objects - status: - $ref: '#/components/schemas/MessageStatus' - MessageStatus: - type: object - properties: - code: - type: integer - format: int32 - description: An integer set to the HTTP Status Code appropriate for the - status of the response - detail: - type: string - description: A string that describes a terse summary of the status - RequestObject: - type: object - properties: - messages: - type: array - items: - $ref: '#/components/schemas/MessageRequestObject' - RequestStatus: - type: object - properties: - code: - type: integer - format: int32 - description: An integer set to the HTTP Status Code appropriate for the - status of the response - detail: - type: string - description: A string that describes a terse summary of the status - ResponseObject: - type: object - properties: - replies: - type: array - items: - $ref: '#/components/schemas/MessageResponseObject' - status: - $ref: '#/components/schemas/RequestStatus' diff --git a/resources/openapi/yaml/self-description-api.yaml b/resources/openapi/yaml/self-description-api.yaml deleted file mode 100644 index e5d6b51a2..000000000 --- a/resources/openapi/yaml/self-description-api.yaml +++ /dev/null @@ -1,128 +0,0 @@ -openapi: 3.0.1 -info: - title: Eclipse Dataspace Connector Identity Hub - version: 0.0.1 -paths: - /identity-hub: - post: - description: A Decentralized Web Node (https://identity.foundation/decentralized-web-node/spec) - compatible endpoint supporting operations to read and write Verifiable Credentials - into an Identity Hub - operationId: handleRequest - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/RequestObject' - responses: - default: - content: - application/json: - schema: - $ref: '#/components/schemas/ResponseObject' - description: default response - tags: - - IdentityHub - /identity-hub/self-description: - get: - description: Serve Self-Description document. - operationId: getSelfDescription - responses: - default: - content: - application/json: - schema: - $ref: '#/components/schemas/JsonNode' - description: default response - tags: - - SelfDescription -components: - schemas: - Descriptor: - type: object - properties: - dataCid: - type: string - description: "[UNSUPPORTED] If data is available, this field should contain\ - \ stringified Version 1 CID of the DAG PB encoded data" - dataFormat: - type: string - description: "if data is available, this field should contain a registered\ - \ IANA Media Type data format. Use 'application/vc+ldp' for Verifiable\ - \ Credentials." - dateCreated: - type: integer - format: int64 - description: Unix epoch timestamp interpreted as the time the logical entry - was created by the DID owner or another permitted party - method: - type: string - description: A string that matches a Decentralized Web Node Interface method - recordId: - type: string - description: Unique identifier of the record - JsonNode: - type: object - MessageRequestObject: - type: object - properties: - data: - type: array - description: Optional base64Url encoded string of the message data - items: - type: string - format: byte - description: Optional base64Url encoded string of the message data - descriptor: - $ref: '#/components/schemas/Descriptor' - MessageResponseObject: - type: object - properties: - entries: - type: array - description: Resulting message entries returned from the invocation of the - corresponding message as free form objects - items: - type: object - description: Resulting message entries returned from the invocation of - the corresponding message as free form objects - status: - $ref: '#/components/schemas/MessageStatus' - MessageStatus: - type: object - properties: - code: - type: integer - format: int32 - description: An integer set to the HTTP Status Code appropriate for the - status of the response - detail: - type: string - description: A string that describes a terse summary of the status - RequestObject: - type: object - properties: - messages: - type: array - items: - $ref: '#/components/schemas/MessageRequestObject' - RequestStatus: - type: object - properties: - code: - type: integer - format: int32 - description: An integer set to the HTTP Status Code appropriate for the - status of the response - detail: - type: string - description: A string that describes a terse summary of the status - ResponseObject: - type: object - properties: - replies: - type: array - items: - $ref: '#/components/schemas/MessageResponseObject' - status: - $ref: '#/components/schemas/RequestStatus' diff --git a/settings.gradle.kts b/settings.gradle.kts index 1c15fd6fe..e8da34bd6 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -22,19 +22,19 @@ dependencyResolutionManagement { mavenLocal() } } - -include(":core:identity-hub") -include(":core:identity-hub-client") -include(":core:identity-hub-credentials-verifier") -include(":extensions:credentials:identity-hub-credentials-jwt") -include(":extensions:identity-hub-api") -include(":extensions:identity-hub-verifier-jwt") -include(":extensions:self-description-api") -include(":extensions:store:sql:identity-hub-store-sql") -include(":identity-hub-cli") -include(":launcher") -include(":spi:identity-hub-client-spi") +// SPI modules include(":spi:identity-hub-spi") include(":spi:identity-hub-store-spi") -include(":system-tests") + +// core modules +include(":core:identity-hub-api") + + +// other modules +include(":launcher") +include(":e2e-tests") include(":version-catalog") +include("core:identity-hub-transform") + +// test modules +include(":e2e-tests:api-tests") diff --git a/spi/identity-hub-client-spi/src/main/java/org/eclipse/edc/identityhub/client/spi/IdentityHubClient.java b/spi/identity-hub-client-spi/src/main/java/org/eclipse/edc/identityhub/client/spi/IdentityHubClient.java deleted file mode 100644 index 32b91b02a..000000000 --- a/spi/identity-hub-client-spi/src/main/java/org/eclipse/edc/identityhub/client/spi/IdentityHubClient.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.client.spi; - -import org.eclipse.edc.identityhub.spi.credentials.model.CredentialEnvelope; -import org.eclipse.edc.spi.result.Result; - -import java.util.Collection; - -/** - * This client is used to call the IdentityHub endpoints in order query and write VerifiableCredentials. - * Eventually, this may be expanded to handle other types of objects and operations. - */ -public interface IdentityHubClient { - - /** - * Get VerifiableCredentials provided by an Identity Hub instance. - * - * @param hubBaseUrl Base URL of the IdentityHub instance. - * @return result containing VerifiableCredentials if request successful. - */ - Result> getVerifiableCredentials(String hubBaseUrl); - - /** - * Write a VerifiableCredential. - * - * @param hubBaseUrl Base URL of the IdentityHub instance. - * @param verifiableCredential A verifiable credential to be saved. - * @return result. - */ - Result addVerifiableCredential(String hubBaseUrl, CredentialEnvelope verifiableCredential); -} diff --git a/spi/identity-hub-spi/build.gradle.kts b/spi/identity-hub-spi/build.gradle.kts index 1237692b1..a9663e4ae 100644 --- a/spi/identity-hub-spi/build.gradle.kts +++ b/spi/identity-hub-spi/build.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Microsoft Corporation + * 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 @@ -8,7 +8,7 @@ * SPDX-License-Identifier: Apache-2.0 * * Contributors: - * Microsoft Corporation - initial implementation + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation * */ @@ -22,6 +22,7 @@ val swagger: String by project dependencies { + api(libs.edc.spi.identitytrust) implementation(libs.jackson.databind) implementation(libs.nimbus.jwt) implementation(libs.edc.spi.identity.did) diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/model/Credential.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/model/Credential.java deleted file mode 100644 index c18bf8952..000000000 --- a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/model/Credential.java +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright (c) 2023 Amadeus - * - * 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: - * Amadeus - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.spi.credentials.model; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonFormat; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; -import org.eclipse.edc.spi.EdcException; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Objects; - -import static com.fasterxml.jackson.annotation.JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY; - -/** - * Represents a credential defined at W3C specification. - */ -@JsonInclude(JsonInclude.Include.NON_EMPTY) -@JsonDeserialize(builder = Credential.Builder.class) -public class Credential { - - private static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'"; - private static final String JSON_PROP_CONTEXTS = "@context"; - private static final String JSON_PROP_TYPES = "type"; - - @JsonProperty(JSON_PROP_CONTEXTS) - @JsonFormat(with = ACCEPT_SINGLE_VALUE_AS_ARRAY) - private final List contexts = new ArrayList<>(); - - private String id; - - @JsonProperty(JSON_PROP_TYPES) - @JsonFormat(with = ACCEPT_SINGLE_VALUE_AS_ARRAY) - private final List types = new ArrayList<>(); - - private String issuer; - - private CredentialSubject credentialSubject; - - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = DATE_FORMAT) - private Date issuanceDate; - - @Nullable - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = DATE_FORMAT) - private Date expirationDate; - - private CredentialStatus credentialStatus; - - protected Credential() { - } - - /** - * Returns the contexts. - * - * @return contexts - * @see Contexts - */ - @NotNull - public List getContexts() { - return contexts; - } - - /** - * Returns the credential id. - * - * @return id - * @see Identifier - */ - @NotNull - public String getId() { - return id; - } - - /** - * Returns the types. - * - * @return types - * @see Types - */ - @NotNull - public List getTypes() { - return types; - } - - /** - * Returns the issuer. - * - * @return issuer - * @see Issuer - */ - @NotNull - public String getIssuer() { - return issuer; - } - - /** - * Returns the credential claims. - * - * @return credential subject - * @see Credential subject - */ - @NotNull - public CredentialSubject getCredentialSubject() { - return credentialSubject; - } - - /** - * Returns the issuance date of the credentia. - * - * @return issuance date - * @see Issuance date - */ - @NotNull - public Date getIssuanceDate() { - return issuanceDate; - } - - /** - * Returns the expiration date of the credential. - * - * @return expiration date - * @see Expiration - */ - @Nullable - public Date getExpirationDate() { - return expirationDate; - } - - /** - * Returns the status of the credential. - * - * @return credential status - * @see Credential status - */ - @Nullable - public CredentialStatus getCredentialStatus() { - return credentialStatus; - } - - @JsonPOJOBuilder(withPrefix = "") - public static class Builder { - private final Credential credential; - - private Builder(Credential credential) { - this.credential = credential; - } - - @JsonCreator - public static Builder newInstance() { - return new Builder(new Credential()); - } - - public Builder context(String context) { - credential.contexts.add(context); - return this; - } - - @JsonProperty(JSON_PROP_CONTEXTS) - public Builder contexts(List contexts) { - credential.contexts.addAll(contexts); - return this; - } - - public Builder id(String id) { - credential.id = id; - return this; - } - - @JsonProperty(JSON_PROP_TYPES) - public Builder types(List types) { - credential.types.addAll(types); - return this; - } - - public Builder type(String type) { - credential.types.add(type); - return this; - } - - public Builder issuer(String issuer) { - credential.issuer = issuer; - return this; - } - - public Builder credentialSubject(CredentialSubject credentialSubject) { - credential.credentialSubject = credentialSubject; - return this; - } - - public Builder issuanceDate(Date issuanceDate) { - credential.issuanceDate = issuanceDate; - return this; - } - - public Builder expirationDate(Date expirationDate) { - credential.expirationDate = expirationDate; - return this; - } - - public Builder credentialStatus(CredentialStatus credentialStatus) { - credential.credentialStatus = credentialStatus; - return this; - } - - public Credential build() { - if (credential.contexts.isEmpty()) { - throw new EdcException("Credential must have at least one context."); - } - Objects.requireNonNull(credential.issuer, "Credential must contain `issuer` property."); - Objects.requireNonNull(credential.id, "Credential must contain `id` property."); - Objects.requireNonNull(credential.credentialSubject, "Credential must contain `credentialSubject` property."); - Objects.requireNonNull(credential.issuanceDate, "Credential must contain `issuanceDate` property."); - return credential; - } - } -} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/model/CredentialEnvelope.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/model/CredentialEnvelope.java deleted file mode 100644 index 5f20be591..000000000 --- a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/model/CredentialEnvelope.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.spi.credentials.model; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.eclipse.edc.spi.result.Result; - -import java.util.List; - -/** - * The {@link CredentialEnvelope} it's used to wrap an implementation of verifiable credential with a given format. - */ -public interface CredentialEnvelope { - - /** - * Returns the Media type that implementor of {@link CredentialEnvelope} is able to validate. - */ - String format(); - - /** - * Convert the content of {@link CredentialEnvelope} to {@link VerifiableCredential}. - * - * @param mapper The json mapper. - * @return The result of the conversion process - */ - Result> toVerifiableCredentials(ObjectMapper mapper); - -} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/model/CredentialStatus.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/model/CredentialStatus.java deleted file mode 100644 index 3d3b5ff6a..000000000 --- a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/model/CredentialStatus.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2023 Amadeus - * - * 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: - * Amadeus - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.spi.credentials.model; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; -import org.jetbrains.annotations.NotNull; - -import java.util.Objects; - -/** - * Credential status as defined in W3C specification. - */ -public class CredentialStatus { - - private String id; - private String type; - - private CredentialStatus() { - } - - @NotNull - public String getId() { - return id; - } - - @NotNull - public String getType() { - return type; - } - - - @JsonPOJOBuilder(withPrefix = "") - public static final class Builder { - CredentialStatus status; - - private Builder() { - status = new CredentialStatus(); - } - - @JsonCreator - public static CredentialStatus.Builder newInstance() { - return new CredentialStatus.Builder(); - } - - public CredentialStatus.Builder id(String id) { - status.id = id; - return this; - } - - public CredentialStatus.Builder type(String type) { - status.type = type; - return this; - } - - public CredentialStatus build() { - Objects.requireNonNull(status.id, "CredentialStatus must contain `id` property."); - Objects.requireNonNull(status.type, "CredentialStatus must contain `type` property."); - return status; - } - } -} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/model/CredentialSubject.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/model/CredentialSubject.java deleted file mode 100644 index 05a2837e7..000000000 --- a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/model/CredentialSubject.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2023 Amadeus - * - * 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: - * Amadeus - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.spi.credentials.model; - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; -import org.jetbrains.annotations.NotNull; - -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; - -/** - * Credential subject as defined in W3C specification. - */ -@JsonDeserialize(builder = CredentialSubject.Builder.class) -public class CredentialSubject { - - private String id; - private final Map claims = new HashMap<>(); - - private CredentialSubject() { - } - - @NotNull - public String getId() { - return id; - } - - @JsonAnyGetter - public Map getClaims() { - return claims; - } - - @JsonPOJOBuilder(withPrefix = "") - public static final class Builder { - CredentialSubject subject; - - private Builder() { - subject = new CredentialSubject(); - } - - @JsonCreator - public static CredentialSubject.Builder newInstance() { - return new CredentialSubject.Builder(); - } - - public CredentialSubject.Builder id(String id) { - subject.id = id; - return this; - } - - @JsonAnySetter - public CredentialSubject.Builder claim(String key, Object value) { - subject.claims.put(key, value); - return this; - } - - public CredentialSubject build() { - Objects.requireNonNull(subject.id, "CredentialSubject must contain `id` property."); - return subject; - } - } -} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/model/Proof.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/model/Proof.java deleted file mode 100644 index 13ebc07fd..000000000 --- a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/model/Proof.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (c) 2023 Amadeus - * - * 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: - * Amadeus - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.spi.credentials.model; - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonFormat; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; -import org.jetbrains.annotations.NotNull; - -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; - -/** - * Proof as designed in W3C specification. - */ -@JsonDeserialize(builder = Proof.Builder.class) -public class Proof { - - private static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'"; - - private String type; - - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = DATE_FORMAT) - private Date created; - - private String verificationMethod; - - private String proofPurpose; - - private final Map extensions = new HashMap<>(); - - private Proof() { - } - - @NotNull - public String getType() { - return type; - } - - @NotNull - public Date getCreated() { - return created; - } - - @NotNull - public String getVerificationMethod() { - return verificationMethod; - } - - @NotNull - public String getProofPurpose() { - return proofPurpose; - } - - @JsonAnyGetter - public Map getExtensions() { - return extensions; - } - - @JsonPOJOBuilder(withPrefix = "") - public static final class Builder { - Proof proof; - - private Builder() { - proof = new Proof(); - } - - @JsonCreator - public static Proof.Builder newInstance() { - return new Proof.Builder(); - } - - public Proof.Builder type(String type) { - proof.type = type; - return this; - } - - public Proof.Builder created(Date created) { - proof.created = created; - return this; - } - - public Proof.Builder verificationMethod(String verificationMethod) { - proof.verificationMethod = verificationMethod; - return this; - } - - public Proof.Builder proofPurpose(String proofPurpose) { - proof.proofPurpose = proofPurpose; - return this; - } - - @JsonAnySetter - public Proof.Builder extension(String key, String value) { - proof.extensions.put(key, value); - return this; - } - - public Proof build() { - Objects.requireNonNull(proof.type, "Proof must contain `type` property."); - Objects.requireNonNull(proof.created, "Proof must contain `created` property."); - Objects.requireNonNull(proof.verificationMethod, "Proof must contain `verificationMethod` property."); - Objects.requireNonNull(proof.proofPurpose, "Proof must contain `proofPurpose` property."); - return proof; - } - } -} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/model/Verifiable.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/model/Verifiable.java deleted file mode 100644 index 03e1685c4..000000000 --- a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/model/Verifiable.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2023 Amadeus - * - * 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: - * Amadeus - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.spi.credentials.model; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonUnwrapped; - -/** - * Abstraction over a cryptographically-verifiable object. - */ -public abstract class Verifiable { - - @JsonUnwrapped - protected final T item; - - protected final Proof proof; - - protected Verifiable(T item, Proof proof) { - this.proof = proof; - this.item = item; - } - - @JsonCreator - protected Verifiable(@JsonProperty(value = "proof", required = true) Proof proof) { - this(null, proof); - } - - /** - * Returns the verifiable item. - * - * @return the verifiable item. - */ - public T getItem() { - return item; - } - - /** - * Returns the cryptographic proof. - * - * @return proof - * @see Proof - */ - public Proof getProof() { - return proof; - } -} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/model/VerifiableCredential.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/model/VerifiableCredential.java deleted file mode 100644 index 83ab3c695..000000000 --- a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/model/VerifiableCredential.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2023 Amadeus - * - * 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: - * Amadeus - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.spi.credentials.model; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.util.Objects; - -/** - * Represents a verifiable credential as defined in W3C specification. - */ -public class VerifiableCredential extends Verifiable { - - public static final String DEFAULT_CONTEXT = "https://www.w3.org/2018/credentials/v1"; - public static final String DEFAULT_TYPE = "VerifiableCredential"; - - @JsonCreator - public VerifiableCredential(@JsonProperty("proof") Proof proof) { - super(proof); - } - - public VerifiableCredential(Credential credential, Proof proof) { - super(credential, proof); - } - - @JsonIgnore - public boolean isValid() { - Objects.requireNonNull(item, "Credential cannot be null."); - return item.getContexts().contains(DEFAULT_CONTEXT) && item.getTypes().contains(DEFAULT_TYPE); - } -} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/transformer/CredentialEnvelopeTransformer.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/transformer/CredentialEnvelopeTransformer.java deleted file mode 100644 index 4abd7a4f6..000000000 --- a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/transformer/CredentialEnvelopeTransformer.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.spi.credentials.transformer; - -import org.eclipse.edc.identityhub.spi.credentials.model.CredentialEnvelope; -import org.eclipse.edc.identityhub.spi.processor.MessageProcessor; -import org.eclipse.edc.spi.result.Result; - -/** - * The {@link MessageProcessor} is able to process messages that could contain data. If data available in the current implementation - * we want to be sure that the data is in the correct format based on the Media type. - */ -public interface CredentialEnvelopeTransformer { - - - /** - * Validate and parse the input data into a {@link CredentialEnvelope} - * - * @param data Input - * @return The result of the validation - */ - Result parse(byte[] data); - - - /** - * Serialize the {@link CredentialEnvelope} into byte array - * - * @param envelope The input envelope - * @return The result of the serialization - */ - Result serialize(T envelope); - - - /** - * Returns the Media type that implementor of {@link CredentialEnvelopeTransformer} is able to validate. - */ - String dataFormat(); - -} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/transformer/CredentialEnvelopeTransformerRegistry.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/transformer/CredentialEnvelopeTransformerRegistry.java deleted file mode 100644 index b7eeca97b..000000000 --- a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/transformer/CredentialEnvelopeTransformerRegistry.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.spi.credentials.transformer; - -import org.eclipse.edc.runtime.metamodel.annotation.ExtensionPoint; -import org.jetbrains.annotations.Nullable; - -/** - * Contains a set of validators that will be applied based on dataFormat when receiving a CollectionWrite request - */ - -@ExtensionPoint -public interface CredentialEnvelopeTransformerRegistry { - - /** - * Register a {@link CredentialEnvelopeTransformer} into the registry, and it will be associated to {@link CredentialEnvelopeTransformer#dataFormat} Media type - * - * @param transformer The transformer - */ - void register(CredentialEnvelopeTransformer transformer); - - - /** - * Returns a validator associated to the input dateFormat. If not present returns null - * - * @param dataFormat The input dataFormat - * @return {@link CredentialEnvelopeTransformer} - */ - @Nullable - CredentialEnvelopeTransformer resolve(String dataFormat); -} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/transformer/CredentialEnvelopeTransformerRegistryImpl.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/transformer/CredentialEnvelopeTransformerRegistryImpl.java deleted file mode 100644 index 54ef2c4c5..000000000 --- a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/transformer/CredentialEnvelopeTransformerRegistryImpl.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.spi.credentials.transformer; - -import java.util.HashMap; -import java.util.Map; - -public class CredentialEnvelopeTransformerRegistryImpl implements CredentialEnvelopeTransformerRegistry { - - - private final Map transformers = new HashMap<>(); - - - @Override - public void register(CredentialEnvelopeTransformer transformer) { - transformers.put(transformer.dataFormat(), transformer); - } - - @Override - public CredentialEnvelopeTransformer resolve(String dataFormat) { - return transformers.get(dataFormat); - } -} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/verifier/CredentialEnvelopeVerifier.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/verifier/CredentialEnvelopeVerifier.java deleted file mode 100644 index 80aabb50f..000000000 --- a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/verifier/CredentialEnvelopeVerifier.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.spi.credentials.verifier; - -import org.eclipse.edc.iam.did.spi.document.DidDocument; -import org.eclipse.edc.identityhub.spi.credentials.model.Credential; -import org.eclipse.edc.identityhub.spi.credentials.model.CredentialEnvelope; -import org.eclipse.edc.spi.result.Result; - -import java.util.List; - -/** - * Abstraction over the verification process on verifiable credential carried in a {@link CredentialEnvelope} - */ -public interface CredentialEnvelopeVerifier { - - Result> verify(T vc, DidDocument didDocument); -} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/verifier/CredentialEnvelopeVerifierRegistry.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/verifier/CredentialEnvelopeVerifierRegistry.java deleted file mode 100644 index 9d3c90139..000000000 --- a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/verifier/CredentialEnvelopeVerifierRegistry.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.spi.credentials.verifier; - -import org.eclipse.edc.runtime.metamodel.annotation.ExtensionPoint; - -/** - * Registry interface for {@link CredentialEnvelopeVerifier} for verifying credentials in {@link org.eclipse.edc.identityhub.spi.credentials.model.CredentialEnvelope} - * based on a given format. - */ -@ExtensionPoint -public interface CredentialEnvelopeVerifierRegistry { - - - void register(String format, CredentialEnvelopeVerifier verifier); - - CredentialEnvelopeVerifier resolve(String format); - -} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/verifier/CredentialEnvelopeVerifierRegistryImpl.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/verifier/CredentialEnvelopeVerifierRegistryImpl.java deleted file mode 100644 index 91a97eccb..000000000 --- a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/credentials/verifier/CredentialEnvelopeVerifierRegistryImpl.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2023 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.spi.credentials.verifier; - - -import org.jetbrains.annotations.Nullable; - -import java.util.HashMap; -import java.util.Map; - -/** - * Default implementation for {@link CredentialEnvelopeVerifierRegistry} - */ -public class CredentialEnvelopeVerifierRegistryImpl implements CredentialEnvelopeVerifierRegistry { - - private final Map registry = new HashMap<>(); - - @Override - public void register(String format, CredentialEnvelopeVerifier verifier) { - registry.put(format, verifier); - } - - @Override - @Nullable - public CredentialEnvelopeVerifier resolve(String format) { - return registry.get(format); - } -} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/generator/PresentationGenerator.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/generator/PresentationGenerator.java new file mode 100644 index 000000000..b2cfc64d4 --- /dev/null +++ b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/generator/PresentationGenerator.java @@ -0,0 +1,31 @@ +/* + * 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.edc.identityhub.spi.generator; + +import org.eclipse.edc.identityhub.spi.model.PresentationResponse; +import org.eclipse.edc.identityhub.spi.model.presentationdefinition.PresentationDefinition; +import org.eclipse.edc.identitytrust.model.VerifiableCredentialContainer; +import org.eclipse.edc.spi.result.Result; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * Represents a Presentation Generator that creates a presentation based on a list of verifiable credentials + * and an optional presentation definition. + */ +public interface PresentationGenerator { + Result createPresentation(List credentials, @Nullable PresentationDefinition presentationDefinition); +} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/Descriptor.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/Descriptor.java deleted file mode 100644 index e8b0e77e5..000000000 --- a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/Descriptor.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.spi.model; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; -import io.swagger.v3.oas.annotations.media.Schema; - -import java.util.Objects; - -/** - * See message descriptor documentation. - */ -@JsonDeserialize(builder = Descriptor.Builder.class) -public class Descriptor { - private String method; - private long dateCreated; - private String recordId; - private String dataCid; - private String dataFormat; - - private Descriptor() { - } - - @Schema(description = "A string that matches a Decentralized Web Node Interface method") - public String getMethod() { - return method; - } - - @Schema(description = "Unique identifier of the record") - public String getRecordId() { - return recordId; - } - - @Schema(description = "Unix epoch timestamp interpreted as the time the logical entry was created by the DID owner or another permitted party") - public long getDateCreated() { - return dateCreated; - } - - @Schema(description = "[UNSUPPORTED] If data is available, this field should contain stringified Version 1 CID of the DAG PB encoded data") - public String getDataCid() { - return dataCid; - } - - @Schema(description = "if data is available, this field should contain a registered IANA Media Type data format. Use 'application/vc+ldp' for Verifiable Credentials.") - public String getDataFormat() { - return dataFormat; - } - - @JsonPOJOBuilder(withPrefix = "") - public static class Builder { - - private final Descriptor descriptor; - - private Builder() { - descriptor = new Descriptor(); - } - - @JsonCreator() - public static Builder newInstance() { - return new Builder(); - } - - public Builder method(String method) { - descriptor.method = method; - return this; - } - - - public Builder dateCreated(long dateCreated) { - descriptor.dateCreated = dateCreated; - return this; - } - - public Builder recordId(String recordId) { - descriptor.recordId = recordId; - return this; - } - - public Builder dataCid(String dataCid) { - descriptor.dataCid = dataCid; - return this; - } - - public Builder dataFormat(String dataFormat) { - descriptor.dataFormat = dataFormat; - return this; - } - - public Descriptor build() { - Objects.requireNonNull(descriptor.method, "Descriptor must contain method property."); - return descriptor; - } - } -} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/FeatureDetection.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/FeatureDetection.java deleted file mode 100644 index e6b3c707f..000000000 --- a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/FeatureDetection.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.spi.model; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; - -/** - * Feature Detection object produced by a compliant decentralized Web Node. - * See: Feature detection identity foundation documentation. - */ -@JsonDeserialize(builder = FeatureDetection.Builder.class) -public class FeatureDetection { - - private static final String TYPE = "FeatureDetection"; - private WebNodeInterfaces interfaces; - - private FeatureDetection() { - } - - public String getType() { - return TYPE; - } - - public WebNodeInterfaces getInterfaces() { - return interfaces; - } - - @JsonPOJOBuilder(withPrefix = "") - public static class Builder { - private final FeatureDetection featureDetection; - - public Builder() { - featureDetection = new FeatureDetection(); - } - - @JsonCreator() - public static FeatureDetection.Builder newInstance() { - return new FeatureDetection.Builder(); - } - - public FeatureDetection.Builder interfaces(WebNodeInterfaces interfaces) { - featureDetection.interfaces = interfaces; - return this; - } - - public FeatureDetection build() { - return featureDetection; - } - - } -} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/IdentityHubConstants.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/IdentityHubConstants.java new file mode 100644 index 000000000..e9bcb1def --- /dev/null +++ b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/IdentityHubConstants.java @@ -0,0 +1,22 @@ +/* + * 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.edc.identityhub.spi.model; + +public interface IdentityHubConstants { + String IATP_PREFIX = "https://w3id.org/tractusx-trust/v0.8/"; + String IATP_CONTEXT_URL = "https://w3id.org/tractusx-trust/v0.8"; + String PRESENTATION_EXCHANGE_URL = "https://identity.foundation/presentation-exchange/submission/v1"; + +} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/InputDescriptorMapping.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/InputDescriptorMapping.java new file mode 100644 index 000000000..ec4bf9f1d --- /dev/null +++ b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/InputDescriptorMapping.java @@ -0,0 +1,21 @@ +/* + * 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.edc.identityhub.spi.model; + +/** + * Represents the {@code descriptor_map} of a Presentation Submission + */ +public record InputDescriptorMapping(String id, String format, String path) { +} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/MessageRequestObject.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/MessageRequestObject.java deleted file mode 100644 index 9bac775b1..000000000 --- a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/MessageRequestObject.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.spi.model; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; -import io.swagger.v3.oas.annotations.media.Schema; - -import java.util.Objects; - -/** - * See Message documentation - */ -@JsonDeserialize(builder = MessageRequestObject.Builder.class) -public class MessageRequestObject { - private Descriptor descriptor; - private byte[] data; - - private MessageRequestObject() { - } - - public Descriptor getDescriptor() { - return descriptor; - } - - @Schema(description = "Optional base64Url encoded string of the message data") - public byte[] getData() { - return data; - } - - @JsonPOJOBuilder(withPrefix = "") - public static class Builder { - private final MessageRequestObject messageRequestObject; - - private Builder() { - this(new MessageRequestObject()); - } - - private Builder(MessageRequestObject messageRequestObject) { - this.messageRequestObject = messageRequestObject; - } - - @JsonCreator() - public static MessageRequestObject.Builder newInstance() { - return new MessageRequestObject.Builder(); - } - - public MessageRequestObject.Builder descriptor(Descriptor descriptor) { - messageRequestObject.descriptor = descriptor; - return this; - } - - public MessageRequestObject.Builder data(byte[] data) { - messageRequestObject.data = data == null ? null : data.clone(); - return this; - } - - - public MessageRequestObject build() { - Objects.requireNonNull(messageRequestObject.getDescriptor(), "MessageRequestObject must contain a descriptor property."); - return messageRequestObject; - } - } -} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/MessageResponseObject.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/MessageResponseObject.java deleted file mode 100644 index b42bb1ca9..000000000 --- a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/MessageResponseObject.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.spi.model; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; -import io.swagger.v3.oas.annotations.media.Schema; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Objects; - -/** - * MessageResponseObject are objects in the replies of a Response Object . - * See message documentation. - */ -@JsonDeserialize(builder = MessageResponseObject.Builder.class) -public class MessageResponseObject { - - private MessageStatus status; - private Collection entries = new ArrayList<>(); - - private MessageResponseObject() { - } - - - public MessageStatus getStatus() { - return status; - } - - @Schema(description = "Resulting message entries returned from the invocation of the corresponding message as free form objects") - public Collection getEntries() { - return entries; - } - - @JsonPOJOBuilder(withPrefix = "") - public static final class Builder { - private final MessageResponseObject messageResponseObject; - - private Builder() { - messageResponseObject = new MessageResponseObject(); - } - - @JsonCreator() - public static Builder newInstance() { - return new Builder(); - } - - - public Builder status(MessageStatus status) { - messageResponseObject.status = status; - return this; - } - - public Builder entries(Collection entries) { - messageResponseObject.entries = Collections.unmodifiableCollection(entries); - return this; - } - - public MessageResponseObject build() { - Objects.requireNonNull(messageResponseObject.status, "MessageResponseObject must contain status property."); - return messageResponseObject; - } - } -} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/MessageStatus.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/MessageStatus.java deleted file mode 100644 index 2b080300a..000000000 --- a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/MessageStatus.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.spi.model; - -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * See response objects documentation - * and status doc. - */ -public class MessageStatus extends Status { - public static final MessageStatus OK = new MessageStatus(200, "The message was successfully processed"); - public static final MessageStatus MALFORMED_MESSAGE = new MessageStatus(400, "The message was malformed or improperly constructed"); - public static final MessageStatus UNHANDLED_ERROR = new MessageStatus(500, "The request could not be processed correctly"); - public static final MessageStatus INTERFACE_NOT_IMPLEMENTED = new MessageStatus(501, "The interface method is not implemented"); - - private MessageStatus(@JsonProperty("code") int code, @JsonProperty("detail") String detail) { - super(code, detail); - } -} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/PresentationQuery.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/PresentationQuery.java new file mode 100644 index 000000000..c5bbf1af0 --- /dev/null +++ b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/PresentationQuery.java @@ -0,0 +1,78 @@ +/* + * 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.edc.identityhub.spi.model; + +import org.eclipse.edc.identityhub.spi.model.presentationdefinition.PresentationDefinition; + +import java.util.ArrayList; +import java.util.List; + +import static org.eclipse.edc.identityhub.spi.model.IdentityHubConstants.IATP_PREFIX; + + +/** + * Represents a query that is sent to a CredentialService. + * + * @see IATP Specification + */ +public class PresentationQuery { + public static final String PRESENTATION_QUERY_SCOPE_PROPERTY = IATP_PREFIX + "scope"; + public static final String PRESENTATION_QUERY_DEFINITION_PROPERTY = IATP_PREFIX + "presentation_definition"; + public static final String PRESENTATION_QUERY_TYPE_PROPERTY = IATP_PREFIX + "Query"; + private List scopes = new ArrayList<>(); + private PresentationDefinition presentationDefinition; + + private PresentationQuery() { + } + + public List getScopes() { + return scopes; + } + + public PresentationDefinition getPresentationDefinition() { + return presentationDefinition; + } + + public static final class Builder { + private final PresentationQuery query; + + private Builder() { + query = new PresentationQuery(); + } + + public static Builder newinstance() { + return new Builder(); + } + + public Builder scopes(List scopes) { + this.query.scopes = scopes; + return this; + } + + public Builder scope(String scope) { + this.query.scopes.add(scope); + return this; + } + + public Builder presentationDefinition(PresentationDefinition presentationDefinition) { + this.query.presentationDefinition = presentationDefinition; + return this; + } + + public PresentationQuery build() { + return query; + } + } +} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/PresentationResponse.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/PresentationResponse.java new file mode 100644 index 000000000..6dd2610ac --- /dev/null +++ b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/PresentationResponse.java @@ -0,0 +1,22 @@ +/* + * 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.edc.identityhub.spi.model; + +/** + * A representation of a Presentation Response + * that the credential service sends back to the requester. + */ +public record PresentationResponse(String vpToken, PresentationSubmission presentationSubmission) { +} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/PresentationSubmission.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/PresentationSubmission.java new file mode 100644 index 000000000..b3b1fa4bb --- /dev/null +++ b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/PresentationSubmission.java @@ -0,0 +1,27 @@ +/* + * 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.edc.identityhub.spi.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +/** + * Representation of a DIF Presentation Submission. + */ +public record PresentationSubmission(@JsonProperty("id") String id, + @JsonProperty("definition_id") String definitionId, + @JsonProperty("descriptor_map") List descriptorMap) { +} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/Record.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/Record.java deleted file mode 100644 index fc19de7ad..000000000 --- a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/Record.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.spi.model; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; - -import java.util.Objects; - -/** - * The {@link Record} is an object produced by CollectionQuery interface - */ -@JsonDeserialize(builder = Record.Builder.class) -public class Record { - - private String id; - private byte[] data; - - private String dataFormat; - - private long createdAt; - - private Record() { - - } - - public byte[] getData() { - return data; - } - - public long getCreatedAt() { - return createdAt; - } - - public String getDataFormat() { - return dataFormat; - } - - public String getId() { - return id; - } - - @JsonPOJOBuilder(withPrefix = "") - public static final class Builder { - - private final Record record; - - private Builder() { - record = new Record(); - } - - @JsonCreator() - public static Record.Builder newInstance() { - return new Record.Builder(); - } - - public Builder id(String id) { - record.id = id; - return this; - } - - public Builder data(byte[] data) { - record.data = data; - return this; - } - - public Builder dataFormat(String dataFormat) { - record.dataFormat = dataFormat; - return this; - } - - public Builder createdAt(long createdAt) { - record.createdAt = createdAt; - return this; - } - - public Record build() { - Objects.requireNonNull(record.id, "RecordId cannot be null"); - return record; - } - } -} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/RequestObject.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/RequestObject.java deleted file mode 100644 index e91f3ff75..000000000 --- a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/RequestObject.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.spi.model; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; - -import java.util.Collection; -import java.util.Collections; - -/** - * See Request Object documentation - */ -@JsonDeserialize(builder = RequestObject.Builder.class) -public class RequestObject { - - private Collection messages; - - private RequestObject() { - } - - public Collection getMessages() { - return messages; - } - - @JsonPOJOBuilder(withPrefix = "") - public static class Builder { - private final RequestObject requestObject; - - private Builder() { - this(new RequestObject()); - } - - private Builder(RequestObject requestObject) { - this.requestObject = requestObject; - } - - @JsonCreator() - public static RequestObject.Builder newInstance() { - return new RequestObject.Builder(); - } - - public RequestObject.Builder messages(Collection messages) { - requestObject.messages = Collections.unmodifiableCollection(messages); - return this; - } - - public RequestObject build() { - return requestObject; - } - } -} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/RequestStatus.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/RequestStatus.java deleted file mode 100644 index 556e3b975..000000000 --- a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/RequestStatus.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.spi.model; - -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * See response objects documentation - * and status doc. - */ -public class RequestStatus extends Status { - public static final RequestStatus OK = new RequestStatus(200, "The request was successfully processed"); - public static final RequestStatus DID_NOT_FOUND = new RequestStatus(404, "Target DID not found within the Decentralized Web Node"); - public static final RequestStatus ERROR = new RequestStatus(500, "The request could not be processed correctly"); - - private RequestStatus(@JsonProperty("code") int code, @JsonProperty("detail") String detail) { - super(code, detail); - } -} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/ResponseObject.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/ResponseObject.java deleted file mode 100644 index 3b634698c..000000000 --- a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/ResponseObject.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.spi.model; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -/** - * See Response Object documentation - */ -@JsonDeserialize(builder = ResponseObject.Builder.class) -public class ResponseObject { - private RequestStatus status; - private Collection replies = new ArrayList<>(); - - private ResponseObject() { - } - - public RequestStatus getStatus() { - return status; - } - - public Collection getReplies() { - return replies; - } - - @JsonPOJOBuilder(withPrefix = "") - public static final class Builder { - ResponseObject responseObject; - - private Builder() { - responseObject = new ResponseObject(); - } - - @JsonCreator - public static Builder newInstance() { - return new Builder(); - } - - - public Builder status(RequestStatus status) { - responseObject.status = status; - return this; - } - - public Builder replies(List replies) { - responseObject.replies = Collections.unmodifiableCollection(replies); - return this; - } - - public ResponseObject build() { - return responseObject; - } - } -} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/Status.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/Status.java deleted file mode 100644 index c9da3711f..000000000 --- a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/Status.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.spi.model; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; - -/** - * Describes status of the request done by calling the identity-hub endpoint. - */ -public abstract class Status { - private final int code; - private final String detail; - - Status(int code, String detail) { - this.code = code; - this.detail = detail; - } - - @Schema(description = "An integer set to the HTTP Status Code appropriate for the status of the response") - public int getCode() { - return code; - } - - @Schema(description = "A string that describes a terse summary of the status") - public String getDetail() { - return detail; - } - - - @JsonIgnore - public boolean isSuccess() { - return code >= 200 && code < 300; - } -} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/WebNodeInterfaceMethod.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/WebNodeInterfaceMethod.java deleted file mode 100644 index 4c3515419..000000000 --- a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/WebNodeInterfaceMethod.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.spi.model; - -import org.jetbrains.annotations.NotNull; - -import java.util.stream.Stream; - -/** - * Interfaces methods supported by a Web Node - * See: Web node interface identity foundation documentation. - */ -public enum WebNodeInterfaceMethod { - COLLECTIONS_QUERY("CollectionsQuery"), - COLLECTIONS_WRITE("CollectionsWrite"), - FEATURE_DETECTION_READ("FeatureDetectionRead"), - INVALID_METHOD("InvalidMethod"); - - private final String name; - - WebNodeInterfaceMethod(String name) { - this.name = name; - } - - @NotNull - public static WebNodeInterfaceMethod fromName(String name) { - return Stream.of(values()) - .filter(v -> v.name.equals(name)) - .findFirst() - .orElse(INVALID_METHOD); - } - - public String getName() { - return name; - } -} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/WebNodeInterfaces.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/WebNodeInterfaces.java deleted file mode 100644 index d9f41efa4..000000000 --- a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/WebNodeInterfaces.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.spi.model; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; - -import java.util.HashMap; -import java.util.Map; - -/** - * Interfaces supported by a Web Node - * See: Web node interface identity foundation documentation. - */ -@JsonDeserialize(builder = WebNodeInterfaces.Builder.class) -public class WebNodeInterfaces { - - private final Map collections = new HashMap<>(); - // not supported interfaces ATM: - private final Map actions = new HashMap<>(); - private final Map permissions = new HashMap<>(); - private final Map messaging = new HashMap<>(); - - private WebNodeInterfaces() { - } - - public Map getCollections() { - return collections; - } - - public Map getActions() { - return actions; - } - - public Map getPermissions() { - return permissions; - } - - public Map getMessaging() { - return messaging; - } - - @JsonPOJOBuilder(withPrefix = "") - public static class Builder { - private final WebNodeInterfaces webNodeInterfaces; - - public Builder() { - webNodeInterfaces = new WebNodeInterfaces(); - } - - @JsonCreator() - public static WebNodeInterfaces.Builder newInstance() { - return new WebNodeInterfaces.Builder(); - } - - public WebNodeInterfaces.Builder supportedCollection(String collection) { - webNodeInterfaces.collections.put(collection, true); - return this; - } - - public WebNodeInterfaces build() { - return webNodeInterfaces; - } - - } -} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/presentationdefinition/Constraints.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/presentationdefinition/Constraints.java new file mode 100644 index 000000000..70772735c --- /dev/null +++ b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/presentationdefinition/Constraints.java @@ -0,0 +1,20 @@ +/* + * 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.edc.identityhub.spi.model.presentationdefinition; + +import java.util.List; + +public record Constraints(List fields) { +} \ No newline at end of file diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/presentationdefinition/Field.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/presentationdefinition/Field.java new file mode 100644 index 000000000..32bb2f98e --- /dev/null +++ b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/presentationdefinition/Field.java @@ -0,0 +1,94 @@ +/* + * 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.edc.identityhub.spi.model.presentationdefinition; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class Field { + private List paths = new ArrayList<>(); + private String id; + private String name; + private String purpose; + private FilterExpression expr; + + private Field() { + + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public String getPurpose() { + return purpose; + } + + public List getPaths() { + return paths; + } + + public FilterExpression getExpr() { + return expr; + } + + + public static final class Builder { + private final Field field; + + private Builder() { + field = new Field(); + } + + public static Builder newInstance() { + return new Builder(); + } + + public Builder paths(List paths) { + this.field.paths = paths; + return this; + } + + public Builder id(String id) { + this.field.id = id; + return this; + } + + public Builder name(String name) { + this.field.name = name; + return this; + } + + public Builder purpose(String purpose) { + this.field.purpose = purpose; + return this; + } + + public Builder expr(FilterExpression expr) { + this.field.expr = expr; + return this; + } + + public Field build() { + Objects.requireNonNull(field.paths, "Must contain a paths property."); + return field; + } + } +} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/presentationdefinition/FilterExpression.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/presentationdefinition/FilterExpression.java new file mode 100644 index 000000000..6a70d87eb --- /dev/null +++ b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/presentationdefinition/FilterExpression.java @@ -0,0 +1,18 @@ +/* + * 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.edc.identityhub.spi.model.presentationdefinition; + +public record FilterExpression(String type, String pattern) { +} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/presentationdefinition/Format.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/presentationdefinition/Format.java new file mode 100644 index 000000000..f6a23b506 --- /dev/null +++ b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/presentationdefinition/Format.java @@ -0,0 +1,18 @@ +/* + * 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.edc.identityhub.spi.model.presentationdefinition; + +public class Format { +} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/presentationdefinition/InputDescriptor.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/presentationdefinition/InputDescriptor.java new file mode 100644 index 000000000..2e4840709 --- /dev/null +++ b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/presentationdefinition/InputDescriptor.java @@ -0,0 +1,88 @@ +/* + * 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.edc.identityhub.spi.model.presentationdefinition; + +import java.util.Objects; + +public class InputDescriptor { + private String id; + private String name; + private String purpose; + private Format format; + private Constraints constraints; + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public String getPurpose() { + return purpose; + } + + public Format getFormat() { + return format; + } + + public Constraints getConstraints() { + return constraints; + } + + public static final class Builder { + private final InputDescriptor descriptor; + + private Builder() { + descriptor = new InputDescriptor(); + } + + public static Builder newInstance() { + return new Builder(); + } + + public Builder id(String id) { + this.descriptor.id = id; + return this; + } + + public Builder name(String name) { + this.descriptor.name = name; + return this; + } + + public Builder purpose(String purpose) { + this.descriptor.purpose = purpose; + return this; + } + + public Builder format(Format format) { + this.descriptor.format = format; + return this; + } + + public Builder constraints(Constraints constraints) { + this.descriptor.constraints = constraints; + return this; + } + + public InputDescriptor build() { + Objects.requireNonNull(descriptor.id, "InputDescriptor must have an ID."); + Objects.requireNonNull(descriptor.constraints, "InputDescriptor must have a Constraints object."); + return descriptor; + } + } +} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/presentationdefinition/PresentationDefinition.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/presentationdefinition/PresentationDefinition.java new file mode 100644 index 000000000..3346242d2 --- /dev/null +++ b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/model/presentationdefinition/PresentationDefinition.java @@ -0,0 +1,102 @@ +/* + * 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.edc.identityhub.spi.model.presentationdefinition; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; + +import java.util.List; +import java.util.Objects; + +/** + * Represents a DIF Presentation Definition + */ + +public class PresentationDefinition { + private String id; + private String name; + private String purpose; + @JsonProperty("input_descriptors") + private List inputDescriptors; + private Format format; + + private PresentationDefinition() { + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public String getPurpose() { + return purpose; + } + + public List getInputDescriptors() { + return inputDescriptors; + } + + public Format getFormat() { + return format; + } + + @JsonPOJOBuilder(withPrefix = "") + public static final class Builder { + private final PresentationDefinition presentationDefinition; + + private Builder() { + presentationDefinition = new PresentationDefinition(); + } + + @JsonCreator + public static Builder newInstance() { + return new Builder(); + } + + public Builder id(String id) { + this.presentationDefinition.id = id; + return this; + } + + public Builder name(String name) { + this.presentationDefinition.name = name; + return this; + } + + public Builder purpose(String purpose) { + this.presentationDefinition.purpose = purpose; + return this; + } + + public Builder inputDescriptors(List inputDescriptor) { + this.presentationDefinition.inputDescriptors = inputDescriptor; + return this; + } + + public Builder format(Format format) { + this.presentationDefinition.format = format; + return this; + } + + public PresentationDefinition build() { + Objects.requireNonNull(presentationDefinition.id, "PresentationDefinition must have an ID."); + return presentationDefinition; + } + } +} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/processor/MessageProcessor.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/processor/MessageProcessor.java deleted file mode 100644 index 44ecdd34a..000000000 --- a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/processor/MessageProcessor.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.spi.processor; - - -import org.eclipse.edc.identityhub.spi.model.MessageRequestObject; -import org.eclipse.edc.identityhub.spi.model.MessageResponseObject; -import org.eclipse.edc.identityhub.spi.model.WebNodeInterfaces; - -/** - *

- * Each implementor of the MessageProcessor interface handles a message of a different Decentralized Web Node Interface method. - * Refer to the spec for a list of interfaces available. - *

- * Messages may or may not contain additional data associated with it (when data is desired or required to be present for a given method invocation). - * The MessageProcessor gets handed over this data in case it is available, or null otherwise. - *

- * See {@link WebNodeInterfaces} for a list of currently supported interfaces. Currently the only supported interface that accepts data is "CollectionsWrite". - */ -@FunctionalInterface -public interface MessageProcessor { - - /** - * Processes a message - * - * @param requestObject Request object to be processed. - * @return MessageResponseObject - */ - MessageResponseObject process(MessageRequestObject requestObject); -} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/processor/MessageProcessorRegistry.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/processor/MessageProcessorRegistry.java deleted file mode 100644 index 23de4d552..000000000 --- a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/processor/MessageProcessorRegistry.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.spi.processor; - -import org.eclipse.edc.identityhub.spi.model.WebNodeInterfaceMethod; -import org.eclipse.edc.runtime.metamodel.annotation.ExtensionPoint; - -@ExtensionPoint -public interface MessageProcessorRegistry { - - void register(WebNodeInterfaceMethod method, MessageProcessor messageProcessor); - - MessageProcessor resolve(WebNodeInterfaceMethod method); - -} diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/resolution/CredentialQueryResolver.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/resolution/CredentialQueryResolver.java new file mode 100644 index 000000000..66fdd1493 --- /dev/null +++ b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/resolution/CredentialQueryResolver.java @@ -0,0 +1,37 @@ +/* + * 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.edc.identityhub.spi.resolution; + +import org.eclipse.edc.identityhub.spi.model.PresentationQuery; +import org.eclipse.edc.identitytrust.model.VerifiableCredentialContainer; +import org.eclipse.edc.spi.result.Result; + +import java.util.List; + +/** + * Resolves a list of {@link VerifiableCredentialContainer} objects based on an incoming {@link PresentationQuery} and a list of scope strings. + */ +public interface CredentialQueryResolver { + + /** + * Query method for fetching credentials. If this method returns a successful result, it will contain a list of {@link VerifiableCredentialContainer}. + * If a failure is returned, that means that the given query does not match the given issuer scopes, which would be equivalent to an unauthorized access (c.f. HTTP 403 error). + * The Result could also contain information about any errors or issues the occurred during the query execution. + * + * @param query The representation of the query to be executed. + * @param issuerScopes The list of issuer scopes to be considered during the query processing. + */ + Result> query(PresentationQuery query, List issuerScopes); +} \ No newline at end of file diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/verification/AccessTokenVerifier.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/verification/AccessTokenVerifier.java new file mode 100644 index 000000000..539e63feb --- /dev/null +++ b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/verification/AccessTokenVerifier.java @@ -0,0 +1,39 @@ +/* + * 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.edc.identityhub.spi.verification; + +import org.eclipse.edc.spi.result.Result; + +import java.util.List; + +/** + * The AccessTokenVerifier interface represents a verifier for Self-Issued JWT tokens. It takes a base64-encoded ID token. + */ +public interface AccessTokenVerifier { + /** + * Performs the verification on a self-issued ID token, asserting the following aspects: + *

    + *
  • iss == aud
  • + *
  • aud == the Verifiers own DID. In practice, this will be the DID of the participant agent (i.e. the connector)
  • + *
  • the token contains an {@code access_token} claim, and that it is also in JWT format
  • + *
  • access_token.sub == sub
  • + *
  • that the access_token contains >1 scope strings
  • + *
+ * + * @param token The token to be verified. Must be a JWT in base64 encoding. + * @return A {@code Result} containing a {@code List} of scope strings. + */ + Result> verify(String token); +} diff --git a/spi/identity-hub-spi/src/test/java/org/eclipse/edc/identityhub/spi/credentials/model/AbstractSerDeserTest.java b/spi/identity-hub-spi/src/test/java/org/eclipse/edc/identityhub/spi/credentials/model/AbstractSerDeserTest.java deleted file mode 100644 index 40b114e27..000000000 --- a/spi/identity-hub-spi/src/test/java/org/eclipse/edc/identityhub/spi/credentials/model/AbstractSerDeserTest.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2023 Amadeus - * - * 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: - * Amadeus - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.spi.credentials.model; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.eclipse.edc.spi.types.TypeManager; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -abstract class AbstractSerDeserTest { - - private static final ObjectMapper MAPPER = new TypeManager().getMapper(); - - @Test - void verifySerDes() throws JsonProcessingException { - var vc = getEntity(); - - var json = MAPPER.writeValueAsString(vc); - - assertNotNull(json); - - var deser = MAPPER.readValue(json, getClazz()); - assertThat(deser).usingRecursiveComparison().isEqualTo(vc); - } - - protected ObjectMapper getMapper() { - return MAPPER; - } - - protected abstract Class getClazz(); - - protected abstract T getEntity(); -} diff --git a/spi/identity-hub-spi/src/test/java/org/eclipse/edc/identityhub/spi/credentials/model/CredentialStatusTest.java b/spi/identity-hub-spi/src/test/java/org/eclipse/edc/identityhub/spi/credentials/model/CredentialStatusTest.java deleted file mode 100644 index 9c20e3423..000000000 --- a/spi/identity-hub-spi/src/test/java/org/eclipse/edc/identityhub/spi/credentials/model/CredentialStatusTest.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2023 Amadeus - * - * 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: - * Amadeus - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.spi.credentials.model; - -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThatNullPointerException; - -class CredentialStatusTest extends AbstractSerDeserTest { - - @Override - protected Class getClazz() { - return CredentialStatus.class; - } - - @Override - protected CredentialStatus getEntity() { - return CredentialStatus.Builder.newInstance() - .type("type") - .id("id") - .build(); - } - - @Test - void verifyIdMandatory() { - assertThatNullPointerException().isThrownBy(() -> CredentialStatus.Builder.newInstance().type("type").build()) - .withMessageContaining("`id`"); - } - - @Test - void verifyTypeMandatory() { - assertThatNullPointerException().isThrownBy(() -> CredentialStatus.Builder.newInstance().id("id").build()) - .withMessageContaining("`type`"); - } -} \ No newline at end of file diff --git a/spi/identity-hub-spi/src/test/java/org/eclipse/edc/identityhub/spi/credentials/model/CredentialSubjectTest.java b/spi/identity-hub-spi/src/test/java/org/eclipse/edc/identityhub/spi/credentials/model/CredentialSubjectTest.java deleted file mode 100644 index 70089a75e..000000000 --- a/spi/identity-hub-spi/src/test/java/org/eclipse/edc/identityhub/spi/credentials/model/CredentialSubjectTest.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2023 Amadeus - * - * 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: - * Amadeus - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.spi.credentials.model; - -import com.fasterxml.jackson.core.JsonProcessingException; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatNullPointerException; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -class CredentialSubjectTest extends AbstractSerDeserTest { - - @Override - protected Class getClazz() { - return CredentialSubject.class; - } - - @Override - protected CredentialSubject getEntity() { - return CredentialSubject.Builder.newInstance() - .id("id-test") - .claim("foo", "bar") - .claim("hello", "world") - .build(); - } - - @Test - void verifyIdMandatory() { - assertThatNullPointerException().isThrownBy(() -> CredentialSubject.Builder.newInstance().build()) - .withMessageContaining("`id`"); - } - - @Test - void verifySerializationFlattensClaims() throws JsonProcessingException { - var subject = CredentialSubject.Builder.newInstance() - .id("id-test") - .claim("foo", "bar") - .build(); - - var json = getMapper().writeValueAsString(subject); - assertNotNull(json); - - assertThat(json) - .contains("\"id\":\"id-test\"") - .contains("\"foo\":\"bar\"") - .doesNotContain("claims"); - } - - @Test - void verifySerializationIgnoresEmptyClaims() throws JsonProcessingException { - var subject = CredentialSubject.Builder.newInstance() - .id("id-test") - .build(); - - var json = getMapper().writeValueAsString(subject); - assertNotNull(json); - - assertThat(json).isEqualTo("{\"id\":\"id-test\"}"); - - var deser = getMapper().readValue(json, getClazz()); - assertThat(deser).usingRecursiveComparison().isEqualTo(subject); - } -} \ No newline at end of file diff --git a/spi/identity-hub-spi/src/test/java/org/eclipse/edc/identityhub/spi/credentials/model/CredentialTest.java b/spi/identity-hub-spi/src/test/java/org/eclipse/edc/identityhub/spi/credentials/model/CredentialTest.java deleted file mode 100644 index 282e66dd8..000000000 --- a/spi/identity-hub-spi/src/test/java/org/eclipse/edc/identityhub/spi/credentials/model/CredentialTest.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2023 Amadeus - * - * 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: - * Amadeus - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.spi.credentials.model; - -import com.fasterxml.jackson.core.JsonProcessingException; -import org.assertj.core.api.Assertions; -import org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil; -import org.eclipse.edc.spi.EdcException; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.assertThatNullPointerException; - -class CredentialTest extends AbstractSerDeserTest { - - @Override - protected Class getClazz() { - return Credential.class; - } - - @Override - protected Credential getEntity() { - return VerifiableCredentialTestUtil.generateCredential(); - } - - @Test - void verifyNullFieldNotSerialized() throws JsonProcessingException { - var vc = VerifiableCredentialTestUtil.generateCredential(); - - var json = getMapper().writeValueAsString(vc); - - Assertions.assertThat(json) - .doesNotContain("credentialStatus") - .doesNotContain("expirationDate"); - } - - @Test - void verifyAtLeastOneContext() { - assertThatExceptionOfType(EdcException.class).isThrownBy(() -> Credential.Builder.newInstance().build()) - .withMessageContaining("context"); - } - - @Test - void verifyIssuerMandatory() { - assertThatNullPointerException().isThrownBy(() -> Credential.Builder.newInstance() - .context("context") - .build()) - .withMessageContaining("`issuer`"); - } - - @Test - void verifyIdMandatory() { - assertThatNullPointerException().isThrownBy(() -> Credential.Builder.newInstance() - .context("context") - .issuer("issuer") - .build()) - .withMessageContaining("`id`"); - } - - @Test - void verifyCredentialSubjectMandatory() { - assertThatNullPointerException().isThrownBy(() -> Credential.Builder.newInstance() - .context("context") - .issuer("issuer") - .id("id") - .build()) - .withMessageContaining("`credentialSubject`"); - } - - @Test - void verifyIssuanceMandatoryMandatory() { - assertThatNullPointerException().isThrownBy(() -> Credential.Builder.newInstance() - .context("context") - .issuer("issuer") - .id("id") - .credentialSubject(CredentialSubject.Builder.newInstance().id("test").build()) - .build()) - .withMessageContaining("`issuanceDate`"); - } -} diff --git a/spi/identity-hub-spi/src/test/java/org/eclipse/edc/identityhub/spi/credentials/model/ProofTest.java b/spi/identity-hub-spi/src/test/java/org/eclipse/edc/identityhub/spi/credentials/model/ProofTest.java deleted file mode 100644 index ee001cd4d..000000000 --- a/spi/identity-hub-spi/src/test/java/org/eclipse/edc/identityhub/spi/credentials/model/ProofTest.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2023 Amadeus - * - * 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: - * Amadeus - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.spi.credentials.model; - -import org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil; -import org.junit.jupiter.api.Test; - -import java.util.Date; - -import static org.assertj.core.api.Assertions.assertThatNullPointerException; - -class ProofTest extends AbstractSerDeserTest { - - @Override - protected Class getClazz() { - return Proof.class; - } - - @Override - protected Proof getEntity() { - return VerifiableCredentialTestUtil.generateProof(); - } - - @Test - void verifyTypeMandatory() { - assertThatNullPointerException().isThrownBy(() -> Proof.Builder.newInstance().build()) - .withMessageContaining("`type`"); - } - - @Test - void verifyCreatedMandatory() { - assertThatNullPointerException().isThrownBy(() -> Proof.Builder.newInstance().type("type").build()) - .withMessageContaining("`created`"); - } - - @Test - void verifyVerificationMethodMandatory() { - assertThatNullPointerException().isThrownBy(() -> Proof.Builder.newInstance() - .type("type") - .created(new Date()) - .build()) - .withMessageContaining("`verificationMethod`"); - } - - @Test - void verifyProofPurposeMandatory() { - assertThatNullPointerException().isThrownBy(() -> Proof.Builder.newInstance() - .type("type") - .created(new Date()) - .verificationMethod("verificationMethod") - .build()) - .withMessageContaining("`proofPurpose`"); - } -} \ No newline at end of file diff --git a/spi/identity-hub-spi/src/test/java/org/eclipse/edc/identityhub/spi/credentials/model/RecordTest.java b/spi/identity-hub-spi/src/test/java/org/eclipse/edc/identityhub/spi/credentials/model/RecordTest.java deleted file mode 100644 index e9cf59c81..000000000 --- a/spi/identity-hub-spi/src/test/java/org/eclipse/edc/identityhub/spi/credentials/model/RecordTest.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2020 - 2022 Amadeus - * - * 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: - * Amadeus - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.spi.credentials.model; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.eclipse.edc.identityhub.spi.model.Record; -import org.junit.jupiter.api.Test; - -import java.util.UUID; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -class RecordTest { - - @Test - void verifySerDes() throws JsonProcessingException { - var mapper = new ObjectMapper(); - var record = Record.Builder.newInstance() - .id(UUID.randomUUID().toString()) - .data(new byte[]{}) - .createdAt(System.currentTimeMillis()) - .dataFormat("application/json") - .build(); - - var json = mapper.writeValueAsString(record); - - assertNotNull(json); - - var deser = mapper.readValue(json, Record.class); - assertThat(deser).usingRecursiveComparison().isEqualTo(record); - } -} diff --git a/spi/identity-hub-spi/src/test/java/org/eclipse/edc/identityhub/spi/credentials/model/VerifiableCredentialTest.java b/spi/identity-hub-spi/src/test/java/org/eclipse/edc/identityhub/spi/credentials/model/VerifiableCredentialTest.java deleted file mode 100644 index 0a723bf90..000000000 --- a/spi/identity-hub-spi/src/test/java/org/eclipse/edc/identityhub/spi/credentials/model/VerifiableCredentialTest.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2023 Amadeus - * - * 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: - * Amadeus - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.spi.credentials.model; - -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.sql.Date; -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.Objects; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil.generateProof; - -class VerifiableCredentialTest extends AbstractSerDeserTest { - - private static final String VC_FILE = "vc.json"; - - @Override - protected Class getClazz() { - return VerifiableCredential.class; - } - - @Override - protected VerifiableCredential getEntity() { - try (var is = getClass().getClassLoader().getResourceAsStream(VC_FILE)) { - Objects.requireNonNull(is, "Failed to open file: " + VC_FILE); - return getMapper().readValue(is, VerifiableCredential.class); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - @Test - void verifyVcIsValid() { - var credential = defaultCredential() - .type("VerifiableCredential") - .context("https://www.w3.org/2018/credentials/v1") - .build(); - - var vc = new VerifiableCredential(credential, generateProof()); - - assertThat(vc.isValid()).isTrue(); - } - - @Test - void verifyVcNotValidIfMissingDefaultContext() { - var credential = defaultCredential() - .context("unknown-context") - .type("VerifiableCredential") - .build(); - - var vc = new VerifiableCredential(credential, generateProof()); - - assertThat(vc.isValid()).isFalse(); - } - - @Test - void verifyVcNotValidIfMissingDefaultType() { - var credential = defaultCredential() - .context("https://www.w3.org/2018/credentials/v1") - .build(); - - var vc = new VerifiableCredential(credential, generateProof()); - - assertThat(vc.isValid()).isFalse(); - } - - private static Credential.Builder defaultCredential() { - return Credential.Builder.newInstance() - .id("test") - .issuer("issuer") - .issuanceDate(Date.from(Instant.now().truncatedTo(ChronoUnit.SECONDS))) - .credentialSubject(CredentialSubject.Builder.newInstance().id("test").build()); - } -} \ No newline at end of file diff --git a/spi/identity-hub-spi/src/test/java/org/eclipse/edc/identityhub/spi/model/PresentationSubmissionSerDesTest.java b/spi/identity-hub-spi/src/test/java/org/eclipse/edc/identityhub/spi/model/PresentationSubmissionSerDesTest.java new file mode 100644 index 000000000..5aade13e4 --- /dev/null +++ b/spi/identity-hub-spi/src/test/java/org/eclipse/edc/identityhub/spi/model/PresentationSubmissionSerDesTest.java @@ -0,0 +1,69 @@ +/* + * 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.edc.identityhub.spi.model; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class PresentationSubmissionSerDesTest { + private final ObjectMapper mapper = new ObjectMapper(); + + @Test + void verifyDeserialization() throws JsonProcessingException { + var json = """ + { + "id": "a30e3b91-fb77-4d22-95fa-871689c322e2", + "definition_id": "32f54163-7166-48f1-93d8-ff217bdb0653", + "descriptor_map": [ + { + "id": "banking_input_2", + "format": "jwt_vc", + "path": "$.verifiableCredential[0]" + }, + { + "id": "employment_input", + "format": "ldp_vc", + "path": "$.verifiableCredential[1]" + }, + { + "id": "citizenship_input_1", + "format": "ldp_vc", + "path": "$.verifiableCredential[2]" + } + ] + } + """; + var pd = mapper.readValue(json, PresentationSubmission.class); + assertThat(pd).isNotNull(); + + assertThat(pd.id()).isEqualTo("a30e3b91-fb77-4d22-95fa-871689c322e2"); + assertThat(pd.definitionId()).isEqualTo("32f54163-7166-48f1-93d8-ff217bdb0653"); + assertThat(pd.descriptorMap()).hasSize(3); + } + + @Test + void verifySerialization() throws JsonProcessingException { + var pd = new PresentationSubmission("test-id", "test-def-id", List.of(new InputDescriptorMapping("test-input", "ldp_vc", "$.verifiableCredentials[0]"))); + var json = mapper.writeValueAsString(pd); + + var deser = mapper.readValue(json, PresentationSubmission.class); + assertThat(deser).usingRecursiveComparison().isEqualTo(pd); + } +} \ No newline at end of file diff --git a/spi/identity-hub-spi/src/test/resources/vc.json b/spi/identity-hub-spi/src/test/resources/vc.json deleted file mode 100644 index 6e402e3b0..000000000 --- a/spi/identity-hub-spi/src/test/resources/vc.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://www.w3.org/2018/credentials/examples/v1" - ], - "id": "http://example.gov/credentials/3732", - "type": [ - "VerifiableCredential", - "UniversityDegreeCredential" - ], - "issuer": "https://example.edu", - "issuanceDate": "2010-01-01T19:23:24Z", - "credentialSubject": { - "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", - "degree": { - "type": "BachelorDegree", - "name": "Bachelor of Science and Arts" - } - }, - "proof": { - "type": "Ed25519Signature2020", - "created": "2021-11-13T18:19:39Z", - "verificationMethod": "https://example.edu/issuers/14#key-1", - "proofPurpose": "assertionMethod", - "proofValue": "z58DAdFfa9SkqZMVPxAQpic7ndSayn1PzZs6ZjWp1CktyGesjuTSwRdoWhAfGFCF5bppETSTojQCrfFPP2oumHKtz" - } -} \ No newline at end of file diff --git a/spi/identity-hub-spi/src/testFixtures/java/org/eclipse/edc/identityhub/junit/testfixtures/VerifiableCredentialTestUtil.java b/spi/identity-hub-spi/src/testFixtures/java/org/eclipse/edc/identityhub/junit/testfixtures/VerifiableCredentialTestUtil.java index 6f9da7356..7f850539d 100644 --- a/spi/identity-hub-spi/src/testFixtures/java/org/eclipse/edc/identityhub/junit/testfixtures/VerifiableCredentialTestUtil.java +++ b/spi/identity-hub-spi/src/testFixtures/java/org/eclipse/edc/identityhub/junit/testfixtures/VerifiableCredentialTestUtil.java @@ -25,16 +25,6 @@ import com.nimbusds.jose.jwk.gen.ECKeyGenerator; import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.SignedJWT; -import org.eclipse.edc.identityhub.spi.credentials.model.Credential; -import org.eclipse.edc.identityhub.spi.credentials.model.CredentialSubject; -import org.eclipse.edc.identityhub.spi.credentials.model.Proof; -import org.eclipse.edc.identityhub.spi.credentials.model.VerifiableCredential; - -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.Date; -import java.util.Map; -import java.util.UUID; /** * Util class to manipulate VerifiableCredentials in tests. @@ -47,29 +37,6 @@ public class VerifiableCredentialTestUtil { private VerifiableCredentialTestUtil() { } - public static Credential generateCredential() { - return Credential.Builder.newInstance() - .context(VerifiableCredential.DEFAULT_CONTEXT) - .id(UUID.randomUUID().toString()) - .issuer("issuer") - .issuanceDate(Date.from(Instant.now().truncatedTo(ChronoUnit.SECONDS))) - .type(VerifiableCredential.DEFAULT_TYPE) - .credentialSubject(CredentialSubject.Builder.newInstance() - .id("id-test") - .claim("cred1", UUID.randomUUID().toString()) - .claim("cred2", UUID.randomUUID().toString()) - .build()) - .build(); - } - - public static Proof generateProof() { - return Proof.Builder.newInstance() - .verificationMethod("verificationMethod") - .proofPurpose("proofPurpose") - .created(Date.from(Instant.now().truncatedTo(ChronoUnit.SECONDS))) - .type("type") - .build(); - } public static ECKey generateEcKey() { try { @@ -79,17 +46,6 @@ public static ECKey generateEcKey() { } } - public static SignedJWT buildSignedJwt(Credential credential, String issuer, String subject, ECKey jwk) { - var claims = new JWTClaimsSet.Builder() - .claim("vc", MAPPER.convertValue(credential, Map.class)) - .issuer(issuer) - .subject(subject) - .expirationTime(null) - .notBeforeTime(null) - .build(); - - return buildSignedJwt(claims, jwk); - } public static SignedJWT buildSignedJwt(JWTClaimsSet claims, ECKey jwk) { try { diff --git a/spi/identity-hub-store-spi/src/main/java/org/eclipse/edc/identityhub/store/spi/IdentityHubRecord.java b/spi/identity-hub-store-spi/src/main/java/org/eclipse/edc/identityhub/store/spi/IdentityHubRecord.java deleted file mode 100644 index ae2820fea..000000000 --- a/spi/identity-hub-store-spi/src/main/java/org/eclipse/edc/identityhub/store/spi/IdentityHubRecord.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2022 Amadeus - * - * 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: - * Amadeus - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.store.spi; - -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; -import org.eclipse.edc.spi.entity.Entity; - -import java.util.Objects; - -@JsonDeserialize(builder = IdentityHubRecord.Builder.class) -public class IdentityHubRecord extends Entity { - - private byte[] payload; - - private String payloadFormat; - - private IdentityHubRecord() { - } - - public byte[] getPayload() { - return payload; - } - - public String getPayloadFormat() { - return payloadFormat; - } - - @JsonPOJOBuilder(withPrefix = "") - public static class Builder extends Entity.Builder { - - protected Builder() { - super(new IdentityHubRecord()); - } - - public static Builder newInstance() { - return new Builder(); - } - - @Override - public Builder self() { - return this; - } - - @Override - public IdentityHubRecord build() { - Objects.requireNonNull(entity.id); - Objects.requireNonNull(entity.payload); - return super.build(); - } - - public Builder payload(byte[] payload) { - entity.payload = payload; - return self(); - } - - public Builder payloadFormat(String payloadFormat) { - entity.payloadFormat = payloadFormat; - return self(); - } - } -} diff --git a/spi/identity-hub-store-spi/src/main/java/org/eclipse/edc/identityhub/store/spi/IdentityHubStore.java b/spi/identity-hub-store-spi/src/main/java/org/eclipse/edc/identityhub/store/spi/IdentityHubStore.java deleted file mode 100644 index b8937ac6f..000000000 --- a/spi/identity-hub-store-spi/src/main/java/org/eclipse/edc/identityhub/store/spi/IdentityHubStore.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.store.spi; - -import org.jetbrains.annotations.NotNull; - -import java.util.stream.Stream; - -/** - * IdentityHubStore used to store data in an Identity Hub. - */ -public interface IdentityHubStore { - - /** - * List all {@link IdentityHubRecord}. - * - * @return Stream of store items. - */ - @NotNull Stream getAll(); - - /** - * Put a new record in the store. - * - * @param record Record to be put in the store. - * @throws org.eclipse.edc.spi.persistence.EdcPersistenceException if a record with the exact same id is already present in the store. - */ - void add(IdentityHubRecord record); -} diff --git a/spi/identity-hub-store-spi/src/test/java/org/eclipse/edc/identityhub/store/spi/IdentityHubRecordTest.java b/spi/identity-hub-store-spi/src/test/java/org/eclipse/edc/identityhub/store/spi/IdentityHubRecordTest.java deleted file mode 100644 index 5f9694b2a..000000000 --- a/spi/identity-hub-store-spi/src/test/java/org/eclipse/edc/identityhub/store/spi/IdentityHubRecordTest.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2022 Amadeus - * - * 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: - * Amadeus - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.store.spi; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; -import java.time.Instant; -import java.util.UUID; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -class IdentityHubRecordTest { - - private static final String PAYLOAD = UUID.randomUUID().toString(); - - @Test - void verifyMandatoryId() { - var builder = IdentityHubRecord.Builder.newInstance() - .payload(PAYLOAD.getBytes(StandardCharsets.UTF_8)); - - assertThatExceptionOfType(NullPointerException.class).isThrownBy(builder::build); - } - - @Test - void verifyMandatoryPayload() { - var builder = IdentityHubRecord.Builder.newInstance() - .id(UUID.randomUUID().toString()); - - assertThatExceptionOfType(NullPointerException.class).isThrownBy(builder::build); - } - - @Test - void verifySerDes() throws JsonProcessingException { - var mapper = new ObjectMapper(); - var r = IdentityHubRecord.Builder.newInstance() - .id(UUID.randomUUID().toString()) - .createdAt(Instant.now().getEpochSecond()) - .payload(PAYLOAD.getBytes(StandardCharsets.UTF_8)) - .build(); - - var json = mapper.writeValueAsString(r); - - assertNotNull(json); - - var deser = mapper.readValue(json, IdentityHubRecord.class); - assertThat(deser).usingRecursiveComparison().isEqualTo(r); - } -} diff --git a/spi/identity-hub-store-spi/src/testFixtures/java/org/eclipse/edc/identityhub/store/spi/IdentityHubStoreTestBase.java b/spi/identity-hub-store-spi/src/testFixtures/java/org/eclipse/edc/identityhub/store/spi/IdentityHubStoreTestBase.java deleted file mode 100644 index 10f2f680f..000000000 --- a/spi/identity-hub-store-spi/src/testFixtures/java/org/eclipse/edc/identityhub/store/spi/IdentityHubStoreTestBase.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2020 - 2022 Amadeus - * - * 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: - * Amadeus - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.store.spi; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.eclipse.edc.identityhub.junit.testfixtures.VerifiableCredentialTestUtil; -import org.eclipse.edc.identityhub.spi.credentials.model.Credential; -import org.eclipse.edc.spi.persistence.EdcPersistenceException; -import org.junit.jupiter.api.Test; - -import java.util.List; -import java.util.Random; -import java.util.UUID; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -public abstract class IdentityHubStoreTestBase { - - private static final ObjectMapper MAPPER = new ObjectMapper(); - private static final Random RAND = new Random(); - - @Test - void saveSameVerifiableCredentialTwice_shouldThrows() { - var record = createIdentityHubRecord(); - - getStore().add(record); - - assertThatExceptionOfType(EdcPersistenceException.class).isThrownBy(() -> getStore().add(record)); - } - - @Test - void saveAndListVerifiableCredentials() { - // Arrange - var records = createIdentityHubRecords(); - - // Act - records.forEach(a -> getStore().add(a)); - - // Assert - try (var stream = getStore().getAll()) { - var stored = stream.collect(Collectors.toList()); - assertThat(stored).hasSize(records.size()); - records.forEach(expected -> assertThat(stored).anySatisfy(r -> assertThat(r).usingRecursiveComparison().isEqualTo(expected))); - } - } - - protected abstract IdentityHubStore getStore(); - - private List createIdentityHubRecords() { - var credentialsCount = RAND.nextInt(10) + 1; - return IntStream.range(0, credentialsCount) - .mapToObj(i -> createIdentityHubRecord()) - .collect(Collectors.toList()); - } - - private IdentityHubRecord createIdentityHubRecord() { - var credential = VerifiableCredentialTestUtil.generateCredential(); - return IdentityHubRecord.Builder.newInstance() - .id(UUID.randomUUID().toString()) - .payloadFormat("application/json") - .payload(toByteArray(credential)) - .build(); - } - - private byte[] toByteArray(Credential vc) { - try { - return MAPPER.writeValueAsBytes(vc); - } catch (JsonProcessingException e) { - throw new AssertionError(e); - } - } -} diff --git a/system-tests/src/test/java/org/eclipse/edc/identityhub/systemtests/IntegrationTest.java b/system-tests/src/test/java/org/eclipse/edc/identityhub/systemtests/IntegrationTest.java deleted file mode 100644 index 86c87e6c7..000000000 --- a/system-tests/src/test/java/org/eclipse/edc/identityhub/systemtests/IntegrationTest.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.systemtests; - -import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target({ ElementType.TYPE, ElementType.METHOD }) -@Retention(RetentionPolicy.RUNTIME) -@EnabledIfEnvironmentVariable(named = "INTEGRATION_TEST", matches = "true") -public @interface IntegrationTest { -} diff --git a/system-tests/src/test/java/org/eclipse/edc/identityhub/systemtests/VerifiableCredentialsIntegrationTest.java b/system-tests/src/test/java/org/eclipse/edc/identityhub/systemtests/VerifiableCredentialsIntegrationTest.java deleted file mode 100644 index 9872f2093..000000000 --- a/system-tests/src/test/java/org/eclipse/edc/identityhub/systemtests/VerifiableCredentialsIntegrationTest.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (c) 2022 Microsoft Corporation - * - * 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: - * Microsoft Corporation - initial API and implementation - * - */ - -package org.eclipse.edc.identityhub.systemtests; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.eclipse.edc.iam.did.spi.credentials.CredentialsVerifier; -import org.eclipse.edc.iam.did.spi.resolution.DidResolverRegistry; -import org.eclipse.edc.identityhub.cli.IdentityHubCli; -import org.eclipse.edc.identityhub.spi.credentials.model.Credential; -import org.eclipse.edc.identityhub.spi.credentials.model.CredentialSubject; -import org.eclipse.edc.identityhub.spi.credentials.model.VerifiableCredential; -import org.eclipse.edc.junit.extensions.EdcExtension; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockserver.integration.ClientAndServer; -import picocli.CommandLine; - -import java.io.PrintWriter; -import java.io.StringWriter; -import java.sql.Date; -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.Map; -import java.util.UUID; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.edc.junit.testfixtures.TestUtils.getResourceFileContentAsString; -import static org.mockserver.model.HttpRequest.request; -import static org.mockserver.model.HttpResponse.response; -import static org.mockserver.stop.Stop.stopQuietly; - -@IntegrationTest -@ExtendWith(EdcExtension.class) -class VerifiableCredentialsIntegrationTest { - - private static final String HUB_URL = "http://localhost:8181/api/identity-hub"; - private static final String AUTHORITY_DID = "did:web:localhost%3A8080:authority"; - private static final String PARTICIPANT_DID = "did:web:localhost%3A8080:participant"; - private static final String AUTHORITY_PRIVATE_KEY_PATH = "resources/jwt/authority/private-key.pem"; - private static final ObjectMapper MAPPER = new ObjectMapper(); - private final Credential credential = createCredential(); - private ClientAndServer didServer; - - private final CommandLine cmd = IdentityHubCli.getCommandLine(); - private final StringWriter out = new StringWriter(); - private final StringWriter err = new StringWriter(); - - @BeforeEach - void setUp(EdcExtension extension) { - didServer = ClientAndServer.startClientAndServer(8080); - didServer.when(request().withPath("/authority/did.json")) - .respond(response(getResourceFileContentAsString("webdid/authority/did.json"))); - didServer.when(request().withPath("/participant/did.json")) - .respond(response(getResourceFileContentAsString("webdid/participant/did.json"))); - cmd.setOut(new PrintWriter(out)); - cmd.setErr(new PrintWriter(err)); - - extension.setConfiguration(Map.of( - "web.http.port", "8181", - "web.http.path", "/api", - "edc.iam.did.web.use.https", "false")); - } - - @AfterEach - public void stopServer() { - stopQuietly(didServer); - } - - @Test - void push_and_get_verifiable_credentials(CredentialsVerifier verifier, DidResolverRegistry resolverRegistry) throws Exception { - addVerifiableCredentialWithCli(); - - var verifiedCredential = getVerifiedCredential(verifier, resolverRegistry); - - assertThat(verifiedCredential).isInstanceOf(Credential.class).usingRecursiveComparison() - .ignoringFields("id", "issuanceDate") - .isEqualTo(credential); - } - - private void addVerifiableCredentialWithCli() throws JsonProcessingException { - var json = MAPPER.writeValueAsString(credential.getCredentialSubject().getClaims()); - int result = cmd.execute("-s", HUB_URL, "vc", "add", "-c", json, "-i", credential.getIssuer(), "-b", credential.getCredentialSubject().getId(), "-k", AUTHORITY_PRIVATE_KEY_PATH); - - assertThat(result).isZero(); - } - - private Object getVerifiedCredential(CredentialsVerifier verifier, DidResolverRegistry resolverRegistry) { - var didResult = resolverRegistry.resolve(PARTICIPANT_DID); - assertThat(didResult.succeeded()).isTrue(); - - var verifiedCredentials = verifier.getVerifiedCredentials(didResult.getContent()); - assertThat(verifiedCredentials.succeeded()).isTrue(); - - var vcs = verifiedCredentials.getContent(); - assertThat(vcs).hasSize(1); - return vcs.values().stream().findFirst() - .orElseThrow(() -> new AssertionError("Failed to find verified credential")); - } - - private Credential createCredential() { - return Credential.Builder.newInstance() - .context(VerifiableCredential.DEFAULT_CONTEXT) - .id(UUID.randomUUID().toString()) - .type(VerifiableCredential.DEFAULT_TYPE) - .issuer(AUTHORITY_DID) - .issuanceDate(Date.from(Instant.now().truncatedTo(ChronoUnit.SECONDS))) - .credentialSubject(CredentialSubject.Builder.newInstance() - .id(PARTICIPANT_DID) - .claim("hello", "world") - .claim("foo", "bar") - .build()) - .build(); - } -}