diff --git a/.github/actions/run-deployment-test/action.yml b/.github/actions/run-deployment-test/action.yml index 6768c4b..442f4f0 100644 --- a/.github/actions/run-deployment-test/action.yml +++ b/.github/actions/run-deployment-test/action.yml @@ -38,12 +38,12 @@ inputs: rootDir: required: true - description: "The directory that contains the docker file, e.g. edc-controlplane/edc-runtime-memory" + description: "The directory that contains the docker file" - values_file: - # required: true - required: false - description: "A yaml file that contains the values for the test installation. will be modified!" + cluster-config: + required: true + description: "YAML file to contain KinD cluster configuration" + default: system-tests/helm/kind.config.yaml runs: using: "composite" @@ -68,26 +68,45 @@ runs: - name: Create k8s Kind Cluster uses: helm/kind-action@v1.5.0 + with: + config: ${{ inputs.cluster-config }} - name: Load images into KinD shell: bash run: | kind get clusters | xargs -n1 kind load docker-image ${{ inputs.imagename }}:${{ inputs.image_tag }} --name - ################################################### - # Install the test infrastructure - ################################################### - # - name: "Generate test credentials" - # shell: bash - # run: |- - # sh -c "edc-tests/deployment/src/main/resources/prepare-test.sh \ - # ${{ inputs.values_file }}" + - name: "Install NGINX ingress controller" + shell: bash + run: | + # see: https://kind.sigs.k8s.io/docs/user/ingress/#ingress-nginx + # install NGINX ingress controller + kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml + + # wait for ingress to become available + kubectl wait --namespace ingress-nginx \ + --for=condition=ready pod \ + --selector=app.kubernetes.io/component=controller \ + --timeout=90s + + - name: "Install Vault chart" + shell: bash + run: | + helm repo add hashicorp https://helm.releases.hashicorp.com + + helm install vault hashicorp/vault \ + -f system-tests/helm/values-vault-test.yaml \ + --wait-for-jobs --timeout=120s --dependency-update + + # wait for Vault pod to become ready + kubectl wait --for=condition=ready pod \ + --selector=app.kubernetes.io/name=vault \ + --timeout=90s - name: Install Runtime shell: bash run: ${{ inputs.helm_command }} - ################# ### Tear Down ### ################# diff --git a/.github/workflows/deployment-test.yaml b/.github/workflows/deployment-test.yaml index ab5a4f3..8fbaaa0 100644 --- a/.github/workflows/deployment-test.yaml +++ b/.github/workflows/deployment-test.yaml @@ -67,12 +67,14 @@ jobs: with: imagename: ${{ matrix.variant.name }} rootDir: runtimes/${{ matrix.variant.name }} + cluster-config: "system-tests/helm/kind.config.yaml" helm_command: |- helm install ${{ matrix.variant.name }} ${{ matrix.variant.chart }} \ --set server.image.pullPolicy="Never" \ --set server.image.tag="latest" \ --set server.image.repository="${{ matrix.variant.name }}" \ --set fullnameOverride="${{ matrix.variant.name }}" \ + -f system-tests/helm/values-test.yaml \ --wait-for-jobs --timeout=120s --dependency-update # wait for the pod to become ready @@ -80,3 +82,14 @@ jobs: # execute the helm test helm test ${{ matrix.variant.name }} + + # verify ingress is available. expect 401, because we don't have a valid VP/VC (MembershipCred) + code=$(curl -X GET -IL -sw "%{http_code}" -k https://localhost/api/directory/bpn-directory -H "content-type: application/json" -o /dev/null) + if [ "$code" -ne "401" ]; then + echo "BDRS Directory API not ready, status = $code" + exit 1; + fi + + # verify management API is reachable as well. + # in production scenarios, the Managment API should NEVER be on the same ingress as the public API + curl -X GET --fail -k -L https://localhost/api/management/bpn-directory -H "content-type: application/json" -H "x-api-key: password" -o - \ No newline at end of file diff --git a/.github/workflows/helm-lint.yaml b/.github/workflows/helm-lint.yaml index 3f3d7fb..0b56fce 100644 --- a/.github/workflows/helm-lint.yaml +++ b/.github/workflows/helm-lint.yaml @@ -49,7 +49,7 @@ jobs: with: fetch-depth: 0 - name: helm (setup) - uses: azure/setup-helm@v3.5 + uses: azure/setup-helm@v4 with: version: v3.8.1 - name: python (setup) diff --git a/.github/workflows/kics.yml b/.github/workflows/kics.yml index 1435fa0..af309d2 100644 --- a/.github/workflows/kics.yml +++ b/.github/workflows/kics.yml @@ -44,7 +44,7 @@ jobs: - uses: actions/checkout@v4 - name: KICS scan - uses: checkmarx/kics-github-action@v1.6 + uses: checkmarx/kics-github-action@v2 continue-on-error: true # kics 1.6 fails with: path: "." diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index 750cbf5..35f3f92 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -141,7 +141,7 @@ jobs: with: fetch-depth: 0 - name: Install Helm - uses: azure/setup-helm@v3.5 + uses: azure/setup-helm@v4 with: version: v3.8.1 - name: Package helm, update index.yaml and push to gh-pages diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml index b5952f3..3106621 100644 --- a/.github/workflows/trivy.yml +++ b/.github/workflows/trivy.yml @@ -58,7 +58,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Run Trivy vulnerability scanner in repo mode - uses: aquasecurity/trivy-action@0.18.0 + uses: aquasecurity/trivy-action@0.19.0 with: scan-type: "config" # ignore-unfixed: true @@ -98,7 +98,7 @@ jobs: ## the next two steps will only execute if the image exists check was successful - name: Run Trivy vulnerability scanner if: success() && steps.imageCheck.outcome != 'failure' - uses: aquasecurity/trivy-action@0.18.0 + uses: aquasecurity/trivy-action@0.19.0 with: image-ref: "tractusx/${{ matrix.image }}:sha-${{ needs.git-sha7.outputs.value }}" format: "sarif" diff --git a/.github/workflows/upgradeability-test.yaml b/.github/workflows/upgradeability-test.yaml index df486e3..c5d7d2b 100644 --- a/.github/workflows/upgradeability-test.yaml +++ b/.github/workflows/upgradeability-test.yaml @@ -46,12 +46,12 @@ jobs: uses: actions/checkout@v4 - name: "Setup Helm" - uses: azure/setup-helm@v3.5 + uses: azure/setup-helm@v4 with: version: v3.8.1 - name: "Setup Kubectl" - uses: azure/setup-kubectl@v3.2 + uses: azure/setup-kubectl@v4 with: version: 'v1.28.2' diff --git a/DEPENDENCIES b/DEPENDENCIES index 22a8228..f242f59 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -1,43 +1,51 @@ +maven/mavencentral/com.apicatalog/carbon-did/0.3.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.apicatalog/copper-multibase/0.5.0, Apache-2.0, approved, #14501 +maven/mavencentral/com.apicatalog/copper-multicodec/0.1.1, Apache-2.0, approved, #14500 +maven/mavencentral/com.apicatalog/iron-ed25519-cryptosuite-2020/0.14.0, Apache-2.0, approved, #14503 +maven/mavencentral/com.apicatalog/iron-verifiable-credentials/0.14.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.apicatalog/titanium-json-ld/1.0.0, Apache-2.0, approved, clearlydefined maven/mavencentral/com.apicatalog/titanium-json-ld/1.4.0, Apache-2.0, approved, #13683 maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.10.3, Apache-2.0, approved, CQ21280 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.3, Apache-2.0, approved, #7947 maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.16.1, Apache-2.0, approved, #11606 -maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.16.2, Apache-2.0, approved, #11606 +maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.17.0, Apache-2.0, approved, #13672 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.16.1, Apache-2.0 AND MIT, approved, #11602 -maven/mavencentral/com.fasterxml.jackson.core/jackson-core/2.16.2, Apache-2.0 AND MIT, approved, #11602 +maven/mavencentral/com.fasterxml.jackson.core/jackson-core/2.17.0, , approved, #13665 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.3, Apache-2.0, approved, #7934 maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.16.1, Apache-2.0, approved, #11605 -maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.16.2, Apache-2.0, approved, #11605 +maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.17.0, Apache-2.0, approved, #13671 maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-toml/2.15.2, Apache-2.0, approved, #9160 -maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-toml/2.16.2, Apache-2.0, approved, #13145 +maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-toml/2.17.0, Apache-2.0, approved, #14192 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.16.2, Apache-2.0, approved, #11855 -maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jakarta-jsonp/2.16.2, Apache-2.0, approved, #11854 +maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-yaml/2.17.0, Apache-2.0, approved, #13669 +maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jakarta-jsonp/2.17.0, Apache-2.0, approved, #14161 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.16.1, Apache-2.0, approved, #11853 -maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jsr310/2.16.2, Apache-2.0, approved, #11853 -maven/mavencentral/com.fasterxml.jackson.jakarta.rs/jackson-jakarta-rs-base/2.16.2, Apache-2.0, approved, #11851 +maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jsr310/2.17.0, Apache-2.0, approved, #14160 +maven/mavencentral/com.fasterxml.jackson.jakarta.rs/jackson-jakarta-rs-base/2.17.0, Apache-2.0, approved, #14194 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.16.2, Apache-2.0, approved, #11858 -maven/mavencentral/com.fasterxml.jackson.module/jackson-module-jakarta-xmlbind-annotations/2.15.3, Apache-2.0, approved, #9241 -maven/mavencentral/com.fasterxml.jackson.module/jackson-module-jakarta-xmlbind-annotations/2.16.2, Apache-2.0, approved, #11856 +maven/mavencentral/com.fasterxml.jackson.jakarta.rs/jackson-jakarta-rs-json-provider/2.17.0, Apache-2.0, approved, #14195 +maven/mavencentral/com.fasterxml.jackson.module/jackson-module-jakarta-xmlbind-annotations/2.17.0, Apache-2.0, approved, #13668 maven/mavencentral/com.fasterxml.jackson/jackson-bom/2.15.1, Apache-2.0, approved, #7929 maven/mavencentral/com.fasterxml.jackson/jackson-bom/2.16.1, Apache-2.0, approved, #11852 -maven/mavencentral/com.fasterxml.jackson/jackson-bom/2.16.2, Apache-2.0, approved, #11852 +maven/mavencentral/com.fasterxml.jackson/jackson-bom/2.17.0, Apache-2.0, approved, #14162 maven/mavencentral/com.github.docker-java/docker-java-api/3.3.6, Apache-2.0, approved, #10346 maven/mavencentral/com.github.docker-java/docker-java-transport-zerodep/3.3.6, Apache-2.0 AND (Apache-2.0 AND BSD-3-Clause), approved, #7946 maven/mavencentral/com.github.docker-java/docker-java-transport/3.3.6, Apache-2.0, approved, #7942 +maven/mavencentral/com.github.stephenc.jcip/jcip-annotations/1.0-1, Apache-2.0, approved, CQ21949 maven/mavencentral/com.google.code.findbugs/jsr305/3.0.2, Apache-2.0, approved, #20 maven/mavencentral/com.google.code.gson/gson/2.10.1, Apache-2.0, approved, #6159 maven/mavencentral/com.google.collections/google-collections/1.0, Apache-2.0, approved, CQ3285 +maven/mavencentral/com.google.crypto.tink/tink/1.13.0, Apache-2.0, approved, #14502 maven/mavencentral/com.google.errorprone/error_prone_annotations/2.18.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.google.errorprone/error_prone_annotations/2.22.0, Apache-2.0, approved, #10661 maven/mavencentral/com.google.guava/failureaccess/1.0.1, Apache-2.0, approved, CQ22654 maven/mavencentral/com.google.guava/guava/32.0.1-jre, Apache-2.0 AND CC0-1.0 AND CC-PDDC, approved, #8772 maven/mavencentral/com.google.guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava, Apache-2.0, approved, CQ22657 maven/mavencentral/com.google.j2objc/j2objc-annotations/2.8, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.google.protobuf/protobuf-java/3.25.1, BSD-3-Clause, approved, clearlydefined +maven/mavencentral/com.nimbusds/nimbus-jose-jwt/9.37.3, Apache-2.0, approved, #11701 maven/mavencentral/com.puppycrawl.tools/checkstyle/10.12.3, LGPL-2.1-only AND Apache-2.0 AND LGPL-2.1-or-later AND ANTLR-PD AND LicenseRef-fossology-Non-commercial, approved, #13190 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 @@ -60,6 +68,7 @@ maven/mavencentral/io.rest-assured/json-path/5.4.0, Apache-2.0, approved, #12042 maven/mavencentral/io.rest-assured/rest-assured-common/5.4.0, Apache-2.0, approved, #12039 maven/mavencentral/io.rest-assured/rest-assured/5.4.0, Apache-2.0, approved, #12040 maven/mavencentral/io.rest-assured/xml-path/5.4.0, Apache-2.0, approved, #12038 +maven/mavencentral/io.setl/rdf-urdna/1.1, 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, #11362 maven/mavencentral/io.swagger.core.v3/swagger-core-jakarta/2.2.15, Apache-2.0, approved, #5929 @@ -71,17 +80,17 @@ maven/mavencentral/io.swagger.core.v3/swagger-jaxrs2/2.2.15, Apache-2.0, approve maven/mavencentral/io.swagger.core.v3/swagger-models-jakarta/2.2.15, Apache-2.0, approved, #5919 maven/mavencentral/io.swagger.core.v3/swagger-models/2.2.15, Apache-2.0, approved, #10353 maven/mavencentral/jakarta.activation/jakarta.activation-api/1.2.1, EPL-2.0 OR BSD-3-Clause OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jaf -maven/mavencentral/jakarta.activation/jakarta.activation-api/2.1.0, EPL-2.0 OR BSD-3-Clause OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jaf +maven/mavencentral/jakarta.activation/jakarta.activation-api/2.1.3, EPL-2.0 OR BSD-3-Clause OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jaf maven/mavencentral/jakarta.annotation/jakarta.annotation-api/2.1.1, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.ca maven/mavencentral/jakarta.inject/jakarta.inject-api/2.0.1, Apache-2.0, approved, ee4j.cdi -maven/mavencentral/jakarta.json/jakarta.json-api/2.1.1, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jsonp +maven/mavencentral/jakarta.json/jakarta.json-api/2.1.3, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jsonp maven/mavencentral/jakarta.transaction/jakarta.transaction-api/2.0.0, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jta maven/mavencentral/jakarta.validation/jakarta.validation-api/2.0.2, Apache-2.0, approved, ee4j.validation maven/mavencentral/jakarta.validation/jakarta.validation-api/3.0.2, Apache-2.0, approved, ee4j.validation maven/mavencentral/jakarta.ws.rs/jakarta.ws.rs-api/3.1.0, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.rest maven/mavencentral/jakarta.xml.bind/jakarta.xml.bind-api/2.3.2, BSD-3-Clause, approved, ee4j.jaxb maven/mavencentral/jakarta.xml.bind/jakarta.xml.bind-api/3.0.0, BSD-3-Clause, approved, ee4j.jaxb -maven/mavencentral/jakarta.xml.bind/jakarta.xml.bind-api/4.0.0, BSD-3-Clause, approved, ee4j.jaxb +maven/mavencentral/jakarta.xml.bind/jakarta.xml.bind-api/4.0.2, BSD-3-Clause, approved, ee4j.jaxb maven/mavencentral/javax.servlet/javax.servlet-api/3.1.0, (CDDL-1.1 OR GPL-2.0-only WITH Classpath-exception-2.0) AND Apache-2.0, approved, CQ7248 maven/mavencentral/javax.ws.rs/javax.ws.rs-api/2.1, (CDDL-1.1 OR GPL-2.0 WITH Classpath-exception-2.0) AND Apache-2.0, approved, CQ18121 maven/mavencentral/junit/junit/4.13.2, EPL-2.0, approved, CQ23636 @@ -89,6 +98,7 @@ maven/mavencentral/net.bytebuddy/byte-buddy-agent/1.14.1, Apache-2.0, approved, maven/mavencentral/net.bytebuddy/byte-buddy-agent/1.14.11, Apache-2.0, approved, #7164 maven/mavencentral/net.bytebuddy/byte-buddy/1.14.1, Apache-2.0 AND BSD-3-Clause, approved, #7163 maven/mavencentral/net.bytebuddy/byte-buddy/1.14.11, Apache-2.0 AND BSD-3-Clause, approved, #7163 +maven/mavencentral/net.bytebuddy/byte-buddy/1.14.9, Apache-2.0 AND BSD-3-Clause, approved, #7163 maven/mavencentral/net.java.dev.jna/jna/5.13.0, Apache-2.0 AND LGPL-2.1-or-later, approved, #6709 maven/mavencentral/net.sf.saxon/Saxon-HE/12.3, MPL-2.0-no-copyleft-exception AND Apache-1.1 AND (Apache-2.0 AND MPL-2.0-no-copyleft-exception) AND X11 AND ICU AND BSD-3-Clause AND W3C-20150513 AND (MIT OR GPL-2.0-only) AND (MIT OR GPL-3.0-only) AND LicenseRef-Permissive-license-with-conditions AND MIT AND MPL-1.0 AND MPL-2.0 AND LicenseRef-Public-domain AND W3C-19980720 AND LicenseRef-Permission-Notice, approved, #13191 maven/mavencentral/org.antlr/antlr4-runtime/4.11.1, BSD-3-Clause, approved, clearlydefined @@ -118,6 +128,9 @@ maven/mavencentral/org.apache.xbean/xbean-reflect/3.7, Apache-2.0, approved, cle maven/mavencentral/org.apiguardian/apiguardian-api/1.1.2, Apache-2.0, approved, clearlydefined maven/mavencentral/org.assertj/assertj-core/3.25.1, Apache-2.0, approved, #12585 maven/mavencentral/org.assertj/assertj-core/3.25.3, Apache-2.0, approved, #12585 +maven/mavencentral/org.bouncycastle/bcpkix-jdk18on/1.78, MIT, approved, #14434 +maven/mavencentral/org.bouncycastle/bcprov-jdk18on/1.78, MIT AND CC0-1.0, approved, #14433 +maven/mavencentral/org.bouncycastle/bcutil-jdk18on/1.78, MIT, approved, #14435 maven/mavencentral/org.ccil.cowan.tagsoup/tagsoup/1.2.1, Apache-2.0, approved, clearlydefined maven/mavencentral/org.checkerframework/checker-qual/3.27.0, MIT, approved, clearlydefined maven/mavencentral/org.checkerframework/checker-qual/3.42.0, MIT, approved, clearlydefined @@ -126,34 +139,56 @@ maven/mavencentral/org.codehaus.plexus/plexus-component-annotations/2.1.0, Apach maven/mavencentral/org.codehaus.plexus/plexus-container-default/2.1.0, Apache-2.0, approved, clearlydefined maven/mavencentral/org.codehaus.plexus/plexus-utils/3.1.1, , approved, CQ16492 maven/mavencentral/org.codehaus.plexus/plexus-utils/3.3.0, , approved, CQ21066 -maven/mavencentral/org.eclipse.edc/api-observability/0.6.0, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/auth-spi/0.6.0, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/auth-tokenbased/0.6.0, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/api-observability/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/auth-spi/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/auth-tokenbased/0.6.2, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/autodoc-processor/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/boot/0.6.0, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/core-spi/0.6.0, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/http-lib/0.6.0, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/http-spi/0.6.0, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/jersey-core/0.6.0, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/jersey-providers-lib/0.6.0, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/jetty-core/0.6.0, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/json-ld-lib/0.6.0, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/json-ld-spi/0.6.0, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/json-ld/0.6.0, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/junit-base/0.6.0, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/junit/0.6.0, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/policy-model/0.6.0, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/boot-lib/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/boot-spi/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/boot/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/core-spi/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/crypto-common-lib/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/http-lib/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/http-spi/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/identity-did-core/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/identity-did-spi/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/identity-did-web/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/identity-trust-issuers-configuration/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/identity-trust-service/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/identity-trust-spi/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/identity-trust-transform/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/jersey-core/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/jersey-providers-lib/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/jetty-core/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/json-ld-lib/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/json-ld-spi/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/json-ld/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/json-lib/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/junit-base/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/junit/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/jwt-spi/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/jwt-verifiable-credentials/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/keys-lib/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/keys-spi/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/policy-engine-spi/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/policy-model/0.6.2, Apache-2.0, approved, technology.edc maven/mavencentral/org.eclipse.edc/runtime-metamodel/0.5.1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/runtime-metamodel/0.6.0, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/sql-core/0.6.0, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/sql-pool-apache-commons/0.6.0, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/transaction-datasource-spi/0.6.0, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/transaction-local/0.6.0, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/transaction-spi/0.6.0, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/transform-spi/0.6.0, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/util/0.6.0, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/validator-spi/0.6.0, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/web-spi/0.6.0, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/runtime-metamodel/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/sql-core/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/sql-pool-apache-commons/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/token-core/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/token-spi/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transaction-datasource-spi/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transaction-local/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transaction-spi/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transform-lib/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transform-spi/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/util-lib/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/validator-spi/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/vault-hashicorp/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/verifiable-credentials-spi/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/verifiable-credentials/0.6.2, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/web-spi/0.6.2, 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.20, EPL-2.0 OR Apache-2.0, approved, rt.jetty @@ -176,22 +211,22 @@ maven/mavencentral/org.eclipse.jetty/jetty-servlet/11.0.20, EPL-2.0 OR Apache-2. maven/mavencentral/org.eclipse.jetty/jetty-util/11.0.20, EPL-2.0 OR Apache-2.0, approved, rt.jetty maven/mavencentral/org.eclipse.jetty/jetty-webapp/11.0.20, EPL-2.0 OR Apache-2.0, approved, rt.jetty maven/mavencentral/org.eclipse.jetty/jetty-xml/11.0.20, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.flywaydb/flyway-core/10.10.0, Apache-2.0, approved, #14163 -maven/mavencentral/org.flywaydb/flyway-database-postgresql/10.10.0, Apache-2.0, approved, #14158 -maven/mavencentral/org.glassfish.hk2.external/aopalliance-repackaged/3.0.5, 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.5, 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.5, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.glassfish -maven/mavencentral/org.glassfish.hk2/hk2-utils/3.0.5, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.glassfish -maven/mavencentral/org.glassfish.hk2/osgi-resource-locator/1.0.3, CDDL-1.0, approved, CQ10889 -maven/mavencentral/org.glassfish.jersey.containers/jersey-container-servlet-core/3.1.5, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey -maven/mavencentral/org.glassfish.jersey.containers/jersey-container-servlet/3.1.5, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey -maven/mavencentral/org.glassfish.jersey.core/jersey-client/3.1.5, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey -maven/mavencentral/org.glassfish.jersey.core/jersey-common/3.1.5, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey -maven/mavencentral/org.glassfish.jersey.core/jersey-server/3.1.5, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey -maven/mavencentral/org.glassfish.jersey.ext/jersey-entity-filtering/3.1.5, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey -maven/mavencentral/org.glassfish.jersey.inject/jersey-hk2/3.1.5, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey -maven/mavencentral/org.glassfish.jersey.media/jersey-media-json-jackson/3.1.5, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey -maven/mavencentral/org.glassfish.jersey.media/jersey-media-multipart/3.1.5, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey +maven/mavencentral/org.flywaydb/flyway-core/10.11.1, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.flywaydb/flyway-database-postgresql/10.11.1, Apache-2.0, approved, #14239 +maven/mavencentral/org.glassfish.hk2.external/aopalliance-repackaged/3.0.6, 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.6, 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.6, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.glassfish +maven/mavencentral/org.glassfish.hk2/hk2-utils/3.0.6, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.glassfish +maven/mavencentral/org.glassfish.hk2/osgi-resource-locator/1.0.3, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.glassfish +maven/mavencentral/org.glassfish.jersey.containers/jersey-container-servlet-core/3.1.6, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey +maven/mavencentral/org.glassfish.jersey.containers/jersey-container-servlet/3.1.6, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey +maven/mavencentral/org.glassfish.jersey.core/jersey-client/3.1.6, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey +maven/mavencentral/org.glassfish.jersey.core/jersey-common/3.1.6, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey +maven/mavencentral/org.glassfish.jersey.core/jersey-server/3.1.6, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey +maven/mavencentral/org.glassfish.jersey.ext/jersey-entity-filtering/3.1.6, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey +maven/mavencentral/org.glassfish.jersey.inject/jersey-hk2/3.1.6, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey +maven/mavencentral/org.glassfish.jersey.media/jersey-media-json-jackson/3.1.6, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey +maven/mavencentral/org.glassfish.jersey.media/jersey-media-multipart/3.1.6, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey 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/2.2, BSD-3-Clause, approved, clearlydefined @@ -201,10 +236,11 @@ maven/mavencentral/org.jacoco/org.jacoco.core/0.8.9, EPL-2.0, approved, CQ23283 maven/mavencentral/org.jacoco/org.jacoco.report/0.8.9, EPL-2.0 AND Apache-2.0, approved, CQ23284 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.javassist/javassist/3.30.2-GA, Apache-2.0 AND LGPL-2.1-or-later AND MPL-1.1, approved, #12108 maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-common/1.9.10, Apache-2.0, approved, #14186 -maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.9.10, None, restricted, #14188 +maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.9.10, Apache-2.0, approved, #14193 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, None, restricted, #14185 +maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.9.10, Apache-2.0, approved, #14191 maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib/1.9.10, Apache-2.0, approved, #11827 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 @@ -235,7 +271,6 @@ 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.5, BSD-3-Clause, approved, #7554 maven/mavencentral/org.ow2.asm/asm/9.6, BSD-3-Clause, approved, #10776 -maven/mavencentral/org.postgresql/postgresql/42.7.2, BSD-2-Clause AND Apache-2.0, approved, #11681 maven/mavencentral/org.postgresql/postgresql/42.7.3, BSD-2-Clause AND Apache-2.0, approved, #11681 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 diff --git a/api/authentication/build.gradle.kts b/api/authentication/build.gradle.kts new file mode 100644 index 0000000..1da7676 --- /dev/null +++ b/api/authentication/build.gradle.kts @@ -0,0 +1,57 @@ +/* + * + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + +plugins { + `java-library` + id("io.swagger.core.v3.swagger-gradle-plugin") +} + +dependencies { + implementation(project(":spi:core-spi")) + implementation(libs.edc.spi.core) + implementation(libs.edc.spi.web) + implementation(libs.edc.spi.auth) + implementation(libs.edc.spi.did) + implementation(libs.edc.spi.identitytrust) + implementation(libs.edc.spi.token) + implementation(libs.edc.spi.vc) + + implementation(libs.edc.lib.keys) + implementation(libs.edc.vc.jwt) // JwtPresentationVerifier + implementation(libs.edc.vc) // VerifiableCredentialValidationService + implementation(libs.edc.identitytrust.service) //MultiFormatPresentationVerifier + implementation(libs.edc.identitytrust.transform) //JwtToVerifiablePresentationTransformer + implementation(libs.edc.lib.transform) + implementation(libs.edc.lib.jsonld) + implementation(libs.edc.lib.http) + + implementation(libs.nimbus.jwt) + + testImplementation(libs.restAssured) + testImplementation(testFixtures(libs.edc.core.jersey)) +} + +edcBuild { + swagger { + apiGroup.set("directory-api") + } +} + diff --git a/api/authentication/src/main/java/org/eclipse/tractusx/bdrs/api/directory/authentication/CredentialBasedAuthenticationExtension.java b/api/authentication/src/main/java/org/eclipse/tractusx/bdrs/api/directory/authentication/CredentialBasedAuthenticationExtension.java new file mode 100644 index 0000000..cbca5dc --- /dev/null +++ b/api/authentication/src/main/java/org/eclipse/tractusx/bdrs/api/directory/authentication/CredentialBasedAuthenticationExtension.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.tractusx.bdrs.api.directory.authentication; + +import dev.failsafe.RetryPolicy; +import okhttp3.OkHttpClient; +import org.eclipse.edc.api.auth.spi.AuthenticationRequestFilter; +import org.eclipse.edc.http.client.EdcHttpClientImpl; +import org.eclipse.edc.http.spi.EdcHttpClient; +import org.eclipse.edc.iam.did.spi.resolution.DidPublicKeyResolver; +import org.eclipse.edc.iam.identitytrust.service.verification.MultiFormatPresentationVerifier; +import org.eclipse.edc.iam.identitytrust.transform.to.JwtToVerifiableCredentialTransformer; +import org.eclipse.edc.iam.identitytrust.transform.to.JwtToVerifiablePresentationTransformer; +import org.eclipse.edc.iam.verifiablecredentials.StatusList2021RevocationService; +import org.eclipse.edc.iam.verifiablecredentials.VerifiableCredentialValidationServiceImpl; +import org.eclipse.edc.iam.verifiablecredentials.spi.validation.TrustedIssuerRegistry; +import org.eclipse.edc.jsonld.JsonLdConfiguration; +import org.eclipse.edc.jsonld.TitaniumJsonLd; +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.token.spi.TokenValidationRulesRegistry; +import org.eclipse.edc.token.spi.TokenValidationService; +import org.eclipse.edc.transform.TypeTransformerRegistryImpl; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.eclipse.edc.verifiablecredentials.jwt.JwtPresentationVerifier; +import org.eclipse.edc.web.spi.WebService; + +import java.time.Clock; + +import static org.eclipse.edc.spi.constants.CoreConstants.JSON_LD; +import static org.eclipse.tractusx.bdrs.api.directory.authentication.CredentialBasedAuthenticationExtension.NAME; + +/** + * Registers an authentication service that checks MembershipCredentials. + */ +@Extension(NAME) +public class CredentialBasedAuthenticationExtension implements ServiceExtension { + public static final long DEFAULT_REVOCATION_CACHE_VALIDITY_MILLIS = 15 * 60 * 1000L; + @Setting(value = "Validity period of cached StatusList2021 credential entries in milliseconds.", defaultValue = DEFAULT_REVOCATION_CACHE_VALIDITY_MILLIS + "", type = "long") + public static final String REVOCATION_CACHE_VALIDITY = "edc.iam.credential.revocation.cache.validity"; + public static final String NAME = "Directory API Authentication Extension"; + public static final String MONITOR_PREFIX = "Presentation Transformation"; + private static final String DIRECTORY_CONTEXT = "directory"; + @Inject + private WebService webService; + @Inject + private TypeManager typeManager; + @Inject + private TokenValidationService tokenValidationService; + @Inject + private TokenValidationRulesRegistry rulesRegistry; + @Inject + private DidPublicKeyResolver didPublicKeyResolver; + @Inject + private Clock clock; + + private TrustedIssuerRegistry trustedIssuerRegistry; + private TypeTransformerRegistryImpl typeTransformerRegistry; + + @Override + public String name() { + return NAME; + } + + @Override + public void initialize(ServiceExtensionContext context) { + var mapper = typeManager.getMapper(JSON_LD); + // the DidPublicKeyResolver has a dependency onto the KeyParserRegistry, so that must be created in a separate ext -> KeyParserRegistryExtension + var jwtVerifier = new JwtPresentationVerifier(mapper, tokenValidationService, rulesRegistry, didPublicKeyResolver); + var presentationVerifier = new MultiFormatPresentationVerifier(null, jwtVerifier); + + var validity = context.getConfig().getLong(REVOCATION_CACHE_VALIDITY, DEFAULT_REVOCATION_CACHE_VALIDITY_MILLIS); + var statuslistService = new StatusList2021RevocationService(typeManager.getMapper(), validity); + var validationService = new VerifiableCredentialValidationServiceImpl(presentationVerifier, createTrustedIssuerRegistry(), statuslistService, clock); + + webService.registerResource(DIRECTORY_CONTEXT, new AuthenticationRequestFilter(new CredentialBasedAuthenticationService(context.getMonitor(), typeManager.getMapper(), validationService, typeTransformerRegistry(context)))); + } + + // must provide this, so the TrustedIssuerRegistryConfigurationExtension can inject it + @Provider + public TrustedIssuerRegistry createTrustedIssuerRegistry() { + if (trustedIssuerRegistry == null) { + trustedIssuerRegistry = new TrustedIssuerRegistryImpl(); + } + return trustedIssuerRegistry; + } + + @Provider + public TypeTransformerRegistry typeTransformerRegistry(ServiceExtensionContext context) { + if (typeTransformerRegistry == null) { + typeTransformerRegistry = new TypeTransformerRegistryImpl(); + var monitor = context.getMonitor().withPrefix(MONITOR_PREFIX); + typeTransformerRegistry.register(new JwtToVerifiablePresentationTransformer(monitor, typeManager.getMapper(JSON_LD), new TitaniumJsonLd(monitor, JsonLdConfiguration.Builder.newInstance().build()))); + typeTransformerRegistry.register(new JwtToVerifiableCredentialTransformer(monitor)); + } + return typeTransformerRegistry; + } + + @Provider + public EdcHttpClient httpClient(ServiceExtensionContext context) { + return new EdcHttpClientImpl(new OkHttpClient(), RetryPolicy.ofDefaults(), context.getMonitor().withPrefix(MONITOR_PREFIX)); + } +} \ No newline at end of file diff --git a/api/authentication/src/main/java/org/eclipse/tractusx/bdrs/api/directory/authentication/CredentialBasedAuthenticationService.java b/api/authentication/src/main/java/org/eclipse/tractusx/bdrs/api/directory/authentication/CredentialBasedAuthenticationService.java new file mode 100644 index 0000000..b49fe59 --- /dev/null +++ b/api/authentication/src/main/java/org/eclipse/tractusx/bdrs/api/directory/authentication/CredentialBasedAuthenticationService.java @@ -0,0 +1,105 @@ +package org.eclipse.tractusx.bdrs.api.directory.authentication; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.eclipse.edc.api.auth.spi.AuthenticationService; +import org.eclipse.edc.iam.verifiablecredentials.spi.VerifiableCredentialValidationService; +import org.eclipse.edc.iam.verifiablecredentials.spi.model.CredentialFormat; +import org.eclipse.edc.iam.verifiablecredentials.spi.model.VerifiablePresentation; +import org.eclipse.edc.iam.verifiablecredentials.spi.model.VerifiablePresentationContainer; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.eclipse.edc.web.spi.exception.AuthenticationFailedException; + +import java.io.IOException; +import java.util.Base64; +import java.util.List; +import java.util.Map; + +import static jakarta.ws.rs.core.HttpHeaders.AUTHORIZATION; + +/** + * AuthenticationService that takes a "Bearer" token from the "Authorization" header, and + * checks whether that token is a valid VerifiablePresentation, and contains a valid MembershipCredential. + */ +public class CredentialBasedAuthenticationService implements AuthenticationService { + private final Monitor monitor; + private final ObjectMapper objectMapper; + private final VerifiableCredentialValidationService verifiableCredentialValidationService; + private final TypeTransformerRegistry typeTransformerRegistry; + + public CredentialBasedAuthenticationService(Monitor monitor, ObjectMapper objectMapper, VerifiableCredentialValidationService verifiableCredentialValidationService, TypeTransformerRegistry typeTransformerRegistry) { + this.monitor = monitor; + this.objectMapper = objectMapper; + this.verifiableCredentialValidationService = verifiableCredentialValidationService; + this.typeTransformerRegistry = typeTransformerRegistry; + } + + @Override + public boolean isAuthenticated(Map> headers) { + + if (headers == null || headers.isEmpty()) { + var msg = "Headers were null or empty"; + monitor.warning(msg); + throw new AuthenticationFailedException(msg); + } + + var authHeaders = headers.keySet().stream() + .filter(k -> k.equalsIgnoreCase(AUTHORIZATION)) + .map(headers::get) + .findFirst(); + + return authHeaders.map(this::performCredentialValidation).orElseThrow(() -> { + var msg = "Header '%s' not present"; + monitor.warning(msg); + return new AuthenticationFailedException(msg.formatted(AUTHORIZATION)); + }); + } + + private boolean performCredentialValidation(List authHeaders) { + if (authHeaders.size() != 1) { + monitor.warning("Expected exactly 1 Authorization header, found %d".formatted(authHeaders.size())); + return false; + } + var token = authHeaders.get(0); + if (!token.toLowerCase().startsWith("bearer ")) { + monitor.warning("Authorization header must start with 'bearer '"); + return false; + } + token = token.substring(6).trim(); // "bearer" has 7 characters, it could be upper case, lower case or capitalized + + if (!isValidJwt(token)) { + monitor.warning("Bearer token is not valid JWT"); + return false; + } + + var finalToken = token; + return typeTransformerRegistry.transform(token, VerifiablePresentation.class) + .compose(pres -> verifiableCredentialValidationService.validate(List.of(new VerifiablePresentationContainer(finalToken, CredentialFormat.JWT, pres)), new MustHaveMemberhipCredentialRule())) + .onFailure(f -> monitor.warning("Error validating BDRS client VP: %s".formatted(f.getFailureDetail()))) + .succeeded(); + } + + /** + * checks if a string is a valid JWT by splitting it on ".", and checking that the first two parts are valid JSON + */ + private boolean isValidJwt(String token) { + var parts = token.split("\\."); + if (parts.length != 3) { // The JWT is composed of three parts + return false; + } + var decoder = Base64.getUrlDecoder(); + return canParse(decoder.decode(parts[0])) && canParse(decoder.decode(parts[1])); + } + + /** + * checks if a string is valid JSON + */ + private boolean canParse(byte[] rawInput) { + try (var parser = objectMapper.createParser(rawInput)) { + parser.nextToken(); + return true; + } catch (IOException e) { + return false; + } + } +} diff --git a/api/authentication/src/main/java/org/eclipse/tractusx/bdrs/api/directory/authentication/KeyParserRegistryExtension.java b/api/authentication/src/main/java/org/eclipse/tractusx/bdrs/api/directory/authentication/KeyParserRegistryExtension.java new file mode 100644 index 0000000..e17215c --- /dev/null +++ b/api/authentication/src/main/java/org/eclipse/tractusx/bdrs/api/directory/authentication/KeyParserRegistryExtension.java @@ -0,0 +1,30 @@ +package org.eclipse.tractusx.bdrs.api.directory.authentication; + +import org.eclipse.edc.keys.KeyParserRegistryImpl; +import org.eclipse.edc.keys.keyparsers.JwkParser; +import org.eclipse.edc.keys.keyparsers.PemParser; +import org.eclipse.edc.keys.spi.KeyParserRegistry; +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.spi.types.TypeManager; + +/** + * This extension must be separate from the {@link CredentialBasedAuthenticationExtension} to avoid a cyclic dependency + */ +@Extension(value = "Provides a KeyParserRegistry") +public class KeyParserRegistryExtension implements ServiceExtension { + @Inject + private TypeManager typeManager; + + @Provider + public KeyParserRegistry keyParserRegistry(ServiceExtensionContext context) { + var keyParserRegistry = new KeyParserRegistryImpl(); + var monitor = context.getMonitor().withPrefix("PrivateKeyResolution"); + keyParserRegistry.register(new JwkParser(typeManager.getMapper(), monitor)); + keyParserRegistry.register(new PemParser(monitor)); + return keyParserRegistry; + } +} diff --git a/api/authentication/src/main/java/org/eclipse/tractusx/bdrs/api/directory/authentication/MustHaveMemberhipCredentialRule.java b/api/authentication/src/main/java/org/eclipse/tractusx/bdrs/api/directory/authentication/MustHaveMemberhipCredentialRule.java new file mode 100644 index 0000000..4b5028f --- /dev/null +++ b/api/authentication/src/main/java/org/eclipse/tractusx/bdrs/api/directory/authentication/MustHaveMemberhipCredentialRule.java @@ -0,0 +1,14 @@ +package org.eclipse.tractusx.bdrs.api.directory.authentication; + +import org.eclipse.edc.iam.verifiablecredentials.spi.model.VerifiableCredential; +import org.eclipse.edc.iam.verifiablecredentials.spi.validation.CredentialValidationRule; +import org.eclipse.edc.spi.result.Result; + +public class MustHaveMemberhipCredentialRule implements CredentialValidationRule { + private static final String MEMBERSHIP_TYPE = "MembershipCredential"; + + @Override + public Result apply(VerifiableCredential verifiableCredential) { + return verifiableCredential.getType().contains(MEMBERSHIP_TYPE) ? Result.success() : Result.failure("Expected only credentials containing type '%s', but got '%s'.".formatted(MEMBERSHIP_TYPE, verifiableCredential.getType())); + } +} diff --git a/api/authentication/src/main/java/org/eclipse/tractusx/bdrs/api/directory/authentication/TrustedIssuerRegistryImpl.java b/api/authentication/src/main/java/org/eclipse/tractusx/bdrs/api/directory/authentication/TrustedIssuerRegistryImpl.java new file mode 100644 index 0000000..d07c41c --- /dev/null +++ b/api/authentication/src/main/java/org/eclipse/tractusx/bdrs/api/directory/authentication/TrustedIssuerRegistryImpl.java @@ -0,0 +1,27 @@ +package org.eclipse.tractusx.bdrs.api.directory.authentication; + +import org.eclipse.edc.iam.verifiablecredentials.spi.model.Issuer; +import org.eclipse.edc.iam.verifiablecredentials.spi.validation.TrustedIssuerRegistry; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +public class TrustedIssuerRegistryImpl implements TrustedIssuerRegistry { + private final Map store = new HashMap<>(); + + @Override + public void addIssuer(Issuer issuer) { + store.put(issuer.id(), issuer); + } + + @Override + public Issuer getById(String id) { + return store.get(id); + } + + @Override + public Collection getTrustedIssuers() { + return store.values(); + } +} diff --git a/api/authentication/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/api/authentication/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension new file mode 100644 index 0000000..4a9bce2 --- /dev/null +++ b/api/authentication/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -0,0 +1,22 @@ +# +# +# Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# +# +org.eclipse.tractusx.bdrs.api.directory.authentication.CredentialBasedAuthenticationExtension +org.eclipse.tractusx.bdrs.api.directory.authentication.KeyParserRegistryExtension diff --git a/api/authentication/src/test/java/org/eclipse/tractusx/bdrs/api/directory/authentication/CredentialBasedAuthenticationServiceTest.java b/api/authentication/src/test/java/org/eclipse/tractusx/bdrs/api/directory/authentication/CredentialBasedAuthenticationServiceTest.java new file mode 100644 index 0000000..aa2ba75 --- /dev/null +++ b/api/authentication/src/test/java/org/eclipse/tractusx/bdrs/api/directory/authentication/CredentialBasedAuthenticationServiceTest.java @@ -0,0 +1,76 @@ +package org.eclipse.tractusx.bdrs.api.directory.authentication; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.eclipse.edc.iam.verifiablecredentials.spi.VerifiableCredentialValidationService; +import org.eclipse.edc.iam.verifiablecredentials.spi.model.VerifiablePresentation; +import org.eclipse.edc.iam.verifiablecredentials.spi.validation.CredentialValidationRule; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.result.Result; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.eclipse.edc.web.spi.exception.AuthenticationFailedException; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.startsWith; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class CredentialBasedAuthenticationServiceTest { + + + private final Monitor monitor = mock(); + private final VerifiableCredentialValidationService validationService = mock(); + private final TypeTransformerRegistry typeTransformerRegistry = mock(); + private final CredentialBasedAuthenticationService service = new CredentialBasedAuthenticationService(monitor, new ObjectMapper(), validationService, typeTransformerRegistry); + + @Test + void isAuthenticated_noHeader() { + assertThatThrownBy(() -> service.isAuthenticated(null)).isInstanceOf(AuthenticationFailedException.class); + assertThatThrownBy(() -> service.isAuthenticated(Map.of())).isInstanceOf(AuthenticationFailedException.class); + } + + @Test + void isAuthenticated_noAuthHeader() { + assertThatThrownBy(() -> service.isAuthenticated(Map.of("foo", List.of("bar")))).isInstanceOf(AuthenticationFailedException.class); + } + + @Test + void isAuthenticated_multipleAuthHeaders() { + assertThat(service.isAuthenticated(Map.of("Authorization", List.of("foo", "bar", "baz")))).isFalse(); + } + + @Test + void isAuthenticated_notBearer() { + assertThat(service.isAuthenticated(Map.of("Authorization", List.of("value")))).isFalse(); + } + + @Test + void isAuthenticated_tokenNotJwtFormat() { + assertThat(service.isAuthenticated(Map.of("Authorization", List.of("Bearer value")))).isFalse(); + } + + @Test + void isAuthenticated_vpInvalid() { + when(validationService.validate(anyList(), any(CredentialValidationRule[].class))) + .thenReturn(Result.failure("test failure")); + when(typeTransformerRegistry.transform(any(), eq(VerifiablePresentation.class))) + .thenReturn(Result.success(VerifiablePresentation.Builder.newInstance().type("VerifiablePresentation").build())); + + assertThat(service.isAuthenticated(Map.of("Authorization", List.of("Bearer " + createSerializedJwt())))).isFalse(); + verify(monitor).warning(startsWith("Error validating BDRS client VP")); + } + + private String createSerializedJwt() { + return "eyJhbGciOiJIUzI1NiIsI" + + "nR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwi" + + "aWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"; + } +} \ No newline at end of file diff --git a/api/directory-api/src/main/java/org/eclipse/tractusx/bdrs/api/directory/DirectoryApiExtension.java b/api/directory-api/src/main/java/org/eclipse/tractusx/bdrs/api/directory/DirectoryApiExtension.java index 39f5c73..b5be43f 100644 --- a/api/directory-api/src/main/java/org/eclipse/tractusx/bdrs/api/directory/DirectoryApiExtension.java +++ b/api/directory-api/src/main/java/org/eclipse/tractusx/bdrs/api/directory/DirectoryApiExtension.java @@ -16,6 +16,7 @@ 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.web.spi.WebService; @@ -30,6 +31,11 @@ public class DirectoryApiExtension implements ServiceExtension { public static final String NAME = "BPN Directory API"; + @Setting(value = "Port for the Directory API", required = true) + public static final String MGMT_API_PORT = "web.http.directory.port"; + @Setting(value = "Path for the Management API", required = true) + public static final String MGMT_API_PATH = "web.http.directory.path"; + static final String CONTEXT_NAME = "directory"; @Inject private DidEntryStore store; @@ -43,7 +49,7 @@ public String name() { @Override public void initialize(ServiceExtensionContext context) { - webService.registerResource(new DirectoryApiController(store)); + webService.registerResource(CONTEXT_NAME, new DirectoryApiController(store)); } } diff --git a/api/directory-api/src/test/java/org/eclipse/tractusx/bdrs/api/directory/DirectoryApiExtensionTest.java b/api/directory-api/src/test/java/org/eclipse/tractusx/bdrs/api/directory/DirectoryApiExtensionTest.java index d2f14f7..89ca951 100644 --- a/api/directory-api/src/test/java/org/eclipse/tractusx/bdrs/api/directory/DirectoryApiExtensionTest.java +++ b/api/directory-api/src/test/java/org/eclipse/tractusx/bdrs/api/directory/DirectoryApiExtensionTest.java @@ -21,6 +21,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -34,7 +35,7 @@ class DirectoryApiExtensionTest { void verifyBoot(DirectoryApiExtension extension, ServiceExtensionContext context) { extension.initialize(context); - verify(webService).registerResource(isA(DirectoryApiController.class)); + verify(webService).registerResource(eq("directory"), isA(DirectoryApiController.class)); } @BeforeEach diff --git a/build.gradle.kts b/build.gradle.kts index db50369..4026867 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -90,7 +90,8 @@ subprojects { from("${projectDir}/notice.md") } - dependsOn(tasks.named(ShadowJavaPlugin.SHADOW_JAR_TASK_NAME)) + mustRunAfter(tasks.named(ShadowJavaPlugin.SHADOW_JAR_TASK_NAME)) + mustRunAfter(tasks.named(JavaPlugin.JAR_TASK_NAME)) } //actually apply the plugin to the (sub-)project diff --git a/charts/bdrs-server-memory/Chart.yaml b/charts/bdrs-server-memory/Chart.yaml index e1572a2..47ad4de 100644 --- a/charts/bdrs-server-memory/Chart.yaml +++ b/charts/bdrs-server-memory/Chart.yaml @@ -34,12 +34,12 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.2 +version: 0.0.3 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "0.0.2" +appVersion: "0.0.3" home: https://github.com/eclipse-tractusx/bpn-did-resolution-service/tree/main/charts/bdrs-server sources: - https://github.com/eclipse-tractusx/bpn-did-resolution-service/tree/main/charts/bdrs-server diff --git a/charts/bdrs-server-memory/README.md b/charts/bdrs-server-memory/README.md index fd5c78c..ec5bfa2 100644 --- a/charts/bdrs-server-memory/README.md +++ b/charts/bdrs-server-memory/README.md @@ -1,6 +1,6 @@ # bdrs-server-memory -![Version: 0.0.2](https://img.shields.io/badge/Version-0.0.2-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.0.2](https://img.shields.io/badge/AppVersion-0.0.2-informational?style=flat-square) +![Version: 0.0.3](https://img.shields.io/badge/Version-0.0.3-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.0.3](https://img.shields.io/badge/AppVersion-0.0.3-informational?style=flat-square) A Helm chart for the Tractus-X BPN-DID Resolution Service (only in-memory persistence) @@ -25,7 +25,7 @@ Simply execute these commands on a shell: ```shell helm repo add tractusx https://eclipse-tractusx.github.io/charts/dev -helm install my-release tractusx-edc/bdrs-server --version 0.0.2 \ +helm install my-release tractusx-edc/bdrs-server --version 0.0.3 \ -f /additional-values-file.yaml \ --wait-for-jobs --timeout=120s --dependency-update ``` @@ -75,8 +75,8 @@ helm install my-release tractusx-edc/bdrs-server --version 0.0.2 \ | server.ingresses[0].certManager.issuer | string | `""` | If preset enables certificate generation via cert-manager namespace scoped issuer | | server.ingresses[0].className | string | `""` | Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use | | server.ingresses[0].enabled | bool | `false` | | -| server.ingresses[0].endpoints | list | `["protocol","public"]` | EDC endpoints exposed by this ingress resource | -| server.ingresses[0].hostname | string | `"bdrs-server.local"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | +| server.ingresses[0].endpoints | list | `["directory"]` | EDC endpoints exposed by this ingress resource | +| server.ingresses[0].hostname | string | `"bdrs-server.directory.local"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | | server.ingresses[0].tls | object | `{"enabled":false,"secretName":""}` | TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource | | server.ingresses[0].tls.enabled | bool | `false` | Enables TLS on the ingress resource | | server.ingresses[0].tls.secretName | string | `""` | If present overwrites the default secret name | @@ -85,8 +85,8 @@ helm install my-release tractusx-edc/bdrs-server --version 0.0.2 \ | server.ingresses[1].certManager.issuer | string | `""` | If preset enables certificate generation via cert-manager namespace scoped issuer | | server.ingresses[1].className | string | `""` | Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use | | server.ingresses[1].enabled | bool | `false` | | -| server.ingresses[1].endpoints | list | `["management","control"]` | EDC endpoints exposed by this ingress resource | -| server.ingresses[1].hostname | string | `"edc-control.intranet"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | +| server.ingresses[1].endpoints | list | `["management"]` | EDC endpoints exposed by this ingress resource | +| server.ingresses[1].hostname | string | `"bdrs-server.mgmt.local"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | | server.ingresses[1].tls | object | `{"enabled":false,"secretName":""}` | TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource | | server.ingresses[1].tls.enabled | bool | `false` | Enables TLS on the ingress resource | | server.ingresses[1].tls.secretName | string | `""` | If present overwrites the default secret name | diff --git a/charts/bdrs-server-memory/templates/service.yaml b/charts/bdrs-server-memory/templates/service.yaml index d29cc36..253a3c0 100644 --- a/charts/bdrs-server-memory/templates/service.yaml +++ b/charts/bdrs-server-memory/templates/service.yaml @@ -40,7 +40,7 @@ spec: protocol: TCP name: management - port: {{ .Values.server.endpoints.directory.port }} - targetPort: public + targetPort: directory protocol: TCP name: directory selector: diff --git a/charts/bdrs-server-memory/values.yaml b/charts/bdrs-server-memory/values.yaml index a48b3ed..9081787 100644 --- a/charts/bdrs-server-memory/values.yaml +++ b/charts/bdrs-server-memory/values.yaml @@ -1,6 +1,5 @@ ################################################################################# -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021,2023 Contributors to the Eclipse Foundation +# Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) # # See the NOTICE file(s) distributed with this work for additional # information regarding copyright ownership. @@ -19,7 +18,6 @@ ################################################################################# --- -# Default values for eclipse-dataspace-connector. # This is a YAML-formatted file. # Declare variables to be passed into your templates. @@ -160,13 +158,12 @@ server: ## Public / Internet facing Ingress - enabled: false # -- The hostname to be used to precisely map incoming traffic onto the underlying network service - hostname: "bdrs-server.local" + hostname: "bdrs-server.directory.local" # -- Additional ingress annotations to add annotations: {} # -- EDC endpoints exposed by this ingress resource endpoints: - - protocol - - public + - directory # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use className: "" # -- TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource @@ -181,16 +178,15 @@ server: issuer: "" # -- If preset enables certificate generation via cert-manager cluster-wide issuer clusterIssuer: "" - ## Private / Intranet facing Ingress + ## Ingress for the Management API, should not be internet facing - enabled: false # -- The hostname to be used to precisely map incoming traffic onto the underlying network service - hostname: "edc-control.intranet" + hostname: "bdrs-server.mgmt.local" # -- Additional ingress annotations to add annotations: {} # -- EDC endpoints exposed by this ingress resource endpoints: - management - - control # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use className: "" # -- TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource diff --git a/charts/bdrs-server/Chart.yaml b/charts/bdrs-server/Chart.yaml index 64a8283..27a0201 100644 --- a/charts/bdrs-server/Chart.yaml +++ b/charts/bdrs-server/Chart.yaml @@ -34,12 +34,12 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.2 +version: 0.0.3 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "0.0.2" +appVersion: "0.0.3" home: https://github.com/eclipse-tractusx/bpn-did-resolution-service/tree/main/charts/bdrs-server sources: - https://github.com/eclipse-tractusx/bpn-did-resolution-service/tree/main/charts/bdrs-server @@ -50,3 +50,9 @@ dependencies: version: 12.11.2 repository: https://charts.bitnami.com/bitnami condition: install.postgresql + # HashiCorp Vault + - name: vault + alias: vault + version: "0.27.0" + repository: https://helm.releases.hashicorp.com + condition: install.vault diff --git a/charts/bdrs-server/README.md b/charts/bdrs-server/README.md index 0e68514..3da1d63 100644 --- a/charts/bdrs-server/README.md +++ b/charts/bdrs-server/README.md @@ -1,6 +1,6 @@ # bdrs-server -![Version: 0.0.2](https://img.shields.io/badge/Version-0.0.2-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.0.2](https://img.shields.io/badge/AppVersion-0.0.2-informational?style=flat-square) +![Version: 0.0.3](https://img.shields.io/badge/Version-0.0.3-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.0.3](https://img.shields.io/badge/AppVersion-0.0.3-informational?style=flat-square) A Helm chart for the Tractus-X BPN-DID Resolution Service @@ -22,7 +22,7 @@ Simply execute these commands on a shell: ```shell helm repo add tractusx https://eclipse-tractusx.github.io/charts/dev -helm install my-release tractusx-edc/bdrs-server --version 0.0.2 \ +helm install my-release tractusx-edc/bdrs-server --version 0.0.3 \ -f /additional-values-file.yaml \ --wait-for-jobs --timeout=120s --dependency-update ``` @@ -36,6 +36,7 @@ helm install my-release tractusx-edc/bdrs-server --version 0.0.2 \ | Repository | Name | Version | |------------|------|---------| | https://charts.bitnami.com/bitnami | postgresql(postgresql) | 12.11.2 | +| https://helm.releases.hashicorp.com | vault(vault) | 0.27.0 | ## Values @@ -46,10 +47,11 @@ helm install my-release tractusx-edc/bdrs-server --version 0.0.2 \ | fullnameOverride | string | `""` | | | imagePullSecrets | list | `[]` | Existing image pull secret to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) | | install.postgresql | bool | `true` | | +| install.vault | bool | `true` | | | nameOverride | string | `""` | | | postgresql.auth.database | string | `"bdrs"` | | | postgresql.auth.password | string | `"password"` | | -| postgresql.auth.username | string | `"postgres"` | | +| postgresql.auth.username | string | `"bdrs"` | | | postgresql.jdbcUrl | string | `"jdbc:postgresql://{{ .Release.Name }}-postgresql:5432/bdrs"` | | | postgresql.primary.persistence.enabled | bool | `false` | | | postgresql.readReplicas.persistence.enabled | bool | `false` | | @@ -62,15 +64,15 @@ helm install my-release tractusx-edc/bdrs-server --version 0.0.2 \ | server.debug.enabled | bool | `false` | | | server.debug.port | int | `1044` | | | server.debug.suspendOnStart | bool | `false` | | -| server.endpoints | object | `{"default":{"path":"/api","port":8080},"directory":{"path":"/api/directory","port":8082},"management":{"authKey":"password","path":"/api/management","port":8081}}` | endpoints of the control plane | +| server.endpoints | object | `{"default":{"path":"/api","port":8080},"directory":{"path":"/api/directory","port":8082},"management":{"authKeyAlias":"mgmt-api-key","path":"/api/management","port":8081}}` | endpoints of the control plane | | server.endpoints.default | object | `{"path":"/api","port":8080}` | default api for health checks, should not be added to any ingress | | server.endpoints.default.path | string | `"/api"` | path for incoming api calls | | server.endpoints.default.port | int | `8080` | port for incoming api calls | | server.endpoints.directory | object | `{"path":"/api/directory","port":8082}` | directory API | | server.endpoints.directory.path | string | `"/api/directory"` | path for incoming api calls | | server.endpoints.directory.port | int | `8082` | port for incoming api calls | -| server.endpoints.management | object | `{"authKey":"password","path":"/api/management","port":8081}` | management api, used by internal users, can be added to an ingress and must not be internet facing | -| server.endpoints.management.authKey | string | `"password"` | authentication key, must be attached to each 'X-Api-Key' request header | +| server.endpoints.management | object | `{"authKeyAlias":"mgmt-api-key","path":"/api/management","port":8081}` | management api, used by internal users, can be added to an ingress and must not be internet facing | +| server.endpoints.management.authKeyAlias | string | `"mgmt-api-key"` | authentication key, must be attached to each 'X-Api-Key' request header | | server.endpoints.management.path | string | `"/api/management"` | path for incoming api calls | | server.endpoints.management.port | int | `8081` | port for incoming api calls | | server.env | object | `{}` | | @@ -85,8 +87,8 @@ helm install my-release tractusx-edc/bdrs-server --version 0.0.2 \ | server.ingresses[0].certManager.issuer | string | `""` | If preset enables certificate generation via cert-manager namespace scoped issuer | | server.ingresses[0].className | string | `""` | Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use | | server.ingresses[0].enabled | bool | `false` | | -| server.ingresses[0].endpoints | list | `["protocol","public"]` | EDC endpoints exposed by this ingress resource | -| server.ingresses[0].hostname | string | `"bdrs-server.local"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | +| server.ingresses[0].endpoints | list | `["directory"]` | EDC endpoints exposed by this ingress resource | +| server.ingresses[0].hostname | string | `"bdrs-server.directory.local"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | | server.ingresses[0].tls | object | `{"enabled":false,"secretName":""}` | TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource | | server.ingresses[0].tls.enabled | bool | `false` | Enables TLS on the ingress resource | | server.ingresses[0].tls.secretName | string | `""` | If present overwrites the default secret name | @@ -95,8 +97,8 @@ helm install my-release tractusx-edc/bdrs-server --version 0.0.2 \ | server.ingresses[1].certManager.issuer | string | `""` | If preset enables certificate generation via cert-manager namespace scoped issuer | | server.ingresses[1].className | string | `""` | Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use | | server.ingresses[1].enabled | bool | `false` | | -| server.ingresses[1].endpoints | list | `["management","control"]` | EDC endpoints exposed by this ingress resource | -| server.ingresses[1].hostname | string | `"edc-control.intranet"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | +| server.ingresses[1].endpoints | list | `["management"]` | EDC endpoints exposed by this ingress resource | +| server.ingresses[1].hostname | string | `"bdrs-server.mgmt.local"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | | server.ingresses[1].tls | object | `{"enabled":false,"secretName":""}` | TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource | | server.ingresses[1].tls.enabled | bool | `false` | Enables TLS on the ingress resource | | server.ingresses[1].tls.secretName | string | `""` | If present overwrites the default secret name | @@ -148,6 +150,17 @@ helm install my-release tractusx-edc/bdrs-server --version 0.0.2 \ | serviceAccount.name | string | `""` | | | tests | object | `{"hookDeletePolicy":"before-hook-creation,hook-succeeded"}` | Configurations for Helm tests | | tests.hookDeletePolicy | string | `"before-hook-creation,hook-succeeded"` | Configure the hook-delete-policy for Helm tests | +| vault.hashicorp.healthCheck.enabled | bool | `true` | | +| vault.hashicorp.healthCheck.standbyOk | bool | `true` | | +| vault.hashicorp.paths.health | string | `"/v1/sys/health"` | | +| vault.hashicorp.paths.secret | string | `"/v1/secret"` | | +| vault.hashicorp.timeout | int | `30` | | +| vault.hashicorp.token | string | `"root"` | | +| vault.hashicorp.url | string | `"http://{{ .Release.Name }}-vault:8200"` | | +| vault.injector.enabled | bool | `false` | | +| vault.server.dev.devRootToken | string | `"root"` | | +| vault.server.dev.enabled | bool | `true` | | +| vault.server.postStart | string | `nil` | | ---------------------------------------------- Autogenerated from chart metadata using [helm-docs v1.10.0](https://github.com/norwoodj/helm-docs/releases/v1.10.0) diff --git a/charts/bdrs-server/templates/deployment.yaml b/charts/bdrs-server/templates/deployment.yaml index d71aded..4f2a255 100644 --- a/charts/bdrs-server/templates/deployment.yaml +++ b/charts/bdrs-server/templates/deployment.yaml @@ -64,7 +64,7 @@ spec: {{- if .Values.server.image.repository }} image: "{{ .Values.server.image.repository }}:{{ .Values.server.image.tag | default .Chart.AppVersion }}" {{- else }} - image: "tractusx/bdrs-server-memory:{{ .Values.server.image.tag | default .Chart.AppVersion }}" + image: "tractusx/bdrs-server:{{ .Values.server.image.tag | default .Chart.AppVersion }}" {{- end }} imagePullPolicy: {{ .Values.server.image.pullPolicy }} command: @@ -145,8 +145,8 @@ spec: ####### # API # ####### - - name: "EDC_API_AUTH_KEY" - value: {{ .Values.server.endpoints.management.authKey | required ".Values.runtime.endpoints.management.authKey is required" | quote }} + - name: "EDC_API_AUTH_KEY_ALIAS" + value: {{ .Values.server.endpoints.management.authKeyAlias | required ".Values.runtime.endpoints.management.authKeyAlias is required" | quote }} - name: "WEB_HTTP_PORT" value: {{ .Values.server.endpoints.default.port | quote }} - name: "WEB_HTTP_PATH" @@ -164,16 +164,34 @@ spec: ## POSTGRESQL ## ################ - # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/control-plane/store/sql/asset-index-sql - name: "EDC_DATASOURCE_DIDENTRY_NAME" value: "didentry" - - name: "EDC_DATASOURCE_DIDENTRY_USER" - value: {{ .Values.postgresql.auth.username | required ".Values.postgresql.auth.username is required" | quote }} - - name: "EDC_DATASOURCE_DIDENTRY_PASSWORD" - value: {{ .Values.postgresql.auth.password | required ".Values.postgresql.auth.password is required" | quote }} + + # note that the DB user and password must be put in the vault using the "edc.datasource.didentry.[user|password]" aliases, respectively! - name: "EDC_DATASOURCE_DIDENTRY_URL" value: {{ tpl .Values.postgresql.jdbcUrl . | quote }} + ########### + ## VAULT ## + ########### + + # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/main/edc-extensions/hashicorp-vault + - name: "EDC_VAULT_HASHICORP_URL" + value: {{ tpl .Values.vault.hashicorp.url . | quote }} + - name: "EDC_VAULT_HASHICORP_TOKEN" + value: {{ .Values.vault.hashicorp.token | required ".Values.vault.hashicorp.token is required" | quote }} + - name: "EDC_VAULT_HASHICORP_TIMEOUT_SECONDS" + value: {{ .Values.vault.hashicorp.timeout | quote }} + - name: "EDC_VAULT_HASHICORP_HEALTH_CHECK_ENABLED" + value: {{ .Values.vault.hashicorp.healthCheck.enabled | quote }} + - name: "EDC_VAULT_HASHICORP_HEALTH_CHECK_STANDBY_OK" + value: {{ .Values.vault.hashicorp.healthCheck.standbyOk | quote }} + - name: "EDC_VAULT_HASHICORP_API_SECRET_PATH" + value: {{ .Values.vault.hashicorp.paths.secret | quote }} + - name: "EDC_VAULT_HASHICORP_API_HEALTH_CHECK_PATH" + value: {{ .Values.vault.hashicorp.paths.health | quote }} + + ###################################### ## Additional environment variables ## ###################################### diff --git a/charts/bdrs-server/templates/service.yaml b/charts/bdrs-server/templates/service.yaml index d29cc36..253a3c0 100644 --- a/charts/bdrs-server/templates/service.yaml +++ b/charts/bdrs-server/templates/service.yaml @@ -40,7 +40,7 @@ spec: protocol: TCP name: management - port: {{ .Values.server.endpoints.directory.port }} - targetPort: public + targetPort: directory protocol: TCP name: directory selector: diff --git a/charts/bdrs-server/templates/tests/test.yaml b/charts/bdrs-server/templates/tests/test.yaml index 6cf3bbf..cf01bc6 100644 --- a/charts/bdrs-server/templates/tests/test.yaml +++ b/charts/bdrs-server/templates/tests/test.yaml @@ -36,11 +36,6 @@ spec: image: curlimages/curl command: [ 'curl', '--fail' ] args: [ '{{- printf "http://%s:%v%s/check/readiness" (include "bdrs.fullname" $ ) $.Values.server.endpoints.default.port $.Values.server.endpoints.default.path -}}' ] - {{/* Try getting a BPN/DID mapping via the management API */}} - - name: bdrs-management-api - image: curlimages/curl - command: [ 'curl', '-i', '--fail', '-X', 'GET', '-H', '{{- printf "x-api-key: %s" $.Values.server.endpoints.management.authKey }}' ] - args: [ '{{- printf "http://%s:%v%s/bpn-directory" (include "bdrs.fullname" $ ) $.Values.server.endpoints.management.port $.Values.server.endpoints.management.path -}}' ] restartPolicy: Never securityContext: fsGroup: 101 # curl_group diff --git a/charts/bdrs-server/values.yaml b/charts/bdrs-server/values.yaml index 99e583d..245b581 100644 --- a/charts/bdrs-server/values.yaml +++ b/charts/bdrs-server/values.yaml @@ -1,6 +1,5 @@ ################################################################################# -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021,2023 Contributors to the Eclipse Foundation +# Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) # # See the NOTICE file(s) distributed with this work for additional # information regarding copyright ownership. @@ -19,13 +18,13 @@ ################################################################################# --- -# Default values for eclipse-dataspace-connector. # This is a YAML-formatted file. # Declare variables to be passed into your templates. install: postgresql: true + vault: true fullnameOverride: "" nameOverride: "" @@ -90,7 +89,7 @@ server: # -- path for incoming api calls path: /api/management # -- authentication key, must be attached to each 'X-Api-Key' request header - authKey: "password" + authKeyAlias: "mgmt-api-key" # -- directory API directory: # -- port for incoming api calls @@ -164,13 +163,12 @@ server: ## Public / Internet facing Ingress - enabled: false # -- The hostname to be used to precisely map incoming traffic onto the underlying network service - hostname: "bdrs-server.local" + hostname: "bdrs-server.directory.local" # -- Additional ingress annotations to add annotations: {} # -- EDC endpoints exposed by this ingress resource endpoints: - - protocol - - public + - directory # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use className: "" # -- TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource @@ -185,16 +183,15 @@ server: issuer: "" # -- If preset enables certificate generation via cert-manager cluster-wide issuer clusterIssuer: "" - ## Private / Intranet facing Ingress + ## Ingress for the Management API, should not be internet facing - enabled: false # -- The hostname to be used to precisely map incoming traffic onto the underlying network service - hostname: "edc-control.intranet" + hostname: "bdrs-server.mgmt.local" # -- Additional ingress annotations to add annotations: {} # -- EDC endpoints exposed by this ingress resource endpoints: - management - - control # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use className: "" # -- TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource @@ -281,5 +278,24 @@ postgresql: enabled: false auth: database: "bdrs" - username: "postgres" + username: "bdrs" password: "password" + +vault: + injector: + enabled: false + server: + dev: + enabled: true + devRootToken: "root" + postStart: # must be set externally! + hashicorp: + url: "http://{{ .Release.Name }}-vault:8200" + token: "root" + timeout: 30 + healthCheck: + enabled: true + standbyOk: true + paths: + secret: /v1/secret + health: /v1/sys/health diff --git a/core/core-services/build.gradle.kts b/core/core-services/build.gradle.kts index 610da86..45a91bb 100644 --- a/core/core-services/build.gradle.kts +++ b/core/core-services/build.gradle.kts @@ -25,6 +25,7 @@ plugins { dependencies { implementation(libs.edc.spi.core) + implementation(libs.edc.lib.json) implementation(project(":spi:core-spi")) testImplementation(testFixtures(project(":spi:core-spi"))) } diff --git a/core/core-services/src/main/java/org/eclipse/tractusx/bdrs/core/BdrsCoreExtension.java b/core/core-services/src/main/java/org/eclipse/tractusx/bdrs/core/BdrsCoreExtension.java index 5470c80..907b3a0 100644 --- a/core/core-services/src/main/java/org/eclipse/tractusx/bdrs/core/BdrsCoreExtension.java +++ b/core/core-services/src/main/java/org/eclipse/tractusx/bdrs/core/BdrsCoreExtension.java @@ -21,9 +21,9 @@ package org.eclipse.tractusx.bdrs.core; +import org.eclipse.edc.json.JacksonTypeManager; import org.eclipse.edc.runtime.metamodel.annotation.BaseExtension; 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.security.Vault; @@ -65,10 +65,8 @@ public class BdrsCoreExtension implements ServiceExtension { private static final long DEFAULT_DURATION = 60; private static final int DEFAULT_TP_SIZE = 3; - @Inject - private TypeManager typeManager; - private HealthCheckServiceImpl healthCheckService; + private TypeManager typeManager; @Override public String name() { @@ -94,6 +92,14 @@ public void shutdown() { ServiceExtension.super.shutdown(); } + @Provider + public TypeManager typeManager() { + if (typeManager == null) { + typeManager = new JacksonTypeManager(); + } + return typeManager; + } + @Provider public HealthCheckService healthCheckService() { return healthCheckService; @@ -101,7 +107,7 @@ public HealthCheckService healthCheckService() { @Provider(isDefault = true) public DidEntryStore defaultDidEntryStore() { - return new InMemoryDidEntryStore(typeManager.getMapper()); + return new InMemoryDidEntryStore(typeManager().getMapper()); } @Provider(isDefault = true) diff --git a/extensions/store/sql/did-entry-store-sql/src/main/java/org/eclipse/tractusx/bdrs/sql/migration/DidEntryPostgresMigrationExtension.java b/extensions/store/sql/did-entry-store-sql/src/main/java/org/eclipse/tractusx/bdrs/sql/migration/DidEntryPostgresMigrationExtension.java index 8a5f3a1..e15a4c3 100644 --- a/extensions/store/sql/did-entry-store-sql/src/main/java/org/eclipse/tractusx/bdrs/sql/migration/DidEntryPostgresMigrationExtension.java +++ b/extensions/store/sql/did-entry-store-sql/src/main/java/org/eclipse/tractusx/bdrs/sql/migration/DidEntryPostgresMigrationExtension.java @@ -21,8 +21,18 @@ package org.eclipse.tractusx.bdrs.sql.migration; +import org.eclipse.edc.runtime.metamodel.annotation.Inject; +import org.eclipse.edc.spi.security.Vault; + public class DidEntryPostgresMigrationExtension extends AbstractPostgresqlMigrationExtension { private static final String NAME_SUBSYSTEM = "didentry"; + @Inject + private Vault vault; + + @Override + protected Vault getVault() { + return vault; + } protected String getSubsystemName() { return NAME_SUBSYSTEM; diff --git a/extensions/store/sql/sql-lib/src/main/java/org/eclipse/tractusx/bdrs/sql/migration/AbstractPostgresqlMigrationExtension.java b/extensions/store/sql/sql-lib/src/main/java/org/eclipse/tractusx/bdrs/sql/migration/AbstractPostgresqlMigrationExtension.java index 67ab9bf..6e0831a 100644 --- a/extensions/store/sql/sql-lib/src/main/java/org/eclipse/tractusx/bdrs/sql/migration/AbstractPostgresqlMigrationExtension.java +++ b/extensions/store/sql/sql-lib/src/main/java/org/eclipse/tractusx/bdrs/sql/migration/AbstractPostgresqlMigrationExtension.java @@ -21,28 +21,40 @@ package org.eclipse.tractusx.bdrs.sql.migration; import org.eclipse.edc.runtime.metamodel.annotation.Setting; +import org.eclipse.edc.spi.EdcException; +import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.persistence.EdcPersistenceException; +import org.eclipse.edc.spi.security.Vault; import org.eclipse.edc.spi.system.ServiceExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.spi.system.configuration.Config; import org.eclipse.edc.sql.DriverManagerConnectionFactory; import org.eclipse.edc.sql.datasource.ConnectionFactoryDataSource; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Objects; import java.util.Properties; +import java.util.function.Supplier; +import static java.util.Optional.ofNullable; import static org.flywaydb.core.api.MigrationVersion.LATEST; abstract class AbstractPostgresqlMigrationExtension implements ServiceExtension { private static final String EDC_DATASOURCE_PREFIX = "edc.datasource"; - private static final String DEFAULT_MIGRATION_ENABLED_TEMPLATE = "true"; @Setting(value = "Enable/disables subsystem schema migration", defaultValue = DEFAULT_MIGRATION_ENABLED_TEMPLATE, type = "boolean") private static final String MIGRATION_ENABLED_TEMPLATE = "org.eclipse.tractusx.edc.postgresql.migration.%s.enabled"; - private static final String DEFAULT_MIGRATION_SCHEMA = "public"; @Setting(value = "Schema used for the migration", defaultValue = DEFAULT_MIGRATION_SCHEMA) private static final String MIGRATION_SCHEMA = "org.eclipse.tractusx.edc.postgresql.migration.schema"; + private static final String PASSWORD = "password"; + private static final String USER = "user"; + private static final String URL = "url"; + + + private Monitor monitor; @Override public String name() { @@ -52,6 +64,7 @@ public String name() { @Override public void initialize(final ServiceExtensionContext context) { var config = context.getConfig(); + monitor = context.getMonitor().withPrefix("Migration"); var subSystemName = Objects.requireNonNull(getSubsystemName()); var enabled = config.getBoolean(MIGRATION_ENABLED_TEMPLATE.formatted(subSystemName), Boolean.valueOf(DEFAULT_MIGRATION_ENABLED_TEMPLATE)); @@ -65,15 +78,32 @@ public void initialize(final ServiceExtensionContext context) { var dataSourceName = datasourceConfig.getString("name", null); if (dataSourceName == null) { - context.getMonitor().warning("No 'name' setting in group %s found, no schema migrations will run for subsystem %s" + monitor.warning("No 'name' setting in group %s found, no schema migrations will run for subsystem %s" .formatted(configGroup, subSystemName)); return; } - var jdbcUrl = datasourceConfig.getString("url"); + var rootPath = EDC_DATASOURCE_PREFIX + "." + datasourceConfig.currentNode(); + + var urlProperty = rootPath + "." + URL; + var jdbcUrl = ofNullable(getVault().resolveSecret(urlProperty)).orElseGet(readFromConfig(datasourceConfig, URL)); + + if (jdbcUrl == null) { + throw new EdcException("Mandatory config '%s' not found. Please provide a value for the '%s' property, either as a secret in the vault or an application property.".formatted(urlProperty, urlProperty)); + } + + var jdbcUser = ofNullable(getVault().resolveSecret(rootPath + "." + USER)) + .orElseGet(readFromConfig(datasourceConfig, USER)); + var jdbcPassword = ofNullable(getVault().resolveSecret(rootPath + "." + PASSWORD)) + .orElseGet(readFromConfig(datasourceConfig, PASSWORD)); + var jdbcProperties = new Properties(); jdbcProperties.putAll(datasourceConfig.getRelativeEntries()); + // only set if not-null, otherwise Properties#add throws a NPE + ofNullable(jdbcUser).ifPresent(u -> jdbcProperties.put(USER, u)); + ofNullable(jdbcPassword).ifPresent(p -> jdbcProperties.put(PASSWORD, p)); + var driverManagerConnectionFactory = new DriverManagerConnectionFactory(); var dataSource = new ConnectionFactoryDataSource(driverManagerConnectionFactory, jdbcUrl, jdbcProperties); @@ -88,6 +118,15 @@ public void initialize(final ServiceExtensionContext context) { } } + protected abstract Vault getVault(); + protected abstract String getSubsystemName(); + private @NotNull Supplier<@Nullable String> readFromConfig(Config config, String value) { + return () -> { + var entry = EDC_DATASOURCE_PREFIX + "." + config.currentNode() + "." + value; + monitor.warning("Database configuration value '%s' not found in vault, will fall back to Config. Please consider putting database configuration into the vault.".formatted(entry)); + return config.getString(value, null); + }; + } } diff --git a/gradle.properties b/gradle.properties index cca39e2..2419139 100644 --- a/gradle.properties +++ b/gradle.properties @@ -19,7 +19,7 @@ # # group=org.eclipse.tractusx -version=0.0.2 +version=0.0.3 # these define the versions of the EDC Build Plugin, the Annotation Processor and the Metamodel. # generally this should match the version of EDC in gradle/libs.versions.toml edcGradlePluginsVersion=0.5.1 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ca027d1..3323d66 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,35 +3,52 @@ format.version = "1.1" [versions] assertj = "3.25.3" -edc = "0.6.0" +edc = "0.6.2" nimbus = "9.37.3" restAssured = "5.4.0" jupiter = "5.10.2" postgres = "42.7.3" -flyway = "10.10.0" +flyway = "10.11.1" [libraries] edc-core-jetty = { module = "org.eclipse.edc:jetty-core", version.ref = "edc" } edc-core-jersey = { module = "org.eclipse.edc:jersey-core", version.ref = "edc" } +edc-core-did = { module = "org.eclipse.edc:identity-did-core", version.ref = "edc" } edc-boot = { module = "org.eclipse.edc:boot", version.ref = "edc" } +edc-vc-jwt = { module = "org.eclipse.edc:jwt-verifiable-credentials", version.ref = "edc" } +edc-vc = { module = "org.eclipse.edc:verifiable-credentials", version.ref = "edc" } +edc-identitytrust-service = { module = "org.eclipse.edc:identity-trust-service", version.ref = "edc" } +edc-identitytrust-issuers = { module = "org.eclipse.edc:identity-trust-issuers-configuration", version.ref = "edc" } +edc-identitytrust-transform = { module = "org.eclipse.edc:identity-trust-transform", version.ref = "edc" } +edc-identitydidweb = { module = "org.eclipse.edc:identity-did-web", version.ref = "edc" } edc-spi-core = { module = "org.eclipse.edc:core-spi", version.ref = "edc" } +edc-spi-did = { module = "org.eclipse.edc:identity-did-spi", version.ref = "edc" } edc-spi-web = { module = "org.eclipse.edc:web-spi", version.ref = "edc" } edc-spi-transaction = { module = "org.eclipse.edc:transaction-spi", version.ref = "edc" } edc-spi-transaction-datasource = { module = "org.eclipse.edc:transaction-datasource-spi", version.ref = "edc" } edc-connector-core = { module = "org.eclipse.edc:connector-core", version.ref = "edc" } -#edc-spi-identity-trust = { module = "org.eclipse.edc:identity-trust-spi", version.ref = "edc" } edc-spi-jwt = { module = "org.eclipse.edc:jwt-spi", version.ref = "edc" } +edc-spi-token = { module = "org.eclipse.edc:token-spi", version.ref = "edc" } +edc-spi-identitytrust = { module = "org.eclipse.edc:identity-trust-spi", version.ref = "edc" } +edc-spi-vc = { module = "org.eclipse.edc:verifiable-credentials-spi", version.ref = "edc" } edc-spi-auth = { module = "org.eclipse.edc:auth-spi", version.ref = "edc" } edc-auth-tokenbased = { module = "org.eclipse.edc:auth-tokenbased", version.ref = "edc" } edc-vault-filesystem = { module = "org.eclipse.edc:vault-filesystem", version.ref = "edc" } +edc-vault-hashicorp = { module = "org.eclipse.edc:vault-hashicorp", version.ref = "edc" } edc-junit = { module = "org.eclipse.edc:junit", version.ref = "edc" } edc-api-observability = { module = "org.eclipse.edc:api-observability", version.ref = "edc" } edc-core-sql = { module = "org.eclipse.edc:sql-core", version.ref = "edc" } edc-transaction-local = { module = "org.eclipse.edc:transaction-local", version.ref = "edc" } edc-sql-pool = { module = "org.eclipse.edc:sql-pool-apache-commons", version.ref = "edc" } +edc-lib-keys = { module = "org.eclipse.edc:keys-lib", version.ref = "edc" } +edc-lib-transform = { module = "org.eclipse.edc:transform-lib", version.ref = "edc" } +edc-lib-jsonld = { module = "org.eclipse.edc:json-ld-lib", version.ref = "edc" } +edc-lib-json = { module = "org.eclipse.edc:json-lib", version.ref = "edc" } +edc-lib-http = { module = "org.eclipse.edc:http-lib", version.ref = "edc" } + # Third party libs assertj = { module = "org.assertj:assertj-core", version.ref = "assertj" } nimbus-jwt = { module = "com.nimbusds:nimbus-jose-jwt", version.ref = "nimbus" } @@ -48,9 +65,9 @@ shadow = { id = "com.github.johnrengelman.shadow", version = "8.1.1" } [bundles] # TODO: "edc-vault-filesystem" - remove dependency on org.eclipse.edc.vault.filesystem.JskPrivateKeyResolverExtension bdrs-boot = [ - "edc-core-jetty", - "edc-core-jersey", - "edc-boot", - "edc-spi-auth", - "edc-auth-tokenbased", + "edc-core-jetty", + "edc-core-jersey", + "edc-boot", + "edc-spi-auth", + "edc-auth-tokenbased", ] diff --git a/runtimes/bdrs-server-memory/build.gradle.kts b/runtimes/bdrs-server-memory/build.gradle.kts index 63cc4d5..a66bd78 100644 --- a/runtimes/bdrs-server-memory/build.gradle.kts +++ b/runtimes/bdrs-server-memory/build.gradle.kts @@ -27,10 +27,14 @@ plugins { dependencies { runtimeOnly(libs.bundles.bdrs.boot) runtimeOnly(libs.edc.api.observability) + runtimeOnly(libs.edc.core.did) + runtimeOnly(libs.edc.identitydidweb) + runtimeOnly(libs.edc.identitytrust.issuers) runtimeOnly(project(":core:core-services")) runtimeOnly(project(":api:directory-api")) runtimeOnly(project(":api:management-api")) + runtimeOnly(project(":api:authentication")) } tasks.withType { diff --git a/runtimes/bdrs-server/build.gradle.kts b/runtimes/bdrs-server/build.gradle.kts index 4a9247a..c2662fd 100644 --- a/runtimes/bdrs-server/build.gradle.kts +++ b/runtimes/bdrs-server/build.gradle.kts @@ -25,16 +25,13 @@ plugins { } dependencies { - runtimeOnly(libs.bundles.bdrs.boot) - runtimeOnly(libs.edc.api.observability) + runtimeOnly(project(":runtimes:bdrs-server-memory")) runtimeOnly(libs.edc.transaction.local) runtimeOnly(libs.edc.sql.pool) runtimeOnly(libs.postgres) + runtimeOnly(libs.edc.vault.hashicorp) - runtimeOnly(project(":core:core-services")) runtimeOnly(project(":extensions:store:sql:did-entry-store-sql")) - runtimeOnly(project(":api:directory-api")) - runtimeOnly(project(":api:management-api")) } tasks.withType { diff --git a/settings.gradle.kts b/settings.gradle.kts index 8a8961e..10440c5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -48,6 +48,7 @@ include(":spi:core-spi") include(":core:core-services") include(":api:directory-api") include(":api:management-api") +include(":api:authentication") include(":runtimes:bdrs-server") include(":runtimes:bdrs-server-memory") diff --git a/system-tests/helm/kind.config.yaml b/system-tests/helm/kind.config.yaml new file mode 100644 index 0000000..9d918bb --- /dev/null +++ b/system-tests/helm/kind.config.yaml @@ -0,0 +1,37 @@ +# +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +--- +kind: Cluster +apiVersion: kind.x-k8s.io/v1alpha4 +nodes: + - role: control-plane + kubeadmConfigPatches: + - | + kind: InitConfiguration + nodeRegistration: + kubeletExtraArgs: + node-labels: "ingress-ready=true" + extraPortMappings: + - containerPort: 80 + hostPort: 80 + protocol: TCP + - containerPort: 443 + hostPort: 443 + protocol: TCP \ No newline at end of file diff --git a/system-tests/helm/values-test.yaml b/system-tests/helm/values-test.yaml new file mode 100644 index 0000000..6d0336a --- /dev/null +++ b/system-tests/helm/values-test.yaml @@ -0,0 +1,38 @@ +################################################################################# +# Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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. +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +################################################################################# + +# this file is intended to provide configuration for a BDRS deployment in a CI test +--- +install: + vault: false +server: + ingresses: + - enabled: true + hostname: "localhost" + endpoints: + - directory + - management + className: "nginx" + tls: + enabled: true + secretName: "tls-secret" + +vault: + hashicorp: + url: "http://vault:8200" \ No newline at end of file diff --git a/system-tests/helm/values-vault-test.yaml b/system-tests/helm/values-vault-test.yaml new file mode 100644 index 0000000..13cc072 --- /dev/null +++ b/system-tests/helm/values-vault-test.yaml @@ -0,0 +1,49 @@ +################################################################################# +# Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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. +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +################################################################################# + +# this file is intended to provide configuration for a standalone Hashicorp Vault deployment used in a BDRS CI test +--- +injector: + enabled: false +server: + dev: + enabled: true + devRootToken: "root" + postStart: + - sh + - -c + - |- + { + sleep 5 + + /bin/vault kv put secret/edc.datasource.didentry.user content=bdrs + + /bin/vault kv put secret/edc.datasource.didentry.password content=password + + /bin/vault kv put secret/mgmt-api-key content=password + } +hashicorp: + token: "root" + timeout: 30 + healthCheck: + enabled: true + standbyOk: true + paths: + secret: /v1/secret + health: /v1/sys/health \ No newline at end of file diff --git a/system-tests/test-directory/build.gradle.kts b/system-tests/test-directory/build.gradle.kts index 76161e8..a131bcc 100644 --- a/system-tests/test-directory/build.gradle.kts +++ b/system-tests/test-directory/build.gradle.kts @@ -20,7 +20,10 @@ dependencies { testImplementation(libs.edc.junit) testImplementation(libs.junit.jupiter.api) testImplementation(libs.restAssured) + testImplementation(libs.edc.spi.web) + testImplementation(libs.edc.spi.did) - testCompileOnly(project(":system-tests:test-server")) + testImplementation(testFixtures(libs.edc.vc.jwt)) // JwtCreationUtils + testImplementation(libs.nimbus.jwt) } diff --git a/system-tests/test-directory/src/test/java/org/eclipse/tractusx/bdrs/test/directory/DirectoryEndToEndTest.java b/system-tests/test-directory/src/test/java/org/eclipse/tractusx/bdrs/test/directory/DirectoryEndToEndTest.java index 7387cad..18584ad 100644 --- a/system-tests/test-directory/src/test/java/org/eclipse/tractusx/bdrs/test/directory/DirectoryEndToEndTest.java +++ b/system-tests/test-directory/src/test/java/org/eclipse/tractusx/bdrs/test/directory/DirectoryEndToEndTest.java @@ -14,18 +14,36 @@ package org.eclipse.tractusx.bdrs.test.directory; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.jwk.Curve; +import com.nimbusds.jose.jwk.ECKey; +import com.nimbusds.jose.jwk.gen.ECKeyGenerator; +import io.restassured.response.ValidatableResponse; import io.restassured.specification.RequestSpecification; +import org.eclipse.edc.iam.did.spi.document.DidDocument; +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.junit.annotations.EndToEndTest; import org.eclipse.edc.junit.extensions.EdcRuntimeExtension; +import org.eclipse.edc.spi.result.Result; +import org.eclipse.edc.verifiablecredentials.jwt.JwtCreationUtils; +import org.eclipse.edc.web.spi.ApiErrorDetail; +import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import java.io.ByteArrayInputStream; import java.io.IOException; import java.net.URI; +import java.util.List; import java.util.Map; +import java.util.stream.Stream; import java.util.zip.GZIPInputStream; import static io.restassured.RestAssured.config; @@ -34,7 +52,9 @@ import static io.restassured.config.DecoderConfig.decoderConfig; import static io.restassured.http.ContentType.JSON; import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.edc.spi.result.Result.success; import static org.eclipse.edc.util.io.Ports.getFreePort; +import static org.eclipse.tractusx.bdrs.test.directory.TestData.VP_CONTENT_EXAMPLE; /** * Performs end-to-end testing of the BPN Directory. @@ -43,77 +63,211 @@ public class DirectoryEndToEndTest { private static final URI API_ENDPOINT = URI.create("http://localhost:" + getFreePort() + "/api"); private static final URI MANAGEMENT_ENDPOINT = URI.create("http://localhost:" + getFreePort() + "/management/v1"); + private static final URI DIRECTORY_ENDPOINT = URI.create("http://localhost:" + getFreePort() + "/directory/v1"); private static final String BPN_DIRECTORY = "bpn-directory"; private static final String AUTH_KEY = "1234"; - private static final String BPN1 = "BPN12345"; private static final String DID1 = "did:web:localhost/foo"; + @RegisterExtension + protected static EdcRuntimeExtension runtime = new EdcRuntimeExtension( + ":system-tests:test-server", + "BDRS Server", + Map.of("web.http.port", String.valueOf(API_ENDPOINT.getPort()), + "web.http.management.port", String.valueOf(MANAGEMENT_ENDPOINT.getPort()), + "web.http.management.path", String.valueOf(MANAGEMENT_ENDPOINT.getPath()), + "web.http.directory.port", String.valueOf(DIRECTORY_ENDPOINT.getPort()), + "web.http.directory.path", String.valueOf(DIRECTORY_ENDPOINT.getPath()), + "edc.iam.trusted-issuer.test.id", "did:web:some-issuer", + "edc.api.auth.key", AUTH_KEY) + ); + private final String issuerId = "did:web:some-issuer"; + private final String holderId = "did:web:bdrs-client"; + private ECKey vcIssuerKey; + private ECKey vpHolderKey; + private ObjectMapper mapper; - private static final String BPN2 = "BPN67890"; - private static final String DID2 = "did:web:localhost/bar"; - private static final String UPDATED_DID2 = "did:web:localhost/baz"; + @BeforeEach + void setUp() throws JOSEException { + mapper = new ObjectMapper(); + vcIssuerKey = new ECKeyGenerator(Curve.P_256).keyID(issuerId + "#key-1").generate(); + vpHolderKey = new ECKeyGenerator(Curve.P_256).keyID(holderId + "#key-1").generate(); + } - @RegisterExtension - protected static EdcRuntimeExtension runtime = - new EdcRuntimeExtension( - ":system-tests:test-server", - "bdrs", - Map.of("web.http.port", String.valueOf(API_ENDPOINT.getPort()), - "web.http.management.port", String.valueOf(MANAGEMENT_ENDPOINT.getPort()), - "web.http.management.path", String.valueOf(MANAGEMENT_ENDPOINT.getPath()), - "edc.api.auth.key", AUTH_KEY) - ); + @DisplayName("Reject a request without Authorization header") + @Test + void getDirectory_missingAuthHeader() throws IOException { + seedServer(BPN1, DID1); + var errors = getBpnDirectory(apiRequest()).statusCode(401) + .extract() + .body() + .as(ApiErrorDetail[].class); - private ObjectMapper mapper; + assertThat(errors.length).isEqualTo(1); + assertThat(errors[0].getType()).isEqualTo("AuthenticationFailed"); + assertThat(errors[0].getMessage()).isEqualTo("Header 'Authorization' not present"); + } + @DisplayName("Reject an Authorization header that does not start with \"Bearer\"") @Test - void verifyPublicBpnDirectoryRequest() throws IOException { + void getDirectory_authHeaderNoBearer() throws IOException { seedServer(BPN1, DID1); - var result = getBpnDirectory(apiRequest()); - assertThat(result.get(BPN1)).isEqualTo(DID1); + var errors = getBpnDirectory(apiRequest().header("Authorization", "foobar")).statusCode(401) + .extract() + .body() + .as(ApiErrorDetail[].class); + + assertThat(errors.length).isEqualTo(1); + assertThat(errors[0].getType()).isEqualTo("AuthenticationFailed"); + assertThat(errors[0].getMessage()).isEqualTo("Request could not be authenticated"); } + @DisplayName("Reject an Authorization header that is not in JWT format") @Test - void verifyManagementApi() throws IOException { + void getDirectory_authHeaderNotJwt() throws IOException { seedServer(BPN1, DID1); - seedServer(BPN2, DID2); + var errors = getBpnDirectory(apiRequest().header("Authorization", "Bearer foobar")).statusCode(401) + .extract() + .body() + .as(ApiErrorDetail[].class); - // verify BPNs are returned - var result = getBpnDirectory(managementRequest()); + assertThat(errors.length).isEqualTo(1); + assertThat(errors[0].getType()).isEqualTo("AuthenticationFailed"); + assertThat(errors[0].getMessage()).isEqualTo("Request could not be authenticated"); + } - assertThat(result.get(BPN1)).isEqualTo(DID1); - assertThat(result.get(BPN2)).isEqualTo(DID2); + @DisplayName("Accept a valid VP containing a valid MembershipCredential") + @Test + void getDirectory_validPresentation() throws IOException { + registerDids(); - // verify delete - managementRequest() - .when() - .delete(BPN_DIRECTORY + "/" + BPN1) - .then() - .statusCode(204); + // create VC-JWT (signed by the central issuer) + var vcJwt1 = JwtCreationUtils.createJwt(vcIssuerKey, issuerId, "degreeSub", holderId, Map.of("vc", asMap(TestData.MEMBERSHIP_CREDENTIAL.formatted(holderId)))); - result = getBpnDirectory(managementRequest()); + // create VP-JWT (signed by the presenter) that contains the VP as a claim + var vpJwt = JwtCreationUtils.createJwt(vpHolderKey, holderId, null, "bdrs-server-audience", Map.of("vp", asMap(VP_CONTENT_EXAMPLE.formatted(holderId, "\"" + vcJwt1 + "\"")))); - assertThat(result.get(BPN1)).isNull(); - assertThat(result.get(BPN2)).isEqualTo(DID2); + seedServer(BPN1, DID1); + var bytes = getBpnDirectory(apiRequest().header("Authorization", "Bearer " + vpJwt)) + .statusCode(200) + .extract().response().asByteArray(); + var result = deserialize(bytes); - // verify update - var content = Map.of("bpn", BPN2, "did", UPDATED_DID2); - managementRequest() - .body(content) - .when() - .put(BPN_DIRECTORY) - .then() - .statusCode(204); + assertThat(result).isNotEmpty().containsEntry(BPN1, DID1); + } + + @DisplayName("Reject VPs that contain a single VC, that is not a MembershipCredential") + @Test + void getDirectory_validPresentation_notMembershipCred() throws IOException { + registerDids(); + + // create VC-JWT (signed by the central issuer) + var vcJwt1 = JwtCreationUtils.createJwt(vcIssuerKey, issuerId, "degreeSub", holderId, Map.of("vc", asMap(TestData.SOME_OTHER_CREDENTIAL.formatted(holderId)))); - result = getBpnDirectory(managementRequest()); + // create VP-JWT (signed by the presenter) that contains the VP as a claim + var vpJwt = JwtCreationUtils.createJwt(vpHolderKey, holderId, null, "bdrs-server-audience", Map.of("vp", asMap(VP_CONTENT_EXAMPLE.formatted(holderId, "\"" + vcJwt1 + "\"")))); - assertThat(result.get(BPN2)).isEqualTo(UPDATED_DID2); + seedServer(BPN1, DID1); + getBpnDirectory(apiRequest().header("Authorization", "Bearer " + vpJwt)) + .statusCode(401); } - @BeforeEach - void setUp() { - mapper = new ObjectMapper(); + @DisplayName("Reject VP that contains no credentials") + @Test + void getDirectory_validPresentation_noCredential() throws IOException { + registerDids(); + + // create VP-JWT (signed by the presenter) that contains the VP as a claim + var vpJwt = JwtCreationUtils.createJwt(vpHolderKey, holderId, null, "bdrs-server-audience", Map.of()); + + seedServer(BPN1, DID1); + getBpnDirectory(apiRequest().header("Authorization", "Bearer " + vpJwt)) + .statusCode(401); + } + + @DisplayName("Reject VPs, that contain a MC and other credentials") + @Test + void getDirectory_validPresentation_multipleCredentials() throws IOException { + registerDids(); + + // create VC-JWT (signed by the central issuer) + var vcJwt1 = JwtCreationUtils.createJwt(vcIssuerKey, issuerId, "degreeSub", holderId, Map.of("vc", asMap(TestData.SOME_OTHER_CREDENTIAL.formatted(holderId)))); + + + // create VC-JWT (signed by the central issuer) + var vcJwt2 = JwtCreationUtils.createJwt(vcIssuerKey, issuerId, "degreeSub", holderId, Map.of("vc", asMap(TestData.MEMBERSHIP_CREDENTIAL.formatted(holderId)))); + + // create VP-JWT (signed by the presenter) that contains the VP as a claim + var credentialContent = "\"%s\", \"%s\"".formatted(vcJwt1, vcJwt2); + var vpJwt = JwtCreationUtils.createJwt(vpHolderKey, holderId, null, "bdrs-server-audience", Map.of("vp", asMap(VP_CONTENT_EXAMPLE.formatted(holderId, credentialContent)))); + + seedServer(BPN1, DID1); + getBpnDirectory(apiRequest().header("Authorization", "Bearer " + vpJwt)) + .statusCode(401); + } + + @DisplayName("Reject a spoofed VP") + @Test + void getDirectory_spoofedPresentation() throws Exception { + registerDids(); + + var spoofedKey = new ECKeyGenerator(Curve.P_256).keyID(holderId + "#key-1").generate(); + // create VC-JWT (signed by the central issuer) + var vcJwt1 = JwtCreationUtils.createJwt(vcIssuerKey, issuerId, "degreeSub", holderId, Map.of("vc", asMap(TestData.MEMBERSHIP_CREDENTIAL.formatted(holderId)))); + + // create VP-JWT (signed by the presenter) that contains the VP as a claim + var vpJwt = JwtCreationUtils.createJwt(spoofedKey, holderId, null, "bdrs-server-audience", Map.of("vp", asMap(VP_CONTENT_EXAMPLE.formatted(holderId, "\"" + vcJwt1 + "\"")))); + + seedServer(BPN1, DID1); + getBpnDirectory(apiRequest().header("Authorization", "Bearer " + vpJwt)) + .statusCode(401); + } + + @DisplayName("Reject a spoofed VC") + @Test + void getDirectory_spoofedCredential() throws Exception { + registerDids(); + + var spoofedKey = new ECKeyGenerator(Curve.P_256).keyID(holderId + "#key-1").generate(); + // create VC-JWT (signed by the central issuer) + var vcJwt1 = JwtCreationUtils.createJwt(spoofedKey, issuerId, "degreeSub", holderId, Map.of("vc", asMap(TestData.MEMBERSHIP_CREDENTIAL.formatted(holderId)))); + + // create VP-JWT (signed by the presenter) that contains the VP as a claim + var vpJwt = JwtCreationUtils.createJwt(vpHolderKey, holderId, null, "bdrs-server-audience", Map.of("vp", asMap(VP_CONTENT_EXAMPLE.formatted(holderId, "\"" + vcJwt1 + "\"")))); + + seedServer(BPN1, DID1); + getBpnDirectory(apiRequest().header("Authorization", "Bearer " + vpJwt)) + .statusCode(401); + } + + // registers a DidResolver for the "web" method. this method has to be called from a test method! + private void registerDids() { + runtime.getService(DidResolverRegistry.class) + .register(new DidResolver() { + @Override + public @NotNull String getMethod() { + return "web"; + } + + @Override + public @NotNull Result resolve(String s) { + return Stream.of(vcIssuerKey, vpHolderKey).filter(key -> key.getKeyID().startsWith(s)) + .findFirst() + .map(key -> success(createDidDocument(key.toPublicJWK(), s))) + .orElseGet(() -> Result.failure("No such did")); + } + }); + } + + private DidDocument createDidDocument(ECKey publickey, String did) { + return DidDocument.Builder.newInstance() + .verificationMethod(List.of(VerificationMethod.Builder.newInstance() + .id(publickey.getKeyID()) + .publicKeyJwk(publickey.toJSONObject()) + .type("JsonWebKey2020") + .build())) + .id(did) + .build(); } private void seedServer(String bpn, String did) { @@ -126,21 +280,15 @@ private void seedServer(String bpn, String did) { .statusCode(204); } - private Map getBpnDirectory(RequestSpecification spec) throws IOException { - return deserialize(spec - .config(config().decoderConfig(decoderConfig().contentDecoders(DEFLATE))) + private ValidatableResponse getBpnDirectory(RequestSpecification spec) throws IOException { + return spec.config(config().decoderConfig(decoderConfig().contentDecoders(DEFLATE))) .when() .get(BPN_DIRECTORY) - .then() - .statusCode(200) - .extract() - .response() - .asByteArray()); + .then(); } - private RequestSpecification apiRequest() { - return given().baseUri(API_ENDPOINT.toString()) + return given().baseUri(DIRECTORY_ENDPOINT.toString()) .headers(Map.of()); } @@ -157,4 +305,12 @@ private Map deserialize(byte[] response) throws IOException { return mapper.readValue(decompressed, Map.class); } + private Map asMap(String rawContent) { + try { + return mapper.readValue(rawContent, new TypeReference<>() { + }); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } } diff --git a/system-tests/test-directory/src/test/java/org/eclipse/tractusx/bdrs/test/directory/ManagementApiEndToEndTest.java b/system-tests/test-directory/src/test/java/org/eclipse/tractusx/bdrs/test/directory/ManagementApiEndToEndTest.java new file mode 100644 index 0000000..acd4ad2 --- /dev/null +++ b/system-tests/test-directory/src/test/java/org/eclipse/tractusx/bdrs/test/directory/ManagementApiEndToEndTest.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.tractusx.bdrs.test.directory; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.restassured.specification.RequestSpecification; +import org.eclipse.edc.junit.annotations.EndToEndTest; +import org.eclipse.edc.junit.extensions.EdcRuntimeExtension; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.net.URI; +import java.util.Map; +import java.util.zip.GZIPInputStream; + +import static io.restassured.RestAssured.config; +import static io.restassured.RestAssured.given; +import static io.restassured.config.DecoderConfig.ContentDecoder.DEFLATE; +import static io.restassured.config.DecoderConfig.decoderConfig; +import static io.restassured.http.ContentType.JSON; +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.edc.util.io.Ports.getFreePort; + +/** + * Performs end-to-end testing of the BPN Directory. + */ +@EndToEndTest +public class ManagementApiEndToEndTest { + private static final URI API_ENDPOINT = URI.create("http://localhost:" + getFreePort() + "/api"); + private static final URI MANAGEMENT_ENDPOINT = URI.create("http://localhost:" + getFreePort() + "/management/v1"); + private static final URI DIRECTORY_ENDPOINT = URI.create("http://localhost:" + getFreePort() + "/directory/v1"); + private static final String BPN_DIRECTORY = "bpn-directory"; + + private static final String AUTH_KEY = "1234"; + + private static final String BPN1 = "BPN12345"; + private static final String DID1 = "did:web:localhost/foo"; + + private static final String BPN2 = "BPN67890"; + private static final String DID2 = "did:web:localhost/bar"; + private static final String UPDATED_DID2 = "did:web:localhost/baz"; + + @RegisterExtension + protected static EdcRuntimeExtension runtime = + new EdcRuntimeExtension( + ":system-tests:test-server", + "bdrs", + Map.of("web.http.port", String.valueOf(API_ENDPOINT.getPort()), + "web.http.management.port", String.valueOf(MANAGEMENT_ENDPOINT.getPort()), + "web.http.management.path", String.valueOf(MANAGEMENT_ENDPOINT.getPath()), + "web.http.directory.port", String.valueOf(DIRECTORY_ENDPOINT.getPort()), + "web.http.directory.path", String.valueOf(DIRECTORY_ENDPOINT.getPath()), + "edc.api.auth.key", AUTH_KEY) + ); + + private ObjectMapper mapper; + + + @Test + void verifyManagementApi() throws IOException { + seedServer(BPN1, DID1); + seedServer(BPN2, DID2); + + // verify BPNs are returned + var result = getBpnDirectory(managementRequest()); + + assertThat(result.get(BPN1)).isEqualTo(DID1); + assertThat(result.get(BPN2)).isEqualTo(DID2); + + // verify delete + managementRequest() + .when() + .delete(BPN_DIRECTORY + "/" + BPN1) + .then() + .statusCode(204); + + result = getBpnDirectory(managementRequest()); + + assertThat(result.get(BPN1)).isNull(); + assertThat(result.get(BPN2)).isEqualTo(DID2); + + // verify update + var content = Map.of("bpn", BPN2, "did", UPDATED_DID2); + managementRequest() + .body(content) + .when() + .put(BPN_DIRECTORY) + .then() + .statusCode(204); + + result = getBpnDirectory(managementRequest()); + + assertThat(result.get(BPN2)).isEqualTo(UPDATED_DID2); + } + + @BeforeEach + void setUp() { + mapper = new ObjectMapper(); + } + + private void seedServer(String bpn, String did) { + var content = Map.of("bpn", bpn, "did", did); + managementRequest() + .body(content) + .when() + .post(BPN_DIRECTORY) + .then() + .statusCode(204); + } + + private Map getBpnDirectory(RequestSpecification spec) throws IOException { + return deserialize(spec + .config(config().decoderConfig(decoderConfig().contentDecoders(DEFLATE))) + .when() + .get(BPN_DIRECTORY) + .then() + .statusCode(200) + .extract() + .response() + .asByteArray()); + } + + + private RequestSpecification managementRequest() { + return given().baseUri(MANAGEMENT_ENDPOINT.toString()) + .headers(Map.of("x-api-key", AUTH_KEY)) + .contentType(JSON); + } + + private Map deserialize(byte[] response) throws IOException { + var stream = new GZIPInputStream(new ByteArrayInputStream(response)); + var decompressed = stream.readAllBytes(); + //noinspection unchecked + return mapper.readValue(decompressed, Map.class); + } + +} diff --git a/system-tests/test-directory/src/test/java/org/eclipse/tractusx/bdrs/test/directory/TestData.java b/system-tests/test-directory/src/test/java/org/eclipse/tractusx/bdrs/test/directory/TestData.java new file mode 100644 index 0000000..970feca --- /dev/null +++ b/system-tests/test-directory/src/test/java/org/eclipse/tractusx/bdrs/test/directory/TestData.java @@ -0,0 +1,62 @@ +package org.eclipse.tractusx.bdrs.test.directory; + +public class TestData { + public static final String MEMBERSHIP_CREDENTIAL = """ + { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/catenax/credentials/v1.0.0" + ], + "id": "1f36af58-0fc0-4b24-9b1c-e37d59668089", + "type": [ + "VerifiableCredential", + "MembershipCredential" + ], + "issuer": "did:web:com.example.issuer", + "issuanceDate": "2021-06-16T18:56:59Z", + "expirationDate": "2099-06-16T18:56:59Z", + "credentialSubject": { + "id": "%s", + "holderIdentifier": "BPNL000000001" + } + } + """; + + public static final String SOME_OTHER_CREDENTIAL = """ + { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/catenax/credentials/v1.0.0" + ], + "id": "1f36af58-0fc0-4b24-9b1c-e37d59668089", + "type": [ + "VerifiableCredential", + "SomeOtherCredential" + ], + "issuer": "did:web:com.example.issuer", + "issuanceDate": "2021-06-16T18:56:59Z", + "expirationDate": "2099-06-16T18:56:59Z", + "credentialSubject": { + "id": "%s", + "holderIdentifier": "BPNL000000001" + } + } + """; + + public static final String VP_CONTENT_EXAMPLE = """ + { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/2018/credentials/examples/v1" + ], + "id": "https://exapmle.com/test-vp", + "holder": "%s", + "type": [ + "VerifiablePresentation" + ], + "verifiableCredential": [ + %s + ] + } + """; +} diff --git a/system-tests/test-server/build.gradle.kts b/system-tests/test-server/build.gradle.kts index 646efad..c6cc0fe 100644 --- a/system-tests/test-server/build.gradle.kts +++ b/system-tests/test-server/build.gradle.kts @@ -21,24 +21,19 @@ plugins { id("application") - alias(libs.plugins.shadow) } dependencies { - api(libs.bundles.bdrs.boot) - api(project(":core:core-services")) - api(project(":api:directory-api")) - api(project(":api:management-api")) -} - -tasks.withType { - exclude("**/pom.properties", "**/pom.xm") - mergeServiceFiles() - archiveFileName.set("${project.name}.jar") -} + runtimeOnly(project(":core:core-services")) + runtimeOnly(project(":api:directory-api")) + runtimeOnly(project(":api:management-api")) + runtimeOnly(project(":api:authentication")) + // will replace this with a mocked Did Resolver + // runtimeOnly(libs.edc.identitydidweb) -application { - mainClass.set("org.eclipse.edc.boot.system.runtime.BaseRuntime") + runtimeOnly(libs.edc.identitytrust.issuers) + runtimeOnly(libs.bundles.bdrs.boot) + runtimeOnly(libs.edc.core.did) } edcBuild {