diff --git a/.github/workflows/ci-main.yml b/.github/workflows/ci-main.yml index f5cbe5a..ef4e986 100644 --- a/.github/workflows/ci-main.yml +++ b/.github/workflows/ci-main.yml @@ -9,14 +9,14 @@ jobs: build: runs-on: ubuntu-22.04 steps: - - uses: actions/setup-java@v2 + - uses: actions/setup-java@v4 with: java-version: 17 distribution: adopt - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/cache@v2 + - uses: actions/cache@v3 with: path: | ~/.m2/repository @@ -27,10 +27,6 @@ jobs: APP_LATEST_REV=$(git rev-list --tags --max-count=1) APP_LATEST_TAG=$(git describe --tags ${APP_LATEST_REV} 2> /dev/null || echo 0.0.0) echo "APP_VERSION=${APP_LATEST_TAG}-${APP_SHA}" >> ${GITHUB_ENV} - - name: copyContext - run: |- - rm ./src/main/resources/static/context.json - echo '${{ secrets.CONTEXT_FILE }}' > ./src/main/resources/static/context.json - name: mvn run: |- mvn versions:set \ @@ -57,6 +53,10 @@ jobs: --file ./Dockerfile --tag "${APP_PACKAGES_URL}:${APP_VERSION}"; docker push "${APP_PACKAGES_URL}:${APP_VERSION}"; + docker build . + --file ./DB-Changelog-Dockerfile + --tag "${APP_PACKAGES_URL}-initcontainer:${APP_VERSION}"; + docker push "${APP_PACKAGES_URL}-initcontainer:${APP_VERSION}"; env: APP_PACKAGES_URL: ghcr.io/worldhealthorganization/tng-key-distribution/tng-key-distribution APP_PACKAGES_USERNAME: ${{ github.actor }} diff --git a/.github/workflows/ci-openapi.yml b/.github/workflows/ci-openapi.yml deleted file mode 100644 index ef64dd0..0000000 --- a/.github/workflows/ci-openapi.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: ci-openapi -on: - workflow_dispatch: - release: - types: - - created -jobs: - release: - runs-on: ubuntu-22.04 - steps: - - uses: actions/setup-java@v2 - with: - java-version: 17 - distribution: adopt - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - uses: actions/cache@v2 - with: - path: | - ~/.m2/repository - key: ${{ runner.os }}-${{ hashFiles('**/pom.xml') }} - - name: version - run: >- - APP_SHA=$(git rev-parse --short ${GITHUB_SHA}); - APP_TAG=${GITHUB_REF/refs\/tags\/} - APP_VERSION=${APP_TAG}; - echo "APP_SHA=${APP_SHA}" >> ${GITHUB_ENV}; - echo "APP_TAG=${APP_TAG}" >> ${GITHUB_ENV}; - echo "APP_VERSION=${APP_VERSION}" >> ${GITHUB_ENV}; - - name: mvn - run: >- - mvn versions:set - --batch-mode - --file ./pom.xml - --settings ./settings.xml - --define newVersion="${APP_VERSION}"; - mvn clean verify - --batch-mode - --file ./pom.xml - --settings ./settings.xml - --define app.packages.username="${APP_PACKAGES_USERNAME}" - --define app.packages.password="${APP_PACKAGES_PASSWORD}"; - env: - APP_PACKAGES_USERNAME: ${{ github.actor }} - APP_PACKAGES_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - - name: Upload Artifact - uses: actions/upload-artifact@v2 - with: - name: openapi.json - path: target/openapi.json - - name: Checkout OpenApi Doc Branch - uses: actions/checkout@v2 - with: - ref: openapi-doc - - name: Delete existing openapi.json - run: rm -f openapi.json - - name: Download openapi.json - uses: actions/download-artifact@v2 - with: - name: openapi.json - - name: Commit and Push changes - run: | - git config user.name github-actions - git config user.email github-actions@github.com - git commit -a --allow-empty -m "Update OpenAPI JSON" - git push diff --git a/.github/workflows/ci-release.yml b/.github/workflows/ci-release.yml index 3086fd5..eb49f05 100644 --- a/.github/workflows/ci-release.yml +++ b/.github/workflows/ci-release.yml @@ -9,14 +9,14 @@ jobs: env: APP_VERSION: ${{ github.event.release.tag_name }} steps: - - uses: actions/setup-java@v2 + - uses: actions/setup-java@v4 with: java-version: 17 distribution: adopt - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/cache@v2 + - uses: actions/cache@v3 with: path: | ~/.m2/repository @@ -48,6 +48,10 @@ jobs: --tag "${APP_PACKAGES_URL}:${APP_VERSION}" docker push "${APP_PACKAGES_URL}:latest" docker push "${APP_PACKAGES_URL}:${APP_VERSION}" + docker build . + --file ./DB-Changelog-Dockerfile + --tag "${APP_PACKAGES_URL}-initcontainer:${APP_VERSION}"; + docker push "${APP_PACKAGES_URL}-initcontainer:${APP_VERSION}"; env: APP_PACKAGES_URL: ghcr.io/worldhealthorganization/tng-key-distribution/tng-key-distribution APP_PACKAGES_USERNAME: ${{ github.actor }} @@ -56,38 +60,6 @@ jobs: run: |- gh release upload ${APP_VERSION} \ --clobber \ - ./target/openapi.json#openapi-${APP_VERSION}.json \ ./target/generated-resources/licenses.xml#licenses-${APP_VERSION}.xml env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - deploy: - runs-on: ubuntu-20.04 - environment: dev - needs: - - build - env: - APP_VERSION: ${{ github.event.release.tag_name }} - steps: - - name: cf setup - run: |- - curl -sL "https://packages.cloudfoundry.org/stable?release=${CF_RELEASE}&version=${CF_VERSION}" | \ - sudo tar -zx -C /usr/local/bin - env: - CF_VERSION: 7.2.0 - CF_RELEASE: linux64-binary - - name: cf push - run: |- - cf api ${CF_API} - cf auth - cf target -o ${CF_ORG} -s ${CF_SPACE} - cf push ${APP_NAME} --docker-image ${APP_IMAGE}:${APP_VERSION} --docker-username ${CF_DOCKER_USERNAME} - env: - APP_NAME: tng-key-distribution-test - APP_IMAGE: ghcr.io/worldhealthorganization/tng-key-distribution/tng-key-distribution - CF_API: ${{ secrets.CF_API }} - CF_ORG: ${{ secrets.CF_ORG }} - CF_SPACE: ${{ secrets.CF_SPACE }} - CF_USERNAME: ${{ secrets.CF_USERNAME }} - CF_PASSWORD: ${{ secrets.CF_PASSWORD }} - CF_DOCKER_USERNAME: ${{ secrets.CF_DOCKER_USERNAME }} - CF_DOCKER_PASSWORD: ${{ secrets.CF_DOCKER_PASSWORD }} diff --git a/.github/workflows/helm-release.yml b/.github/workflows/helm-release.yml new file mode 100644 index 0000000..231fb52 --- /dev/null +++ b/.github/workflows/helm-release.yml @@ -0,0 +1,28 @@ +name: Release Charts + +on: + release: + types: + - created + workflow_dispatch: # This allows the workflow to be triggered manually + +jobs: + release: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Configure Git + run: | + git config user.name "$GITHUB_ACTOR" + git config user.email "$GITHUB_ACTOR@users.noreply.github.com" + + - name: Run chart-releaser + uses: helm/chart-releaser-action@v1.6.0 + with: + charts_dir: "k8s/charts" # Specify the path to your charts directory + env: + CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" \ No newline at end of file diff --git a/.gitignore b/.gitignore index 884883e..ae5f491 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ target/ !**/src/main/** !**/src/test/** +application-local.yml + ### STS ### .apt_generated .classpath diff --git a/DB-Changelog-Dockerfile b/DB-Changelog-Dockerfile new file mode 100644 index 0000000..5b7b375 --- /dev/null +++ b/DB-Changelog-Dockerfile @@ -0,0 +1,3 @@ +FROM liquibase/liquibase + +COPY /src/main/resources/db /liquibase/db diff --git a/README.md b/README.md index 320ca5a..fb5f5a6 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ -

+

TNG Key Distribution Service

-

+

-

+
-

+

AboutDevelopmentDocumentation • @@ -131,7 +131,9 @@ docker-compose up --build After all containers have started, you will be able to reach the service on your [local machine](http://localhost:8080/api/docs) under port 8080. -## Documentation +## Cloud deployment + +## Documentation [OpenAPI Spec](https://worldhealthorganization.github.io/tng-key-distribution/) @@ -147,11 +149,11 @@ The following channels are available for discussions, feedback, and support requ | **Issues** | | | **Other requests** | | -## How to contribute +## How to contribute Contribution and feedback is encouraged and always welcome. For more information about how to contribute, the project structure, as well as additional contribution information, see our [Contribution Guidelines](./CONTRIBUTING.md). By participating in this project, you agree to abide by its [Code of Conduct](./CODE_OF_CONDUCT.md) at all times. -## Contributors +## Contributors Our commitment to open source means that we are enabling -in fact encouraging- all interested parties to contribute and become part of its developer community. diff --git a/certs/From Files to secrets to container with mounted volumes.md b/certs/From Files to secrets to container with mounted volumes.md index e4d19d1..fd4bad6 100644 --- a/certs/From Files to secrets to container with mounted volumes.md +++ b/certs/From Files to secrets to container with mounted volumes.md @@ -1,58 +1,68 @@ -### How to populate the keystores and truststores, trustanchor files in k8s cluster -A general approach how to secrets are mounted volumes can be found in the official [documentation](https://kubernetes.io/docs/tasks/inject-data-application/distribute-credentials-secure/#create-a-pod-that-has-access-to-the-secret-data-through-a-volume) -1.) generate the keystore, truststore trust_anchor as described in [PlaceYourGatewayAccessKeysHere.md](PlaceYourGatewayAccessKeysHere.md) -2.) combine the resulting files in a single secret with -```(bash) -kubectl create secret generic mtls_secret --dry-run=client -o yaml --from-file=tls_key_store.p12 --from-file=tng_tls_server_truststore.p12 --from-file=trustanchor_store.jks > mtls_secret.yaml -kubectl create secret generic --dry-run=client -o yaml --from-file= --from-file=.p12 --from-file= > combined_tls_secret.yaml -``` -this will result in a yaml file containing the base64 encoded file contents of that three files -```(json) -apiVersion: v1 -data: - tls_key_store.p12: MIIF3wIBAzCCBZUGDQEJFDEkHiIAYwBsAGkAZQBuAHQAYwByAGUAZABlAG4AdABpAGEAbABzMEEwMTANBglghkgBZQMEAgEFAAQgt/aPlSTVrkAIplPg++vrX...../czGzdjH1XPrutiae8EAFoECKv4c1pYD2TDAgIIAA== - trustanchor_store.jks: /u3+7QAAAAIAAAABAAAAAgAadG5nLXRscy1zZXJ2ZXItY2VydGlmaWNhdGUAAAGLVC9h5gAFWC41MDkAAAUaMIIFFjCCAv6gAwIBAgIRAJErCEr - tng_tls_server_truststore.p12: /u3+7QAAAAIAAAABAAAAAgAXoB1.....lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4avAuvDsz -kind: Secret -metadata: - creationTimestamp: null - name: mtls_secret -``` -This file then can be temporarily included in your helm charts or directly applied to your cluster with -```(shell) -kubectl apply -f mtls_secret.yaml # will apply the secret to current context -``` -**Note that your secrets with keystores/truststores contain sensible data. Keep them in save place** - -In the deployment of your helm chart include the the secret as volumes in the template spec -````(helm) -spec: - template: - spec: - volumes: - - name: secrets-jks - secret: - secretName: mtls-secret - items: - - key: tls_key_store.p12 - path: tls_key_store.p12 - - key: trustanchor_store.jks - path: trustanchor_store.jks - - key: tng_tls_server_truststore.p12 - path: tng_tls_server_truststore.p12 -```` -The items array is optional as long as the keynames reflect the filenames and all keys in the secret -shall be mapped to files - -The according volume mounts are defined in the container section -````(helm) -spec: - templates: - spec: - containers: - volumeMounts: - - name: secrets-jks - mountPath: /certs - readOnly: true -```` - +### How to populate the keystores and truststores, trustanchor files in k8s cluster + +A general approach how to secrets are mounted volumes can be found in the official [documentation](https://kubernetes.io/docs/tasks/inject-data-application/distribute-credentials-secure/#create-a-pod-that-has-access-to-the-secret-data-through-a-volume) +1.) generate the keystore, truststore trust_anchor as described in [PlaceYourGatewayAccessKeysHere.md](PlaceYourGatewayAccessKeysHere.md) +2.) combine the resulting files in a single secret with + +```(bash) +kubectl create secret generic mtls-secret --dry-run=client --namespace=kds -o yaml --from-file=tls_key_store.p12 --from-file=tng_tls_server_truststore.p12 --from-file=trustanchor_store.jks > mtls_secret.yaml +kubectl create secret generic --dry-run=client --namespace= -o yaml --from-file= --from-file=.p12 --from-file= > combined_tls_secret.yaml +``` + +this will result in a yaml file containing the base64 encoded file contents of that three files + +```(json) +apiVersion: v1 +data: + tls_key_store.p12: MIIF3wIBAzCCBZUGDQEJFDEkHiIAYwBsAGkAZQBuAHQAYwByAGUAZABlAG4AdABpAGEAbABzMEEwMTANBglghkgBZQMEAgEFAAQgt/aPlSTVrkAIplPg++vrX...../czGzdjH1XPrutiae8EAFoECKv4c1pYD2TDAgIIAA== + trustanchor_store.jks: /u3+7QAAAAIAAAABAAAAAgAadG5nLXRscy1zZXJ2ZXItY2VydGlmaWNhdGUAAAGLVC9h5gAFWC41MDkAAAUaMIIFFjCCAv6gAwIBAgIRAJErCEr + tng_tls_server_truststore.p12: /u3+7QAAAAIAAAABAAAAAgAXoB1.....lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4avAuvDsz +kind: Secret +metadata: + creationTimestamp: null + name: mtls-secret +``` + +This file then can be temporarily included in your helm charts or directly applied to your cluster with + +```(shell) +kubectl apply -f mtls-secret.yaml # will apply the secret to current context +``` + +**Note that your secrets with keystores/truststores contain sensible data. Keep them in save place** + +In the deployment of your helm chart include the secret as volumes in the template spec + +```(helm) +spec: + template: + spec: + volumes: + - name: secrets-jks + secret: + secretName: mtls-secret + items: + - key: tls_key_store.p12 + path: tls_key_store.p12 + - key: trustanchor_store.jks + path: trustanchor_store.jks + - key: tng_tls_server_truststore.p12 + path: tng_tls_server_truststore.p12 +``` + +The items array is optional as long as the keynames reflect the filenames and all keys in the secret +shall be mapped to files + +The according volume mounts are defined in the container section + +```(helm) +spec: + templates: + spec: + containers: + volumeMounts: + - name: secrets-jks + mountPath: /certs + readOnly: true +``` + diff --git a/certs/PlaceYourGatewayAccessKeysHere.md b/certs/PlaceYourGatewayAccessKeysHere.md index f181094..649a702 100644 --- a/certs/PlaceYourGatewayAccessKeysHere.md +++ b/certs/PlaceYourGatewayAccessKeysHere.md @@ -29,3 +29,31 @@ Create a pkcs12 KeyStore from the TLS.pem and TLS.key: ``` openssl pkcs12 -export -out tls_key_store.p12 -inkey TLS.key -in TLS.pem -passout pass:dgcg-p4ssw0rd -name clientcredentials ``` + +# How to setup signing material for DID Signing + +KDS is able to provide a DID-Document holding the downloaded keys. The DID-Document will be signed by a private key provided in a KeyStore. + +Generate Private Key (Choose another Curve depending your needs) + +``` +openssl ecparam -name prime256v1 -genkey -noout -out did-signer.pem +``` + +Convert PEM-File to KeyStore + +``` +openssl pkcs12 -export -out did-signer.p12 -inkey did-signer.pem -nocerts -passout pass:secure-password -name did-signer +``` + +This will result in a KeyStore (P12) containing the previously generated private key stored with alias "did-signer" and secured with password "secure-password" + +```yaml +dgc: + did: + didUploadProvider: local-file + localKeyStore: + alias: did-signer + password: secure-password + path: ./did-signer.p12 +``` diff --git a/codestyle/checkstyle.xml b/codestyle/checkstyle.xml index f50e861..9bca361 100644 --- a/codestyle/checkstyle.xml +++ b/codestyle/checkstyle.xml @@ -44,6 +44,7 @@ + @@ -315,4 +316,4 @@ - \ No newline at end of file + diff --git a/k8s/helm/tngkds/README.md b/k8s/helm/tngkds/README.md new file mode 100644 index 0000000..b814bca --- /dev/null +++ b/k8s/helm/tngkds/README.md @@ -0,0 +1,44 @@ +# tngkds + +![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.16.0](https://img.shields.io/badge/AppVersion-1.16.0-informational?style=flat-square) + +A Helm chart for Kubernetes + +## Requirements +The versions from umbrella chart are currently not used, please refer to to corresponding image tags in value files + +| Repository | Name | Version | +|------------|------|---------| +| | tngkds-backend | 0.1.0 | +| | tngkds-postgres | 0.1.0 | + +## Values + +| Key | Type | Default | Description | +|-----------------------------------------------------------|--------|-------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| tngkds-backend.gateway.connector.enabled | bool | `true` | flag whether the kds is connected to the TNG | +| tngkds-backend.gateway.connector.endpoint | string | `""` | The url where the TNG can be reached | +| tngkds-backend.gateway.connector.max-cache-age | int | `300` | | +| tngkds-backend.gateway.connector.proxy.enabled | bool | `false` | used for development, when KDS is run behind a proxy. If set to true, _tngkds-backend.gateway.connector.proxy.port_ and _tngkds-backend.gateway.connector.proxy.host_ also need to be applied | +| tngkds-backend.gateway.connector.tls_key_store.alias | string | `"clientcredentials"` | | +| tngkds-backend.gateway.connector.tls_key_store.password | string | `""` | | +| tngkds-backend.gateway.connector.tls_key_store.path | string | `"/certs/tls_key_store.p12"` | | +| tngkds-backend.gateway.connector.tls_trust_store.alias | string | `"tng-tls-server-certificate"` | | +| tngkds-backend.gateway.connector.tls_trust_store.password | string | `""` | | +| tngkds-backend.gateway.connector.tls_trust_store.path | string | `"/certs/tng_tls_server_truststore.p12"` | | +| tngkds-backend.gateway.connector.trust_anchor.alias | string | `"trustanchor"` | | +| tngkds-backend.gateway.connector.trust_anchor.password | string | `""` | | +| tngkds-backend.gateway.connector.trust_anchor.path | string | `"/certs/trustanchor_store.jks"` | | +| tngkds-backend.image.tag | string | `""` | | +| tngkds-backend.liquibaseImage.tag | string | `""` | | +| tngkds-backend.path | string | `"/()(*)"` | | +| tngkds-backend.port | int | `8080` | | +| tngkds-backend.db.driverclass | String | `org.h2.Driver` | The JDBC driver class | +| tngkds-backend.db.plattform | String | `org.hibernate.dialect.H2Dialect` | The Hibernate dialect | +| tngkds-backend.db.url | String | `jdbc:h2:mem:dgc;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1;` | The JDBC URL for the database connection | +| tngkds-backend.db.username | String | `sa` | The username for the database connection | +| tngkds-backend.db.password | String | `''` | The password for the database connection | +| tngkds-postgres.asPod.enabled | bool | `false` | | +| tngkds-postgres.path | string | `"/()(*)"` | | +| tngkds-postgres.port | int | `5432` | | + diff --git a/k8s/helm/tngkds/charts/tngkds-backend/README.md b/k8s/helm/tngkds/charts/tngkds-backend/README.md new file mode 100644 index 0000000..42a95f8 --- /dev/null +++ b/k8s/helm/tngkds/charts/tngkds-backend/README.md @@ -0,0 +1,86 @@ + +# tngkds-backend + +![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.16.0](https://img.shields.io/badge/AppVersion-1.16.0-informational?style=flat-square) + +A Helm chart for TNG Key Distribution Service + +## Values + +| Key | Type | Default | Description | +|--------------------------------------------|--------|---------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| affinity | object | `{}` | | +| autoscaling.enabled | bool | `false` | | +| autoscaling.maxReplicas | int | `100` | | +| autoscaling.minReplicas | int | `1` | | +| autoscaling.targetCPUUtilizationPercentage | int | `80` | | +| did.cron | string | `"*/10 * * * * *"` | spring cronjob configuration, how often shall the did file be generated | +| did.didUploadProvider | string | `"local-file"` | Upload provider for Did document, currently local-file | +| did.localFile.directory | string | `"trustlist"` | If upload provider is local-file: directory of the generated file | +| did.localFile.file-name | string | `"did.json"` | If upload provider is local-file: file-name of the generated file | +| did.did_controller | string | `"did:web:def"` | The controller that is generating the did.json / controlling its contents | +| did.did_id | string | `"did:web:abc"` | The ID of the did entry | +| did.enableDidGeneration | bool | `true` | Shall the did documents be generated | +| did.ld_proof_nonce | string | `"n0nc3"` | Nonce of the Did Document | +| did.ld_proof_verification_method | string | `"did:web:dummy.net"` | Verification Method of the DID Signer. Usually a did-link to a did.json containing the public key material that was used to sign this DID | +| did.trust_list_controller_prefix | string | `"did:web:abc"` | | +| did.trust_list_id_prefix | string | `"did:web:abc"` | | +| did.workdir | string | `"/tmp/kdsgituploader"` | local folder used for checkout and update git repository | +| did.prefix | string | `"v2.0.0"` | prefix used as root folder name for generated files | +| did.url | string | `"https://github.com/WorldHealthOrganization/tng-cdn-dev"` | the git repository to work in | +| did.pat | string | `"git did pat by secret tng-bot-dev"` | the personal access token of the technical user that has permission to write to the repository | +| did.didSigningProvider | string | `"dummy"`, `"local-keystore"` for configured private key | signing provider to be used to sign the did documents (proof section). dummy can be used for dev. "local-keystore"` should be used with configured private key in keystore. (see: How to setup signing material for DID Signing in [certs documentation](../../../../../certs/PlaceYourGatewayAccessKeysHere.md) | +| did.trust-list-path | string | `"trustlist"` | path that contains DID documents of trustlist | +| did.trust-list-ref-path | string | `"trustlist-ref"` | path that contains DID documents with references only | +| fullnameOverride | string | `""` | | +| gateway.connector.enabled | bool | `true` | | +| gateway.connector.endpoint | string | `""` | | +| gateway.connector.max-cache-age | int | `300` | | +| gateway.connector.proxy.enabled | bool | `false` | used for development, when your machine needs a proxy to access _tng.who.int_ | +| gateway.connector.tls_key_store.alias | string | `""` | KDS application accesses the cert via its alias | +| gateway.connector.tls_key_store.password | string | `""` | | +| gateway.connector.tls_key_store.path | string | `""` | | +| gateway.connector.tls_trust_store.alias | string | `""` | KDS application accesses the cert via its alias | +| gateway.connector.tls_trust_store.password | string | `""` | | +| gateway.connector.tls_trust_store.path | string | `""` | | +| gateway.connector.trust_anchor.alias | string | `""` | tng application access the cert via its alias | +| gateway.connector.trust_anchor.password | string | `""` | | +| gateway.connector.trust_anchor.path | string | `""` | | +| image.pullPolicy | string | `"IfNotPresent"` | | +| image.repository | string | `"ghcr.io/worldhealthorganization/tng-key-distribution/tng-key-distribution"` | | +| image.tag | string | `"0.0.1-d890889"` | version of the container image to be used for deployment | +| imagePullSecrets | string | `"tng-distribution-pull-secret"` | | +| ingress.annotations | object | `{}` | | +| ingress.className | string | `""` | | +| ingress.enabled | bool | `false` | | +| ingress.hosts[0].host | string | `"chart-example.local"` | | +| ingress.hosts[0].paths[0].path | string | `"/"` | | +| ingress.hosts[0].paths[0].pathType | string | `"ImplementationSpecific"` | | +| ingress.tls | list | `[]` | | +| liquibaseImage.repository | string | `"ghcr.io/worldhealthorganization/tng-key-distribution/tng-key-distribution-initcontainer"` | | +| liquibaseImage.tag | string | `""` | version of the initcontainer image to be used, the tag is the same as for _image.tag_ | +| nameOverride | string | `""` | | +| nodeSelector | object | `{}` | | +| podAnnotations | object | `{}` | | +| podSecurityContext | object | `{}` | | +| db.driverclass | String | `org.h2.Driver` | The JDBC driver class | +| db.plattform | String | `org.hibernate.dialect.H2Dialect` | The Hibernate dialect | +| db.url | String | `jdbc:h2:mem:dgc;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1;` | The JDBC URL for the database connection | +| db.username | String | `sa` | The username for the database connection | +| db.password | String | `''` | The password for the database connection | +| replicaCount | int | `1` | | +| resources | object | `{}` | | +| securityContext | object | `{}` | | +| server.port | int | `8080` | port of the kds applications api server | +| service.ports[0].name | string | `"http"` | | +| service.ports[0].nodePort | int | `30166` | | +| service.ports[0].port | int | `8080` | | +| service.ports[0].protocol | string | `"TCP"` | | +| service.ports[0].targetPort | int | `8080` | | +| service.type | string | `"NodePort"` | | +| serviceAccount.annotations | object | `{}` | | +| serviceAccount.create | bool | `true` | | +| serviceAccount.name | string | `""` | | +| spring.profile | string | `"cloud"` | {_0..n_} Spring profiles to be activated, usually used for feature toggle, currently not in use (existing values will be ignored) | +| tolerations | list | `[]` | | + diff --git a/k8s/helm/tngkds/charts/tngkds-backend/templates/configmap.yml b/k8s/helm/tngkds/charts/tngkds-backend/templates/configmap.yml deleted file mode 100644 index fb77939..0000000 --- a/k8s/helm/tngkds/charts/tngkds-backend/templates/configmap.yml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "tngkds-backend.fullname" . }}-cfg -data: - host: "{{ .Values.configMap.data.host }}" - dbname: "{{ .Values.configMap.data.dbName }}" diff --git a/k8s/helm/tngkds/charts/tngkds-backend/templates/deployment.yaml b/k8s/helm/tngkds/charts/tngkds-backend/templates/deployment.yaml index 19b940c..828c5e6 100644 --- a/k8s/helm/tngkds/charts/tngkds-backend/templates/deployment.yaml +++ b/k8s/helm/tngkds/charts/tngkds-backend/templates/deployment.yaml @@ -4,6 +4,7 @@ metadata: name: {{ include "tngkds-backend.fullname" . }} labels: {{- include "tngkds-backend.labels" . | nindent 4 }} + namespace: {{ .Release.Namespace }} spec: {{- if not .Values.autoscaling.enabled }} replicas: {{ .Values.replicaCount }} @@ -40,45 +41,83 @@ spec: - name: SPRING_PROFILES_ACTIVE value: {{ .Values.spring.profile }} - name: SPRING_DATASOURCE_URL - value: "jdbc:postgresql://{{ .Values.psql.serviceName }}.{{ .Release.Namespace }}.{{ .Values.psql.cluster }}:{{ .Values.psql.port }}/{{ .Values.psql.dbName }}" + value: {{ .Values.db.url | quote }} - name: SPRING_DATASOURCE_DRIVERCLASSNAME - value: "org.postgresql.Driver" + value: {{ .Values.db.driverclass | quote }} - name: SPRING_DATASOURCE_JNDI_NAME value: "false" - name: SPRING_JPA_DATABASEPLATFORM - value: "org.hibernate.dialect.PostgreSQLDialect" + value: {{ .Values.db.plattform | quote }} - name: SPRING_DATASOURCE_USERNAME - valueFrom: - secretKeyRef: - name: {{ include "tngkds-backend.fullname" . }}-secret - key: pgUser + value: {{ .Values.db.username | quote }} - name: SPRING_DATASOURCE_PASSWORD - valueFrom: - secretKeyRef: - name: {{ include "tngkds-backend.fullname" . }}-secret - key: pgPassword + value: {{ .Values.db.password | quote }} - name: DGC_GATEWAY_CONNECTOR_TLS_TRUST_STORE_PATH value: {{ .Values.gateway.connector.tls_trust_store.path }} - name: DGC_GATEWAY_CONNECTOR_TLS_TRUST_STORE_PASSWORD - value: {{ .Values.gateway.connector.tls_trust_store.password }} + value: {{ .Values.gateway.connector.tls_trust_store.password | quote }} - name: DGC_GATEWAY_CONNECTOR_TLS_TRUST_STORE_ALIAS value: {{ .Values.gateway.connector.tls_trust_store.alias }} - name: DGC_GATEWAY_CONNECTOR_TLS_KEY_STORE_PATH value: {{ .Values.gateway.connector.tls_key_store.path }} - name: DGC_GATEWAY_CONNECTOR_TLS_KEY_STORE_PASSWORD - value: {{ .Values.gateway.connector.tls_key_store.password }} + value: {{ .Values.gateway.connector.tls_key_store.password | quote }} - name: DGC_GATEWAY_CONNECTOR_TLS_KEY_STORE_ALIAS value: {{.Values.gateway.connector.tls_key_store.alias }} - name: DGC_GATEWAY_CONNECTOR_TRUST_ANCHOR_PATH value: {{ .Values.gateway.connector.trust_anchor.path }} - name: DGC_GATEWAY_CONNECTOR_TRUST_ANCHOR_PASSWORD - value: {{ .Values.gateway.connector.trust_anchor.password }} + value: {{ .Values.gateway.connector.trust_anchor.password | quote }} - name: DGC_GATEWAY_CONNECTOR_TRUST_ANCHOR_ALIAS value: {{ .Values.gateway.connector.trust_anchor.alias }} - name: DGC_GATEWAY_CONNECTOR_ENABLED - value: {{ .Values.gateway.connector.enabled |quote }} + value: {{ .Values.gateway.connector.enabled | quote }} - name: DGC_GATEWAY_CONNECTOR_ENDPOINT value: {{ .Values.gateway.connector.endpoint }} + {{- if .Values.did.enableDidGeneration }} + - name: DGC_DID_CRON + value: "{{ .Values.did.cron }}" + - name: DGC_DID_DIDUPLOADPROVIDER + value: "{{ .Values.did.didUploadProvider }}" + - name: DGC_DID_LOCALFILE_DIRECTORY + value: "{{ .Values.did.localFile.directory }}" + - name: DGC_DID_LOCALFILE_FILENAME + value: "{{ .Values.did.localFile.filename }}" + - name: DGC_DID_GIT_WORKDIR + value: "{{ .Values.did.git.workdir }}" + - name: DGC_DID_GIT_PREFIX + value: "{{ .Values.did.git.prefix }}" + - name: DGC_DID_GIT_URL + value: "{{ .Values.did.git.url }}" + - name: DGC_DID_GIT_PAT + value: "{{ .Values.did.git.pat }}" + - name: DGC_DID_DIDSIGNINGPROVIDER + value: "{{ .Values.did.didSigningProvider }}" + - name: DGC_DID_LDPROOFVERIFICATIONMETHOD + value: "{{ index .Values.did "ld-proof-verification-method" }}" + - name: DGC_DID_DIDID + value: "{{ index .Values.did "did-id" }}" + - name: DGC_DID_TRUSTLISTPATH + value: "{{ index .Values.did "trust-list-path" }}" + - name: DGC_DID_TRUSTLISTREFPATH + value: "{{ index .Values.did "trust-list-ref-path" }}" + - name: DGC_DID_DIDCONTROLLER + value: "{{ index .Values.did "did-controller" }}" + - name: DGC_DID_TRUSTLISTIDPREFIX + value: "{{ index .Values.did "trust-list-id-prefix" }}" + - name: DGC_DID_TRUSTLISTCONTROLLEPREFIX + value: "{{ index .Values.did "trust-list-controller-prefix" }}" + {{- range $key, $value := .Values.did.virtualCountries }} + - name: DGC_DID_VIRTUALCOUNTRIES_{{ $key | toString | upper }} + value: "{{ $value }}" + {{- end }} + - name: DGC_DID_GROUPDENYLIST + value: "{{ join "," (index .Values.did "group-deny-list") }}" + {{- range $key, $value := index .Values.did "group-name-mapping" }} + - name: DGC_DID_GROUPNAMEMAPPING_{{ $key | toString | upper }} + value: "{{ $value }}" + {{- end }} + {{- end }} volumeMounts: - name: secrets-jks mountPath: /certs diff --git a/k8s/helm/tngkds/charts/tngkds-backend/templates/secrets.yml b/k8s/helm/tngkds/charts/tngkds-backend/templates/secrets.yml deleted file mode 100644 index eead9f3..0000000 --- a/k8s/helm/tngkds/charts/tngkds-backend/templates/secrets.yml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - name: {{ include "tngkds-backend.fullname" . }}-secret -stringData: - pgUser: "postgres" - pgPassword: "postgres" diff --git a/k8s/helm/tngkds/charts/tngkds-backend/values-local.yaml b/k8s/helm/tngkds/charts/tngkds-backend/values-local.yaml index 8c4729f..a2d6ed5 100644 --- a/k8s/helm/tngkds/charts/tngkds-backend/values-local.yaml +++ b/k8s/helm/tngkds/charts/tngkds-backend/values-local.yaml @@ -88,12 +88,13 @@ tolerations: [] affinity: {} -# Postgres -psql: - serviceName: postgres - port: 5432 - dbName: postgres - cluster: svc.cluster.local +# H2 +db: + driverclass: org.h2.Driver + plattform: org.hibernate.dialect.H2Dialect + url: jdbc:h2:mem:dgc;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1; + username: sa + password: '' # ConfigMap configMap: diff --git a/k8s/helm/tngkds/charts/tngkds-backend/values.yaml b/k8s/helm/tngkds/charts/tngkds-backend/values.yaml index 5490a6b..c26858c 100644 --- a/k8s/helm/tngkds/charts/tngkds-backend/values.yaml +++ b/k8s/helm/tngkds/charts/tngkds-backend/values.yaml @@ -10,7 +10,7 @@ image: repository: ghcr.io/worldhealthorganization/tng-key-distribution/tng-key-distribution pullPolicy: IfNotPresent # Overrides the image tag whose default is the chart appVersion. - tag: "0.0.1-d890889" #"0.0.1-2840d04" + tag: "0.0.1-d890889" #"0.0.1-2840d04" #kubectl -n create secret docker-registry --docker-server=ghcr.io --docker-username= --docker-password= --docker-email= -o yaml #kubectl -n kds create secret docker-registry tng-distribution-pull-secret --docker-server=ghcr.io --docker-username=anonymous --docker-password= --docker-email=anonymous@example.com -o yaml @@ -18,6 +18,11 @@ imagePullSecrets: tng-distribution-pull-secret nameOverride: "" fullnameOverride: "" +# Liquibase +liquibaseImage: + repository: ghcr.io/worldhealthorganization/tng-key-distribution/tng-key-distribution-initcontainer + tag: + serviceAccount: # Specifies whether a service account should be created create: true @@ -29,10 +34,12 @@ serviceAccount: podAnnotations: {} -podSecurityContext: {} +podSecurityContext: + {} # fsGroup: 2000 -securityContext: {} +securityContext: + {} # capabilities: # drop: # - ALL @@ -52,7 +59,8 @@ service: ingress: enabled: false className: "" - annotations: {} + annotations: + {} # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" hosts: @@ -65,7 +73,8 @@ ingress: # hosts: # - chart-example.local -resources: {} +resources: + {} # We usually recommend not to specify default resources and to leave this as a conscious # choice for the user. This also increases chances charts run on environments with little # resources, such as Minikube. If you do want to specify resources, uncomment the following @@ -90,18 +99,19 @@ tolerations: [] affinity: {} -# Postgres -psql: - serviceName: postgres - port: 5432 - dbName: postgres - cluster: svc.cluster.local +# H2 +db: + driverclass: org.h2.Driver + plattform: org.hibernate.dialect.H2Dialect + url: jdbc:h2:mem:dgc;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1; + username: sa + password: '' # ConfigMap -configMap: - data: - host: localhost - dbName: postgres +# configMap: +# data: +# host: localhost +# dbName: postgres # Spring spring: @@ -111,7 +121,7 @@ spring: server: port: 8080 - +# Smart TN Gateway gateway: connector: enabled: true @@ -131,3 +141,36 @@ gateway: alias: password: path: +did: + cron: "*/10 * * * * *" + enableDidGeneration: true + didUploadProvider: local-file + localFile: + directory: /tmp/kdsgitworkdir/tng-cdn-dev/v2.0.0 + file-name: did.json + git: + workdir: /tmp/kdsgituploader # oon clonind will checkout e.g. tng-cdn-dev + prefix: v2.0.0 #for copy action into git workdir from local file exporter path + url: https://github.com/WorldHealthOrganization/tng-cdn-dev + pat: #TODO: set by secret + didSigningProvider: dummy + ld-proof-verification-method: did:web:dummy.net + did-id: did:web:worldhealthorganization.github.io:tng-cdn-dev:v2.0.0 + trust-list-path: trustlist + trust-list-ref-path: trustlist-ref + did-controller: did:web:def + trust-list-id-prefix: did:web:abc + trust-list-controller-prefix: did:web:abc + contextMapping: + "[https://www.w3.org/ns/did/v1]": did_v1.json + "[https://w3id.org/security/suites/jws-2020/v1]": jws-2020_v1.json + virtualCountries: + XA: XXA + XB: XXB + XO: XXO + XL: XCL + EU: XEU + group-deny-list: + - UPLOAD + group-name-mapping: + CSCA: SCA diff --git a/k8s/helm/tngkds/charts/tngkds-postgres/README.md b/k8s/helm/tngkds/charts/tngkds-postgres/README.md new file mode 100644 index 0000000..13a1375 --- /dev/null +++ b/k8s/helm/tngkds/charts/tngkds-postgres/README.md @@ -0,0 +1,45 @@ +# tngkds-postgres + +![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.16.0](https://img.shields.io/badge/AppVersion-1.16.0-informational?style=flat-square) + +A Helm chart for PostgreSQL database for TNG Key Distribution Service + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| affinity | object | `{}` | | +| autoscaling.enabled | bool | `false` | Pod autoscaling | +| autoscaling.maxReplicas | int | `100` | | +| autoscaling.minReplicas | int | `1` | | +| autoscaling.targetCPUUtilizationPercentage | int | `80` | | +| configMap.data.dbName | string | `"postgres"` | used as service name when db is deployed as pod | +| configMap.data.host | string | `"localhost"` | used as host name when db is deployed as pod | +| dbpassword | string | `""` | password of the dbuser | +| dbuser | string | `""` | username of the dbuser | +| fullnameOverride | string | `""` | | +| image.pullPolicy | string | `"IfNotPresent"` | | +| image.repository | string | `"nginx"` | | +| image.tag | string | `""` | | +| imagePullSecrets | list | `[]` | | +| ingress.annotations | object | `{}` | | +| ingress.className | string | `""` | | +| ingress.enabled | bool | `false` | | +| ingress.hosts[0].host | string | `"chart-example.local"` | | +| ingress.hosts[0].paths[0].path | string | `"/"` | | +| ingress.hosts[0].paths[0].pathType | string | `"ImplementationSpecific"` | | +| ingress.tls | list | `[]` | | +| nameOverride | string | `""` | | +| nodeSelector | object | `{}` | | +| podAnnotations | object | `{}` | | +| podSecurityContext | object | `{}` | | +| replicaCount | int | `1` | | +| resources | object | `{}` | | +| securityContext | object | `{}` | | +| service.ports[0].port | int | `5432` | db service port | +| service.type | string | `"NodePort"` | | +| serviceAccount.annotations | object | `{}` | | +| serviceAccount.create | bool | `true` | | +| serviceAccount.name | string | `""` | | +| tolerations | list | `[]` | | + diff --git a/k8s/helm/tngkds/charts/tngkds-postgres/templates/configmap.yml b/k8s/helm/tngkds/charts/tngkds-postgres/templates/configmap.yml index 34d2e43..bf656fc 100644 --- a/k8s/helm/tngkds/charts/tngkds-postgres/templates/configmap.yml +++ b/k8s/helm/tngkds/charts/tngkds-postgres/templates/configmap.yml @@ -1,7 +1,10 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "tngkds-postgres.fullname" . }}-cfg -data: - host: "{{ .Values.configMap.data.host }}" - dbname: "{{ .Values.configMap.data.dbName }}" +{{- if .Values.asPod.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "tngkds-postgres.fullname" . }}-cfg + namespace: {{ .Release.Namespace }} +data: + host: "{{ .Values.configMap.data.host }}" + dbname: "{{ .Values.configMap.data.dbName }}" +{{- end }} diff --git a/k8s/helm/tngkds/charts/tngkds-postgres/templates/deployment.yaml b/k8s/helm/tngkds/charts/tngkds-postgres/templates/deployment.yaml index 5745ff2..e13ecd8 100644 --- a/k8s/helm/tngkds/charts/tngkds-postgres/templates/deployment.yaml +++ b/k8s/helm/tngkds/charts/tngkds-postgres/templates/deployment.yaml @@ -1,49 +1,52 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "tngkds-postgres.fullname" . }} - labels: - {{- include "tngkds-postgres.labels" . | nindent 4 }} - tier: database -spec: - selector: - matchLabels: - {{- include "tngkds-postgres.selectorLabels" . | nindent 6 }} - strategy: - type: Recreate - template: - metadata: - labels: - {{- include "tngkds-postgres.labels" . | nindent 8 }} - tier: database - spec: - containers: - - name: postgres - image: postgres - imagePullPolicy: "IfNotPresent" - env: - - name: POSTGRES_USER - valueFrom: - secretKeyRef: - name: {{ include "tngkds-postgres.fullname" . }}-secret - key: pgUser - - name: POSTGRES_PASSWORD - valueFrom: - secretKeyRef: - name: {{ include "tngkds-postgres.fullname" . }}-secret - key: pgPassword - - name: POSTGRES_DB - valueFrom: - configMapKeyRef: - name: {{ include "tngkds-postgres.fullname" . }}-cfg - key: dbname - ports: - - containerPort: 5432 - name: postgres - volumeMounts: - - mountPath: /var/lib/postgresql/kds/data - name: postgres-persistance-storage - volumes: - - name: postgres-persistance-storage - persistentVolumeClaim: - claimName: {{ include "tngkds-postgres.fullname" . }}-pv-claim +{{- if .Values.asPod.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "tngkds-postgres.fullname" . }} + labels: + {{- include "tngkds-postgres.labels" . | nindent 4 }} + tier: database + namespace: {{ .Release.Namespace }} +spec: + selector: + matchLabels: + {{- include "tngkds-postgres.selectorLabels" . | nindent 6 }} + strategy: + type: Recreate + template: + metadata: + labels: + {{- include "tngkds-postgres.labels" . | nindent 8 }} + tier: database + spec: + containers: + - name: postgres + image: postgres + imagePullPolicy: "IfNotPresent" + env: + - name: POSTGRES_USER + valueFrom: + secretKeyRef: + name: {{ include "tngkds-postgres.fullname" . }}-secret + key: pgUser + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "tngkds-postgres.fullname" . }}-secret + key: pgPassword + - name: POSTGRES_DB + valueFrom: + configMapKeyRef: + name: {{ include "tngkds-postgres.fullname" . }}-cfg + key: dbname + ports: + - containerPort: 5432 + name: postgres + volumeMounts: + - mountPath: /var/lib/postgresql/kds/data + name: postgres-persistance-storage + volumes: + - name: postgres-persistance-storage + persistentVolumeClaim: + claimName: {{ include "tngkds-postgres.fullname" . }}-pv-claim +{{- end }} diff --git a/k8s/helm/tngkds/charts/tngkds-postgres/templates/pv-claim.yaml b/k8s/helm/tngkds/charts/tngkds-postgres/templates/pv-claim.yaml index 6a6bd21..c3c4d34 100644 --- a/k8s/helm/tngkds/charts/tngkds-postgres/templates/pv-claim.yaml +++ b/k8s/helm/tngkds/charts/tngkds-postgres/templates/pv-claim.yaml @@ -1,13 +1,16 @@ -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: {{ include "tngkds-postgres.fullname" . }}-pv-claim - labels: - {{- include "tngkds-postgres.labels" . | nindent 4 }} - tier: database -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 1Gi +{{- if .Values.asPod.enabled }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "tngkds-postgres.fullname" . }}-pv-claim + labels: + {{- include "tngkds-postgres.labels" . | nindent 4 }} + tier: database + namespace: {{ .Release.Namespace }} +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi +{{- end }} diff --git a/k8s/helm/tngkds/charts/tngkds-postgres/templates/secrets.yml b/k8s/helm/tngkds/charts/tngkds-postgres/templates/secrets.yml index 1720e47..32bfca0 100644 --- a/k8s/helm/tngkds/charts/tngkds-postgres/templates/secrets.yml +++ b/k8s/helm/tngkds/charts/tngkds-postgres/templates/secrets.yml @@ -1,7 +1,10 @@ -apiVersion: v1 -kind: Secret -metadata: - name: {{ include "tngkds-postgres.fullname" . }}-secret -stringData: - pgUser: "postgres" - pgPassword: "postgres" +{{- if .Values.asPod.enabled }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "tngkds-postgres.fullname" . }}-secret + namespace: {{ .Release.Namespace }} +stringData: + pgUser: {{ .Values.dbuser | quote }} + pgPassword: {{ .Values.dbpassword | quote }} +{{- end }} diff --git a/k8s/helm/tngkds/charts/tngkds-postgres/templates/service.yaml b/k8s/helm/tngkds/charts/tngkds-postgres/templates/service.yaml index e6fcc29..5f28c27 100644 --- a/k8s/helm/tngkds/charts/tngkds-postgres/templates/service.yaml +++ b/k8s/helm/tngkds/charts/tngkds-postgres/templates/service.yaml @@ -1,16 +1,19 @@ -apiVersion: v1 -kind: Service -metadata: - name: postgres - labels: - {{- include "tngkds-postgres.labels" . | nindent 4 }} - tier: database -spec: - type: NodePort - ports: - {{ range .Values.service.ports }} - - port: {{ .port }} - {{ end }} - selector: - {{- include "tngkds-postgres.selectorLabels" . | nindent 4 }} - tier: database +{{- if .Values.asPod.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: postgres + labels: + {{- include "tngkds-postgres.labels" . | nindent 4 }} + tier: database + namespace: {{ .Release.Namespace }} +spec: + type: NodePort + ports: + {{ range .Values.service.ports }} + - port: {{ .port }} + {{ end }} + selector: + {{- include "tngkds-postgres.selectorLabels" . | nindent 4 }} + tier: database +{{- end }} diff --git a/k8s/helm/tngkds/charts/tngkds-postgres/values.yaml b/k8s/helm/tngkds/charts/tngkds-postgres/values.yaml index 2b66f43..4561a40 100644 --- a/k8s/helm/tngkds/charts/tngkds-postgres/values.yaml +++ b/k8s/helm/tngkds/charts/tngkds-postgres/values.yaml @@ -1,89 +1,95 @@ -# Default values for tngkds-postgres. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - -replicaCount: 1 - -image: - repository: nginx - pullPolicy: IfNotPresent - # Overrides the image tag whose default is the chart appVersion. - tag: "" - -imagePullSecrets: [] -nameOverride: "" -fullnameOverride: "" - -serviceAccount: - # Specifies whether a service account should be created - create: true - # Annotations to add to the service account - annotations: {} - # The name of the service account to use. - # If not set and create is true, a name is generated using the fullname template - name: "" - -podAnnotations: {} - -podSecurityContext: {} - # fsGroup: 2000 - -securityContext: {} - # capabilities: - # drop: - # - ALL - # readOnlyRootFilesystem: true - # runAsNonRoot: true - # runAsUser: 1000 - -service: - type: NodePort - ports: - - port: 5432 - -ingress: - enabled: false - className: "" - annotations: {} - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" - hosts: - - host: chart-example.local - paths: - - path: / - pathType: ImplementationSpecific - tls: [] - # - secretName: chart-example-tls - # hosts: - # - chart-example.local - -resources: {} - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m - # memory: 128Mi - -autoscaling: - enabled: false - minReplicas: 1 - maxReplicas: 100 - targetCPUUtilizationPercentage: 80 - # targetMemoryUtilizationPercentage: 80 - -nodeSelector: {} - -tolerations: [] - -affinity: {} - -# ConfigMap -configMap: - data: - host: localhost - dbName: postgres +# Default values for tngkds-postgres. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + repository: nginx + pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart appVersion. + tag: "" + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + +podAnnotations: {} + +podSecurityContext: + {} + # fsGroup: 2000 + +securityContext: + {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +service: + type: NodePort + ports: + - port: 5432 + +ingress: + enabled: false + className: "" + annotations: + {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + hosts: + - host: chart-example.local + paths: + - path: / + pathType: ImplementationSpecific + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +resources: + {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +# ConfigMap +configMap: + data: + host: localhost + dbName: postgres +dbuser: +dbpassword: diff --git a/k8s/helm/tngkds/values.yaml b/k8s/helm/tngkds/values.yaml index a57c952..0d6c767 100644 --- a/k8s/helm/tngkds/values.yaml +++ b/k8s/helm/tngkds/values.yaml @@ -1,13 +1,43 @@ -# Default values for tngkds. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - -tngkds-postgres: - enabled: true - path: /()(*) - port: 5432 - -tngkds-backend: - enabled: true - path: /()(*) - port: 8080 +# Default values for tngkds. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +tngkds-postgres: + path: /()(*) + port: 5432 + + asPod: + enabled: false # run db as Service or Pod,set in conjunction with tngkds-backend + +tngkds-backend: + image: + tag: + liquibaseImage: + tag: + path: /()(*) + port: 8080 + db: + driverclass: org.h2.Driver + plattform: org.hibernate.dialect.H2Dialect + url: jdbc:h2:mem:dgc;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1; + username: sa + password: '' + gateway: + connector: + enabled: true + endpoint: + proxy: + enabled: false + max-cache-age: 300 + tls_trust_store: #CA of TNG Gateway of TLS certificate + alias: tng-tls-server-certificate + password: + path: /certs/tng_tls_server_truststore.p12 + tls_key_store: # client cert key pair of participant + alias: clientcredentials + password: + path: /certs/tls_key_store.p12 + trust_anchor: # TA pem file of TNG that was used for signing + alias: trustanchor + password: + path: /certs/trustanchor_store.jks diff --git a/pom.xml b/pom.xml index d263321..7973785 100644 --- a/pom.xml +++ b/pom.xml @@ -1,518 +1,310 @@ + xmlns="http://maven.apache.org/POM/4.0.0" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> - 4.0.0 + 4.0.0 - tng.trustnetwork.keydistribution - tng-key-distribution - latest - jar + + org.springframework.cloud + spring-cloud-starter-parent + 2023.0.1 + - tng-key-distribution - GDHCN Key Distribution Service project. - - T-Systems International GmbH - + tng.trustnetwork.keydistribution + tng-key-distribution + latest + jar - - - 17 - 17 - 17 - - UTF-8 - UTF-8 - - 8.3.1 - 3.0.9 - 2022.0.3 - 2.1.0 - 5.3.23 - 6.1.2 - 1.18.28 - 4.22.0 - 2.1.0 - 2.4.2 - 1.5.5.Final - 5.0.0-alpha.11 - 5.4.0 - 2.2.220 - 1.5 - - 3.3.0 - 3.9.1.2184 - 0.8.10 - 1.7.0 - 3.1.2 - - Key Distribution Service / tng-key-distribution - 2021 - apache_v2 - - WorldHealthOrganization - tng-key-distribution - - WorldHealthOrganization - ${sonar.organization}_${project.artifactId} - https://sonarcloud.io - - **/KeyDistributionServiceApplication.java, - **/model/*, - **/mapper/*, - **/entity/*, - **/DgcKeyStore.java, - **/ErrorHandler.java - - + tng-key-distribution + GDHCN Key Distribution Service project. + + T-Systems International GmbH + - https://github.com/WorldHealthOrganization/tng-key-distribution - - https://github.com/WorldHealthOrganization/tng-key-distribution/actions - - - https://github.com/WorldHealthOrganization/tng-key-distribution/issues - - - https://github.com/WorldHealthOrganization/tng-key-distribution - + + + 17 + 17 + 17 + + UTF-8 + UTF-8 + + 8.3.1 + 1.5.5.Final + 5.12.0 + 1.78.1 + + 3.3.0 + 0.8.10 + 2.4.0 + + Key Distribution Service / tng-key-distribution + 2021 + apache_v2 + + WorldHealthOrganization + tng-key-distribution + + WorldHealthOrganization + ${sonar.organization}_${project.artifactId} + https://sonarcloud.io + + **/KeyDistributionServiceApplication.java, + **/model/*, + **/mapper/*, + **/entity/*, + **/DgcKeyStore.java, + **/ErrorHandler.java + + - - - docker - - docker - jar - - - - - org.springframework.boot - spring-boot-maven-plugin - - ${project.build.directory}/docker - ddccg - - - - maven-assembly-plugin - - - make-zip-ACC - none - - - make-zip-test - none - - - make-zip-PRD - none - - - - - - - + https://github.com/WorldHealthOrganization/tng-key-distribution + + https://github.com/WorldHealthOrganization/tng-key-distribution/actions + + + https://github.com/WorldHealthOrganization/tng-key-distribution/issues + + + https://github.com/WorldHealthOrganization/tng-key-distribution + - - - who-github - https://maven.pkg.github.com/${github.organization}/* - - + + + who-github + https://maven.pkg.github.com/${github.organization}/* + + + danubetech-maven-public + https://repo.danubetech.com/repository/maven-public/ + + - - - who-github - https://maven.pkg.github.com/${github.organization}/${github.project} - - + + + who-github + https://maven.pkg.github.com/${github.organization}/${github.project} + + - - - - - org.yaml - snakeyaml - 2.0 - - - com.fasterxml.jackson.core - jackson-databind - 2.15.2 - - - com.fasterxml.jackson.core - jackson-core - 2.15.2 - - - com.fasterxml.jackson.datatype - jackson-datatype-jsr310 - 2.15.2 - - - com.fasterxml.jackson.core - jackson-annotations - 2.15.2 - - - com.fasterxml.jackson.jr - jackson-jr-objects - 2.15.2 - - - com.fasterxml.jackson.datatype - jackson-datatype-jdk8 - 2.15.2 - - - com.fasterxml.jackson.module - jackson-module-parameter-names - 2.15.2 - - - com.fasterxml.jackson.dataformat - jackson-dataformat-yaml - 2.15.2 - - - org.json - json - 20230618 - - - com.google.guava - guava - 32.0.1-jre - - - org.springframework.boot - spring-boot-dependencies - ${spring.boot.version} - pom - import - - - org.springframework.cloud - spring-cloud-dependencies - ${spring.cloud.version} - pom - import - - - org.projectlombok - lombok - ${lombok.version} - - - org.springdoc - springdoc-openapi-starter-webmvc-ui - ${springdoc.version} - - - org.liquibase - liquibase-core - ${liquibase.version} - - - + + eu.europa.ec.dgc + ddcc-gateway-lib + 2.0.2 + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + org.springframework + spring-web + + + + + org.springframework + spring-web + 6.1.6 + - - - eu.europa.ec.dgc - ddcc-gateway-lib - 2.0.2 - - - org.springframework.boot - spring-boot-starter - - - org.springframework.boot - spring-boot-starter-data-jpa - - - org.springframework.boot - spring-boot-starter-validation - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-actuator - - - org.liquibase - liquibase-core - - - org.springframework.boot - spring-boot-starter-test - test - - - org.projectlombok - lombok - provided - - - org.springdoc - springdoc-openapi-starter-webmvc-ui - ${springdoc.version} - - - com.h2database - h2 - ${h2.version} - runtime - - - org.postgresql - postgresql - runtime - - - org.mapstruct - mapstruct - ${mapstruct.version} - - - org.springframework.security - spring-security-web - ${spring.security.version} - - - com.squareup.okhttp3 - okhttp - ${okhttp.version} - test - - - net.javacrumbs.shedlock - shedlock-provider-jdbc-template - ${shedlock.version} - - - net.javacrumbs.shedlock - shedlock-spring - ${shedlock.version} - - - io.pivotal.cfenv - java-cfenv-boot - ${pivotal.cfenv.version} - - - org.springframework.boot - spring-boot - - - - - - com.google.code.bean-matchers - bean-matchers - 0.14 - test - - - commons-fileupload - commons-fileupload - ${commons-fileupload.version} - - - org.springframework.cloud - spring-cloud-starter-feign - 1.4.7.RELEASE - - + + + org.liquibase + liquibase-core + + + com.h2database + h2 + runtime + + + org.postgresql + postgresql + runtime + 42.7.3 + - - - - - org.springframework.boot - spring-boot-maven-plugin - ${spring.boot.version} - - - org.apache.maven.plugins - maven-checkstyle-plugin - ${plugin.checkstyle.version} - - - com.puppycrawl.tools - checkstyle - 8.41.1 - - - - - org.sonarsource.scanner.maven - sonar-maven-plugin - ${plugin.sonar.version} - - - org.jacoco - jacoco-maven-plugin - ${plugin.jacoco.version} - - - org.apache.maven.plugins - maven-surefire-plugin - ${plugin.surefire.version} - - - org.apache.maven.plugins - maven-compiler-plugin - 3.8.1 - - - org.codehaus.mojo - license-maven-plugin - 2.0.0 - - - org.apache.maven.plugins - maven-resources-plugin - 3.2.0 - - UTF-8 - - - - - - - org.owasp - dependency-check-maven - ${owasp.version} - - ./owasp/suppressions.xml - 8 - false - - - - org.apache.maven.plugins - maven-surefire-plugin - - - org.springframework.boot - spring-boot-maven-plugin - - - - repackage - build-info - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - ./codestyle/checkstyle.xml - ./target/**/* - true - true - warning - true - false - - - - check - validate - - check - - - - - - org.jacoco - jacoco-maven-plugin - ${plugin.jacoco.version} - - - - prepare-agent - - - - report - - report - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - - org.projectlombok - lombok - ${lombok.version} - - - org.mapstruct - mapstruct-processor - ${mapstruct.version} - - - - - - org.codehaus.mojo - license-maven-plugin - - **/*.java - ${project.organization.name} and all other contributors - ---license-start - ---license-end - --- - false - true - true - - - - download-licenses - validate - - download-licenses - - - - - - org.springdoc - springdoc-openapi-maven-plugin - 1.3 - - http://localhost:8080/api/docs - - - - integration-test - - generate - - - - - - + + + org.projectlombok + lombok + + + org.mapstruct + mapstruct + ${mapstruct.version} + + + net.javacrumbs.shedlock + shedlock-provider-jdbc-template + ${shedlock.version} + + + net.javacrumbs.shedlock + shedlock-spring + ${shedlock.version} + + + org.bouncycastle + bcpkix-jdk18on + ${bcpkix.version} + + + info.weboftrust + ld-signatures-java + 1.1.0 + + + org.bitcoinj + bitcoinj-core + + + + + com.github.ben-manes.caffeine + caffeine + 3.1.8 + + + org.eclipse.jgit + org.eclipse.jgit + 6.9.0.202403050737-r + + + + + + + org.owasp + dependency-check-maven + ${owasp.version} + + ./owasp/suppressions.xml + 8 + false + + + + org.apache.maven.plugins + maven-surefire-plugin + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-checkstyle-plugin + ${plugin.checkstyle.version} + + ./codestyle/checkstyle.xml + ./target/**/* + true + true + warning + true + false + + + + check + validate + + check + + + + + + com.puppycrawl.tools + checkstyle + 8.41.1 + + + + + org.jacoco + jacoco-maven-plugin + ${plugin.jacoco.version} + + + + prepare-agent + + + + report + + report + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + org.projectlombok + lombok + ${lombok.version} + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + + + + + + org.codehaus.mojo + license-maven-plugin + ${plugin.license.version} + + **/*.java + ${project.organization.name} and all other contributors + ---license-start + ---license-end + --- + false + true + true + + + + download-licenses + validate + + download-licenses + + + + + + diff --git a/src/main/java/tng/trustnetwork/keydistribution/KeyDistributionServiceApplication.java b/src/main/java/tng/trustnetwork/keydistribution/KeyDistributionServiceApplication.java index 65bcac8..ec29444 100644 --- a/src/main/java/tng/trustnetwork/keydistribution/KeyDistributionServiceApplication.java +++ b/src/main/java/tng/trustnetwork/keydistribution/KeyDistributionServiceApplication.java @@ -2,7 +2,7 @@ * ---license-start * WorldHealthOrganization / tng-key-distribution * --- - * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors * --- * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,7 +33,7 @@ @SpringBootApplication @EnableConfigurationProperties(KdsConfigProperties.class) @EnableFeignClients -public class KeyDistributionServiceApplication extends SpringBootServletInitializer { +public class KeyDistributionServiceApplication { /** * The main Method. diff --git a/src/main/java/tng/trustnetwork/keydistribution/clients/UniversalResolverClient.java b/src/main/java/tng/trustnetwork/keydistribution/clients/UniversalResolverClient.java index afa781f..c32a99b 100644 --- a/src/main/java/tng/trustnetwork/keydistribution/clients/UniversalResolverClient.java +++ b/src/main/java/tng/trustnetwork/keydistribution/clients/UniversalResolverClient.java @@ -1,15 +1,34 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + package tng.trustnetwork.keydistribution.clients; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; -import tng.trustnetwork.keydistribution.model.DidDocument; @FeignClient(value = "universalresolver", url = "${universal.resolver}", configuration = UniversalResolverClientConfig.class) public interface UniversalResolverClient { @GetMapping(value = "/{didKey}", produces = "application/json") - DidDocument getDidDocument(@PathVariable("didKey") String didKey); + String getDidDocument(@PathVariable("didKey") String didKey); } diff --git a/src/main/java/tng/trustnetwork/keydistribution/clients/UniversalResolverClientConfig.java b/src/main/java/tng/trustnetwork/keydistribution/clients/UniversalResolverClientConfig.java index 16fab5d..cb034e0 100644 --- a/src/main/java/tng/trustnetwork/keydistribution/clients/UniversalResolverClientConfig.java +++ b/src/main/java/tng/trustnetwork/keydistribution/clients/UniversalResolverClientConfig.java @@ -1,3 +1,23 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + package tng.trustnetwork.keydistribution.clients; import feign.Client; diff --git a/src/main/java/tng/trustnetwork/keydistribution/config/ErrorHandler.java b/src/main/java/tng/trustnetwork/keydistribution/config/ErrorHandler.java deleted file mode 100644 index dbe4f21..0000000 --- a/src/main/java/tng/trustnetwork/keydistribution/config/ErrorHandler.java +++ /dev/null @@ -1,79 +0,0 @@ -/*- - * ---license-start - * WorldHealthOrganization / tng-key-distribution - * --- - * Copyright (C) 2021 T-Systems International GmbH and all other contributors - * --- - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://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. - * ---license-end - */ - -package tng.trustnetwork.keydistribution.config; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.annotation.Configuration; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.ControllerAdvice; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.server.ResponseStatusException; -import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; -import tng.trustnetwork.keydistribution.exception.BadRequestException; -import tng.trustnetwork.keydistribution.restapi.dto.ProblemReportDto; - -@ControllerAdvice -@Configuration -@RequiredArgsConstructor -@Slf4j -public class ErrorHandler extends ResponseEntityExceptionHandler { - - /** - * Handles {@link BadRequestException} when a validation failed. - * - * @param e the thrown {@link BadRequestException} - * @return A ResponseEntity with a ErrorMessage inside. - */ - @ExceptionHandler(BadRequestException.class) - public ResponseEntity handleException(BadRequestException e) { - return ResponseEntity - .status(e.getStatus()) - .body(e.getMessage()); - } - - - /** - * Global Exception Handler to wrap exceptions into a readable JSON Object. - * - * @param e the thrown exception - * @return ResponseEntity with readable data. - */ - @ExceptionHandler(Exception.class) - public ResponseEntity handleException(Exception e) { - if (e instanceof ResponseStatusException) { - return ResponseEntity - .status(((ResponseStatusException) e).getStatusCode()) - .contentType(MediaType.APPLICATION_JSON) - .body(new ProblemReportDto("co", "prob", "val", "det")); - } else { - log.error("Uncatched exception", e); - return ResponseEntity - .status(HttpStatus.INTERNAL_SERVER_ERROR) - .contentType(MediaType.APPLICATION_JSON) - .body(new ProblemReportDto("0x500", "Internal Server Error", "", "")); - } - } - - -} diff --git a/src/main/java/tng/trustnetwork/keydistribution/config/KdsConfigProperties.java b/src/main/java/tng/trustnetwork/keydistribution/config/KdsConfigProperties.java index 9a60f1c..f4c3e11 100644 --- a/src/main/java/tng/trustnetwork/keydistribution/config/KdsConfigProperties.java +++ b/src/main/java/tng/trustnetwork/keydistribution/config/KdsConfigProperties.java @@ -2,7 +2,7 @@ * ---license-start * WorldHealthOrganization / tng-key-distribution * --- - * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors * --- * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,12 @@ package tng.trustnetwork.keydistribution.config; +import eu.europa.ec.dgc.gateway.connector.config.DgcGatewayConnectorConfigProperties; +import eu.europa.ec.dgc.gateway.connector.model.TrustedIssuer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.Setter; @@ -30,11 +36,12 @@ @ConfigurationProperties("dgc") //TODO separate kds and dgc-lib properties public class KdsConfigProperties { + private final CertificatesDownloader certificatesDownloader = new CertificatesDownloader(); private final TrustedIssuerDownloader trustedIssuerDownloader = new TrustedIssuerDownloader(); - private String context = ""; + private final DidConfig did = new DidConfig(); /** * Http-Proxy Configuration. @@ -55,6 +62,8 @@ public static class TrustedIssuerDownloader { private boolean enabled; private Integer timeInterval; private Integer lockLimit; + private List staticTrustedIssuer = new ArrayList<>(); + private boolean enableTrustedIssuerResolving = false; } @Getter @@ -80,4 +89,54 @@ public static class Proxy { private int port; } + @Getter + @Setter + public static class DidConfig { + + private Boolean enableDidGeneration; + + private String didId; + private String didController; + + private String trustListPath; + private String trustListRefPath; + + private String trustListIdPrefix; + private String trustListControllerPrefix; + + private String ldProofVerificationMethod; + private String ldProofDomain; + + private String didSigningProvider; + private String didUploadProvider; + + private Map contextMapping = new HashMap<>(); + private Map virtualCountries = new HashMap<>(); + + private LocalFileConfig localFile = new LocalFileConfig(); + private GitConfig git = new GitConfig(); + + private DgcGatewayConnectorConfigProperties.KeyStoreWithAlias localKeyStore = + new DgcGatewayConnectorConfigProperties.KeyStoreWithAlias(); + + private List groupDenyList = new ArrayList<>(); + private Map groupNameMapping = new HashMap<>(); + + @Getter + @Setter + public static class LocalFileConfig { + private String fileName; + private String directory; + } + + @Getter + @Setter + public static class GitConfig { + private String prefix; + private String workdir; + private String pat; + private String url; + } + } + } diff --git a/src/main/java/tng/trustnetwork/keydistribution/config/OpenApiConfig.java b/src/main/java/tng/trustnetwork/keydistribution/config/OpenApiConfig.java deleted file mode 100644 index 51d05bb..0000000 --- a/src/main/java/tng/trustnetwork/keydistribution/config/OpenApiConfig.java +++ /dev/null @@ -1,35 +0,0 @@ -package tng.trustnetwork.keydistribution.config; - -import io.swagger.v3.oas.models.OpenAPI; -import io.swagger.v3.oas.models.info.Info; -import io.swagger.v3.oas.models.info.License; -import lombok.Generated; -import lombok.RequiredArgsConstructor; -import org.springframework.boot.info.BuildProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Generated -@Configuration -@RequiredArgsConstructor -public class OpenApiConfig { - - private final BuildProperties buildProperties; - - /** - * Configure the OpenApi bean with title and version. - * - * @return the OpenApi bean. - */ - @Bean - public OpenAPI openApi() { - return new OpenAPI() - .info(new Info() - .title("TNG Key Distribution Service") - .description("The API defines the key distribution service for digital green certificates.") - .version(buildProperties.getVersion()) - .license(new License() - .name("Apache 2.0") - .url("https://www.apache.org/licenses/LICENSE-2.0"))); - } -} diff --git a/src/main/java/tng/trustnetwork/keydistribution/config/SchedulerConfig.java b/src/main/java/tng/trustnetwork/keydistribution/config/SchedulerConfig.java index 0c6424b..c98d2fd 100644 --- a/src/main/java/tng/trustnetwork/keydistribution/config/SchedulerConfig.java +++ b/src/main/java/tng/trustnetwork/keydistribution/config/SchedulerConfig.java @@ -2,7 +2,7 @@ * ---license-start * WorldHealthOrganization / tng-key-distribution * --- - * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors * --- * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/tng/trustnetwork/keydistribution/config/ShedLockConfig.java b/src/main/java/tng/trustnetwork/keydistribution/config/ShedLockConfig.java index 8539d9e..8f172fc 100644 --- a/src/main/java/tng/trustnetwork/keydistribution/config/ShedLockConfig.java +++ b/src/main/java/tng/trustnetwork/keydistribution/config/ShedLockConfig.java @@ -2,7 +2,7 @@ * ---license-start * WorldHealthOrganization / tng-key-distribution * --- - * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors * --- * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/tng/trustnetwork/keydistribution/dto/TrustedIssuerDto.java b/src/main/java/tng/trustnetwork/keydistribution/dto/TrustedIssuerDto.java deleted file mode 100644 index 1c2f0ed..0000000 --- a/src/main/java/tng/trustnetwork/keydistribution/dto/TrustedIssuerDto.java +++ /dev/null @@ -1,55 +0,0 @@ -/*- - * ---license-start - * WHO Digital Documentation Covid Certificate Gateway Service / ddcc-gateway - * --- - * Copyright (C) 2022 T-Systems International GmbH and all other contributors - * --- - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://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. - * ---license-end - */ - -package tng.trustnetwork.keydistribution.dto; - -import java.time.ZonedDateTime; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@NoArgsConstructor -@AllArgsConstructor -public class TrustedIssuerDto { - - private String url; - - private UrlTypeDto type; - - private String country; - - private String thumbprint; - - private String sslPublicKey; - - private String keyStorageType; - - private String signature; - - private ZonedDateTime timestamp; - - private String name; - - public enum UrlTypeDto { - HTTP, - DID - } -} diff --git a/src/main/java/tng/trustnetwork/keydistribution/entity/DecentralizedIdentifierEntity.java b/src/main/java/tng/trustnetwork/keydistribution/entity/DecentralizedIdentifierEntity.java new file mode 100644 index 0000000..445af22 --- /dev/null +++ b/src/main/java/tng/trustnetwork/keydistribution/entity/DecentralizedIdentifierEntity.java @@ -0,0 +1,66 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + +package tng.trustnetwork.keydistribution.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; +import java.time.ZonedDateTime; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + + +@Data +@Getter +@Setter +@Entity +@Table(name = "decentralized_identifier") +@AllArgsConstructor +@NoArgsConstructor +public class DecentralizedIdentifierEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", columnDefinition = "BIGINT") + private Long id; + + @Column(name = "created_at", nullable = false) + private ZonedDateTime createdAt = ZonedDateTime.now(); + + @Column(name = "did_id", length = 100) + private String didId; + + @OneToMany(mappedBy = "parentDocument", fetch = FetchType.EAGER) + private List verificationMethods; + + @Column(name = "raw", length = 10_000_000) + private String raw; + +} diff --git a/src/main/java/tng/trustnetwork/keydistribution/entity/InfoEntity.java b/src/main/java/tng/trustnetwork/keydistribution/entity/EcPublicKeyJwkEntity.java similarity index 68% rename from src/main/java/tng/trustnetwork/keydistribution/entity/InfoEntity.java rename to src/main/java/tng/trustnetwork/keydistribution/entity/EcPublicKeyJwkEntity.java index cbc0d9a..80d731a 100644 --- a/src/main/java/tng/trustnetwork/keydistribution/entity/InfoEntity.java +++ b/src/main/java/tng/trustnetwork/keydistribution/entity/EcPublicKeyJwkEntity.java @@ -2,7 +2,7 @@ * ---license-start * WorldHealthOrganization / tng-key-distribution * --- - * Copyright (C) 2022 T-Systems International GmbH and all other contributors + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors * --- * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,9 +21,8 @@ package tng.trustnetwork.keydistribution.entity; import jakarta.persistence.Column; +import jakarta.persistence.DiscriminatorValue; import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.Table; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; @@ -31,23 +30,19 @@ @Getter @Setter -@Entity @AllArgsConstructor @NoArgsConstructor -@Table(name = "vs_info") -public class InfoEntity { +@Entity +@DiscriminatorValue("EC") +public class EcPublicKeyJwkEntity extends PublicKeyJwkEntity { + + @Column(name = "crv", length = 100) + private String crv; - /** - * The KID of the Key used to sign the CMS. - */ - @Id - @Column(name = "identifier_key") - private String identifierKey; + @Column(name = "x", length = 100) + private String xvalue; - /** - * Type of Revocation Hashes. - */ - @Column(name = "property_value") - private String value; + @Column(name = "y", length = 100) + private String yvalue; } diff --git a/src/main/java/tng/trustnetwork/keydistribution/entity/PublicKeyJwkEntity.java b/src/main/java/tng/trustnetwork/keydistribution/entity/PublicKeyJwkEntity.java new file mode 100644 index 0000000..e1ea9f7 --- /dev/null +++ b/src/main/java/tng/trustnetwork/keydistribution/entity/PublicKeyJwkEntity.java @@ -0,0 +1,59 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + +package tng.trustnetwork.keydistribution.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.DiscriminatorColumn; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; +import jakarta.persistence.Table; +import java.time.ZonedDateTime; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@Entity +@Table(name = "public_key_jwk") +@Inheritance(strategy = InheritanceType.SINGLE_TABLE) +@DiscriminatorColumn(name = "kty", columnDefinition = "varchar(10)") +public abstract class PublicKeyJwkEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + + @Column(name = "created_at", nullable = false) + private ZonedDateTime createdAt = ZonedDateTime.now(); + + @Column(name = "x5c", length = 7000) + private String x5c; + +} diff --git a/src/main/java/tng/trustnetwork/keydistribution/restapi/dto/ProblemReportDto.java b/src/main/java/tng/trustnetwork/keydistribution/entity/RsaPublicKeyJwkEntity.java similarity index 54% rename from src/main/java/tng/trustnetwork/keydistribution/restapi/dto/ProblemReportDto.java rename to src/main/java/tng/trustnetwork/keydistribution/entity/RsaPublicKeyJwkEntity.java index 3a01d14..d463a9b 100644 --- a/src/main/java/tng/trustnetwork/keydistribution/restapi/dto/ProblemReportDto.java +++ b/src/main/java/tng/trustnetwork/keydistribution/entity/RsaPublicKeyJwkEntity.java @@ -2,7 +2,7 @@ * ---license-start * WorldHealthOrganization / tng-key-distribution * --- - * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors * --- * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,33 +18,28 @@ * ---license-end */ -package tng.trustnetwork.keydistribution.restapi.dto; +package tng.trustnetwork.keydistribution.entity; -import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.persistence.Column; +import jakarta.persistence.DiscriminatorValue; +import jakarta.persistence.Entity; import lombok.AllArgsConstructor; -import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; -@Schema( - name = "ProblemReport", - type = "object", - example = "{\n" - + "\"code\":\"0x001\",\n" - + "\"problem\":\"[PROBLEM]\",\n" - + "\"sent value\":\"[Sent Value]\",\n" - + "\"details\":\"...\"\n" - + "}" -) - -@Data +@Getter +@Setter @AllArgsConstructor -public class ProblemReportDto { - - private String code; - - private String problem; +@NoArgsConstructor +@Entity +@DiscriminatorValue("RSA") +public class RsaPublicKeyJwkEntity extends PublicKeyJwkEntity { - private String sendValue; + @Column(name = "n", length = 1000) + private String nvalue; - private String details; + @Column(name = "e", length = 1000) + private String evalue; } diff --git a/src/main/java/tng/trustnetwork/keydistribution/entity/ShedlockEntity.java b/src/main/java/tng/trustnetwork/keydistribution/entity/ShedlockEntity.java index a4f1544..2e0b209 100644 --- a/src/main/java/tng/trustnetwork/keydistribution/entity/ShedlockEntity.java +++ b/src/main/java/tng/trustnetwork/keydistribution/entity/ShedlockEntity.java @@ -2,7 +2,7 @@ * ---license-start * WorldHealthOrganization / tng-key-distribution * --- - * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors * --- * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/tng/trustnetwork/keydistribution/entity/SignerInformationEntity.java b/src/main/java/tng/trustnetwork/keydistribution/entity/SignerInformationEntity.java index 78c35fa..05bcfc2 100644 --- a/src/main/java/tng/trustnetwork/keydistribution/entity/SignerInformationEntity.java +++ b/src/main/java/tng/trustnetwork/keydistribution/entity/SignerInformationEntity.java @@ -2,7 +2,7 @@ * ---license-start * WorldHealthOrganization / tng-key-distribution * --- - * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors * --- * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -65,7 +65,7 @@ public class SignerInformationEntity { * Base64 encoded certificate raw data. */ @Column(name = "raw_data", nullable = false, length = 4096) - String rawData; + private String rawData; /** * The country code of the cert. @@ -74,23 +74,21 @@ public class SignerInformationEntity { private String country; /** - * The thumbprint of the cert. + * The domain of the cert. */ - @Column(name = "thumbprint") - private String thumbprint; + @Column(name = "domain") + private String domain; /** - * Timestamp of the last record update. + * The group of the cert. */ - @Column(name = "updated_at", nullable = false) - private ZonedDateTime updatedAt = ZonedDateTime.now(); + @Column(name = "groupx") + private String group; /** - * Marks the record as deleted. + * SHA-256 Hash-Value of Certificate Subject (hex). */ - @Column(name = "deleted") - private boolean deleted = false; - - + @Column(name = "subject_hash") + private String subjectHash; } diff --git a/src/main/java/tng/trustnetwork/keydistribution/entity/TrustedIssuerEntity.java b/src/main/java/tng/trustnetwork/keydistribution/entity/TrustedIssuerEntity.java index 8311783..57a37d0 100644 --- a/src/main/java/tng/trustnetwork/keydistribution/entity/TrustedIssuerEntity.java +++ b/src/main/java/tng/trustnetwork/keydistribution/entity/TrustedIssuerEntity.java @@ -2,7 +2,7 @@ * ---license-start * WorldHealthOrganization / tng-key-distribution * --- - * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors * --- * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -50,12 +50,6 @@ public class TrustedIssuerEntity { @Column(name = "id") private Long id; - /** - * The revoked hash. - */ - @Column(name = "etag", nullable = false, length = 36) - private String etag; - /** * Timestamp of the Record. */ diff --git a/src/main/java/tng/trustnetwork/keydistribution/entity/VerificationMethodEntity.java b/src/main/java/tng/trustnetwork/keydistribution/entity/VerificationMethodEntity.java new file mode 100644 index 0000000..25b9fa0 --- /dev/null +++ b/src/main/java/tng/trustnetwork/keydistribution/entity/VerificationMethodEntity.java @@ -0,0 +1,74 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + +package tng.trustnetwork.keydistribution.entity; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; +import java.time.ZonedDateTime; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Data +@Getter +@Setter +@Entity +@Table(name = "verification_method") +@AllArgsConstructor +@NoArgsConstructor +public class VerificationMethodEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", columnDefinition = "BIGINT") + private Long id; + + @Column(name = "vm_id", length = 100) + private String vmId; + + @Column(name = "type", length = 100) + private String type; + + @Column(name = "controller", length = 100) + private String controller; + + @OneToOne(cascade = CascadeType.ALL) + @JoinColumn(name = "public_key_jwk_id") + private PublicKeyJwkEntity publicKeyJwk; + + @ManyToOne + @JoinColumn(name = "parent_document_id") + private DecentralizedIdentifierEntity parentDocument; + + @Column(name = "created_at", nullable = false) + private ZonedDateTime createdAt = ZonedDateTime.now(); + +} diff --git a/src/main/java/tng/trustnetwork/keydistribution/mapper/DidMapper.java b/src/main/java/tng/trustnetwork/keydistribution/mapper/DidMapper.java new file mode 100644 index 0000000..081bcbb --- /dev/null +++ b/src/main/java/tng/trustnetwork/keydistribution/mapper/DidMapper.java @@ -0,0 +1,87 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + +package tng.trustnetwork.keydistribution.mapper; + +import java.util.List; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.SubclassExhaustiveStrategy; +import org.mapstruct.SubclassMapping; +import tng.trustnetwork.keydistribution.entity.DecentralizedIdentifierEntity; +import tng.trustnetwork.keydistribution.entity.EcPublicKeyJwkEntity; +import tng.trustnetwork.keydistribution.entity.PublicKeyJwkEntity; +import tng.trustnetwork.keydistribution.entity.RsaPublicKeyJwkEntity; +import tng.trustnetwork.keydistribution.entity.VerificationMethodEntity; +import tng.trustnetwork.keydistribution.model.DidDocument; +import tng.trustnetwork.keydistribution.model.EcPublicKeyJwk; +import tng.trustnetwork.keydistribution.model.JwkVerificationMethod; +import tng.trustnetwork.keydistribution.model.PublicKeyJwk; +import tng.trustnetwork.keydistribution.model.RsaPublicKeyJwk; +import tng.trustnetwork.keydistribution.model.StringOrObject; +import tng.trustnetwork.keydistribution.model.VerificationMethod; + +@Mapper(componentModel = "spring", subclassExhaustiveStrategy = SubclassExhaustiveStrategy.RUNTIME_EXCEPTION) +public interface DidMapper { + + @Mapping(target = "didId", source = "didDocument.id") + @Mapping(target = "verificationMethods", source = "didDocument.verificationMethod") + @Mapping(target = "id", ignore = true) + @Mapping(target = "createdAt", ignore = true) + DecentralizedIdentifierEntity toEntity(DidDocument didDocument, String raw); + + @SubclassMapping(target = RsaPublicKeyJwkEntity.class, source = RsaPublicKeyJwk.class) + @SubclassMapping(target = EcPublicKeyJwkEntity.class, source = EcPublicKeyJwk.class) + @Mapping(target = "id", ignore = true) + @Mapping(target = "createdAt", ignore = true) + PublicKeyJwkEntity toEntity(PublicKeyJwk publicKeyJwk); + + @Mapping(target = "id", ignore = true) + @Mapping(target = "createdAt", ignore = true) + EcPublicKeyJwkEntity toEntity(EcPublicKeyJwk model); + + @Mapping(target = "id", ignore = true) + @Mapping(target = "createdAt", ignore = true) + RsaPublicKeyJwkEntity toEntity(RsaPublicKeyJwk model); + + @SubclassMapping(target = VerificationMethodEntity.class, source = JwkVerificationMethod.class) + @Mapping(target = "vmId", source = "verificationMethod.id") + @Mapping(target = "id", ignore = true) + @Mapping(target = "createdAt", ignore = true) + @Mapping(target = "parentDocument", ignore = true) + @Mapping(target = "publicKeyJwk", ignore = true) + VerificationMethodEntity toEntity(VerificationMethod verificationMethod); + + @Mapping(target = "type", constant = "JsonWebKey2020") + @Mapping(target = "vmId", source = "verificationMethod.id") + @Mapping(target = "id", ignore = true) + @Mapping(target = "parentDocument", ignore = true) + @Mapping(target = "createdAt", ignore = true) + VerificationMethodEntity toEntity(JwkVerificationMethod verificationMethod); + + default T unwrap(StringOrObject wrapped) { + return wrapped.getObjectValue(); + } + + default String toSingleString(List list) { + + return list == null ? null : String.join(",", list); + } +} diff --git a/src/main/java/tng/trustnetwork/keydistribution/mapper/IssuerMapper.java b/src/main/java/tng/trustnetwork/keydistribution/mapper/IssuerMapper.java index b37b94a..b0a6ae2 100644 --- a/src/main/java/tng/trustnetwork/keydistribution/mapper/IssuerMapper.java +++ b/src/main/java/tng/trustnetwork/keydistribution/mapper/IssuerMapper.java @@ -1,8 +1,8 @@ /*- * ---license-start - * WHO Digital Documentation Covid Certificate Gateway Service / ddcc-gateway-lib + * WorldHealthOrganization / tng-key-distribution * --- - * Copyright (C) 2022 T-Systems International GmbH and all other contributors + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors * --- * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,23 +24,16 @@ import java.util.List; import org.mapstruct.Mapper; import org.mapstruct.Mapping; -import tng.trustnetwork.keydistribution.dto.TrustedIssuerDto; import tng.trustnetwork.keydistribution.entity.TrustedIssuerEntity; @Mapper(componentModel = "spring") public interface IssuerMapper { @Mapping(source = "type", target = "urlType") + @Mapping(target = "id", ignore = true) + @Mapping(target = "createdAt", ignore = true) TrustedIssuerEntity trustedIssuerToTrustedIssuerEntity(TrustedIssuer trustedIssuer); - List trustedIssuerToTrustedIssuerEntity(List trustedIssuer); - @Mapping(source = "urlType", target = "type") - @Mapping(source = "createdAt", target = "timestamp") - TrustedIssuerDto trustedIssuerEntityToTrustedIssuerDto(TrustedIssuerEntity trustedIssuerEntity); - - @Mapping(source = "createdAt", target = "timestamp") - List trustedIssuerEntityToTrustedIssuerDto(List trustedIssuerEntities); - } diff --git a/src/main/java/tng/trustnetwork/keydistribution/model/DidContext.java b/src/main/java/tng/trustnetwork/keydistribution/model/DidContext.java new file mode 100644 index 0000000..8e8d2d9 --- /dev/null +++ b/src/main/java/tng/trustnetwork/keydistribution/model/DidContext.java @@ -0,0 +1,43 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + +package tng.trustnetwork.keydistribution.model; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@JsonInclude(JsonInclude.Include.NON_NULL) +public class DidContext { + + private String base; + + private String rating; + + private String publicAccess; + + private String additionalType; +} diff --git a/src/main/java/tng/trustnetwork/keydistribution/model/DidDocument.java b/src/main/java/tng/trustnetwork/keydistribution/model/DidDocument.java index e578968..b8bc421 100644 --- a/src/main/java/tng/trustnetwork/keydistribution/model/DidDocument.java +++ b/src/main/java/tng/trustnetwork/keydistribution/model/DidDocument.java @@ -1,6 +1,27 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + package tng.trustnetwork.keydistribution.model; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; import lombok.Getter; @@ -8,18 +29,20 @@ @Getter @Setter +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) public class DidDocument { @JsonProperty("@context") - @JsonIgnoreProperties(ignoreUnknown = true) - private List context; - @JsonIgnoreProperties(ignoreUnknown = true) + private List> context; + private String id; - @JsonIgnoreProperties(ignoreUnknown = true) + private String controller; - @JsonIgnoreProperties(ignoreUnknown = true) - private Object verificationMethod; - @JsonIgnoreProperties(ignoreUnknown = true) + + + private List> verificationMethod; + private Proof proof; } diff --git a/src/main/java/tng/trustnetwork/keydistribution/model/DidDocumentUnmarshal.java b/src/main/java/tng/trustnetwork/keydistribution/model/DidDocumentUnmarshal.java deleted file mode 100644 index a3afc04..0000000 --- a/src/main/java/tng/trustnetwork/keydistribution/model/DidDocumentUnmarshal.java +++ /dev/null @@ -1,24 +0,0 @@ -package tng.trustnetwork.keydistribution.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import java.util.List; -import lombok.Getter; -import lombok.Setter; - - -@Getter -@Setter -public class DidDocumentUnmarshal { - - @JsonIgnoreProperties(ignoreUnknown = true) - private List context; - @JsonIgnoreProperties(ignoreUnknown = true) - private String id; - @JsonIgnoreProperties(ignoreUnknown = true) - private String controller; - @JsonIgnoreProperties(ignoreUnknown = true) - private VerificationMethod verificationMethod; - @JsonIgnoreProperties(ignoreUnknown = true) - private Proof proof; - -} diff --git a/src/main/java/tng/trustnetwork/keydistribution/model/EcPublicKeyJwk.java b/src/main/java/tng/trustnetwork/keydistribution/model/EcPublicKeyJwk.java new file mode 100644 index 0000000..83f5870 --- /dev/null +++ b/src/main/java/tng/trustnetwork/keydistribution/model/EcPublicKeyJwk.java @@ -0,0 +1,57 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + +package tng.trustnetwork.keydistribution.model; + +import com.fasterxml.jackson.annotation.JsonEnumDefaultValue; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@JsonInclude(JsonInclude.Include.NON_NULL) +public class EcPublicKeyJwk extends PublicKeyJwk { + + private Curve crv; + + @JsonProperty("x") + private String xvalue; + + @JsonProperty("y") + private String yvalue; + + public enum Curve { + @JsonEnumDefaultValue + UNKNOWN, + + @JsonProperty("P-256") + P256, + + @JsonProperty("P-384") + P384, + + @JsonProperty("P-521") + P521 + + } + +} diff --git a/src/main/java/tng/trustnetwork/keydistribution/exception/BadRequestException.java b/src/main/java/tng/trustnetwork/keydistribution/model/JwkVerificationMethod.java similarity index 58% rename from src/main/java/tng/trustnetwork/keydistribution/exception/BadRequestException.java rename to src/main/java/tng/trustnetwork/keydistribution/model/JwkVerificationMethod.java index 1ca09c5..630850f 100644 --- a/src/main/java/tng/trustnetwork/keydistribution/exception/BadRequestException.java +++ b/src/main/java/tng/trustnetwork/keydistribution/model/JwkVerificationMethod.java @@ -2,7 +2,7 @@ * ---license-start * WorldHealthOrganization / tng-key-distribution * --- - * Copyright (C) 2022 T-Systems International GmbH and all other contributors + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors * --- * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,24 +18,17 @@ * ---license-end */ -package tng.trustnetwork.keydistribution.exception; +package tng.trustnetwork.keydistribution.model; -public class BadRequestException extends RuntimeException { - public int getStatus() { - return STATUS; - } +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Getter; +import lombok.Setter; - private static final int STATUS = 400; +@Getter +@Setter +@JsonInclude(JsonInclude.Include.NON_NULL) +public class JwkVerificationMethod extends VerificationMethod { - - /** - * Constructor for BadRequestException. - * - * @param message Massage of the exception. - */ - public BadRequestException(String message) { - - super(message); - } + private PublicKeyJwk publicKeyJwk; } diff --git a/src/main/java/tng/trustnetwork/keydistribution/model/Proof.java b/src/main/java/tng/trustnetwork/keydistribution/model/Proof.java index 556f7da..0626a0e 100644 --- a/src/main/java/tng/trustnetwork/keydistribution/model/Proof.java +++ b/src/main/java/tng/trustnetwork/keydistribution/model/Proof.java @@ -1,24 +1,44 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + package tng.trustnetwork.keydistribution.model; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; import lombok.Getter; import lombok.Setter; @Getter @Setter +@JsonInclude(JsonInclude.Include.NON_NULL) public class Proof { - @JsonIgnoreProperties(ignoreUnknown = true) private String type; - @JsonIgnoreProperties(ignoreUnknown = true) + private String created; - @JsonIgnoreProperties(ignoreUnknown = true) + private String nonce; - @JsonIgnoreProperties(ignoreUnknown = true) + private String proofPurpose; - @JsonIgnoreProperties(ignoreUnknown = true) + private String verificationMethod; - @JsonIgnoreProperties(ignoreUnknown = true) + private String jws; } diff --git a/src/main/java/tng/trustnetwork/keydistribution/model/PublicKeyJwk.java b/src/main/java/tng/trustnetwork/keydistribution/model/PublicKeyJwk.java index 45b0438..96569a0 100644 --- a/src/main/java/tng/trustnetwork/keydistribution/model/PublicKeyJwk.java +++ b/src/main/java/tng/trustnetwork/keydistribution/model/PublicKeyJwk.java @@ -1,26 +1,94 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + package tng.trustnetwork.keydistribution.model; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; import java.util.List; import lombok.Getter; import lombok.Setter; @Getter @Setter -public class PublicKeyJwk { +@JsonTypeInfo(property = "kty", use = JsonTypeInfo.Id.NAME) +@JsonSubTypes({ + @JsonSubTypes.Type(name = "EC", value = EcPublicKeyJwk.class), + @JsonSubTypes.Type(name = "RSA", value = RsaPublicKeyJwk.class), +}) +@JsonInclude(JsonInclude.Include.NON_NULL) +public abstract class PublicKeyJwk { - @JsonIgnoreProperties(ignoreUnknown = true) private String kty; - @JsonIgnoreProperties(ignoreUnknown = true) - private String crv; - @JsonProperty("x") - @JsonIgnoreProperties(ignoreUnknown = true) - private String xvalue; - @JsonProperty("y") - @JsonIgnoreProperties(ignoreUnknown = true) - private String yvalue; - @JsonIgnoreProperties(ignoreUnknown = true) + + private String kid; + + private String x5u; + private List x5c; + private String x5t; + + @JsonProperty("x5t#S256") + private String x5tS256; + + private Use use; + + @JsonProperty("key_ops") + private List keyOps; + + public enum KeyOps { + @JsonProperty("sign") + SIGN, + + @JsonProperty("verify") + VERIFY, + + @JsonProperty("encrypt") + ENCRYPT, + + @JsonProperty("decrypt") + DECRYPT, + + @JsonProperty("wrapKey") + WRAP_KEY, + + @JsonProperty("unwrapKey") + UNWRAP_KEY, + + @JsonProperty("deriveKey") + DERIVE_KEY, + + @JsonProperty("deriveBits") + DERIVE_BITS + + } + + public enum Use { + @JsonProperty("sig") + SIGNATURE, + + @JsonProperty("enc") + ENCRYPTION + } + } diff --git a/src/main/java/tng/trustnetwork/keydistribution/model/ResolvedKey.java b/src/main/java/tng/trustnetwork/keydistribution/model/ResolvedKey.java deleted file mode 100644 index 8143924..0000000 --- a/src/main/java/tng/trustnetwork/keydistribution/model/ResolvedKey.java +++ /dev/null @@ -1,21 +0,0 @@ -package tng.trustnetwork.keydistribution.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import lombok.Getter; -import lombok.Setter; - - -@Getter -@Setter -public class ResolvedKey { - - @JsonIgnoreProperties(ignoreUnknown = true) - private String type; - @JsonIgnoreProperties(ignoreUnknown = true) - private String id; - @JsonIgnoreProperties(ignoreUnknown = true) - private String controller; - @JsonIgnoreProperties(ignoreUnknown = true) - private PublicKeyJwk publicKeyJwk; - -} diff --git a/src/main/java/tng/trustnetwork/keydistribution/model/RsaPublicKeyJwk.java b/src/main/java/tng/trustnetwork/keydistribution/model/RsaPublicKeyJwk.java new file mode 100644 index 0000000..4006ef8 --- /dev/null +++ b/src/main/java/tng/trustnetwork/keydistribution/model/RsaPublicKeyJwk.java @@ -0,0 +1,39 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + +package tng.trustnetwork.keydistribution.model; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@JsonInclude(JsonInclude.Include.NON_NULL) +public class RsaPublicKeyJwk extends PublicKeyJwk { + + @JsonProperty("n") + private String nvalue; + + @JsonProperty("e") + private String evalue; + +} diff --git a/src/main/java/tng/trustnetwork/keydistribution/model/StringOrObject.java b/src/main/java/tng/trustnetwork/keydistribution/model/StringOrObject.java new file mode 100644 index 0000000..ae961b9 --- /dev/null +++ b/src/main/java/tng/trustnetwork/keydistribution/model/StringOrObject.java @@ -0,0 +1,37 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + +package tng.trustnetwork.keydistribution.model; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@JsonDeserialize(using = StringOrObjectDeserializer.class) +public class StringOrObject { + + private T objectValue; + + private String stringValue; +} diff --git a/src/main/java/tng/trustnetwork/keydistribution/model/StringOrObjectDeserializer.java b/src/main/java/tng/trustnetwork/keydistribution/model/StringOrObjectDeserializer.java new file mode 100644 index 0000000..8cea6c7 --- /dev/null +++ b/src/main/java/tng/trustnetwork/keydistribution/model/StringOrObjectDeserializer.java @@ -0,0 +1,64 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + +package tng.trustnetwork.keydistribution.model; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.deser.ContextualDeserializer; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; +import java.io.IOException; + +//@RequiredArgsConstructor +public class StringOrObjectDeserializer extends JsonDeserializer> implements ContextualDeserializer { + + private JavaType type; + + //private final ObjectMapper objectMapper; + + @Override + public JsonDeserializer createContextual(DeserializationContext context, BeanProperty property) { + + this.type = property.getType().containedType(0).containedType(0); + return this; + } + + @Override + public StringOrObject deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) + throws IOException { + + JsonNode jsonNode = jsonParser.getCodec().readTree(jsonParser); + + if (jsonNode instanceof TextNode textNode) { + + return new StringOrObject<>(null, textNode.textValue()); + } else if (jsonNode instanceof ObjectNode objectNode) { + + return new StringOrObject<>(deserializationContext.readTreeAsValue(objectNode, type), null); + } else { + return null; + } + } +} diff --git a/src/main/java/tng/trustnetwork/keydistribution/model/VerificationMethod.java b/src/main/java/tng/trustnetwork/keydistribution/model/VerificationMethod.java index 9f72641..a30596b 100644 --- a/src/main/java/tng/trustnetwork/keydistribution/model/VerificationMethod.java +++ b/src/main/java/tng/trustnetwork/keydistribution/model/VerificationMethod.java @@ -1,20 +1,45 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + package tng.trustnetwork.keydistribution.model; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import java.util.List; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; import lombok.Getter; import lombok.Setter; +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") +@JsonSubTypes({ + @JsonSubTypes.Type(value = JwkVerificationMethod.class, name = "JsonWebKey2020") +}) @Getter @Setter -@JsonDeserialize(using = VerificationMethodDeserializer.class) -public class VerificationMethod { +@JsonInclude(JsonInclude.Include.NON_NULL) +public abstract class VerificationMethod { + + private String id; - private List resolvedKeys; - private List unResolvedKeys; + private String controller; - public VerificationMethod() { + private String type; - } } diff --git a/src/main/java/tng/trustnetwork/keydistribution/model/VerificationMethodDeserializer.java b/src/main/java/tng/trustnetwork/keydistribution/model/VerificationMethodDeserializer.java deleted file mode 100644 index 364f875..0000000 --- a/src/main/java/tng/trustnetwork/keydistribution/model/VerificationMethodDeserializer.java +++ /dev/null @@ -1,55 +0,0 @@ -package tng.trustnetwork.keydistribution.model; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.deser.std.StdDeserializer; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -public class VerificationMethodDeserializer extends StdDeserializer { - - public VerificationMethodDeserializer() { - - this(null); - } - - protected VerificationMethodDeserializer(Class vc) { - - super(vc); - } - - @Override - public VerificationMethod deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - - JsonNode node = p.getCodec().readTree(p); - - ObjectMapper mapper = new ObjectMapper(); - - VerificationMethod verificationMethod = new VerificationMethod(); - List resolvedKeys = new ArrayList<>(); - List unResolvedKeys = new ArrayList<>(); - - if (node.isArray()) { - for (int i = 0; i <= node.size() - 1; i++) { - JsonNode chileNode = node.get(i); - Iterator> fields = chileNode.fields(); - - if (fields.hasNext()) { - ResolvedKey resolvedKey = mapper.readValue(chileNode.toString(), ResolvedKey.class); - resolvedKeys.add(resolvedKey); - } else { - unResolvedKeys.add(chileNode.toString()); - } - } - verificationMethod.setResolvedKeys(resolvedKeys); - verificationMethod.setUnResolvedKeys(unResolvedKeys); - } - return verificationMethod; - } - -} diff --git a/src/main/java/tng/trustnetwork/keydistribution/model/VerificationMethodModel.java b/src/main/java/tng/trustnetwork/keydistribution/model/VerificationMethodModel.java new file mode 100644 index 0000000..dbbc4a7 --- /dev/null +++ b/src/main/java/tng/trustnetwork/keydistribution/model/VerificationMethodModel.java @@ -0,0 +1,45 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + +package tng.trustnetwork.keydistribution.model; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import lombok.Getter; +import lombok.Setter; + + +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") +@JsonSubTypes({ + @JsonSubTypes.Type(value = JwkVerificationMethod.class, name = "JsonWebKey2020") +}) +@Getter +@Setter +@JsonInclude(JsonInclude.Include.NON_NULL) +public abstract class VerificationMethodModel { + + private String id; + + private String controller; + + private String type; + +} diff --git a/src/main/java/tng/trustnetwork/keydistribution/model/VerificationMethodReference.java b/src/main/java/tng/trustnetwork/keydistribution/model/VerificationMethodReference.java new file mode 100644 index 0000000..11bdabe --- /dev/null +++ b/src/main/java/tng/trustnetwork/keydistribution/model/VerificationMethodReference.java @@ -0,0 +1,36 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + +package tng.trustnetwork.keydistribution.model; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@JsonInclude(JsonInclude.Include.NON_NULL) +public class VerificationMethodReference extends DidContext { + + private String reference; + +} diff --git a/src/main/java/tng/trustnetwork/keydistribution/repository/DecentralizedIdentifierRepository.java b/src/main/java/tng/trustnetwork/keydistribution/repository/DecentralizedIdentifierRepository.java new file mode 100644 index 0000000..14b4369 --- /dev/null +++ b/src/main/java/tng/trustnetwork/keydistribution/repository/DecentralizedIdentifierRepository.java @@ -0,0 +1,27 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + +package tng.trustnetwork.keydistribution.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import tng.trustnetwork.keydistribution.entity.DecentralizedIdentifierEntity; + +public interface DecentralizedIdentifierRepository extends JpaRepository { +} diff --git a/src/main/java/tng/trustnetwork/keydistribution/repository/InfoRepository.java b/src/main/java/tng/trustnetwork/keydistribution/repository/PublicKeyJwkRepository.java similarity index 76% rename from src/main/java/tng/trustnetwork/keydistribution/repository/InfoRepository.java rename to src/main/java/tng/trustnetwork/keydistribution/repository/PublicKeyJwkRepository.java index 7187520..0f057c5 100644 --- a/src/main/java/tng/trustnetwork/keydistribution/repository/InfoRepository.java +++ b/src/main/java/tng/trustnetwork/keydistribution/repository/PublicKeyJwkRepository.java @@ -2,7 +2,7 @@ * ---license-start * WorldHealthOrganization / tng-key-distribution * --- - * Copyright (C) 2022 T-Systems International GmbH and all other contributors + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors * --- * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,8 +21,7 @@ package tng.trustnetwork.keydistribution.repository; import org.springframework.data.jpa.repository.JpaRepository; -import tng.trustnetwork.keydistribution.entity.InfoEntity; - -public interface InfoRepository extends JpaRepository { +import tng.trustnetwork.keydistribution.entity.PublicKeyJwkEntity; +public interface PublicKeyJwkRepository extends JpaRepository { } diff --git a/src/main/java/tng/trustnetwork/keydistribution/repository/SignerInformationRepository.java b/src/main/java/tng/trustnetwork/keydistribution/repository/SignerInformationRepository.java index 0e5150a..627ad7e 100644 --- a/src/main/java/tng/trustnetwork/keydistribution/repository/SignerInformationRepository.java +++ b/src/main/java/tng/trustnetwork/keydistribution/repository/SignerInformationRepository.java @@ -2,7 +2,7 @@ * ---license-start * WorldHealthOrganization / tng-key-distribution * --- - * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors * --- * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,43 +20,36 @@ package tng.trustnetwork.keydistribution.repository; -import java.time.ZonedDateTime; import java.util.List; -import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; import tng.trustnetwork.keydistribution.entity.SignerInformationEntity; public interface SignerInformationRepository extends JpaRepository { - Optional findFirstByIdIsNotNullOrderByIdAsc(); + List getByCountryIs(String country); - Optional findFirstByIdGreaterThanOrderByIdAsc(Long id); + List getByDomainIs(String domain); - List findAllByOrderByIdAsc(); + List getByDomainIsAndCountryIs(String domain, String country); - @Modifying - @Query("UPDATE SignerInformationEntity s SET s.deleted = true WHERE s.kid not in :kids") - void setDeletedByKidsNotIn(@Param("kids") List kids); + List getByCountryIsAndGroupIs(String country, String group); - @Modifying - @Query("UPDATE SignerInformationEntity s SET s.deleted = true") - void setAllDeleted(); + List getByDomainIsAndGroupIs(String domain, String group); - void deleteByKidNotIn(List kids); + List getByGroupIs(String group); + List getByDomainIsAndCountryIsAndGroupIs(String domain, String country, String group); - List findAllByDeletedOrderByIdAsc(boolean deleted); + List getBySubjectHashIsAndCountryIsAndDomainIs( + String subjectHash, String country, String domain); - void deleteByKidIn(List kids); + @Query("SELECT DISTINCT s.country FROM SignerInformationEntity s") + List getCountryList(); - List findAllByUpdatedAtAfterOrderByIdAsc(ZonedDateTime ifModifiedDateTime); + @Query("SELECT DISTINCT s.domain FROM SignerInformationEntity s") + List getDomainsList(); - List findAllByKidIn(List kids); - - Optional findFirstByIdIsNotNullAndDeletedOrderByIdAsc(boolean deleted); - - Optional findFirstByIdGreaterThanAndDeletedOrderByIdAsc(Long resumeToken, boolean deleted); + @Query("SELECT DISTINCT s.group FROM SignerInformationEntity s") + List getGroupList(); } diff --git a/src/main/java/tng/trustnetwork/keydistribution/repository/TrustedIssuerRepository.java b/src/main/java/tng/trustnetwork/keydistribution/repository/TrustedIssuerRepository.java index c8e76a5..f78e6b5 100644 --- a/src/main/java/tng/trustnetwork/keydistribution/repository/TrustedIssuerRepository.java +++ b/src/main/java/tng/trustnetwork/keydistribution/repository/TrustedIssuerRepository.java @@ -2,7 +2,7 @@ * ---license-start * WorldHealthOrganization / tng-key-distribution * --- - * Copyright (C) 2022 T-Systems International GmbH and all other contributors + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors * --- * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,9 +24,7 @@ import org.springframework.data.jpa.repository.JpaRepository; import tng.trustnetwork.keydistribution.entity.TrustedIssuerEntity; -public interface TrustedIssuerRepository extends JpaRepository { +public interface TrustedIssuerRepository extends JpaRepository { - void deleteAllByEtag(String etag); - - List findAllByEtag(String etag); + List findAllByUrlTypeIs(TrustedIssuerEntity.UrlType urlType); } diff --git a/src/main/java/tng/trustnetwork/keydistribution/repository/VerificationMethodRepository.java b/src/main/java/tng/trustnetwork/keydistribution/repository/VerificationMethodRepository.java new file mode 100644 index 0000000..0fc619f --- /dev/null +++ b/src/main/java/tng/trustnetwork/keydistribution/repository/VerificationMethodRepository.java @@ -0,0 +1,27 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + +package tng.trustnetwork.keydistribution.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import tng.trustnetwork.keydistribution.entity.VerificationMethodEntity; + +public interface VerificationMethodRepository extends JpaRepository { +} diff --git a/src/main/java/tng/trustnetwork/keydistribution/restapi/controller/ContextController.java b/src/main/java/tng/trustnetwork/keydistribution/restapi/controller/ContextController.java deleted file mode 100644 index 28621ff..0000000 --- a/src/main/java/tng/trustnetwork/keydistribution/restapi/controller/ContextController.java +++ /dev/null @@ -1,96 +0,0 @@ -/*- - * ---license-start - * WorldHealthOrganization / tng-key-distribution - * --- - * Copyright (C) 2021 T-Systems International GmbH and all other contributors - * --- - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://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. - * ---license-end - */ - -package tng.trustnetwork.keydistribution.restapi.controller; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.media.ExampleObject; -import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.io.IOUtils; -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.Resource; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import tng.trustnetwork.keydistribution.config.KdsConfigProperties; - -@RestController -@RequestMapping("/context") -@Slf4j -@RequiredArgsConstructor -public class ContextController { - - private final KdsConfigProperties properties; - - /** - * Http Method for getting the current context. - */ - @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) - @Operation( - summary = "Provide configuration information for the verifier app", - tags = {"Configuration"}, - responses = { - @ApiResponse( - responseCode = "200", - description = "Returns the current context for the verifier app.", - content = @Content( - mediaType = MediaType.APPLICATION_JSON_VALUE, - schema = @Schema(implementation = String.class), - examples = {@ExampleObject(value = "{\"origin\":\"DE\",\"versions\":{\"default\":{\"privacyUrl\":" - + "\"https://publications.europa.eu/en/web/about-us/legal-notices/eu-mobile-apps\"," - + "\"context\":{\"url\":\"https://dgca-verifier-service.example.com/context\",\"pubKeys\":" - + "[\"lKdU1EbQubxyDDm2q3N8KclZ2C94+3eI=\"," - + "\"r/mIkG3eEpVdm+u/ko/cwxzOMo1bkA5E=\"]},\"endpoints\":{\"status\":{\"url\":" - + "\"https://dgca-verifier-service.example.com/signercertificateStatus\",\"pubKeys\":" - + "[\"lKdU1EbQubxyDDm2q3N8KclZ2C94+3eI=\"," - + "\"r/mIkG3eEpVdm+u/ko/cwxzOMo1bkA5E=\"]},\"update\":{\"url\":" - + "\"https://dgca-verifier-service.example.com/signercertificateUpdate\",\"pubKeys\":" - + "[\"lKdU1EbQubxyDDm2q3N8KclZ2C94+3eI=\"," - + "\"r/mIkG3eEpVdm+u/ko/cwxzOMo1bkA5E=\"]}}},\"0.1.0\":{\"outdated\":true}}}")})) - } - ) - public ResponseEntity getContext() { - try { - - String context = null; - - if (properties.getContext().isEmpty()) { - Resource resource = new ClassPathResource("/static/context.json");//TODO: cleanup EU context information - context = IOUtils.toString(resource.getInputStream(), StandardCharsets.UTF_8); - } else { - context = properties.getContext(); - } - - return ResponseEntity.ok(context); - } catch (IOException e) { - log.error("Could not read context file"); - } - return ResponseEntity.ok(""); - } - -} diff --git a/src/main/java/tng/trustnetwork/keydistribution/restapi/controller/SignerInformationController.java b/src/main/java/tng/trustnetwork/keydistribution/restapi/controller/SignerInformationController.java deleted file mode 100644 index 092db14..0000000 --- a/src/main/java/tng/trustnetwork/keydistribution/restapi/controller/SignerInformationController.java +++ /dev/null @@ -1,248 +0,0 @@ -/*- - * ---license-start - * WorldHealthOrganization / tng-key-distribution - * --- - * Copyright (C) 2021 T-Systems International GmbH and all other contributors - * --- - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://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. - * ---license-end - */ - -package tng.trustnetwork.keydistribution.restapi.controller; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.enums.ParameterIn; -import io.swagger.v3.oas.annotations.headers.Header; -import io.swagger.v3.oas.annotations.media.ArraySchema; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.media.ExampleObject; -import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import jakarta.validation.Valid; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeParseException; -import java.util.List; -import java.util.Map; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestHeader; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import tng.trustnetwork.keydistribution.entity.SignerInformationEntity; -import tng.trustnetwork.keydistribution.exception.BadRequestException; -import tng.trustnetwork.keydistribution.restapi.dto.CertificatesLookupResponseItemDto; -import tng.trustnetwork.keydistribution.restapi.dto.DeltaListDto; -import tng.trustnetwork.keydistribution.service.SignerInformationService; - -@RestController -@RequestMapping("/") -@Slf4j -@RequiredArgsConstructor -public class SignerInformationController { - - private static final String X_RESUME_TOKEN_HEADER = "X-RESUME-TOKEN"; - private static final String X_KID_HEADER = "X-KID"; - - private final SignerInformationService signerInformationService; - - - /** - * Http Method for getting signer certificate. - */ - @GetMapping(path = "/signercertificateUpdate", produces = MediaType.TEXT_PLAIN_VALUE) - @Operation( - summary = "Gets one signer certificate and a resume token.", - description = "This method return one signer certificate and a corresponding resume token. In order to " - + "download all available certificates, start calling this method without the resume token set. Then repeat" - + " to call this method, with the resume token parameter set to the value of the last response. When you " - + "receive a 204 response you have downloaded all available certificates.", - tags = {"Signer Information" }, - parameters = { - @Parameter( - in = ParameterIn.HEADER, - name = "X-RESUME-TOKEN", - description = "Defines where to resume the download of the certificates", - schema = @Schema(implementation = Long.class)) - }, - responses = { - @ApiResponse( - responseCode = "200", - description = "Returns one signer certificate and as header parameter a resume token and the kid. " - + "There might be more certificates available to download. Repeat the request with the resume " - + "token parameter set to the actual value, until you get a 204 response.", - headers = { - @Header( - name = "X-RESUME-TOKEN", - description = "Token can be used to resume the download of the certificates."), - @Header( - name = "X-KID", - description = "The kid of the returned certificate.") - }, - content = @Content( - mediaType = MediaType.TEXT_PLAIN_VALUE, - schema = @Schema(implementation = String.class), - examples = {@ExampleObject(value = - "MIIBGzCBwqADAgECAgRggUObMAoGCCqGSM49BAMCMBYxFDASBgNVBAMMC2VkZ2Nf" - + "ZGV2X2VjMB4XDTIxMDQyMjA5MzYyN1oXDTIyMDQyMjA5MzYyN1owFjEUMBIGA1UE" - + "AwwLZWRnY19kZXZfZWMwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQVQc9JY190" - + "s/Jn0CBSq/AWuxmqUzRVu+AsCe6gfbqk3s0e4jonzp5v/5IMW/9t7v5Fu2ITMmOT" - + "VfKL1TuM+aixMAoGCCqGSM49BAMCA0gAMEUCIQCGWIk6ZET3afRxdpFVuXdrEYtF" - + "iR1MGDx4HweZfspjSgIgBdCJsT746/FI3euIbzKDoeY65m+Qx2/4Cd/vOayNbuw=")})), - @ApiResponse( - responseCode = "204", - description = "No Content available. All certificates already downloaded.", - content = @Content(schema = @Schema(hidden = true))) - } - ) - public ResponseEntity getSignerCertificateUpdate( - @RequestHeader(value = X_RESUME_TOKEN_HEADER, required = false) Long resumeToken - ) { - HttpHeaders responseHeaders = new HttpHeaders(); - SignerInformationEntity signerInformation = signerInformationService.getCertificate(resumeToken).orElse(null); - - if (signerInformation == null) { - return ResponseEntity.noContent().build(); - } - - responseHeaders.set(X_RESUME_TOKEN_HEADER, signerInformation.getId().toString()); - responseHeaders.set(X_KID_HEADER, signerInformation.getKid()); - - return ResponseEntity.ok() - .headers(responseHeaders) - .body(signerInformation.getRawData()); - } - - - /** - * Http Method for getting list of valid certificates ids. - */ - @GetMapping(path = "/signercertificateStatus", produces = MediaType.APPLICATION_JSON_VALUE) - @Operation( - summary = "Gets a list of kids from all valid certificates.", - tags = {"Signer Information" }, - description = "Gets a list of kids from all valid certificates. This list can be used to verify, that the " - + "downloaded certificates are still valid. If a kid of a downloaded certificate is not part of the list, " - + "the certificate is not valid any more.", - responses = { - @ApiResponse( - responseCode = "200", - description = "Returns a list of kids of all valid certificates.", - content = @Content( - mediaType = MediaType.APPLICATION_JSON_VALUE, - array = @ArraySchema(schema = @Schema(implementation = String.class)), - examples = {@ExampleObject(value = "[\"8xYtW2837bc=\",\"zoQi+KT68LM=\"]")})) - }) - public ResponseEntity> getSignerCertificateStatus() { - - return ResponseEntity.ok(signerInformationService.getListOfValidKids()); - } - - - /** - * Http Method for getting delta list of certificates changes. - */ - @GetMapping(path = "/signercertificateStatus/delta", produces = MediaType.APPLICATION_JSON_VALUE) - @Operation( - summary = "Gets a list of kids from all valid certificates.", - tags = {"Signer Information" }, - description = "Gets a list of kids from all valid certificates. This list can be used to verify, that the " - + "downloaded certificates are still valid. If a kid of a downloaded certificate is not part of the list, " - + "the certificate is not valid any more.", - parameters = { - @Parameter( - in = ParameterIn.HEADER, - name = "If-Modified-Since", - description = "Returns only the objects which are modified behind the given date.", - required = false, - schema = @Schema(implementation = String.class))}, - responses = { - @ApiResponse( - responseCode = "200", - description = "Returns a list of kids of all valid certificates.", - content = @Content( - mediaType = MediaType.APPLICATION_JSON_VALUE, - schema = @Schema(implementation = DeltaListDto.class) - )) - }) - public ResponseEntity getDeltaList( - @RequestHeader(value = HttpHeaders.IF_MODIFIED_SINCE, required = false) String ifModifiedSince) { - - DeltaListDto result; - - if (ifModifiedSince != null) { - ZonedDateTime ifModifiedDateTime = parseIfModifiedSinceHeader(ifModifiedSince); - result = signerInformationService.getDeltaList(ifModifiedDateTime); - } else { - result = signerInformationService.getDeltaList(); - } - - return ResponseEntity.ok(result); - } - - private ZonedDateTime parseIfModifiedSinceHeader(String ifModifiedSince) throws BadRequestException { - ZonedDateTime ifModifiedDateTime; - try { - ifModifiedDateTime = ZonedDateTime.parse(ifModifiedSince); - } catch (DateTimeParseException e) { - try { - ifModifiedDateTime = ZonedDateTime.parse(ifModifiedSince, DateTimeFormatter.RFC_1123_DATE_TIME); - } catch (DateTimeParseException ex) { - throw new BadRequestException("Can not parse if-modified-since header"); - } - } - return ifModifiedDateTime; - } - - /** - * Http Method for looking up certificate data. - * - * @param requestedCertList list of kids, for which the data should be returned - * @return the requested certificate data. - */ - @PostMapping(path = "signercertificateUpdate", - consumes = MediaType.APPLICATION_JSON_VALUE, - produces = MediaType.APPLICATION_JSON_VALUE) - @Operation( - summary = "Returns the data for the requested certificates.", - description = "Returns the certificate data for all kids in the request body.", - tags = {"Signer Information" }, - requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody( - required = true, - content = @Content(array = @ArraySchema( - schema = @Schema(implementation = String.class, name = "kid"))) - ), - responses = { - @ApiResponse( - responseCode = "200", - description = "Returns the certificate data.", - content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, - examples = {@ExampleObject(value = "{ \n “DE”: [“MII….”,”MII…”],\n “NL”: [“MII…”,”MII…”]\n}\n")})) - } - ) - public ResponseEntity>> lookupCertificateData( - @Valid @RequestBody(required = true) List requestedCertList) { - - return ResponseEntity.ok(signerInformationService.getCertificatesData(requestedCertList)); - - } - - -} diff --git a/src/main/java/tng/trustnetwork/keydistribution/restapi/controller/TrustedIssuerController.java b/src/main/java/tng/trustnetwork/keydistribution/restapi/controller/TrustedIssuerController.java deleted file mode 100644 index 45908a1..0000000 --- a/src/main/java/tng/trustnetwork/keydistribution/restapi/controller/TrustedIssuerController.java +++ /dev/null @@ -1,101 +0,0 @@ -/*- - * ---license-start - * WorldHealthOrganization / tng-key-distribution - * --- - * Copyright (C) 2021 T-Systems International GmbH and all other contributors - * --- - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://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. - * ---license-end - */ - -package tng.trustnetwork.keydistribution.restapi.controller; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.enums.ParameterIn; -import io.swagger.v3.oas.annotations.headers.Header; -import io.swagger.v3.oas.annotations.media.ArraySchema; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import java.util.List; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestHeader; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import tng.trustnetwork.keydistribution.dto.TrustedIssuerDto; -import tng.trustnetwork.keydistribution.mapper.IssuerMapper; -import tng.trustnetwork.keydistribution.service.TrustedIssuerService; - -@RestController -@RequestMapping("/") -@Slf4j -@RequiredArgsConstructor -public class TrustedIssuerController { - - - private final TrustedIssuerService trustedIssuerService; - - private final IssuerMapper issuerMapper; - - - /** - * Http Method for getting trusted issuers. - */ - @GetMapping(path = "/trustedissuers", produces = MediaType.APPLICATION_JSON_VALUE) - @Operation( - summary = "Gets one trusted issuers", - description = "This method return a list of trusted issuers.", - tags = {"Trusted Issuers"}, - parameters = { - @Parameter( - in = ParameterIn.HEADER, - name = "IF-NONE-MATCH", - description = "When the eTag matches the current Tag, there is a 304 response.", - required = false, - schema = @Schema(implementation = String.class)) - }, - responses = { - @ApiResponse( - responseCode = "200", - description = "Returns the the trusted issuers list.", - headers = @Header(name = HttpHeaders.ETAG, description = "ETAG of the current data set"), - content = @Content( - mediaType = MediaType.APPLICATION_JSON_VALUE, - array = @ArraySchema(schema = - @Schema(implementation = TrustedIssuerDto.class)))), - @ApiResponse( - responseCode = "304", - description = "Not modified.") - } - ) - public ResponseEntity> getTrustedIssuers( - @RequestHeader(value = HttpHeaders.IF_NONE_MATCH, defaultValue = "") String ifNoneMatch) { - - String currentEtag = trustedIssuerService.getEtag(); - - if (ifNoneMatch.equals(currentEtag)) { - return ResponseEntity.status(HttpStatus.NOT_MODIFIED).build(); - } - - return ResponseEntity.ok().eTag(currentEtag).body(issuerMapper.trustedIssuerEntityToTrustedIssuerDto( - trustedIssuerService.getAllIssuers(currentEtag))); - } - -} diff --git a/src/main/java/tng/trustnetwork/keydistribution/restapi/controller/UniversalResolverController.java b/src/main/java/tng/trustnetwork/keydistribution/restapi/controller/UniversalResolverController.java deleted file mode 100644 index 4c1e241..0000000 --- a/src/main/java/tng/trustnetwork/keydistribution/restapi/controller/UniversalResolverController.java +++ /dev/null @@ -1,34 +0,0 @@ -package tng.trustnetwork.keydistribution.restapi.controller; - -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RestController; -import tng.trustnetwork.keydistribution.model.DidDocumentUnmarshal; -import tng.trustnetwork.keydistribution.service.UniversalResolverService; - -@RestController -public class UniversalResolverController { - - @Autowired - private UniversalResolverService urService; - - /** - * Fetches DID document. - * - * @param didKey key id of the did document - * @return ResponseEntity as http response - */ - @GetMapping(path = "/getDID/{didKey}", produces = MediaType.APPLICATION_JSON_VALUE) - @Tag(name = "Temporary Testing API") - public ResponseEntity getDidDocument(@PathVariable String didKey) { - - DidDocumentUnmarshal didDocumentUnmarshal = urService.universalResolverApiCall(didKey); - return ResponseEntity.ok(didDocumentUnmarshal); - - } - -} diff --git a/src/main/java/tng/trustnetwork/keydistribution/restapi/dto/CertificatesLookupResponseItemDto.java b/src/main/java/tng/trustnetwork/keydistribution/restapi/dto/CertificatesLookupResponseItemDto.java deleted file mode 100644 index 5fe812c..0000000 --- a/src/main/java/tng/trustnetwork/keydistribution/restapi/dto/CertificatesLookupResponseItemDto.java +++ /dev/null @@ -1,21 +0,0 @@ -package tng.trustnetwork.keydistribution.restapi.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Getter; - -@Schema( - name = "DeltaList", - type = "object", - example = "{\n" - + "\"updated\": [\"33333d=\",\"333311=\",\"55554=\"],\n" - + "\"deleted\":[\"3115adf=\"]\n" - + "}" -) - -@Getter -@AllArgsConstructor -public class CertificatesLookupResponseItemDto { - String kid; - String rawData; -} diff --git a/src/main/java/tng/trustnetwork/keydistribution/restapi/dto/DeltaListDto.java b/src/main/java/tng/trustnetwork/keydistribution/restapi/dto/DeltaListDto.java deleted file mode 100644 index e7fa43e..0000000 --- a/src/main/java/tng/trustnetwork/keydistribution/restapi/dto/DeltaListDto.java +++ /dev/null @@ -1,24 +0,0 @@ -package tng.trustnetwork.keydistribution.restapi.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import java.util.List; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Schema( - name = "DeltaList", - type = "object", - example = "{\n" - + "\"updated\": [\"33333d=\",\"333311=\",\"55554=\"],\n" - + "\"deleted\":[\"3115adf=\"]\n" - + "}" -) - -@Data -@AllArgsConstructor -@NoArgsConstructor -public class DeltaListDto { - List updated; - List deleted; -} diff --git a/src/main/java/tng/trustnetwork/keydistribution/service/DecentralizedIdentifierService.java b/src/main/java/tng/trustnetwork/keydistribution/service/DecentralizedIdentifierService.java new file mode 100644 index 0000000..37763d6 --- /dev/null +++ b/src/main/java/tng/trustnetwork/keydistribution/service/DecentralizedIdentifierService.java @@ -0,0 +1,70 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + +package tng.trustnetwork.keydistribution.service; + +import java.util.Objects; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import tng.trustnetwork.keydistribution.entity.DecentralizedIdentifierEntity; +import tng.trustnetwork.keydistribution.mapper.DidMapper; +import tng.trustnetwork.keydistribution.model.DidDocument; +import tng.trustnetwork.keydistribution.repository.DecentralizedIdentifierRepository; +import tng.trustnetwork.keydistribution.repository.PublicKeyJwkRepository; +import tng.trustnetwork.keydistribution.repository.VerificationMethodRepository; + +@Slf4j +@Service +@RequiredArgsConstructor +public class DecentralizedIdentifierService { + + private final DecentralizedIdentifierRepository decentralizedIdentifierRepository; + + private final VerificationMethodRepository verificationMethodRepository; + + private final PublicKeyJwkRepository publicKeyJwkRepository; + + private final DidMapper didMapper; + + /** + * Update the list of Decentralized Identifier Documents. + * + * @param didDocument parsed DID Document + * @param raw RAW JSON Representation (This is required to be able to verify integrity of DID afterwords) + */ + @Transactional + public void updateDecentralizedIdentifierList(DidDocument didDocument, String raw) { + + DecentralizedIdentifierEntity didEntity = didMapper.toEntity(didDocument, raw); + decentralizedIdentifierRepository.save(didEntity); + + didEntity.getVerificationMethods() + .stream() + .filter(Objects::nonNull) + .forEach(verificationMethod -> { + + verificationMethod.setParentDocument(didEntity); + publicKeyJwkRepository.save(verificationMethod.getPublicKeyJwk()); + verificationMethodRepository.save(verificationMethod); + }); + } +} diff --git a/src/main/java/tng/trustnetwork/keydistribution/service/InfoService.java b/src/main/java/tng/trustnetwork/keydistribution/service/InfoService.java deleted file mode 100644 index 69e2c3a..0000000 --- a/src/main/java/tng/trustnetwork/keydistribution/service/InfoService.java +++ /dev/null @@ -1,61 +0,0 @@ -/*- - * ---license-start - * WorldHealthOrganization / tng-key-distribution - * --- - * Copyright (C) 2022 T-Systems International GmbH and all other contributors - * --- - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://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. - * ---license-end - */ - -package tng.trustnetwork.keydistribution.service; - -import java.util.Optional; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import tng.trustnetwork.keydistribution.entity.InfoEntity; -import tng.trustnetwork.keydistribution.repository.InfoRepository; - -@Slf4j -@RequiredArgsConstructor -@Service -public class InfoService { - - public static final String CURRENT_ETAG = "CURRENTETAG"; - - private final InfoRepository infoRepository; - - /** - * Gets a value for the given key from the db. - * - * @param key the key, for which the value should be returned - * @return the value or null if not found in db - */ - public String getValueForKey(String key) { - Optional optionalValue = infoRepository.findById(key); - - return optionalValue.map(InfoEntity::getValue).orElse(null); - } - - /** - * Saves the value for a given key in the db. - * - * @param key key of the value to save. - * @param value the value to save - */ - public void setValueForKey(String key, String value) { - InfoEntity infoEntity = new InfoEntity(key, value); - infoRepository.save(infoEntity); - } -} diff --git a/src/main/java/tng/trustnetwork/keydistribution/service/KdsCertUtils.java b/src/main/java/tng/trustnetwork/keydistribution/service/KdsCertUtils.java new file mode 100644 index 0000000..f1fb954 --- /dev/null +++ b/src/main/java/tng/trustnetwork/keydistribution/service/KdsCertUtils.java @@ -0,0 +1,34 @@ +package tng.trustnetwork.keydistribution.service; + +import eu.europa.ec.dgc.utils.CertificateUtils; +import java.io.IOException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.Base64; +import lombok.RequiredArgsConstructor; +import org.bouncycastle.cert.X509CertificateHolder; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class KdsCertUtils { + + private final CertificateUtils certificateUtils; + + /** + * Parse Base64 Encoded Certificate. + * + * @param raw Base64 encoded certificate in DER format + * @return parsed Certificate instance + */ + public X509Certificate parseCertificate(String raw) { + + try { + byte[] rawDataBytes = Base64.getDecoder().decode(raw); + X509CertificateHolder certificateHolder = new X509CertificateHolder(rawDataBytes); + return certificateUtils.convertCertificate(certificateHolder); + } catch (CertificateException | IOException e) { + return null; + } + } +} diff --git a/src/main/java/tng/trustnetwork/keydistribution/service/SignerCertificateDownloadService.java b/src/main/java/tng/trustnetwork/keydistribution/service/SignerCertificateDownloadService.java index 650c9c8..11e9229 100644 --- a/src/main/java/tng/trustnetwork/keydistribution/service/SignerCertificateDownloadService.java +++ b/src/main/java/tng/trustnetwork/keydistribution/service/SignerCertificateDownloadService.java @@ -2,7 +2,7 @@ * ---license-start * WorldHealthOrganization / tng-key-distribution * --- - * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors * --- * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,10 +20,41 @@ package tng.trustnetwork.keydistribution.service; -public interface SignerCertificateDownloadService { +import eu.europa.ec.dgc.gateway.connector.DgcGatewayDownloadConnector; +import eu.europa.ec.dgc.gateway.connector.model.TrustListItem; +import eu.europa.ec.dgc.gateway.connector.model.TrustedCertificateTrustListItem; +import java.util.List; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import net.javacrumbs.shedlock.spring.annotation.SchedulerLock; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +/** + * A service to download the signer certificates from the digital green certificate gateway. + */ +@Slf4j +@RequiredArgsConstructor +@Component +public class SignerCertificateDownloadService { + + private final DgcGatewayDownloadConnector dgcGatewayConnector; + + private final SignerInformationService signerInformationService; /** - * Synchronises the signer certificates with the gateway. + * Download TrustedCertificates from Gateway. */ - void downloadCertificates(); + @Scheduled(fixedDelayString = "${dgc.certificatesDownloader.timeInterval}") + @SchedulerLock(name = "SignerCertificateDownloadService_downloadCertificates", lockAtLeastFor = "PT0S", + lockAtMostFor = "${dgc.certificatesDownloader.lockLimit}") + public void downloadCertificates() { + + log.info("Certificates download started"); + + List trustedCerts = dgcGatewayConnector.getDdccTrustedCertificates(); + signerInformationService.updateTrustedCertsList(trustedCerts); + + log.info("Certificates download finished"); + } } diff --git a/src/main/java/tng/trustnetwork/keydistribution/service/SignerCertificateDownloadServiceImpl.java b/src/main/java/tng/trustnetwork/keydistribution/service/SignerCertificateDownloadServiceImpl.java deleted file mode 100644 index e885f28..0000000 --- a/src/main/java/tng/trustnetwork/keydistribution/service/SignerCertificateDownloadServiceImpl.java +++ /dev/null @@ -1,60 +0,0 @@ -/*- - * ---license-start - * WorldHealthOrganization / tng-key-distribution - * --- - * Copyright (C) 2021 T-Systems International GmbH and all other contributors - * --- - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://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. - * ---license-end - */ - -package tng.trustnetwork.keydistribution.service; - -import eu.europa.ec.dgc.gateway.connector.DgcGatewayDownloadConnector; -import eu.europa.ec.dgc.gateway.connector.model.TrustListItem; -import java.util.List; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import net.javacrumbs.shedlock.spring.annotation.SchedulerLock; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Profile; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; - -/** - * A service to download the signer certificates from the digital green certificate gateway. - */ -@Slf4j -@RequiredArgsConstructor -@Component -@Profile("!btp") -//@ConditionalOnProperty("dgc.gateway.connector.enabled") -public class SignerCertificateDownloadServiceImpl implements SignerCertificateDownloadService { - - private final DgcGatewayDownloadConnector dgcGatewayConnector; - private final SignerInformationService signerInformationService; - - @Override - @Scheduled(fixedDelayString = "${dgc.certificatesDownloader.timeInterval}") - @SchedulerLock(name = "SignerCertificateDownloadService_downloadCertificates", lockAtLeastFor = "PT0S", - lockAtMostFor = "${dgc.certificatesDownloader.lockLimit}") - public void downloadCertificates() { - log.info("Certificates download started"); - - List trustedCerts = dgcGatewayConnector.getTrustedCertificates(); - - signerInformationService.updateTrustedCertsList(trustedCerts); - - log.info("Certificates download finished"); - } -} diff --git a/src/main/java/tng/trustnetwork/keydistribution/service/SignerInformationService.java b/src/main/java/tng/trustnetwork/keydistribution/service/SignerInformationService.java index a1bba61..26ffe98 100644 --- a/src/main/java/tng/trustnetwork/keydistribution/service/SignerInformationService.java +++ b/src/main/java/tng/trustnetwork/keydistribution/service/SignerInformationService.java @@ -2,7 +2,7 @@ * ---license-start * WorldHealthOrganization / tng-key-distribution * --- - * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors * --- * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,21 +20,18 @@ package tng.trustnetwork.keydistribution.service; -import eu.europa.ec.dgc.gateway.connector.model.TrustListItem; +import eu.europa.ec.dgc.gateway.connector.model.TrustedCertificateTrustListItem; +import eu.europa.ec.dgc.utils.CertificateUtils; +import java.security.NoSuchAlgorithmException; +import java.security.cert.X509Certificate; import java.time.ZonedDateTime; -import java.util.ArrayList; import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import tng.trustnetwork.keydistribution.entity.SignerInformationEntity; import tng.trustnetwork.keydistribution.repository.SignerInformationRepository; -import tng.trustnetwork.keydistribution.restapi.dto.CertificatesLookupResponseItemDto; -import tng.trustnetwork.keydistribution.restapi.dto.DeltaListDto; @Slf4j @Component @@ -42,128 +39,179 @@ public class SignerInformationService { private final SignerInformationRepository signerInformationRepository; + private final CertificateUtils certificateUtils; + private final KdsCertUtils kdsCertUtils; /** - * Method to query the db for a certificate with a resume token. + * Update stored certificates with given list of new certificates. * - * @param resumeToken defines which certificate should be returned. - * @return Optional holding the certificate if found. + * @param trustedCerts defines the list of trusted certificates. */ - public Optional getCertificate(Long resumeToken) { - if (resumeToken == null) { - return signerInformationRepository.findFirstByIdIsNotNullAndDeletedOrderByIdAsc(false); - } else { - return signerInformationRepository.findFirstByIdGreaterThanAndDeletedOrderByIdAsc(resumeToken, false); + @Transactional + public void updateTrustedCertsList(List trustedCerts) { + + signerInformationRepository.deleteAll(); + + trustedCerts.stream() + .map(this::getSignerInformationEntity) + .forEach(signerInformationRepository::save); + } + + private SignerInformationEntity getSignerInformationEntity(TrustedCertificateTrustListItem cert) { + + SignerInformationEntity signerEntity = new SignerInformationEntity(); + signerEntity.setKid(cert.getKid()); + signerEntity.setCreatedAt(ZonedDateTime.now()); + signerEntity.setCountry(cert.getCountry()); + signerEntity.setRawData(cert.getCertificate()); + signerEntity.setDomain(cert.getDomain()); + signerEntity.setGroup(cert.getGroup()); + + try { + X509Certificate parsedCertificate = kdsCertUtils.parseCertificate(cert.getCertificate()); + byte[] subjectBytes = parsedCertificate.getSubjectX500Principal().getEncoded(); + signerEntity.setSubjectHash(certificateUtils.calculateHash(subjectBytes)); + } catch (NoSuchAlgorithmException e) { + log.error("Failed to calculate Hash for certificate {}", cert.getKid()); } + + return signerEntity; } /** - * Method to query the db for a list of kid from all certificates. + * Returns a list of 2-Digit Country-Codes which have at least one signing certificates present in DB. * - * @return A list of kids of all certificates found. If no certificate was found an empty list is returned. + * @return Distinct list of Country-Codes */ - public List getListOfValidKids() { + public List getCountryList() { - List certsList = signerInformationRepository.findAllByDeletedOrderByIdAsc(false); + return signerInformationRepository.getCountryList(); + } - return certsList.stream().map(SignerInformationEntity::getKid).collect(Collectors.toList()); + /** + * Returns a list of groups for which certificates are imported. + * + * @return list of groups + */ + public List getGroupList() { + return signerInformationRepository.getGroupList(); } /** - * Method to synchronise the certificates in the db with the given List of trusted certificates. - * - * @param trustedCerts defines the list of trusted certificates. + * Returns a list of domains for which certificates are imported. * + * @return list of domains */ - @Transactional - public void updateTrustedCertsList(List trustedCerts) { + public List getDomainsList() { - List trustedCertsKids = trustedCerts.stream().map(TrustListItem::getKid).collect(Collectors.toList()); - List alreadyStoredCerts = getListOfValidKids(); - List certsToDelete = new ArrayList<>(); + return signerInformationRepository.getDomainsList(); + } - if (trustedCertsKids.isEmpty()) { - signerInformationRepository.setAllDeleted(); - return; - } else { - signerInformationRepository.setDeletedByKidsNotIn(trustedCertsKids); - } + /** + * Returns a list of all certificates. + * + * @return List of SignerInformationEntity + */ + public List getAllCertificates() { - List signerInformationEntities = new ArrayList<>(); + return signerInformationRepository.findAll(); + } - for (TrustListItem cert : trustedCerts) { - if (!alreadyStoredCerts.contains(cert.getKid())) { - signerInformationEntities.add(getSingerInformationEntity(cert)); - certsToDelete.add(cert.getKid()); - } - } + /** + * Returns signer information that are active filtered by domain, participant and group. + * + * @param domain a domain name used as filter + * @param participant a participant aka country code, used as filter + * @param group group name, used as filter + * @return matching SignerInformationEntities + */ + public List getCertificatesByDomainParticipantGroup( + String domain, String participant, String group) { - //Delete all certificates that got updated, so that they get a new id. - signerInformationRepository.deleteByKidIn(certsToDelete); - signerInformationRepository.saveAllAndFlush(signerInformationEntities); + return signerInformationRepository.getByDomainIsAndCountryIsAndGroupIs(domain, participant, group); } - private SignerInformationEntity getSingerInformationEntity(TrustListItem cert) { - SignerInformationEntity signerEntity = new SignerInformationEntity(); - signerEntity.setKid(cert.getKid()); - signerEntity.setCreatedAt(cert.getTimestamp()); - signerEntity.setCountry(cert.getCountry()); - signerEntity.setThumbprint((cert.getThumbprint())); - signerEntity.setRawData(cert.getRawData()); + /** + * Returns signer information that are filtered by participant. + * + * @param country a participant aka country code, used as filter + * @return matching SignerInformationEntities + */ + public List getCertificatesByCountry(String country) { - return signerEntity; + return signerInformationRepository.getByCountryIs(country); } /** - * Gets the deleted/updated state of the certificates. - * @return state of the certificates represented by their kids + * Returns signer information that are filtered by domain and participant. + * + * @param domain a domain name used as filter + * @param country a participant aka country code, used as filter + * @return matching SignerInformationEntities */ - public DeltaListDto getDeltaList() { - - List certs = - signerInformationRepository.findAllByOrderByIdAsc(); + public List getCertificatesByCountryDomain(String country, String domain) { - Map> partitioned = - certs.stream().collect(Collectors.partitioningBy(SignerInformationEntity::isDeleted, - Collectors.mapping(SignerInformationEntity::getKid, Collectors.toList()))); + return signerInformationRepository.getByDomainIsAndCountryIs(domain, country); + } - return new DeltaListDto(partitioned.get(Boolean.FALSE),partitioned.get(Boolean.TRUE)); + /** + * Returns signer information that are filtered by domain. + * + * @param domain a domain name used as filter + * @return matching SignerInformationEntities + */ + public List getCertificatesByDomain(String domain) { + return signerInformationRepository.getByDomainIs(domain); } /** - * Gets the deleted/updated state of the certificates after the given value. - * @return state of the certificates represented by their kids + * Returns signer information that are filtered by participant and group. + * + * @param group group name, used as filter + * @param country a participant aka country code, used as filter + * @return matching SignerInformationEntities */ - public DeltaListDto getDeltaList(ZonedDateTime ifModifiedDateTime) { + public List getCertificatesByGroupCountry(String group, String country) { - List certs = - signerInformationRepository.findAllByUpdatedAtAfterOrderByIdAsc(ifModifiedDateTime); - - Map> partitioned = - certs.stream().collect(Collectors.partitioningBy(SignerInformationEntity::isDeleted, - Collectors.mapping(SignerInformationEntity::getKid, Collectors.toList()))); + return signerInformationRepository.getByCountryIsAndGroupIs(country, group); + } - return new DeltaListDto(partitioned.get(Boolean.FALSE),partitioned.get(Boolean.TRUE)); + /** + * Returns signer information that are filtered by domain and group. + * + * @param domain a domain name used as filter + * @param group group name, used as filter + * @return matching SignerInformationEntities + */ + public List getCertificatesByDomainGroup(String domain, String group) { + return signerInformationRepository.getByDomainIsAndGroupIs(domain, group); } /** - * Gets the raw data of the certificates for a given kid list. - * @param requestedCertList list of kids - * @return raw data of certificates + * Returns signer information that are filtered by group. + * + * @param group group name, used as filter + * @return matching SignerInformationEntities */ - public Map> getCertificatesData(List requestedCertList) { - - List certs = - signerInformationRepository.findAllByKidIn(requestedCertList); + public List getCertificatesByGroup(String group) { - return certs.stream().collect(Collectors.groupingBy(SignerInformationEntity::getCountry, - Collectors.mapping(this::map, Collectors.toList()))); + return signerInformationRepository.getByGroupIs(group); } - private CertificatesLookupResponseItemDto map(SignerInformationEntity entity) { - return new CertificatesLookupResponseItemDto(entity.getKid(), entity.getRawData()); + /** + * Returns signer information that are filtered by subjectHash, country, and domain. + * + * @param subjectHash SHA256 hash of certificate subject to filter + * @param country CountryCode/Participant code to filter + * @param domain Domain value to filter for + * @return matching SignerInformationEntities + */ + public List getCertificatesBySubjectHashCountryDomain(String subjectHash, String country, + String domain) { + + return signerInformationRepository.getBySubjectHashIsAndCountryIsAndDomainIs(subjectHash, country, domain); } } diff --git a/src/main/java/tng/trustnetwork/keydistribution/service/TrustedIssuerDownloadService.java b/src/main/java/tng/trustnetwork/keydistribution/service/TrustedIssuerDownloadService.java index 507636c..27fca30 100644 --- a/src/main/java/tng/trustnetwork/keydistribution/service/TrustedIssuerDownloadService.java +++ b/src/main/java/tng/trustnetwork/keydistribution/service/TrustedIssuerDownloadService.java @@ -1,10 +1,67 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + package tng.trustnetwork.keydistribution.service; +import eu.europa.ec.dgc.gateway.connector.DgcGatewayTrustedIssuerDownloadConnector; +import eu.europa.ec.dgc.gateway.connector.model.TrustedIssuer; +import java.util.ArrayList; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import net.javacrumbs.shedlock.spring.annotation.SchedulerLock; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import tng.trustnetwork.keydistribution.config.KdsConfigProperties; + +/** + * A service to download the signer certificates from the digital green certificate gateway. + */ +@Slf4j +@RequiredArgsConstructor +@Component +@ConditionalOnProperty("dgc.trustedIssuerDownloader.enabled") +public class TrustedIssuerDownloadService { + + private final DgcGatewayTrustedIssuerDownloadConnector downloadConnector; + + private final TrustedIssuerService trustedIssuerService; -public interface TrustedIssuerDownloadService { + private final KdsConfigProperties configProperties; /** - * Synchronises the trusted issuers with the gateway. + * Download TrustedIssuers and Resolve DID Documents. */ - void downloadTrustedIssuers(); + @Scheduled(fixedDelayString = "${dgc.trustedIssuerDownloader.timeInterval}") + @SchedulerLock(name = "TrustedIssuerDownloadService_downloadTrustedIssuers", lockAtLeastFor = "PT0S", + lockAtMostFor = "${dgc.trustedIssuerDownloader.lockLimit}") + public void downloadTrustedIssuers() { + + log.info("Trusted issuers download started"); + + ArrayList trustedIssuers = new ArrayList<>(); + trustedIssuers.addAll(configProperties.getTrustedIssuerDownloader().getStaticTrustedIssuer()); + trustedIssuers.addAll(downloadConnector.getTrustedIssuers()); + + trustedIssuerService.updateTrustedIssuersList(trustedIssuers); + + log.info("Trusted issuers download finished. {} issuers downloaded.", trustedIssuers.size()); + } } diff --git a/src/main/java/tng/trustnetwork/keydistribution/service/TrustedIssuerDownloadServiceImpl.java b/src/main/java/tng/trustnetwork/keydistribution/service/TrustedIssuerDownloadServiceImpl.java deleted file mode 100644 index 07f187b..0000000 --- a/src/main/java/tng/trustnetwork/keydistribution/service/TrustedIssuerDownloadServiceImpl.java +++ /dev/null @@ -1,60 +0,0 @@ -/*- - * ---license-start - * WorldHealthOrganization / tng-key-distribution - * --- - * Copyright (C) 2021 T-Systems International GmbH and all other contributors - * --- - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://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. - * ---license-end - */ - -package tng.trustnetwork.keydistribution.service; - -import eu.europa.ec.dgc.gateway.connector.DgcGatewayTrustedIssuerDownloadConnector; -import eu.europa.ec.dgc.gateway.connector.model.TrustedIssuer; -import java.util.List; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import net.javacrumbs.shedlock.spring.annotation.SchedulerLock; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Profile; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; - -/** - * A service to download the signer certificates from the digital green certificate gateway. - */ -@Slf4j -@RequiredArgsConstructor -@Component -@Profile("!btp") -@ConditionalOnProperty("dgc.trustedIssuerDownloader.enabled") -public class TrustedIssuerDownloadServiceImpl implements TrustedIssuerDownloadService { - - private final DgcGatewayTrustedIssuerDownloadConnector downloadConnector; - private final TrustedIssuerService trustedIssuerService; - - @Override - @Scheduled(fixedDelayString = "${dgc.trustedIssuerDownloader.timeInterval}") - @SchedulerLock(name = "TrustedIssuerDownloadService_downloadTrustedIssuers", lockAtLeastFor = "PT0S", - lockAtMostFor = "${dgc.trustedIssuerDownloader.lockLimit}") - public void downloadTrustedIssuers() { - log.info("Trusted issuers download started"); - - List trustedIssuers = downloadConnector.getTrustedIssuers(); - - trustedIssuerService.updateTrustedIssuersList(trustedIssuers); - - log.info("Trusted issuers download finished. {} issuers downloaded.", trustedIssuers.size()); - } -} diff --git a/src/main/java/tng/trustnetwork/keydistribution/service/TrustedIssuerService.java b/src/main/java/tng/trustnetwork/keydistribution/service/TrustedIssuerService.java index 797e1aa..1a786c4 100644 --- a/src/main/java/tng/trustnetwork/keydistribution/service/TrustedIssuerService.java +++ b/src/main/java/tng/trustnetwork/keydistribution/service/TrustedIssuerService.java @@ -2,7 +2,7 @@ * ---license-start * WorldHealthOrganization / tng-key-distribution * --- - * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors * --- * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,8 @@ package tng.trustnetwork.keydistribution.service; +import com.fasterxml.jackson.core.JsonProcessingException; +import eu.europa.ec.dgc.gateway.connector.mapper.TrustedIssuerMapper; import eu.europa.ec.dgc.gateway.connector.model.TrustedIssuer; import java.util.ArrayList; import java.util.List; @@ -28,6 +30,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; +import tng.trustnetwork.keydistribution.config.KdsConfigProperties; import tng.trustnetwork.keydistribution.entity.TrustedIssuerEntity; import tng.trustnetwork.keydistribution.mapper.IssuerMapper; import tng.trustnetwork.keydistribution.repository.TrustedIssuerRepository; @@ -37,68 +40,61 @@ @RequiredArgsConstructor public class TrustedIssuerService { - private final InfoService infoService; - private final IssuerMapper issuerMapper; private final TrustedIssuerRepository trustedIssuerRepository; - /** - * Get the current etag. - * @return the current etag - */ + private final UniversalResolverService urService; - public String getEtag() { - String etag = infoService.getValueForKey(InfoService.CURRENT_ETAG); - if (etag == null) { - etag = ""; - } - return etag; - } + private final DecentralizedIdentifierService decentralizedIdentifierService; + + private final KdsConfigProperties configProperties; /** - * Method to query the db for all trusted issuers. + * Method to query the db for DID documents. * * @return List holding the found trusted issuers. */ - public List getAllIssuers(String etag) { - return trustedIssuerRepository.findAllByEtag(etag); + public List getAllDid() { + + return trustedIssuerRepository.findAllByUrlTypeIs(TrustedIssuerEntity.UrlType.DID); } /** * Method to synchronise the issuers in the db with the given List of trusted issuers. * * @param trustedIssuers defines the list of trusted issuers. - * */ @Transactional public void updateTrustedIssuersList(List trustedIssuers) { - String newEtag = UUID.randomUUID().toString(); - List trustedIssuerEntities = new ArrayList<>(); + trustedIssuerRepository.deleteAll(); for (TrustedIssuer trustedIssuer : trustedIssuers) { - trustedIssuerEntities.add(getTrustedIssuerEntity(newEtag, trustedIssuer)); - } - trustedIssuerRepository.saveAll(trustedIssuerEntities); + trustedIssuerRepository.save(issuerMapper.trustedIssuerToTrustedIssuerEntity(trustedIssuer)); - String oldEtag = getEtag(); - infoService.setValueForKey(InfoService.CURRENT_ETAG, newEtag); + if (trustedIssuer.getType() == TrustedIssuer.UrlType.DID) { + resolveDid(trustedIssuer); + } + } + } - cleanupData(oldEtag); + private void resolveDid(TrustedIssuer trustedIssuer) { - } + if (!configProperties.getTrustedIssuerDownloader().isEnableTrustedIssuerResolving()) { + return; + } - private TrustedIssuerEntity getTrustedIssuerEntity(String etag, TrustedIssuer trustedIssuer) { - TrustedIssuerEntity entity = issuerMapper.trustedIssuerToTrustedIssuerEntity(trustedIssuer); - entity.setEtag(etag); - return entity; - } + try { + UniversalResolverService.DidDocumentWithRawResponse didDocument = + urService.universalResolverApiCall(trustedIssuer.getUrl()); - private void cleanupData(String etag) { - trustedIssuerRepository.deleteAllByEtag(etag); + decentralizedIdentifierService.updateDecentralizedIdentifierList(didDocument.didDocument(), + didDocument.raw()); + } catch (JsonProcessingException e) { + log.error("Failed to download/parse DID {}", trustedIssuer.getUrl()); + } } - } diff --git a/src/main/java/tng/trustnetwork/keydistribution/service/UniversalResolverService.java b/src/main/java/tng/trustnetwork/keydistribution/service/UniversalResolverService.java index fbbcc4e..5093b37 100644 --- a/src/main/java/tng/trustnetwork/keydistribution/service/UniversalResolverService.java +++ b/src/main/java/tng/trustnetwork/keydistribution/service/UniversalResolverService.java @@ -1,9 +1,62 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + package tng.trustnetwork.keydistribution.service; -import tng.trustnetwork.keydistribution.model.DidDocumentUnmarshal; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import tng.trustnetwork.keydistribution.clients.UniversalResolverClient; +import tng.trustnetwork.keydistribution.model.DidDocument; + + +@Slf4j +@Service +@RequiredArgsConstructor +public class UniversalResolverService { + + private final UniversalResolverClient universalResolverClient; + + private final ObjectMapper objectMapper; + + /** + * Try to resolve DID Document by ID at UniversalResolverService. + * + * @param didId Identifier of document to resolve + * @return Parsed and RAW DID Document + * @throws JsonProcessingException when parsing of downloaded document failed. + */ + public DidDocumentWithRawResponse universalResolverApiCall(String didId) throws JsonProcessingException { + + String rawResponse = universalResolverClient.getDidDocument(didId); + DidDocument didDocument = objectMapper.readValue(rawResponse, DidDocument.class); + + return new DidDocumentWithRawResponse(didDocument, rawResponse); + } -public interface UniversalResolverService { + public record DidDocumentWithRawResponse( + DidDocument didDocument, - public DidDocumentUnmarshal universalResolverApiCall(String didKey); + String raw) { + } } diff --git a/src/main/java/tng/trustnetwork/keydistribution/service/UniversalResolverServiceImpl.java b/src/main/java/tng/trustnetwork/keydistribution/service/UniversalResolverServiceImpl.java deleted file mode 100644 index 820a9e6..0000000 --- a/src/main/java/tng/trustnetwork/keydistribution/service/UniversalResolverServiceImpl.java +++ /dev/null @@ -1,51 +0,0 @@ -package tng.trustnetwork.keydistribution.service; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import tng.trustnetwork.keydistribution.clients.UniversalResolverClient; -import tng.trustnetwork.keydistribution.model.DidDocument; -import tng.trustnetwork.keydistribution.model.DidDocumentUnmarshal; -import tng.trustnetwork.keydistribution.model.VerificationMethod; - - -@Slf4j -@Service -public class UniversalResolverServiceImpl implements UniversalResolverService { - - @Autowired - private UniversalResolverClient universalResolverClient; - - @Override - public DidDocumentUnmarshal universalResolverApiCall(String didKey) { - - DidDocument did = universalResolverClient.getDidDocument(didKey); - - if (null != did) { - ObjectMapper mapper = new ObjectMapper(); - - try { - - String verificationMethodStr = mapper.writeValueAsString(did.getVerificationMethod()); - VerificationMethod verificationMethod = - mapper.readValue(verificationMethodStr, VerificationMethod.class); - - DidDocumentUnmarshal didDocumentUnmarshal = new DidDocumentUnmarshal(); - didDocumentUnmarshal.setContext(did.getContext()); - didDocumentUnmarshal.setController(did.getController()); - didDocumentUnmarshal.setId(did.getId()); - didDocumentUnmarshal.setProof(did.getProof()); - didDocumentUnmarshal.setVerificationMethod(verificationMethod); - return didDocumentUnmarshal; - - } catch (JsonProcessingException ex) { - log.error("Issue parsing DID document", ex); - } - } - - return null; - } - -} diff --git a/src/main/java/tng/trustnetwork/keydistribution/service/did/DidTrustListService.java b/src/main/java/tng/trustnetwork/keydistribution/service/did/DidTrustListService.java new file mode 100644 index 0000000..d9187a0 --- /dev/null +++ b/src/main/java/tng/trustnetwork/keydistribution/service/did/DidTrustListService.java @@ -0,0 +1,439 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + +package tng.trustnetwork.keydistribution.service.did; + +import static tng.trustnetwork.keydistribution.service.did.KdsDidContextDocumentLoaderConfig.DID_CONTEXTS; + +import com.apicatalog.jsonld.loader.DocumentLoader; +import com.danubetech.keyformats.crypto.ByteSigner; +import com.fasterxml.jackson.databind.ObjectMapper; +import eu.europa.ec.dgc.utils.CertificateUtils; +import foundation.identity.jsonld.JsonLDException; +import foundation.identity.jsonld.JsonLDObject; +import info.weboftrust.ldsignatures.jsonld.LDSecurityKeywords; +import info.weboftrust.ldsignatures.signer.JsonWebSignature2020LdSigner; +import java.io.IOException; +import java.net.URI; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.security.cert.X509Certificate; +import java.security.interfaces.ECPublicKey; +import java.security.interfaces.RSAPublicKey; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.MissingResourceException; +import java.util.function.Supplier; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import net.javacrumbs.shedlock.spring.annotation.SchedulerLock; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import tng.trustnetwork.keydistribution.config.KdsConfigProperties; +import tng.trustnetwork.keydistribution.entity.SignerInformationEntity; +import tng.trustnetwork.keydistribution.entity.TrustedIssuerEntity; +import tng.trustnetwork.keydistribution.service.KdsCertUtils; +import tng.trustnetwork.keydistribution.service.SignerInformationService; +import tng.trustnetwork.keydistribution.service.TrustedIssuerService; +import tng.trustnetwork.keydistribution.service.did.entity.DidTrustList; +import tng.trustnetwork.keydistribution.service.did.entity.DidTrustListEntry; + +@Slf4j +@Service +@RequiredArgsConstructor +@ConditionalOnProperty("dgc.did.enableDidGeneration") +public class DidTrustListService { + + private static final String WILDCARD_CHAR = "-"; + + private static final String SEPARATOR_DID_PATH = ":"; + + private static final String SEPARATOR_DID_ID = "#"; + + private final SignerInformationService signerInformationService; + + private final KdsConfigProperties configProperties; + + private final ByteSigner byteSigner; + + private final DidUploader didUploader; + + private final ObjectMapper objectMapper; + + private final CertificateUtils certificateUtils; + + private final TrustedIssuerService trustedIssuerService; + + private final GitProvider gitProvider; + + private final DocumentLoader documentLoader; + + private final KdsConfigProperties kdsConfigProperties; + + private final KdsCertUtils kdsCertUtils; + + @RequiredArgsConstructor + @Getter + private class DidSpecification { + + @Getter(AccessLevel.PRIVATE) + private final List path; + + private final Supplier> certSupplier; + + private final Supplier> issuerSupplier; + + public List getPath(boolean ref) { + ArrayList path = new ArrayList<>(this.path); + path.add(0, getListPathElement(ref)); + return path; + } + + public String getDocumentId(boolean ref) { + //Example: did:web:tng-cdn-dev.who.int:trustlist:v.2.0.0:DDCC:XXA:DSC + return configProperties.getDid().getDidId() + + SEPARATOR_DID_PATH + getListPathElement(ref) + + (path.isEmpty() ? "" : SEPARATOR_DID_PATH + + String.join(SEPARATOR_DID_PATH, path)); + } + + public String getEntryId(String kid) { + //Example: did:web:tng-cdn-dev.who.int:trustlist:v.2.0.0:DDCC:XXA:DSC#kidkidkid + return getDocumentId(false) + SEPARATOR_DID_ID + kid; + } + + private String getListPathElement(boolean ref) { + if (ref && configProperties.getDid().getTrustListRefPath() != null + && !configProperties.getDid().getTrustListRefPath().isEmpty()) { + return configProperties.getDid().getTrustListRefPath(); + + } else if (!ref && configProperties.getDid().getTrustListPath() != null + && !configProperties.getDid().getTrustListPath().isEmpty()) { + return configProperties.getDid().getTrustListPath(); + } else { + return ""; + } + } + } + + /** + * Create and upload DID Document holding Uploaded DSC and Trusted Issuer. + */ + @Scheduled(cron = "${dgc.did.cron}") + @SchedulerLock(name = "didTrustListGenerator") + public void job() { + + List didSpecifications = new ArrayList<>(); + List domains = signerInformationService.getDomainsList(); + List countries = signerInformationService.getCountryList(); + + //CHECKSTYLE:OFF + List groups = signerInformationService.getGroupList(); + //CHECKSTYLE:ON + + // Add overall DID + didSpecifications.add(new DidSpecification( + Collections.emptyList(), + signerInformationService::getAllCertificates, + trustedIssuerService::getAllDid)); + + // Add all Domain DID + domains.forEach( + domain -> didSpecifications.add(new DidSpecification( + List.of(domain), + () -> signerInformationService.getCertificatesByDomain(domain), + trustedIssuerService::getAllDid))); + + // Add all Country and Domain specific DID + domains.forEach( + domain -> countries.forEach( + country -> didSpecifications.add(new DidSpecification( + List.of(domain, getParticipantCode(country)), + () -> signerInformationService.getCertificatesByCountryDomain(country, domain), + trustedIssuerService::getAllDid) + ))); + + // Add all Domain independent and country specific DID + countries.forEach( + country -> didSpecifications.add(new DidSpecification( + List.of(WILDCARD_CHAR, getParticipantCode(country)), + () -> signerInformationService.getCertificatesByCountry(country), + trustedIssuerService::getAllDid))); + + // Add all domain, country and group specific did + domains.forEach( + domain -> countries.forEach( + country -> groups.forEach( + group -> didSpecifications.add(new DidSpecification( + List.of(domain, getParticipantCode(country), getMappedGroupName(group)), + () -> signerInformationService.getCertificatesByDomainParticipantGroup(domain, country, group), + trustedIssuerService::getAllDid))))); + + // Add all country and group specific did + countries.forEach( + country -> groups.forEach( + group -> didSpecifications.add(new DidSpecification( + List.of(WILDCARD_CHAR, getParticipantCode(country), getMappedGroupName(group)), + () -> signerInformationService.getCertificatesByGroupCountry(group, country), + trustedIssuerService::getAllDid)))); + + // Add all domain and group specific did + domains.forEach( + domain -> groups.forEach( + group -> didSpecifications.add(new DidSpecification( + List.of(domain, WILDCARD_CHAR, getMappedGroupName(group)), + () -> signerInformationService.getCertificatesByDomainGroup(domain, group), + trustedIssuerService::getAllDid)))); + + // Add all group specific did + groups.forEach( + group -> didSpecifications.add(new DidSpecification( + List.of(WILDCARD_CHAR, WILDCARD_CHAR, getMappedGroupName(group)), + () -> signerInformationService.getCertificatesByGroup(group), + trustedIssuerService::getAllDid))); + + Map didDocuments = new HashMap<>(); + didSpecifications.forEach(specification -> didDocuments + .put(specification, this.generateTrustList(specification, false))); + + Map didRefDocuments = new HashMap<>(); + didSpecifications.forEach(specification -> didRefDocuments + .put(specification, this.generateTrustList(specification, true))); + + didDocuments.forEach((specification, document) -> + saveDid(String.join("/", specification.getPath(false)), document)); + + didRefDocuments.forEach((specification, document) -> + saveDid(String.join("/", specification.getPath(true)), document)); + + log.info("Finished DID Export Process: {} documents", didDocuments.size()); + + gitProvider.upload(configProperties.getDid().getLocalFile().getDirectory()); + + } + + private void saveDid(String containerPath, String didDocument) { + + try { + didUploader.uploadDid(containerPath, + didDocument == null ? null : didDocument.getBytes(StandardCharsets.UTF_8)); + } catch (Exception e) { + log.error("Failed to Upload DID-TrustList: {}", e.getMessage()); + } + } + + private String generateTrustList(DidSpecification specification, boolean onlyReferences) { + + List signerInformationEntities = filterEntities(specification.getCertSupplier().get()); + List trustedIssuerEntities = specification.getIssuerSupplier().get(); + + if (signerInformationEntities.isEmpty() || trustedIssuerEntities.isEmpty()) { + log.info("Empty DID for path {}", specification.getPath()); + return null; + } + + DidTrustList trustList = new DidTrustList(); + trustList.setContext(DID_CONTEXTS); + trustList.setId(specification.getDocumentId(onlyReferences)); + trustList.setController(specification.getDocumentId(onlyReferences)); + trustList.setVerificationMethod(new ArrayList<>()); + + // Add Certificates + + for (SignerInformationEntity signerInformationEntity : signerInformationEntities) { + + if (onlyReferences) { + trustList.getVerificationMethod().add(specification.getEntryId( + URLEncoder.encode(signerInformationEntity.getKid(), StandardCharsets.UTF_8))); + + } else { + X509Certificate parsedCertificate = kdsCertUtils.parseCertificate(signerInformationEntity.getRawData()); + if (parsedCertificate == null) { + log.error("Could not parse cert {} of country {}", + signerInformationEntity.getKid(), + signerInformationEntity.getCountry()); + return null; + } + + PublicKey publicKey = parsedCertificate.getPublicKey(); + DidTrustListEntry.PublicKeyJwk publicKeyJwk = null; + if (publicKey instanceof RSAPublicKey rsaPublicKey) { + publicKeyJwk = new DidTrustListEntry.RsaPublicKeyJwk( + rsaPublicKey, List.of(signerInformationEntity.getRawData())); + + } else if (publicKey instanceof ECPublicKey ecPublicKey) { + publicKeyJwk = new DidTrustListEntry.EcPublicKeyJwk( + ecPublicKey, List.of(signerInformationEntity.getRawData())); + + } else { + log.error("Public Key is not RSA or EC Public Key for cert {} of country {}", + signerInformationEntity.getKid(), + signerInformationEntity.getCountry()); + } + + addTrustListEntry(trustList, specification, signerInformationEntity, publicKeyJwk); + } + } + + // Add Trusted Issuer (DID References) + // TODO: Add filtering for TrustedIssuers + trustedIssuerEntities.forEach(did -> trustList.getVerificationMethod().add(did.getUrl())); + + // Sign Document + JsonWebSignature2020LdSigner signer = new JsonWebSignature2020LdSigner(byteSigner); + signer.setCreated(new Date()); + signer.setProofPurpose(LDSecurityKeywords.JSONLD_TERM_ASSERTIONMETHOD); + signer.setVerificationMethod(URI.create(configProperties.getDid().getLdProofVerificationMethod())); + signer.setDomain(configProperties.getDid().getLdProofDomain()); + signer.setNonce(generateNonce()); + + + try { + JsonLDObject jsonLdObject = JsonLDObject.fromJson(objectMapper.writeValueAsString(trustList)); + jsonLdObject.setDocumentLoader(documentLoader); + signer.sign(jsonLdObject); + return jsonLdObject.toJson(); + } catch (IOException | GeneralSecurityException | JsonLDException e) { + log.error("Failed to sign DID-TrustList: {}", e.getMessage()); + return null; + } + } + + private String getParticipantCode(String country) { + + if (country == null || country.length() != 2 && country.length() != 3) { + return null; + } else if (country.length() == 3) { + return country.toUpperCase(); + } + + return configProperties.getDid().getVirtualCountries().computeIfAbsent(country, (c) -> { + try { + return new Locale("en", c).getISO3Country().toUpperCase(); + } catch (MissingResourceException e) { + log.error("Country Code to alpha 3 conversion issue for country {} : {}", + c, e.getMessage()); + return c.toUpperCase(); + } + }); + } + + private void addTrustListEntry(DidTrustList trustList, + DidSpecification specification, + SignerInformationEntity signerInformationEntity, + DidTrustListEntry.PublicKeyJwk publicKeyJwk) { + + List issuers = new ArrayList<>(); + searchIssuer(issuers, signerInformationEntity); + + issuers.forEach(issuer -> publicKeyJwk.getEncodedX509Certificates().add(issuer.getRawData())); + + DidTrustListEntry trustListEntry = new DidTrustListEntry(); + trustListEntry.setType("JsonWebKey2020"); + trustListEntry.setId(specification.getEntryId( + URLEncoder.encode(signerInformationEntity.getKid(), StandardCharsets.UTF_8))); + trustListEntry.setController(specification.getDocumentId(false)); + trustListEntry.setPublicKeyJwk(publicKeyJwk); + + trustList.getVerificationMethod().add(trustListEntry); + } + + + private List filterEntities(List entities) { + + return entities.stream() + .filter(entity -> kdsConfigProperties.getDid().getGroupDenyList().stream() + .noneMatch(e -> entity.getGroup().equalsIgnoreCase(e))) + .toList(); + } + + private String getMappedGroupName(String groupName) { + + return kdsConfigProperties.getDid().getGroupNameMapping() + .computeIfAbsent(groupName, g -> g); + } + + /** + * Recursively resolve certificate chains based on current database. + * Resolving is done country-code and domain aware. + * + * @param issuers List of SignerInformationEntity will be filled with found certs. + * Provide an empty List for initial call. + * @param cert SignerInformationEntity to search issuers for. + */ + private void searchIssuer(List issuers, SignerInformationEntity cert) { + + try { + X509Certificate parsedCertificate = kdsCertUtils.parseCertificate(cert.getRawData()); + String issuerSubjectHash = certificateUtils.calculateHash(parsedCertificate.getIssuerX500Principal() + .getEncoded()); + + List possibleIssuers = signerInformationService + .getCertificatesBySubjectHashCountryDomain(issuerSubjectHash, cert.getCountry(), cert.getDomain()); + + possibleIssuers.forEach(possibleIssuer -> { + X509Certificate parsedPossibleIssuer = kdsCertUtils.parseCertificate(possibleIssuer.getRawData()); + + if (parsedPossibleIssuer.equals(parsedCertificate)) { + // Self-signed Certificate detected --> Stopping Cert Chain resolving + return; + } + + try { + parsedCertificate.verify(parsedPossibleIssuer.getPublicKey()); + // Signature check passed --> Adding issuer to chain + issuers.add(possibleIssuer); + // Also try to resolve issuer cert + searchIssuer(issuers, possibleIssuer); + + } catch (Exception ignored) { + // Signature Check failed -> Do not add this issuer to chain + } + }); + } catch (NoSuchAlgorithmException ignored) { + log.error("Failed to calculate Hash for Certificate Subject"); + } + } + + private String generateNonce() { + + final String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"; + final int nonceLength = 32; + StringBuilder nonce = new StringBuilder(); + + while (nonce.length() < nonceLength) { + nonce.append(chars.charAt((int) (Math.random() * chars.length()))); + } + + return nonce.toString(); + } + +} diff --git a/src/main/java/tng/trustnetwork/keydistribution/restapi/dto/KidDto.java b/src/main/java/tng/trustnetwork/keydistribution/service/did/DidUploader.java similarity index 66% rename from src/main/java/tng/trustnetwork/keydistribution/restapi/dto/KidDto.java rename to src/main/java/tng/trustnetwork/keydistribution/service/did/DidUploader.java index 39621ae..af7b243 100644 --- a/src/main/java/tng/trustnetwork/keydistribution/restapi/dto/KidDto.java +++ b/src/main/java/tng/trustnetwork/keydistribution/service/did/DidUploader.java @@ -2,7 +2,7 @@ * ---license-start * WorldHealthOrganization / tng-key-distribution * --- - * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors * --- * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,18 +18,12 @@ * ---license-end */ -package tng.trustnetwork.keydistribution.restapi.dto; +package tng.trustnetwork.keydistribution.service.did; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Value; +public interface DidUploader { -@Schema( - name = "kid", - type = "string", - example = "8xYtW2837fc=" -) + void uploadDid(byte[] content); + + void uploadDid(String subContainer, byte[] content); -@Value -public class KidDto { - String kid; } diff --git a/src/main/java/tng/trustnetwork/keydistribution/service/did/DummyByteSigner.java b/src/main/java/tng/trustnetwork/keydistribution/service/did/DummyByteSigner.java new file mode 100644 index 0000000..4948215 --- /dev/null +++ b/src/main/java/tng/trustnetwork/keydistribution/service/did/DummyByteSigner.java @@ -0,0 +1,42 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + +package tng.trustnetwork.keydistribution.service.did; + +import com.danubetech.keyformats.crypto.ByteSigner; +import java.security.GeneralSecurityException; +import java.security.MessageDigest; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Service; + +@Service +@ConditionalOnProperty(name = "dgc.did.didSigningProvider", havingValue = "dummy") +public class DummyByteSigner extends ByteSigner { + + public DummyByteSigner() { + super("EC"); + } + + @Override + protected byte[] sign(byte[] content) throws GeneralSecurityException { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + return digest.digest(content); + } +} diff --git a/src/main/java/tng/trustnetwork/keydistribution/service/did/DummyDidUploader.java b/src/main/java/tng/trustnetwork/keydistribution/service/did/DummyDidUploader.java new file mode 100644 index 0000000..3ab9337 --- /dev/null +++ b/src/main/java/tng/trustnetwork/keydistribution/service/did/DummyDidUploader.java @@ -0,0 +1,42 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + +package tng.trustnetwork.keydistribution.service.did; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Service; + +@ConditionalOnProperty(name = "dgc.did.didUploadProvider", havingValue = "dummy") +@Service +@Slf4j +public class DummyDidUploader implements DidUploader { + + @Override + public void uploadDid(byte[] content) { + log.info("Uploaded {} bytes", content.length); + } + + @Override + public void uploadDid(String subContainer, byte[] content) { + log.info("Uploaded {} bytes to subContainer {}", content.length, subContainer); + } + +} diff --git a/src/main/java/tng/trustnetwork/keydistribution/service/did/DummyGitUploader.java b/src/main/java/tng/trustnetwork/keydistribution/service/did/DummyGitUploader.java new file mode 100644 index 0000000..bb53be3 --- /dev/null +++ b/src/main/java/tng/trustnetwork/keydistribution/service/did/DummyGitUploader.java @@ -0,0 +1,48 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + +package tng.trustnetwork.keydistribution.service.did; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Service; +import tng.trustnetwork.keydistribution.config.KdsConfigProperties; + +@ConditionalOnProperty(name = "dgc.did.didUploadProvider", havingValue = "dummy") +@Service +@Slf4j +@RequiredArgsConstructor +public class DummyGitUploader implements GitProvider { + + private final KdsConfigProperties configProperties; + + /** + * upload dummy method used for unit tests. + * @param sourcePath will only be used for log output + */ + + public void upload(String sourcePath) { + + log.info("Uploaded from {} to {}", sourcePath, configProperties.getDid().getGit().getWorkdir()); + + } + +} diff --git a/src/main/java/tng/trustnetwork/keydistribution/service/did/GitProvider.java b/src/main/java/tng/trustnetwork/keydistribution/service/did/GitProvider.java new file mode 100644 index 0000000..a91fd91 --- /dev/null +++ b/src/main/java/tng/trustnetwork/keydistribution/service/did/GitProvider.java @@ -0,0 +1,27 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + +package tng.trustnetwork.keydistribution.service.did; + +public interface GitProvider { + + void upload(String sourcePath); + +} diff --git a/src/main/java/tng/trustnetwork/keydistribution/service/did/GitUploader.java b/src/main/java/tng/trustnetwork/keydistribution/service/did/GitUploader.java new file mode 100644 index 0000000..a9080f8 --- /dev/null +++ b/src/main/java/tng/trustnetwork/keydistribution/service/did/GitUploader.java @@ -0,0 +1,138 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + +package tng.trustnetwork.keydistribution.service.did; + +import java.io.File; +import java.io.IOException; +import java.nio.file.DirectoryStream; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.StandardCopyOption; +import java.nio.file.attribute.BasicFileAttributes; +import java.time.Instant; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Service; +import tng.trustnetwork.keydistribution.config.KdsConfigProperties; + +@ConditionalOnProperty(name = "dgc.did.didUploadProvider", havingValue = "local-file") +@Service +@Slf4j +@RequiredArgsConstructor +public class GitUploader implements GitProvider { + + private final KdsConfigProperties configProperties; + + /** + * upload method clones a git repositor, copies the contents of sourcePath to the cloned git repository + * and commits and pushes the contents, replacing the previous contents of the repository. + * @param sourcePath sourcePath from where the files are copied for upload + */ + + public void upload(String sourcePath) { + + Path sourceDirectory = Paths.get(sourcePath); + Path targetDirectory = Paths.get(configProperties.getDid().getGit().getWorkdir() + + File.separator + + configProperties.getDid().getGit().getPrefix()); + + deleteDirectoryAndContents(configProperties.getDid().getGit().getWorkdir()); + + try { + Git.cloneRepository() + .setURI(configProperties.getDid().getGit().getUrl()) + .setDirectory(new File(configProperties.getDid().getGit().getWorkdir())) + .setCredentialsProvider( + new UsernamePasswordCredentialsProvider( + "anonymous", configProperties.getDid().getGit().getPat())) + .call(); + } catch (Exception e) { + log.error("Failed to clone repository {}: {}", + configProperties.getDid().getGit().getUrl(), e.getMessage()); + } + + try { + Files.walkFileTree(sourceDirectory, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Path targetFile = targetDirectory.resolve(sourceDirectory.relativize(file)); + Files.createDirectories(targetFile.getParent()); + Files.copy(file, targetFile, StandardCopyOption.REPLACE_EXISTING); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + Path targetDir = targetDirectory.resolve(sourceDirectory.relativize(dir)); + Files.createDirectories(targetDir); + return FileVisitResult.CONTINUE; + } + }); + } catch (IOException e) { + log.error("Failed to copy files from {} to {}: {}", sourcePath, targetDirectory, e.getMessage()); + } + + try { + Git git = Git.open(new File(configProperties.getDid().getGit().getWorkdir())); + git.add().addFilepattern(".").call(); + git.commit().setMessage("Added DID files on " + Instant.now()).call(); + git.push().setCredentialsProvider(new UsernamePasswordCredentialsProvider( + "anonymous", configProperties.getDid().getGit().getPat())).call(); + git.close(); + log.info("Successfully uploaded DID files to Git repository {}", + configProperties.getDid().getGit().getUrl()); + } catch (GitAPIException | IOException e) { + log.error("Error during Git commit & push: {}",e.getMessage()); + } + } + + private void deleteDirectoryAndContents(String directoryPath) { + Path dir = Paths.get(directoryPath); + if (dir.toFile().exists()) { + + try (DirectoryStream stream = Files.newDirectoryStream(dir)) { + for (Path path : stream) { + if (Files.isDirectory(path)) { + deleteDirectoryAndContents(path.toString()); + } else { + Files.delete(path); + } + } + } catch (IOException e) { + log.error("Error deleting file {}",e.getMessage()); + } + try { + Files.delete(dir); + } catch (IOException e) { + log.error("Error deleting root directory {}",e.getMessage()); + } + } else { + log.info("Directory {} does not exist, skippig deletion", dir); + } + } +} diff --git a/src/main/java/tng/trustnetwork/keydistribution/service/did/KdsDidContextDocumentLoaderConfig.java b/src/main/java/tng/trustnetwork/keydistribution/service/did/KdsDidContextDocumentLoaderConfig.java new file mode 100644 index 0000000..58e2e37 --- /dev/null +++ b/src/main/java/tng/trustnetwork/keydistribution/service/did/KdsDidContextDocumentLoaderConfig.java @@ -0,0 +1,51 @@ +package tng.trustnetwork.keydistribution.service.did; + + +import com.apicatalog.jsonld.document.JsonDocument; +import com.apicatalog.jsonld.loader.DocumentLoader; +import foundation.identity.jsonld.ConfigurableDocumentLoader; +import java.io.InputStream; +import java.net.URI; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.BeanInitializationException; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import tng.trustnetwork.keydistribution.config.KdsConfigProperties; + +@Slf4j +@Configuration +public class KdsDidContextDocumentLoaderConfig { + + public static final List DID_CONTEXTS = List.of( + "https://www.w3.org/ns/did/v1", + "https://w3id.org/security/suites/jws-2020/v1"); + + private static final String DID_CONTEXT_PATH = "did_contexts/"; + + @Bean + DocumentLoader kdsContextLoader(KdsConfigProperties configProperties) { + + Map contextMap = new HashMap<>(); + for (String didContext : DID_CONTEXTS) { + String didContextFile = configProperties.getDid().getContextMapping().get(didContext); + + if (didContextFile == null) { + throw new BeanInitializationException("Failed to load DID-Context Document for " + didContext + + " : No Mapping to local JSON-File."); + } + + try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream( + DID_CONTEXT_PATH + didContextFile)) { + if (inputStream != null) { + contextMap.put(URI.create(didContext), JsonDocument.of(inputStream)); + } + } catch (Exception e) { + throw new BeanInitializationException("Failed to load DID-Context Document", e); + } + } + return new ConfigurableDocumentLoader(contextMap); + } +} diff --git a/src/main/java/tng/trustnetwork/keydistribution/service/did/LocalFileDidUploader.java b/src/main/java/tng/trustnetwork/keydistribution/service/did/LocalFileDidUploader.java new file mode 100644 index 0000000..b1c6d97 --- /dev/null +++ b/src/main/java/tng/trustnetwork/keydistribution/service/did/LocalFileDidUploader.java @@ -0,0 +1,92 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + +package tng.trustnetwork.keydistribution.service.did; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Paths; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Service; +import tng.trustnetwork.keydistribution.config.KdsConfigProperties; + +@ConditionalOnProperty(name = "dgc.did.didUploadProvider", havingValue = "local-file") +@Service +@Slf4j +@RequiredArgsConstructor +public class LocalFileDidUploader implements DidUploader { + + private final KdsConfigProperties configProperties; + + @Override + public void uploadDid(byte[] content) { + + uploadDid(null, content); + } + + @Override + public void uploadDid(String subContainer, byte[] content) { + + File targetFile; + + if (subContainer == null) { + targetFile = Paths.get( + configProperties.getDid().getLocalFile().getDirectory(), + configProperties.getDid().getLocalFile().getFileName() + ).toFile(); + } else { + targetFile = Paths.get( + configProperties.getDid().getLocalFile().getDirectory(), + subContainer, + configProperties.getDid().getLocalFile().getFileName() + ).toFile(); + } + + if (targetFile.exists() && !targetFile.delete()) { + log.error("Failed to delete existing file."); + return; + } + + if (content == null) { + log.info("Requested to store file with null content - only deleting existing file"); + return; + } + + log.info("Storing {} bytes to {}", content.length, targetFile.getAbsolutePath()); + + if (targetFile.getParentFile().mkdirs()) { + log.info("Created required directory {}", targetFile.getParentFile().getAbsolutePath()); + } + + try (FileOutputStream fileOutputStream = new FileOutputStream(targetFile)) { + fileOutputStream.write(content); + } catch (IOException e) { + log.error("Failed to write DID Content to file: {}", e.getMessage()); + return; + } + + log.info("Successfully saved file locally."); + + } + +} diff --git a/src/main/java/tng/trustnetwork/keydistribution/service/did/LocalKeystoreByteSigner.java b/src/main/java/tng/trustnetwork/keydistribution/service/did/LocalKeystoreByteSigner.java new file mode 100644 index 0000000..bd5b2e3 --- /dev/null +++ b/src/main/java/tng/trustnetwork/keydistribution/service/did/LocalKeystoreByteSigner.java @@ -0,0 +1,76 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + +package tng.trustnetwork.keydistribution.service.did; + +import com.danubetech.keyformats.crypto.ByteSigner; +import java.io.FileInputStream; +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.Signature; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Service; +import org.springframework.util.ResourceUtils; +import tng.trustnetwork.keydistribution.config.KdsConfigProperties; + +@Service +@ConditionalOnProperty(name = "dgc.did.didSigningProvider", havingValue = "local-keystore") +public class LocalKeystoreByteSigner extends ByteSigner { + + private final PrivateKey signingKey; + + /** + * Initialize LocalKeyStoreByteSigner. Configured Key will be loaded. + */ + public LocalKeystoreByteSigner(KdsConfigProperties kdsConfigProperties) + throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, + UnrecoverableKeyException { + + super("EC"); + + KeyStore keyStore = KeyStore.getInstance("JKS"); + + try (FileInputStream fileInputStream = new FileInputStream( + ResourceUtils.getFile(kdsConfigProperties.getDid().getLocalKeyStore().getPath())); + ) { + keyStore.load(fileInputStream, kdsConfigProperties.getDid().getLocalKeyStore().getPassword()); + } + + signingKey = (PrivateKey) keyStore.getKey( + kdsConfigProperties.getDid().getLocalKeyStore().getAlias(), + kdsConfigProperties.getDid().getLocalKeyStore().getPassword()); + + } + + @Override + protected byte[] sign(byte[] content) throws GeneralSecurityException { + + Signature signature = Signature.getInstance("SHA256withECDSA"); + signature.initSign(signingKey); + signature.update(content); + return signature.sign(); + } +} diff --git a/src/main/java/tng/trustnetwork/keydistribution/service/did/entity/DidTrustList.java b/src/main/java/tng/trustnetwork/keydistribution/service/did/entity/DidTrustList.java new file mode 100644 index 0000000..34485ff --- /dev/null +++ b/src/main/java/tng/trustnetwork/keydistribution/service/did/entity/DidTrustList.java @@ -0,0 +1,41 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + +package tng.trustnetwork.keydistribution.service.did.entity; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import java.util.List; +import lombok.Data; + +@Data +@JsonPropertyOrder({"@context", "id", "controller", "verificationMethod"}) +public class DidTrustList { + + @JsonProperty("@context") + private List context; + + private String id; + + private String controller; + + private List verificationMethod; + +} diff --git a/src/main/java/tng/trustnetwork/keydistribution/service/did/entity/DidTrustListEntry.java b/src/main/java/tng/trustnetwork/keydistribution/service/did/entity/DidTrustListEntry.java new file mode 100644 index 0000000..8faa054 --- /dev/null +++ b/src/main/java/tng/trustnetwork/keydistribution/service/did/entity/DidTrustListEntry.java @@ -0,0 +1,121 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + +package tng.trustnetwork.keydistribution.service.did.entity; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.security.interfaces.ECPublicKey; +import java.security.interfaces.RSAPublicKey; +import java.util.ArrayList; +import java.util.Base64; +import java.util.List; +import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.bouncycastle.jce.spec.ECNamedCurveSpec; + +@Data +public class DidTrustListEntry { + + private String id; + + private String type; + + private String controller; + + private PublicKeyJwk publicKeyJwk; + + @NoArgsConstructor + @Setter + @Getter + public abstract static class PublicKeyJwk { + @JsonProperty("kty") + private String keyType; + + @JsonProperty("x5c") + private List encodedX509Certificates; + + private PublicKeyJwk(String keyType, List encodedX509Certificates) { + this.keyType = keyType; + this.encodedX509Certificates = new ArrayList<>(encodedX509Certificates); + } + } + + @Getter + @Setter + public static class EcPublicKeyJwk extends PublicKeyJwk { + + @JsonProperty("crv") + private String curve; + + @JsonProperty("x") + private String valueX; + + @JsonProperty("y") + private String valueY; + + /** + * Instantiate EC PublicKey JWK Class. + * + * @param ecPublicKey EC Public Key that should be wrapped. + * @param base64EncodedCertificates List of Base64 encoded Certificates assigned to provided Public Key. + * They will be added within x5c property of JWK. + */ + public EcPublicKeyJwk(ECPublicKey ecPublicKey, List base64EncodedCertificates) { + super("EC", base64EncodedCertificates); + valueX = Base64.getEncoder().encodeToString(ecPublicKey.getW().getAffineX().toByteArray()); + valueY = Base64.getEncoder().encodeToString(ecPublicKey.getW().getAffineY().toByteArray()); + + ECNamedCurveSpec curveSpec = (ECNamedCurveSpec) ecPublicKey.getParams(); + switch (curveSpec.getName()) { + case "prime256v1" -> curve = "P-256"; + case "prime384v1" -> curve = "P-384"; + case "prime521v1" -> curve = "P-521"; + default -> curve = "UNKNOWN CURVE"; + } + } + } + + @Getter + @Setter + public static class RsaPublicKeyJwk extends PublicKeyJwk { + + @JsonProperty("e") + private String valueE; + + @JsonProperty("n") + private String valueN; + + /** + * Instantiate RSA PublicKey JWK Class. + * + * @param rsaPublicKey RSA Public Key that should be wrapped. + * @param base64EncodedCertificates List of Base64 encoded Certificates assigned to provided Public Key. + * They will be added within x5c property of JWK. + */ + public RsaPublicKeyJwk(RSAPublicKey rsaPublicKey, List base64EncodedCertificates) { + super("RSA", base64EncodedCertificates); + valueN = Base64.getEncoder().encodeToString(rsaPublicKey.getModulus().toByteArray()); + valueE = Base64.getEncoder().encodeToString(rsaPublicKey.getPublicExponent().toByteArray()); + } + } + +} diff --git a/src/main/resources/application-cloud.yml b/src/main/resources/application-cloud.yml index 4659664..96e1a79 100644 --- a/src/main/resources/application-cloud.yml +++ b/src/main/resources/application-cloud.yml @@ -9,3 +9,5 @@ spring: password: postgres jpa: database-platform: org.hibernate.dialect.PostgreSQLDialect + liquibase: + enabled: false diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index bbf60c2..1691039 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -8,12 +8,17 @@ spring: url: jdbc:h2:mem:dgc;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1; username: sa password: '' + jackson: + deserialization: + read-unknown-enum-values-using-default-value: true + jpa: database-platform: org.hibernate.dialect.H2Dialect hibernate: ddl-auto: validate liquibase: - change-log: classpath:db/changelog.xml + enabled: true + change-log: classpath:db/changelog.yaml h2: console: enabled: true @@ -37,12 +42,6 @@ management: info: name: ${spring.application.name} profiles: ${spring.profiles.active} -springdoc: - api-docs: - path: /api/docs - enabled: true - swagger-ui: - path: /swagger universal: resolver: "https://dev.uniresolver.io/1.0/identifiers" dgc: @@ -58,21 +57,62 @@ dgc: enabled: true timeInterval: 1800000 lockLimit: 3600000 + static-trusted-issuer: + - name: WHO Trustlist (DEV) + country: WH + type: did + url: did:web:tng-cdn-dev.who.int:trustlist + signature: No-Signature # required because of DB restrictions + enable-trusted-issuer-resolving: false gateway: connector: - enabled: false -# endpoint: ${DGC_GATEWAY_CONNECTOR_ENDPOINT} -# proxy: -# enabled: false -# max-cache-age: 300 -# tls-trust-store: -# password: ${DGC_GATEWAY_CONNECTOR_TLSTRUSTSTORE_PASSWORD} -# path: ${DGC_GATEWAY_CONNECTOR_TLSTRUSTSTORE_PATH} -# tls-key-store: -# alias: ${DGC_GATEWAY_CONNECTOR_TLSKEYSTORE_ALIAS} -# password: ${DGC_GATEWAY_CONNECTOR_TLSKEYSTORE_PASSWORD} -# path: ${DGC_GATEWAY_CONNECTOR_TLSKEYSTORE_PATH} -# trust-anchor: -# alias: ${DGC_GATEWAY_CONNECTOR_TRUSTANCHOR_ALIAS} -# password: ${DGC_GATEWAY_CONNECTOR_TRUSTANCHOR_PASSWORD} -# path: ${DGC_GATEWAY_CONNECTOR_TRUSTANCHOR_PATH} + enabled: true + endpoint: ${DGC_GATEWAY_CONNECTOR_ENDPOINT} + proxy: + enabled: false + max-cache-age: 300 + tls-trust-store: + password: ${DGC_GATEWAY_CONNECTOR_TLSTRUSTSTORE_PASSWORD} + path: ${DGC_GATEWAY_CONNECTOR_TLSTRUSTSTORE_PATH} + tls-key-store: + alias: ${DGC_GATEWAY_CONNECTOR_TLSKEYSTORE_ALIAS} + password: ${DGC_GATEWAY_CONNECTOR_TLSKEYSTORE_PASSWORD} + path: ${DGC_GATEWAY_CONNECTOR_TLSKEYSTORE_PATH} + trust-anchor: + alias: ${DGC_GATEWAY_CONNECTOR_TRUSTANCHOR_ALIAS} + password: ${DGC_GATEWAY_CONNECTOR_TRUSTANCHOR_PASSWORD} + path: ${DGC_GATEWAY_CONNECTOR_TRUSTANCHOR_PATH} + enable-ddcc-support: true + did: + cron: "0 0 2 * * *" + enableDidGeneration: true + didUploadProvider: local-file + localFile: + directory: + file-name: did.json + git: + workdir: + prefix: + url: + pat: + didSigningProvider: dummy + ld-proof-verification-method: did:web:dummy.net + did-id: did:web:abc + trust-list-path: trustlist + trust-list-ref-path: trustlist-ref + did-controller: did:web:def + trust-list-id-prefix: did:web:abc + trust-list-controller-prefix: did:web:abc + contextMapping: + "[https://www.w3.org/ns/did/v1]": did_v1.json + "[https://w3id.org/security/suites/jws-2020/v1]": jws-2020_v1.json + virtualCountries: + XA: XXA + XB: XXB + XO: XXO + XL: XCL + group-deny-list: + - AUTHENTICATION + - UPLOAD + group-name-mapping: + CSCA: SCA diff --git a/src/main/resources/db/changelog.xml b/src/main/resources/db/changelog.xml deleted file mode 100644 index a34d05e..0000000 --- a/src/main/resources/db/changelog.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - diff --git a/src/main/resources/db/changelog.yaml b/src/main/resources/db/changelog.yaml new file mode 100644 index 0000000..a359c29 --- /dev/null +++ b/src/main/resources/db/changelog.yaml @@ -0,0 +1,13 @@ +databaseChangeLog: + - include: + file: db/changelog/create-shedlock-tables.yaml + - include: + file: db/changelog/create-signer-information-table.yaml + - include: + file: db/changelog/create-trusted-issuer-table.yaml + - include: + file: db/changelog/create-decentralized-identifier-table.yaml + - include: + file: db/changelog/create-public-key-jwk-table.yaml + - include: + file: db/changelog/create-verification-method-table.yaml diff --git a/src/main/resources/db/changelog/alter-signer-information.xml b/src/main/resources/db/changelog/alter-signer-information.xml deleted file mode 100644 index b46d190..0000000 --- a/src/main/resources/db/changelog/alter-signer-information.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/resources/db/changelog/create-decentralized-identifier-table.yaml b/src/main/resources/db/changelog/create-decentralized-identifier-table.yaml new file mode 100644 index 0000000..987c95a --- /dev/null +++ b/src/main/resources/db/changelog/create-decentralized-identifier-table.yaml @@ -0,0 +1,30 @@ +databaseChangeLog: + - changeSet: + id: create-decentralized-identifier-table + author: f11h + changes: + - createTable: + tableName: decentralized_identifier + columns: + - column: + autoIncrement: true + name: id + type: bigint + constraints: + primaryKey: true + primaryKeyName: decentralized_identifier_id + nullable: false + - column: + name: created_at + type: datetime + constraints: + nullable: false + - column: + name: did_id + type: varchar(255) + constraints: + nullable: true + - column: + name: raw + type: clob + diff --git a/src/main/resources/db/changelog/create-info-table.xml b/src/main/resources/db/changelog/create-info-table.xml deleted file mode 100644 index 601bcd5..0000000 --- a/src/main/resources/db/changelog/create-info-table.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - diff --git a/src/main/resources/db/changelog/create-public-key-jwk-table.yaml b/src/main/resources/db/changelog/create-public-key-jwk-table.yaml new file mode 100644 index 0000000..8be08ad --- /dev/null +++ b/src/main/resources/db/changelog/create-public-key-jwk-table.yaml @@ -0,0 +1,57 @@ +databaseChangeLog: + - changeSet: + id: create-public-key-jwk-table + author: f11h + changes: + - createTable: + tableName: public_key_jwk + columns: + - column: + autoIncrement: true + name: id + type: bigint + constraints: + primaryKey: true + primaryKeyName: public_key_jwk_id + nullable: true + - column: + name: created_at + type: datetime + constraints: + nullable: false + - column: + name: kty + type: varchar(50) + constraints: + nullable: false + - column: + name: crv + type: varchar(50) + constraints: + nullable: true + - column: + name: x + type: varchar(100) + constraints: + nullable: true + - column: + name: y + type: varchar(100) + constraints: + nullable: true + - column: + name: e + type: varchar(5000) + constraints: + nullable: true + - column: + name: n + type: varchar(5000) + constraints: + nullable: true + - column: + name: x5c + type: varchar(10000) + constraints: + nullable: true + diff --git a/src/main/resources/db/changelog/create-shedlock-tables.yaml b/src/main/resources/db/changelog/create-shedlock-tables.yaml new file mode 100644 index 0000000..207bf92 --- /dev/null +++ b/src/main/resources/db/changelog/create-shedlock-tables.yaml @@ -0,0 +1,38 @@ +databaseChangeLog: + - objectQuotingStrategy: QUOTE_ONLY_RESERVED_WORDS + - changeSet: + id: shedlock-create + author: admin + changes: + - createTable: + tableName: shedlock + columns: + - column: + name: id + type: bigint + autoIncrement: true + constraints: + primaryKey: true + primaryKeyName: pk_shedlock + nullable: false + - column: + name: name + type: varchar(64) + constraints: + nullable: false + unique: true + - column: + name: lock_until + type: datetime + constraints: + nullable: false + - column: + name: locked_at + type: datetime + constraints: + nullable: false + - column: + name: locked_by + type: varchar(255) + constraints: + nullable: false diff --git a/src/main/resources/db/changelog/create-signer-information-table.yaml b/src/main/resources/db/changelog/create-signer-information-table.yaml new file mode 100644 index 0000000..be4acfb --- /dev/null +++ b/src/main/resources/db/changelog/create-signer-information-table.yaml @@ -0,0 +1,43 @@ +databaseChangeLog: + - changeSet: + id: signer-information-create + author: admin + changes: + - createTable: + tableName: signer_information + columns: + - column: + name: id + type: bigint + autoIncrement: true + constraints: + primaryKey: true + primaryKeyName: pk_signer_information + nullable: false + - column: + name: kid + type: varchar(50) + constraints: + nullable: false + - column: + name: created_at + type: datetime + constraints: + nullable: false + - column: + name: raw_data + type: varchar(4096) + constraints: + nullable: false + - column: + name: country + type: varchar(2) + - column: + name: domain + type: varchar(50) + - column: + name: groupx + type: varchar(50) + - column: + name: subject_hash + type: varchar(64) diff --git a/src/main/resources/db/changelog/create-trusted-issuer-table.xml b/src/main/resources/db/changelog/create-trusted-issuer-table.xml deleted file mode 100644 index b819af9..0000000 --- a/src/main/resources/db/changelog/create-trusted-issuer-table.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/resources/db/changelog/create-trusted-issuer-table.yaml b/src/main/resources/db/changelog/create-trusted-issuer-table.yaml new file mode 100644 index 0000000..76bedec --- /dev/null +++ b/src/main/resources/db/changelog/create-trusted-issuer-table.yaml @@ -0,0 +1,55 @@ +databaseChangeLog: + - changeSet: + id: create-trusted-issuer-table + author: admin + changes: + - createTable: + tableName: trusted_issuer + columns: + - column: + autoIncrement: true + name: id + type: bigint + constraints: + primaryKey: true + primaryKeyName: pk_trusted_issuer + nullable: false + - column: + name: created_at + type: timestamp with time zone + constraints: + nullable: false + - column: + name: country + type: varchar(2) + constraints: + nullable: false + - column: + name: url + type: varchar(1024) + constraints: + nullable: false + - column: + name: name + type: varchar(512) + constraints: + nullable: false + - column: + name: url_type + type: varchar(25) + constraints: + nullable: false + - column: + name: thumbprint + type: varchar(64) + - column: + name: ssl_public_key + type: varchar(2048) + - column: + name: key_storage_type + type: varchar(128) + - column: + name: signature + type: varchar(6000) + constraints: + nullable: false diff --git a/src/main/resources/db/changelog/create-verification-method-table.yaml b/src/main/resources/db/changelog/create-verification-method-table.yaml new file mode 100644 index 0000000..db0d66a --- /dev/null +++ b/src/main/resources/db/changelog/create-verification-method-table.yaml @@ -0,0 +1,58 @@ +databaseChangeLog: + - changeSet: + id: create-verification-method-table + author: f11h + changes: + - createTable: + tableName: verification_method + columns: + - column: + autoIncrement: true + name: id + type: bigint + constraints: + primaryKey: true + primaryKeyName: verification_method_id + nullable: false + - column: + name: created_at + type: datetime + constraints: + nullable: false + - column: + name: vm_id + type: varchar(255) + constraints: + nullable: true + - column: + name: type + type: varchar(100) + constraints: + nullable: true + - column: + name: controller + type: varchar(255) + constraints: + nullable: true + - column: + name: parent_document_id + type: bigint + constraints: + nullable: false + - column: + name: public_key_jwk_id + type: bigint + constraints: + nullable: false + - addForeignKeyConstraint: + constraintName: fk_did_vm + referencedTableName: decentralized_identifier + referencedColumnNames: id + baseTableName: verification_method + baseColumnNames: parent_document_id + - addForeignKeyConstraint: + constraintName: fk_pk_vm + referencedTableName: public_key_jwk + referencedColumnNames: id + baseTableName: verification_method + baseColumnNames: public_key_jwk_id diff --git a/src/main/resources/db/changelog/init-tables.xml b/src/main/resources/db/changelog/init-tables.xml deleted file mode 100644 index 853386e..0000000 --- a/src/main/resources/db/changelog/init-tables.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/resources/did_contexts/did_v1.json b/src/main/resources/did_contexts/did_v1.json new file mode 100644 index 0000000..b447d01 --- /dev/null +++ b/src/main/resources/did_contexts/did_v1.json @@ -0,0 +1,58 @@ +{ + "@context": { + "@protected": true, + "id": "@id", + "type": "@type", + + "alsoKnownAs": { + "@id": "https://www.w3.org/ns/activitystreams#alsoKnownAs", + "@type": "@id" + }, + "assertionMethod": { + "@id": "https://w3id.org/security#assertionMethod", + "@type": "@id", + "@container": "@set" + }, + "authentication": { + "@id": "https://w3id.org/security#authenticationMethod", + "@type": "@id", + "@container": "@set" + }, + "capabilityDelegation": { + "@id": "https://w3id.org/security#capabilityDelegationMethod", + "@type": "@id", + "@container": "@set" + }, + "capabilityInvocation": { + "@id": "https://w3id.org/security#capabilityInvocationMethod", + "@type": "@id", + "@container": "@set" + }, + "controller": { + "@id": "https://w3id.org/security#controller", + "@type": "@id" + }, + "keyAgreement": { + "@id": "https://w3id.org/security#keyAgreementMethod", + "@type": "@id", + "@container": "@set" + }, + "service": { + "@id": "https://www.w3.org/ns/did#service", + "@type": "@id", + "@context": { + "@protected": true, + "id": "@id", + "type": "@type", + "serviceEndpoint": { + "@id": "https://www.w3.org/ns/did#serviceEndpoint", + "@type": "@id" + } + } + }, + "verificationMethod": { + "@id": "https://w3id.org/security#verificationMethod", + "@type": "@id" + } + } +} diff --git a/src/main/resources/did_contexts/jws-2020_v1.json b/src/main/resources/did_contexts/jws-2020_v1.json new file mode 100644 index 0000000..17186cd --- /dev/null +++ b/src/main/resources/did_contexts/jws-2020_v1.json @@ -0,0 +1,82 @@ +{ + "@context": { + "privateKeyJwk": { + "@id": "https://w3id.org/security#privateKeyJwk", + "@type": "@json" + }, + "JsonWebKey2020": { + "@id": "https://w3id.org/security#JsonWebKey2020", + "@context": { + "@protected": true, + "id": "@id", + "type": "@type", + "publicKeyJwk": { + "@id": "https://w3id.org/security#publicKeyJwk", + "@type": "@json" + } + } + }, + "JsonWebSignature2020": { + "@id": "https://w3id.org/security#JsonWebSignature2020", + "@context": { + "@protected": true, + + "id": "@id", + "type": "@type", + + "challenge": "https://w3id.org/security#challenge", + "created": { + "@id": "http://purl.org/dc/terms/created", + "@type": "http://www.w3.org/2001/XMLSchema#dateTime" + }, + "domain": "https://w3id.org/security#domain", + "expires": { + "@id": "https://w3id.org/security#expiration", + "@type": "http://www.w3.org/2001/XMLSchema#dateTime" + }, + "jws": "https://w3id.org/security#jws", + "nonce": "https://w3id.org/security#nonce", + "proofPurpose": { + "@id": "https://w3id.org/security#proofPurpose", + "@type": "@vocab", + "@context": { + "@protected": true, + + "id": "@id", + "type": "@type", + + "assertionMethod": { + "@id": "https://w3id.org/security#assertionMethod", + "@type": "@id", + "@container": "@set" + }, + "authentication": { + "@id": "https://w3id.org/security#authenticationMethod", + "@type": "@id", + "@container": "@set" + }, + "capabilityInvocation": { + "@id": "https://w3id.org/security#capabilityInvocationMethod", + "@type": "@id", + "@container": "@set" + }, + "capabilityDelegation": { + "@id": "https://w3id.org/security#capabilityDelegationMethod", + "@type": "@id", + "@container": "@set" + }, + "keyAgreement": { + "@id": "https://w3id.org/security#keyAgreementMethod", + "@type": "@id", + "@container": "@set" + } + } + }, + "verificationMethod": { + "@id": "https://w3id.org/security#verificationMethod", + "@type": "@id" + } + } + } + } +} diff --git a/src/main/resources/static/context.json b/src/main/resources/static/context.json deleted file mode 100644 index 0034c4f..0000000 --- a/src/main/resources/static/context.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "origin": "DE", - "versions": { - "default": { - "privacyUrl": "https://publications.europa.eu/en/web/about-us/legal-notices/eu-mobile-apps", - "context": { - "url": "https://dgca-verifier-service.cfapps.eu10.hana.ondemand.com/context", - "pubKeys": [ - "lKdU1EbQubxyDDm2q3N8KclZ2C94Num3xXjG0pk+3eI=", - "r/mIkG3eEpVdm+u/ko/cwxzOMo1bk4TyHIlByibiA5E=" - ] - }, - "endpoints": { - "status": { - "url": "https://dgca-verifier-service.cfapps.eu10.hana.ondemand.com/signercertificateStatus", - "pubKeys": [ - "lKdU1EbQubxyDDm2q3N8KclZ2C94Num3xXjG0pk+3eI=", - "r/mIkG3eEpVdm+u/ko/cwxzOMo1bk4TyHIlByibiA5E=" - ] - }, - "update": { - "url": "https://dgca-verifier-service.cfapps.eu10.hana.ondemand.com/signercertificateUpdate", - "pubKeys": [ - "lKdU1EbQubxyDDm2q3N8KclZ2C94Num3xXjG0pk+3eI=", - "r/mIkG3eEpVdm+u/ko/cwxzOMo1bk4TyHIlByibiA5E=" - ] - }, - "countryList": { - "url": "https://dgca-businessrule-service.cfapps.eu10.hana.ondemand.com/countrylist", - "pubKeys": [ - "lKdU1EbQubxyDDm2q3N8KclZ2C94Num3xXjG0pk+3eI=", - "r/mIkG3eEpVdm+u/ko/cwxzOMo1bk4TyHIlByibiA5E=" - ] - }, - "rules": { - "url": "https://dgca-businessrule-service.cfapps.eu10.hana.ondemand.com/rules", - "pubKeys": [ - "lKdU1EbQubxyDDm2q3N8KclZ2C94Num3xXjG0pk+3eI=", - "r/mIkG3eEpVdm+u/ko/cwxzOMo1bk4TyHIlByibiA5E=" - ] - }, - "valuesets": { - "url": "https://dgca-businessrule-service.cfapps.eu10.hana.ondemand.com/valuesets", - "pubKeys": [ - "lKdU1EbQubxyDDm2q3N8KclZ2C94Num3xXjG0pk+3eI=", - "r/mIkG3eEpVdm+u/ko/cwxzOMo1bk4TyHIlByibiA5E=" - ] - } - } - }, - "0.1.0": { - "outdated": true - } - } -} diff --git a/src/test/java/tng/trustnetwork/keydistribution/OpenApiTest.java b/src/test/java/tng/trustnetwork/keydistribution/OpenApiTest.java deleted file mode 100644 index ddeac81..0000000 --- a/src/test/java/tng/trustnetwork/keydistribution/OpenApiTest.java +++ /dev/null @@ -1,42 +0,0 @@ -package tng.trustnetwork.keydistribution; - -import eu.europa.ec.dgc.gateway.connector.DgcGatewayDownloadConnector; -import java.io.BufferedInputStream; -import java.io.FileOutputStream; -import java.net.URL; -import lombok.extern.slf4j.Slf4j; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; - -@Slf4j -@SpringBootTest( - properties = { - "server.port=8080", - "springdoc.api-docs.enabled=true", - "springdoc.api-docs.path=/openapi" - }, - webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT -) -class OpenApiTest { - - @MockBean - private DgcGatewayDownloadConnector dgcGatewayDownloadConnector; - - @Test - void apiDocs() { - try (BufferedInputStream in = new BufferedInputStream(new URL("http://localhost:8080/openapi").openStream()); - FileOutputStream out = new FileOutputStream("target/openapi.json")) { - byte[] buffer = new byte[1024]; - int read; - while ((read = in.read(buffer, 0, buffer.length)) != -1) { - out.write(buffer, 0, read); - } - } catch (Exception e) { - log.error("Failed to download openapi specification.", e); - Assertions.fail(); - } - } - -} diff --git a/src/test/java/tng/trustnetwork/keydistribution/restapi/controller/ContextControllerIntegrationTest.java b/src/test/java/tng/trustnetwork/keydistribution/restapi/controller/ContextControllerIntegrationTest.java deleted file mode 100644 index 2909d61..0000000 --- a/src/test/java/tng/trustnetwork/keydistribution/restapi/controller/ContextControllerIntegrationTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package tng.trustnetwork.keydistribution.restapi.controller; - -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.nio.charset.StandardCharsets; -import org.apache.commons.io.IOUtils; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.Resource; -import org.springframework.http.MediaType; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.MvcResult; - -@SpringBootTest -@AutoConfigureMockMvc -class ContextControllerIntegrationTest { - - @org.springframework.boot.test.mock.mockito.MockBean - eu.europa.ec.dgc.gateway.connector.DgcGatewayDownloadConnector dgcGatewayDownloadConnector; - - @Autowired - private MockMvc mockMvc; - - @Test - void requestContext() throws Exception { - mockMvc.perform(get("/context")) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(result -> assertContextStrEqualFile(result)); - } - - private void assertContextStrEqualFile(MvcResult result) throws UnsupportedEncodingException { - String resultContext = result.getResponse().getContentAsString(); - Resource resource = new ClassPathResource("/static/context.json"); - String fileContext = null; - try { - fileContext = IOUtils.toString(resource.getInputStream(), StandardCharsets.UTF_8); - } catch (IOException e) { - Assertions.fail(e); - } - Assertions.assertEquals(resultContext, fileContext); - } - -} diff --git a/src/test/java/tng/trustnetwork/keydistribution/restapi/controller/ContextControllerWithEnvironmentIntegrationTest.java b/src/test/java/tng/trustnetwork/keydistribution/restapi/controller/ContextControllerWithEnvironmentIntegrationTest.java deleted file mode 100644 index f1c2f8f..0000000 --- a/src/test/java/tng/trustnetwork/keydistribution/restapi/controller/ContextControllerWithEnvironmentIntegrationTest.java +++ /dev/null @@ -1,33 +0,0 @@ -package tng.trustnetwork.keydistribution.restapi.controller; - -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.http.MediaType; -import org.springframework.test.context.TestPropertySource; -import org.springframework.test.web.servlet.MockMvc; - -@SpringBootTest -@AutoConfigureMockMvc -@TestPropertySource(properties = {"dgc.context={\"testContext\": true}"}) -class ContextControllerWithEnvironmentIntegrationTest { - - @MockBean - eu.europa.ec.dgc.gateway.connector.DgcGatewayDownloadConnector dgcGatewayDownloadConnector; - - @Autowired - private MockMvc mockMvc; - - @Test - void requestContext() throws Exception { - mockMvc.perform(get("/context")) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(content().json("{\"testContext\": true}")); - } -} diff --git a/src/test/java/tng/trustnetwork/keydistribution/restapi/controller/SignerInformationIntegrationTest.java b/src/test/java/tng/trustnetwork/keydistribution/restapi/controller/SignerInformationIntegrationTest.java deleted file mode 100644 index 13d566a..0000000 --- a/src/test/java/tng/trustnetwork/keydistribution/restapi/controller/SignerInformationIntegrationTest.java +++ /dev/null @@ -1,364 +0,0 @@ -/*- - * ---license-start - * WorldHealthOrganization / tng-key-distribution - * --- - * Copyright (C) 2021 T-Systems International GmbH and all other contributors - * --- - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://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. - * ---license-end - */ - -package tng.trustnetwork.keydistribution.restapi.controller; - -import tng.trustnetwork.keydistribution.repository.SignerInformationRepository; -import tng.trustnetwork.keydistribution.testdata.SignerInformationTestHelper; -import java.io.UnsupportedEncodingException; -import java.time.ZonedDateTime; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.http.MediaType; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.MvcResult; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -@SpringBootTest -@AutoConfigureMockMvc -class SignerInformationIntegrationTest { - - - private static final String X_RESUME_TOKEN_HEADER = "X-RESUME-TOKEN"; - private static final String X_KID_HEADER = "X-KID"; - - @org.springframework.boot.test.mock.mockito.MockBean - eu.europa.ec.dgc.gateway.connector.DgcGatewayDownloadConnector dgcGatewayDownloadConnector; - - @Autowired - SignerInformationRepository signerInformationRepository; - - @Autowired - SignerInformationTestHelper signerInformationTestHelper; - - @Autowired - private MockMvc mockMvc; - - @BeforeEach - void clearRepositoryData() { - signerInformationRepository.deleteAll(); - } - - @Test - void requestCertificatesFromEmptyCertificateList() throws Exception { - mockMvc.perform(get("/signercertificateUpdate")) - .andExpect(status().isNoContent()); - - } - - @Test - void requestValidIdListFromEmptyCertificatesList() throws Exception { - mockMvc.perform(get("/signercertificateStatus")) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(content().json("[]")); - } - - @Test - void requestOneCertificate() throws Exception { - Long certId_1 = signerInformationTestHelper.insertCertString(SignerInformationTestHelper.TEST_CERT_1_STR); - - mockMvc.perform(get("/signercertificateUpdate")) - .andExpect(status().isOk()) - .andExpect(content().contentTypeCompatibleWith(MediaType.TEXT_PLAIN)) - .andExpect(header().exists(X_KID_HEADER)) - .andExpect(header().exists(X_RESUME_TOKEN_HEADER)) - .andExpect(header().longValue(X_RESUME_TOKEN_HEADER, certId_1)) - .andExpect(header().stringValues(X_KID_HEADER, "8xYtW2837ac=")) - .andExpect(c -> assertCertStrEqual(c, SignerInformationTestHelper.TEST_CERT_1_STR)); - - Long certId_2 = signerInformationTestHelper.insertCertString(SignerInformationTestHelper.TEST_CERT_2_STR); - - mockMvc.perform(get("/signercertificateUpdate")) - .andExpect(status().isOk()) - .andExpect(content().contentTypeCompatibleWith(MediaType.TEXT_PLAIN)) - .andExpect(header().exists(X_KID_HEADER)) - .andExpect(header().exists(X_RESUME_TOKEN_HEADER)) - .andExpect(header().longValue(X_RESUME_TOKEN_HEADER, certId_1)) - .andExpect(header().stringValues(X_KID_HEADER, "8xYtW2837ac=")) - .andExpect(c -> assertCertStrEqual(c, SignerInformationTestHelper.TEST_CERT_1_STR)); - - } - - @Test - void requestValidIdList() throws Exception { - signerInformationTestHelper.insertCertString(SignerInformationTestHelper.TEST_CERT_1_STR); - mockMvc.perform(get("/signercertificateStatus")) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(content().json("[\"8xYtW2837ac=\"]")); - - signerInformationTestHelper.insertCertString(SignerInformationTestHelper.TEST_CERT_2_STR); - mockMvc.perform(get("/signercertificateStatus")) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(content().json("[\"8xYtW2837ac=\",\"EzVuT0kOpJc=\"]")); - - signerInformationTestHelper.insertCertString(SignerInformationTestHelper.TEST_CERT_3_STR); - mockMvc.perform(get("/signercertificateStatus")) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(content().json("[\"8xYtW2837ac=\",\"EzVuT0kOpJc=\",\"zoQi+KTb8LM=\"]")); - } - - @Test - void requestCertificatesWithResumeToken() throws Exception { - Long certId_1 = signerInformationTestHelper.insertCertString(SignerInformationTestHelper.TEST_CERT_1_STR); - - mockMvc.perform(get("/signercertificateUpdate")) - .andExpect(status().isOk()) - .andExpect(content().contentTypeCompatibleWith(MediaType.TEXT_PLAIN)) - .andExpect(header().exists(X_KID_HEADER)) - .andExpect(header().exists(X_RESUME_TOKEN_HEADER)) - .andExpect(header().longValue(X_RESUME_TOKEN_HEADER, certId_1)) - .andExpect(header().stringValues(X_KID_HEADER, "8xYtW2837ac=")) - .andExpect(c -> assertCertStrEqual(c, SignerInformationTestHelper.TEST_CERT_1_STR)); - - mockMvc.perform(get("/signercertificateUpdate").header(X_RESUME_TOKEN_HEADER, certId_1)) - .andExpect(status().isNoContent()); - - Long certId_2 = signerInformationTestHelper.insertCertString(SignerInformationTestHelper.TEST_CERT_2_STR); - Long certId_3 = signerInformationTestHelper.insertCertString(SignerInformationTestHelper.TEST_CERT_3_STR); - - mockMvc.perform(get("/signercertificateUpdate").header(X_RESUME_TOKEN_HEADER, certId_1)) - .andExpect(status().isOk()) - .andExpect(content().contentTypeCompatibleWith(MediaType.TEXT_PLAIN)) - .andExpect(header().exists(X_KID_HEADER)) - .andExpect(header().exists(X_RESUME_TOKEN_HEADER)) - .andExpect(header().longValue(X_RESUME_TOKEN_HEADER, certId_2)) - .andExpect(header().stringValues(X_KID_HEADER, "EzVuT0kOpJc=")) - .andExpect(c -> assertCertStrEqual(c, SignerInformationTestHelper.TEST_CERT_2_STR)); - - mockMvc.perform(get("/signercertificateUpdate").header(X_RESUME_TOKEN_HEADER, certId_2)) - .andExpect(status().isOk()) - .andExpect(content().contentTypeCompatibleWith(MediaType.TEXT_PLAIN)) - .andExpect(header().exists(X_KID_HEADER)) - .andExpect(header().exists(X_RESUME_TOKEN_HEADER)) - .andExpect(header().longValue(X_RESUME_TOKEN_HEADER, certId_3)) - .andExpect(header().stringValues(X_KID_HEADER, "zoQi+KTb8LM=")) - .andExpect(c -> assertCertStrEqual(c, SignerInformationTestHelper.TEST_CERT_3_STR)); - - mockMvc.perform(get("/signercertificateUpdate").header(X_RESUME_TOKEN_HEADER, certId_3)) - .andExpect(status().isNoContent()); - - } - - @Test - void requestCertificatesFromCertListWithRevokedCerts() throws Exception { - Long certId_1 = signerInformationTestHelper.insertCertString(SignerInformationTestHelper.TEST_CERT_1_STR); - Long certId_2 = signerInformationTestHelper.insertCertString(SignerInformationTestHelper.TEST_CERT_2_STR); - Long certId_3 = signerInformationTestHelper.insertCertString(SignerInformationTestHelper.TEST_CERT_3_STR); - - mockMvc.perform(get("/signercertificateUpdate").header(X_RESUME_TOKEN_HEADER, certId_1)) - .andExpect(status().isOk()) - .andExpect(content().contentTypeCompatibleWith(MediaType.TEXT_PLAIN)) - .andExpect(header().exists(X_KID_HEADER)) - .andExpect(header().exists(X_RESUME_TOKEN_HEADER)) - .andExpect(header().longValue(X_RESUME_TOKEN_HEADER, certId_2)) - .andExpect(header().stringValues(X_KID_HEADER, "EzVuT0kOpJc=")) - .andExpect(c -> assertCertStrEqual(c, SignerInformationTestHelper.TEST_CERT_2_STR)); - - signerInformationRepository.deleteById(certId_2); - - mockMvc.perform(get("/signercertificateUpdate").header(X_RESUME_TOKEN_HEADER, certId_1)) - .andExpect(status().isOk()) - .andExpect(content().contentTypeCompatibleWith(MediaType.TEXT_PLAIN)) - .andExpect(header().exists(X_KID_HEADER)) - .andExpect(header().exists(X_RESUME_TOKEN_HEADER)) - .andExpect(header().longValue(X_RESUME_TOKEN_HEADER, certId_3)) - .andExpect(header().stringValues(X_KID_HEADER, "zoQi+KTb8LM=")) - .andExpect(c -> assertCertStrEqual(c, SignerInformationTestHelper.TEST_CERT_3_STR)); - } - - @Test - void requestValidIdListFromCertListWithRevokedCert() throws Exception { - Long certId_1 = signerInformationTestHelper.insertCertString(SignerInformationTestHelper.TEST_CERT_1_STR); - Long certId_2 = signerInformationTestHelper.insertCertString(SignerInformationTestHelper.TEST_CERT_2_STR); - Long certId_3 = signerInformationTestHelper.insertCertString(SignerInformationTestHelper.TEST_CERT_3_STR); - - mockMvc.perform(get("/signercertificateStatus")) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(content().json("[\"8xYtW2837ac=\",\"EzVuT0kOpJc=\",\"zoQi+KTb8LM=\"]")); - - signerInformationRepository.deleteById(certId_2); - - mockMvc.perform(get("/signercertificateStatus")) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(content().json("[\"8xYtW2837ac=\",\"zoQi+KTb8LM=\"]")); - - } - - @Test - void requestDeltaNoHeader() throws Exception { - ZonedDateTime date1 = ZonedDateTime.parse("2022-04-13T02:21:00Z"); - Long certId_1 = signerInformationTestHelper.insertCertString( - SignerInformationTestHelper.TEST_CERT_1_STR, - "de", "thumbp1", date1, false); - - Long certId_2 = signerInformationTestHelper.insertCertString( - SignerInformationTestHelper.TEST_CERT_2_STR, - "de", "thumbp2", date1, true); - - mockMvc.perform(get("/signercertificateStatus/delta")) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(content().json("{\"updated\":[\"8xYtW2837ac=\"],\"deleted\":[\"EzVuT0kOpJc=\"]}")); - - } - - @Test - void requestDeltaWithHeader() throws Exception { - ZonedDateTime date1 = ZonedDateTime.parse("2022-04-04T02:21:00Z"); - ZonedDateTime date2 = ZonedDateTime.parse("2022-04-13T02:21:00Z"); - Long certId_1 = signerInformationTestHelper.insertCertString( - SignerInformationTestHelper.TEST_CERT_1_STR, - "de", "thumbp1", date1, false); - - Long certId_2 = signerInformationTestHelper.insertCertString( - SignerInformationTestHelper.TEST_CERT_2_STR, - "de", "thumbp2", date2, false); - - Long certId_3 = signerInformationTestHelper.insertCertString( - SignerInformationTestHelper.TEST_CERT_3_STR, - "de", "thumbp3", date2, true); - - mockMvc.perform(get("/signercertificateStatus/delta")) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(content(). - json("{\"updated\":[\"8xYtW2837ac=\",\"EzVuT0kOpJc=\"],\"deleted\":[\"zoQi+KTb8LM=\"]}")); - - mockMvc.perform(get("/signercertificateStatus/delta") - .header("if-modified-since","2022-04-04T02:20:00Z")) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(content(). - json("{\"updated\":[\"8xYtW2837ac=\",\"EzVuT0kOpJc=\"],\"deleted\":[\"zoQi+KTb8LM=\"]}")); - - mockMvc.perform(get("/signercertificateStatus/delta") - .header("if-modified-since","2022-04-04T02:21:00Z")) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(content(). - json("{\"updated\":[\"EzVuT0kOpJc=\"],\"deleted\":[\"zoQi+KTb8LM=\"]}")); - - mockMvc.perform(get("/signercertificateStatus/delta") - .header("if-modified-since","2022-04-13T02:21:00Z")) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(content(). - json("{\"updated\":[],\"deleted\":[]}")); - - mockMvc.perform(get("/signercertificateStatus/delta") - .header("if-modified-since","Mon, 04 Apr 2022 02:21:00 GMT")) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(content(). - json("{\"updated\":[\"EzVuT0kOpJc=\"],\"deleted\":[\"zoQi+KTb8LM=\"]}")); - - } - - @Test - void requestDeltaBadRequest() throws Exception { - - mockMvc.perform(get("/signercertificateStatus/delta") - .header("if-modified-since","NotValid")) - .andExpect(status().isBadRequest()) - .andExpect(content().string("Can not parse if-modified-since header")); - - } - - @Test - void requestCertificateData() throws Exception { - ZonedDateTime date1 = ZonedDateTime.parse("2022-04-13T02:21:00Z"); - Long certId_1 = signerInformationTestHelper.insertCertString( - SignerInformationTestHelper.TEST_CERT_1_STR, - "de", "thumbp1", date1, false); - - Long certId_2 = signerInformationTestHelper.insertCertString( - SignerInformationTestHelper.TEST_CERT_2_STR, - "de", "thumbp2", date1, true); - - mockMvc.perform(post("/signercertificateUpdate") - .contentType(MediaType.APPLICATION_JSON) - .content("[\"8xYtW2837ac=\",\"EzVuT0kOpJc=\"]") - .characterEncoding("utf-8")) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(content().json("{\"de\":[{\"kid\":\"8xYtW2837ac=\"," - + "\"rawData\":\"MIICrDCCAZSgAwIBAgIEYH+7ujANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1lZGdjX2Rldl90ZXN0MB4XDT" - + "IxMDQyMTA1NDQyNloXDTIyMDQyMTA1NDQyNlowGDEWMBQGA1UEAwwNZWRnY19kZXZfdGVzdDCCASIwDQYJKoZIhvcNAQEBBQADgg" - + "EPADCCAQoCggEBAOAlpphOE0TH2m+jU6prmP1W6N0ajaExs5X+sxxG58hIGnZchxFkLkeYSZqyC2bPQtPiYIDgVFcPJPgfRO4r5e" - + "x3W7OxQCFS0TJmYhRkLiVQHQDNHeXFmOpu834x2ErPJ8AK2D9KhVyFKl5OX1euU25IXzXs67vQf30eStArvWFlZGX4E+JUy8yIwr" - + "R6WLRe+kgtBdFmJZJywbnnffg/5WT+TEcky8ugBlsEcyTxI5rt6iW5ptNUphui8ZGaE2KtjcnZVaPCvn1IjEv6sdWS/DNDlFySuJ" - + "6LQD1OnKsjCXrNVZFVZS5ae9snPu4Y/gapzdgeSDioRk6BWwZ02E9BE+8CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEApE8H9uGtB6" - + "DuDL3LEqGslyJKyc6EBqJ+4hDlFtPe+13xEDomJsNwq1Uk3p9F1aHgqqXc1MjJfDWn0l7ZDGh02tfi+EgHyV2vrfqZwXm6vuK/P7" - + "fzdb5blLJpKt0NoMCzY+lHhkCxcRGX1R8QOGuuGtnepDrtyeTuoQqsh0mdcMuFgKuTr3c3kKpoQwBWquG/eZ0PhKSkqXy5aEaFAz" - + "dXBLq/dh4zn8FVx+STSpKK1WNmoqjtL7EEFcNgxLTjWJFjusTEZL0Yxa4Ot4Gb6+VK7P34olH7pFcBFYfh6DyOESV9uglrE4kdOQ" - + "7+x+yS5zR/UTeEfM4mW4I2QIEreUN8Jg==\"}," - + "{\"kid\":\"EzVuT0kOpJc=\",\"rawData\":\"MIIBGzCBwqADAgECAgRggU" - + "ObMAoGCCqGSM49BAMCMBYxFDASBgNVBAMMC2VkZ2NfZGV2X2VjMB4XDTIxMDQyMjA5MzYyN1oXDTIyMDQyMjA5MzYyN1owFjEUMB" - + "IGA1UEAwwLZWRnY19kZXZfZWMwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQVQc9JY190s/Jn0CBSq/AWuxmqUzRVu+AsCe6gfb" - + "qk3s0e4jonzp5v/5IMW/9t7v5Fu2ITMmOTVfKL1TuM+aixMAoGCCqGSM49BAMCA0gAMEUCIQCGWIk6ZET3afRxdpFVuXdrEYtFiR" - + "1MGDx4HweZfspjSgIgBdCJsT746/FI3euIbzKDoeY65m+Qx2/4Cd/vOayNbuw=\"}]}")); - - } - - @Test - void requestCertificateDataNotExist() throws Exception { - ZonedDateTime date1 = ZonedDateTime.parse("2022-04-13T02:21:00Z"); - Long certId_1 = signerInformationTestHelper.insertCertString( - SignerInformationTestHelper.TEST_CERT_1_STR, - "de", "thumbp1", date1, false); - - Long certId_2 = signerInformationTestHelper.insertCertString( - SignerInformationTestHelper.TEST_CERT_2_STR, - "de", "thumbp2", date1, true); - - mockMvc.perform(post("/signercertificateUpdate") - .contentType(MediaType.APPLICATION_JSON) - .content("[\"NotAvailable=\"]") - .characterEncoding("utf-8")) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(content().json("{}")); - - } - - - - - private void assertCertStrEqual(MvcResult result, String certStr) throws UnsupportedEncodingException { - String resultCert = result.getResponse().getContentAsString(); - - Assertions.assertEquals(certStr, resultCert); - - } -} diff --git a/src/test/java/tng/trustnetwork/keydistribution/restapi/controller/TrustedIssuerIntegrationTest.java b/src/test/java/tng/trustnetwork/keydistribution/restapi/controller/TrustedIssuerIntegrationTest.java deleted file mode 100644 index ecf101d..0000000 --- a/src/test/java/tng/trustnetwork/keydistribution/restapi/controller/TrustedIssuerIntegrationTest.java +++ /dev/null @@ -1,119 +0,0 @@ -/*- - * ---license-start - * WorldHealthOrganization / tng-key-distribution - * --- - * Copyright (C) 2021 T-Systems International GmbH and all other contributors - * --- - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://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. - * ---license-end - */ - -package tng.trustnetwork.keydistribution.restapi.controller; - -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.http.HttpHeaders; -import org.springframework.test.web.servlet.MockMvc; -import tng.trustnetwork.keydistribution.repository.TrustedIssuerRepository; -import tng.trustnetwork.keydistribution.service.InfoService; -import tng.trustnetwork.keydistribution.testdata.TrustedIssuerTestHelper; - -@SpringBootTest -@AutoConfigureMockMvc -class TrustedIssuerIntegrationTest { - - @org.springframework.boot.test.mock.mockito.MockBean - eu.europa.ec.dgc.gateway.connector.DgcGatewayDownloadConnector dgcGatewayDownloadConnector; - - @Autowired - TrustedIssuerTestHelper trustedIssuerTestHelper; - - @Autowired - TrustedIssuerRepository trustedIssuerRepository; - - @Autowired - InfoService infoService; - - @Autowired - private MockMvc mockMvc; - - @BeforeEach - void clearRepositoryData() { - - trustedIssuerRepository.deleteAll(); - infoService.setValueForKey(InfoService.CURRENT_ETAG,"TestEtag"); - } - - @Test - void requestTrustedIssuersIsEmpty() throws Exception { - mockMvc.perform(get("/trustedissuers")) - .andExpect(status().isOk()) - .andExpect(content().json("[]")); - - } - - - @Test - void requestTrustedIssuers() throws Exception { - trustedIssuerTestHelper.insertTrustedIssuer(trustedIssuerTestHelper.getIssuer(1)); - - - mockMvc.perform(get("/trustedissuers")) - .andExpect(status().isOk()) - .andExpect(jsonPath("$[0].url").value("https://TestUrl.de")) - .andExpect(jsonPath("$[0].type").value("HTTP")) - .andExpect(jsonPath("$[0].country").value("DE")) - .andExpect(jsonPath("$[0].thumbprint").value("thumbprint1")) - .andExpect(jsonPath("$[0].sslPublicKey").value("PublicKey1")) - .andExpect(jsonPath("$[0].keyStorageType").value("JWKS")) - .andExpect(jsonPath("$[0].signature").value("Signature1")) - .andExpect(jsonPath("$[0].name").value("example1.de")); - - } - - @Test - void requestTrustedIssuersWithHeader() throws Exception { - trustedIssuerTestHelper.insertTrustedIssuer(trustedIssuerTestHelper.getIssuer(1)); - - - mockMvc.perform(get("/trustedissuers").header(HttpHeaders.IF_NONE_MATCH, "NoMatchEtag")) - .andExpect(status().isOk()) - .andExpect(jsonPath("$[0].url").value("https://TestUrl.de")) - .andExpect(jsonPath("$[0].type").value("HTTP")) - .andExpect(jsonPath("$[0].country").value("DE")) - .andExpect(jsonPath("$[0].thumbprint").value("thumbprint1")) - .andExpect(jsonPath("$[0].sslPublicKey").value("PublicKey1")) - .andExpect(jsonPath("$[0].keyStorageType").value("JWKS")) - .andExpect(jsonPath("$[0].signature").value("Signature1")) - .andExpect(jsonPath("$[0].name").value("example1.de")); - - } - - @Test - void requestTrustedIssuersWithHeaderMatchEtag() throws Exception { - trustedIssuerTestHelper.insertTrustedIssuer(trustedIssuerTestHelper.getIssuer(1)); - trustedIssuerTestHelper.insertTrustedIssuer(trustedIssuerTestHelper.getIssuer(2)); - - mockMvc.perform(get("/trustedissuers").header(HttpHeaders.IF_NONE_MATCH, "TestEtag")) - .andExpect(status().isNotModified()); - - } - -} diff --git a/src/test/java/tng/trustnetwork/keydistribution/service/DecentralizedIdentifierServiceTest.java b/src/test/java/tng/trustnetwork/keydistribution/service/DecentralizedIdentifierServiceTest.java new file mode 100644 index 0000000..007711e --- /dev/null +++ b/src/test/java/tng/trustnetwork/keydistribution/service/DecentralizedIdentifierServiceTest.java @@ -0,0 +1,121 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + +package tng.trustnetwork.keydistribution.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import eu.europa.ec.dgc.gateway.connector.DgcGatewayDownloadConnector; +import java.util.List; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import tng.trustnetwork.keydistribution.entity.DecentralizedIdentifierEntity; +import tng.trustnetwork.keydistribution.entity.EcPublicKeyJwkEntity; +import tng.trustnetwork.keydistribution.model.DidDocument; +import tng.trustnetwork.keydistribution.repository.DecentralizedIdentifierRepository; + +@SpringBootTest +public class DecentralizedIdentifierServiceTest { + + @MockBean + DgcGatewayDownloadConnector dgcGatewayDownloadConnector; + + @Autowired + DecentralizedIdentifierRepository decentralizedIdentifierRepository; + + @Autowired + DecentralizedIdentifierService decentralizedIdentifierService; + + @Autowired + ObjectMapper objectMapper; + + private static final String testDidId = "did:web:did.actor:mike"; + + private static final String testDid = """ + { + "@context": [ + "https://www.w3.org/ns/did/v1", + { + "@base": "did:web:did.actor:mike", + "rating": "https://schema.org/Rating", + "publicAccess": "https://schema.org/publicAccess", + "additionalType": "https://schema.org/additionalType" + } + ], + "id": "did:web:did.actor:mike", + "rating": 4.5, + "publicAccess": true, + "additionalType": null, + "verificationMethod": [ + { + "id": "#g1", + "controller": "did:web:did.actor:mike", + "type": "JsonWebKey2020", + "publicKeyJwk": { + "kty": "EC", + "crv": "P-256", + "x": "xValue" + } + } + ], + "authentication": [ + "did:web:did.actor:mike#g1" + ], + "assertionMethod": [ + "did:web:did.actor:mike#g1" + ] + } + """; + + @Test + void itShouldPersistDIDsInDb() throws JsonProcessingException { + + DidDocument didDocument = objectMapper.readValue(testDid, DidDocument.class); + + decentralizedIdentifierService.updateDecentralizedIdentifierList(didDocument, testDid); + + List allDids = decentralizedIdentifierRepository.findAll(); + + Assertions.assertEquals(1, allDids.size()); + + DecentralizedIdentifierEntity storedDid = allDids.get(0); + + Assertions.assertEquals(testDidId, storedDid.getDidId()); + Assertions.assertEquals(testDid, storedDid.getRaw()); + Assertions.assertNotNull(storedDid.getCreatedAt()); + Assertions.assertEquals(1, storedDid.getVerificationMethods().size()); + Assertions.assertEquals("#g1", storedDid.getVerificationMethods().get(0).getVmId()); + Assertions.assertEquals("JsonWebKey2020", storedDid.getVerificationMethods().get(0).getType()); + Assertions.assertEquals(testDidId, storedDid.getVerificationMethods().get(0).getController()); + + Assertions.assertInstanceOf(EcPublicKeyJwkEntity.class, storedDid.getVerificationMethods().get(0).getPublicKeyJwk()); + EcPublicKeyJwkEntity ecPublicKeyJwk = + (EcPublicKeyJwkEntity) storedDid.getVerificationMethods().get(0).getPublicKeyJwk(); + Assertions.assertEquals("P256", ecPublicKeyJwk.getCrv()); + Assertions.assertEquals("xValue", ecPublicKeyJwk.getXvalue()); + + Assertions.assertEquals(storedDid, storedDid.getVerificationMethods().get(0).getParentDocument()); + + } + +} diff --git a/src/test/java/tng/trustnetwork/keydistribution/service/DidTrustListServiceTest.java b/src/test/java/tng/trustnetwork/keydistribution/service/DidTrustListServiceTest.java new file mode 100644 index 0000000..6aa61c6 --- /dev/null +++ b/src/test/java/tng/trustnetwork/keydistribution/service/DidTrustListServiceTest.java @@ -0,0 +1,481 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + +package tng.trustnetwork.keydistribution.service; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doNothing; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import eu.europa.ec.dgc.gateway.connector.DgcGatewayDownloadConnector; +import eu.europa.ec.dgc.utils.CertificateUtils; +import foundation.identity.jsonld.JsonLDObject; +import java.math.BigInteger; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.security.interfaces.ECPublicKey; +import java.security.interfaces.RSAPublicKey; +import java.time.Instant; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Base64; +import java.util.LinkedHashMap; +import java.util.List; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.ArgumentCaptor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import tng.trustnetwork.keydistribution.entity.SignerInformationEntity; +import tng.trustnetwork.keydistribution.repository.SignerInformationRepository; +import tng.trustnetwork.keydistribution.repository.TrustedIssuerRepository; +import tng.trustnetwork.keydistribution.service.did.DidTrustListService; +import tng.trustnetwork.keydistribution.service.did.DidUploader; +import tng.trustnetwork.keydistribution.service.did.entity.DidTrustList; +import tng.trustnetwork.keydistribution.testdata.CertificateTestUtils; +import tng.trustnetwork.keydistribution.testdata.TrustedIssuerTestHelper; + +@SpringBootTest +public class DidTrustListServiceTest { + + @Autowired + ObjectMapper objectMapper; + + @Autowired + DidTrustListService didTrustListService; + + @Autowired + SignerInformationRepository signerInformationRepository; + + @Autowired + CertificateUtils certificateUtils; + + @Autowired + TrustedIssuerRepository trustedIssuerRepository; + + @Autowired + TrustedIssuerTestHelper trustedIssuerTestHelper; + + @MockBean + DidUploader didUploaderMock; + + @MockBean + DgcGatewayDownloadConnector dgcGatewayDownloadConnector; + + X509Certificate certCscaDe, certCscaEu, certDscDe, certDscEu, certUploadDe; + + String certDscDeKid, certDscEuKid, certCscaDeKid, certCscaEuKid, certUploadDeKid; + + + @AfterEach + public void cleanUp() { + + signerInformationRepository.deleteAll(); + trustedIssuerRepository.deleteAll(); + } + + void testData(CertificateTestUtils.SignerType signerType) throws Exception { + + cleanUp(); + + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(signerType.getSigningAlgorithm()); + keyPairGenerator.initialize(signerType.getSigningAlgorithmSpec()); + + KeyPair cscaDeKeyPair = keyPairGenerator.generateKeyPair(); + certCscaDe = CertificateTestUtils.generateCertificate(cscaDeKeyPair, "DE", "Test", signerType); + certCscaDeKid = certificateUtils.getCertKid(certCscaDe); + + KeyPair cscaEuKeyPair = keyPairGenerator.generateKeyPair(); + certCscaEu = CertificateTestUtils.generateCertificate(cscaEuKeyPair, "EU", "Test", signerType); + certCscaEuKid = certificateUtils.getCertKid(certCscaEu); + + certDscDe = CertificateTestUtils.generateCertificate(keyPairGenerator.generateKeyPair(), "DE", + "Test", certCscaDe, cscaDeKeyPair.getPrivate(), + signerType); + certDscDeKid = certificateUtils.getCertKid(certDscDe); + + certDscEu = CertificateTestUtils.generateCertificate(keyPairGenerator.generateKeyPair(), "EU", + "Test", certCscaEu, cscaEuKeyPair.getPrivate(), + signerType); + certDscEuKid = certificateUtils.getCertKid(certDscEu); + + certUploadDe = CertificateTestUtils.generateCertificate(keyPairGenerator.generateKeyPair(), "DE", + "Upload Test", certCscaDe, cscaDeKeyPair.getPrivate(), + signerType); + certUploadDeKid = certificateUtils.getCertKid(certUploadDe); + + signerInformationRepository.save(new SignerInformationEntity( + null, + certCscaDeKid, + ZonedDateTime.now(), + Base64.getEncoder().encodeToString(certCscaDe.getEncoded()), + "DE", + "DCC", + "CSCA", + certificateUtils.calculateHash(certCscaDe.getSubjectX500Principal().getEncoded()) + )); + + signerInformationRepository.save(new SignerInformationEntity( + null, + certCscaEuKid, + ZonedDateTime.now(), + Base64.getEncoder().encodeToString(certCscaEu.getEncoded()), + "EU", + "DCC", + "CSCA", + certificateUtils.calculateHash(certCscaEu.getSubjectX500Principal().getEncoded()) + )); + + signerInformationRepository.save(new SignerInformationEntity( + null, + certDscDeKid, + ZonedDateTime.now(), + Base64.getEncoder().encodeToString(certDscDe.getEncoded()), + "DE", + "DCC", + "DSC", + certificateUtils.calculateHash(certDscDe.getSubjectX500Principal().getEncoded()) + )); + + signerInformationRepository.save(new SignerInformationEntity( + null, + certDscEuKid, + ZonedDateTime.now(), + Base64.getEncoder().encodeToString(certDscEu.getEncoded()), + "EU", + "DCC", + "DSC", + certificateUtils.calculateHash(certDscEu.getSubjectX500Principal().getEncoded()) + )); + + // Add Upload cert which should not be added to did + signerInformationRepository.save(new SignerInformationEntity( + null, + certUploadDeKid, + ZonedDateTime.now(), + Base64.getEncoder().encodeToString(certUploadDe.getEncoded()), + "DE", + "DCC", + "UPLOAD", + certificateUtils.calculateHash(certUploadDe.getSubjectX500Principal().getEncoded()) + )); + + trustedIssuerRepository.save(trustedIssuerTestHelper.createTrustedIssuer("DE")); + trustedIssuerRepository.save(trustedIssuerTestHelper.createTrustedIssuer("EU")); + trustedIssuerRepository.save(trustedIssuerTestHelper.createTrustedIssuer("XY")); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void testTrustList(boolean isEcAlgorithm) throws Exception { + + if (isEcAlgorithm) { + testData(CertificateTestUtils.SignerType.EC); + } else { + testData(CertificateTestUtils.SignerType.RSA); + } + ArgumentCaptor uploadArgumentCaptor = ArgumentCaptor.forClass(byte[].class); + doNothing().when(didUploaderMock).uploadDid(anyString(), uploadArgumentCaptor.capture()); + + didTrustListService.job(); + + Assertions.assertEquals(48, uploadArgumentCaptor.getAllValues().size()); + + int expectedNullDid = 12; + + for (byte[] uploadedDid : uploadArgumentCaptor.getAllValues()) { + + if (uploadedDid == null) { + expectedNullDid--; + + Assertions.assertTrue(expectedNullDid >= 0, "DID Collection contains more empty documents than expected. (" + expectedNullDid * -1 + " too much)"); + continue; + } + + SignedDidTrustList parsed = objectMapper.readValue(uploadedDid, SignedDidTrustList.class); + + checkJsonDocument(parsed); + + switch (parsed.getId()) { + case "did:web:abc:trustlist": + Assertions.assertEquals("did:web:abc:trustlist", parsed.getController()); + Assertions.assertEquals(7, parsed.getVerificationMethod().size()); + + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist#" + URLEncoder.encode(certDscDeKid, StandardCharsets.UTF_8)), + certDscDeKid, certDscDe, certCscaDe, "did:web:abc:trustlist"); + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist#" + URLEncoder.encode(certCscaDeKid, StandardCharsets.UTF_8)), + certCscaDeKid, certCscaDe, null, "did:web:abc:trustlist"); + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist#" + URLEncoder.encode(certDscEuKid, StandardCharsets.UTF_8)), + certDscEuKid, certDscEu, certCscaEu, "did:web:abc:trustlist"); + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist#" + URLEncoder.encode(certCscaEuKid, StandardCharsets.UTF_8)), + certCscaEuKid, certCscaEu, null, "did:web:abc:trustlist"); + break; + case "did:web:abc:trustlist:DCC:XEU:DSC": + Assertions.assertEquals("did:web:abc:trustlist:DCC:XEU:DSC", parsed.getController()); + Assertions.assertEquals(4, parsed.getVerificationMethod().size()); + + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist:DCC:XEU:DSC#" + URLEncoder.encode(certDscEuKid, StandardCharsets.UTF_8)), + certDscEuKid, certDscEu, certCscaEu, "did:web:abc:trustlist:DCC:XEU:DSC"); + break; + case "did:web:abc:trustlist:DCC": + Assertions.assertEquals("did:web:abc:trustlist:DCC", parsed.getController()); + Assertions.assertEquals(7, parsed.getVerificationMethod().size()); + + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist:DCC#" + URLEncoder.encode(certDscDeKid, StandardCharsets.UTF_8)), + certDscDeKid, certDscDe, certCscaDe, "did:web:abc:trustlist:DCC"); + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist:DCC#" + URLEncoder.encode(certDscEuKid, StandardCharsets.UTF_8)), + certDscEuKid, certDscEu, certCscaEu, "did:web:abc:trustlist:DCC"); + break; + case "did:web:abc:trustlist:-:XEU": + Assertions.assertEquals("did:web:abc:trustlist:-:XEU", parsed.getController()); + Assertions.assertEquals(5, parsed.getVerificationMethod().size()); + + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist:-:XEU#" + URLEncoder.encode(certCscaEuKid, StandardCharsets.UTF_8)), + certCscaEuKid, certCscaEu, null, "did:web:abc:trustlist:-:XEU"); + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist:-:XEU#" + URLEncoder.encode(certDscEuKid, StandardCharsets.UTF_8)), + certDscEuKid, certDscEu, certCscaEu, "did:web:abc:trustlist:-:XEU"); + break; + case "did:web:abc:trustlist:-:DEU": + Assertions.assertEquals("did:web:abc:trustlist:-:DEU", parsed.getController()); + Assertions.assertEquals(5, parsed.getVerificationMethod().size()); + + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist:-:DEU#" + URLEncoder.encode(certDscDeKid, StandardCharsets.UTF_8)), + certDscDeKid, certDscDe, certCscaDe, "did:web:abc:trustlist:-:DEU"); + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist:-:DEU#" + URLEncoder.encode(certCscaDeKid, StandardCharsets.UTF_8)), + certCscaDeKid, certCscaDe, null, "did:web:abc:trustlist:-:DEU"); + break; + case "did:web:abc:trustlist:DCC:XEU:CSA": + Assertions.assertEquals("did:web:abc:trustlist:DCC:XEU:CSA", parsed.getController()); + Assertions.assertEquals(4, parsed.getVerificationMethod().size()); + + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist:DCC:XEU:CSA#" + URLEncoder.encode(certCscaEuKid, StandardCharsets.UTF_8)), + certCscaEuKid, certCscaEu, null, "did:web:abc:trustlist:DCC:XEU:CSA"); + break; + case "did:web:abc:trustlist:DCC:DEU:DSC": + Assertions.assertEquals("did:web:abc:trustlist:DCC:DEU:DSC", parsed.getController()); + Assertions.assertEquals(4, parsed.getVerificationMethod().size()); + + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist:DCC:DEU:DSC#" + URLEncoder.encode(certDscDeKid, StandardCharsets.UTF_8)), + certDscDeKid, certDscDe, certCscaDe, "did:web:abc:trustlist:DCC:DEU:DSC"); + break; + case "did:web:abc:trustlist:DCC:DEU:CSA": + Assertions.assertEquals("did:web:abc:trustlist:DCC:DEU:CSA", parsed.getController()); + Assertions.assertEquals(4, parsed.getVerificationMethod().size()); + + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist:DCC:DEU:CSA#" + URLEncoder.encode(certCscaDeKid, StandardCharsets.UTF_8)), + certCscaDeKid, certCscaDe, null, "did:web:abc:trustlist:DCC:DEU:CSA"); + break; + case "did:web:abc:trustlist:DCC:DEU": + Assertions.assertEquals("did:web:abc:trustlist:DCC:DEU", parsed.getController()); + Assertions.assertEquals(5, parsed.getVerificationMethod().size()); + + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist:DCC:DEU#" + URLEncoder.encode(certDscDeKid, StandardCharsets.UTF_8)), + certDscDeKid, certDscDe, certCscaDe, "did:web:abc:trustlist:DCC:DEU"); + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist:DCC:DEU#" + URLEncoder.encode(certCscaDeKid, StandardCharsets.UTF_8)), + certCscaDeKid, certCscaDe, null, "did:web:abc:trustlist:DCC:DEU"); + break; + case "did:web:abc:trustlist:DCC:XEU": + Assertions.assertEquals("did:web:abc:trustlist:DCC:XEU", parsed.getController()); + Assertions.assertEquals(5, parsed.getVerificationMethod().size()); + + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist:DCC:XEU#" + URLEncoder.encode(certDscEuKid, StandardCharsets.UTF_8)), + certDscEuKid, certDscEu, certCscaEu, "did:web:abc:trustlist:DCC:XEU"); + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist:DCC:XEU#" + URLEncoder.encode(certCscaEuKid, StandardCharsets.UTF_8)), + certCscaEuKid, certCscaEu, null, "did:web:abc:trustlist:DCC:XEU"); + break; + case "did:web:abc:trustlist:-:XEU:DSC": + Assertions.assertEquals("did:web:abc:trustlist:-:XEU:DSC", parsed.getController()); + Assertions.assertEquals(4, parsed.getVerificationMethod().size()); + + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist:-:XEU:DSC#" + URLEncoder.encode(certDscEuKid, StandardCharsets.UTF_8)), + certDscEuKid, certDscEu, certCscaEu, "did:web:abc:trustlist:-:XEU:DSC"); + break; + case "did:web:abc:trustlist:-:DEU:DSC": + Assertions.assertEquals("did:web:abc:trustlist:-:DEU:DSC", parsed.getController()); + Assertions.assertEquals(4, parsed.getVerificationMethod().size()); + + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist:-:DEU:DSC#" + URLEncoder.encode(certDscDeKid, StandardCharsets.UTF_8)), + certDscDeKid, certDscDe, certCscaDe, "did:web:abc:trustlist:-:DEU:DSC"); + break; + case "did:web:abc:trustlist:-:DEU:CSA": + Assertions.assertEquals("did:web:abc:trustlist:-:DEU:CSA", parsed.getController()); + Assertions.assertEquals(4, parsed.getVerificationMethod().size()); + + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist:-:DEU:CSA#" + URLEncoder.encode(certCscaDeKid, StandardCharsets.UTF_8)), + certCscaDeKid, certCscaDe, null, "did:web:abc:trustlist:-:DEU:CSA"); + break; + case "did:web:abc:trustlist:-:-:CSA": + Assertions.assertEquals("did:web:abc:trustlist:-:-:CSA", parsed.getController()); + Assertions.assertEquals(5, parsed.getVerificationMethod().size()); + + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist:-:-:CSA#" + URLEncoder.encode(certCscaEuKid, StandardCharsets.UTF_8)), + certCscaEuKid, certCscaEu, null, "did:web:abc:trustlist:-:-:CSA"); + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist:-:-:CSA#" + URLEncoder.encode(certCscaDeKid, StandardCharsets.UTF_8)), + certCscaDeKid, certCscaDe, null, "did:web:abc:trustlist:-:-:CSA"); + break; + case "did:web:abc:trustlist:-:-:DSC": + Assertions.assertEquals("did:web:abc:trustlist:-:-:DSC", parsed.getController()); + Assertions.assertEquals(5, parsed.getVerificationMethod().size()); + + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist:-:-:DSC#" + URLEncoder.encode(certDscEuKid, StandardCharsets.UTF_8)), + certDscEuKid, certDscEu, certCscaEu, "did:web:abc:trustlist:-:-:DSC"); + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist:-:-:DSC#" + URLEncoder.encode(certDscDeKid, StandardCharsets.UTF_8)), + certDscDeKid, certDscDe, certCscaDe, "did:web:abc:trustlist:-:-:DSC"); + break; + case "did:web:abc:trustlist:-:XEU:CSA": + Assertions.assertEquals("did:web:abc:trustlist:-:XEU:CSA", parsed.getController()); + Assertions.assertEquals(4, parsed.getVerificationMethod().size()); + + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist:-:XEU:CSA#" + URLEncoder.encode(certCscaEuKid, StandardCharsets.UTF_8)), + certCscaEuKid, certCscaEu, null, "did:web:abc:trustlist:-:XEU:CSA"); + break; + case "did:web:abc:trustlist:DCC:-:DSC": + Assertions.assertEquals("did:web:abc:trustlist:DCC:-:DSC", parsed.getController()); + Assertions.assertEquals(5, parsed.getVerificationMethod().size()); + + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist:DCC:-:DSC#" + URLEncoder.encode(certDscDeKid, StandardCharsets.UTF_8)), + certDscDeKid, certDscDe, certCscaDe, "did:web:abc:trustlist:DCC:-:DSC"); + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist:DCC:-:DSC#" + URLEncoder.encode(certDscEuKid, StandardCharsets.UTF_8)), + certDscEuKid, certDscEu, certCscaEu, "did:web:abc:trustlist:DCC:-:DSC"); + break; + case "did:web:abc:trustlist:DCC:-:CSA": + Assertions.assertEquals("did:web:abc:trustlist:DCC:-:CSA", parsed.getController()); + Assertions.assertEquals(5, parsed.getVerificationMethod().size()); + + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist:DCC:-:CSA#" + URLEncoder.encode(certCscaDeKid, StandardCharsets.UTF_8)), + certCscaDeKid, certCscaDe, null, "did:web:abc:trustlist:DCC:-:CSA"); + assertVerificationMethod(getVerificationMethodByKid(parsed.getVerificationMethod(),"did:web:abc:trustlist:DCC:-:CSA#" + URLEncoder.encode(certCscaEuKid, StandardCharsets.UTF_8)), + certCscaEuKid, certCscaEu, null, "did:web:abc:trustlist:DCC:-:CSA"); + break; + default: + if (!parsed.getId().contains("trustlist-ref")) { + Assertions.fail("Unexpected Document in DID Collection! (" + parsed.getId() + ")"); + } + } + } + } + + private void checkJsonDocument(SignedDidTrustList parsed) throws JsonProcessingException { + + Assertions.assertTrue(parsed.getVerificationMethod().contains("did:trusted:DE:issuer")); + Assertions.assertTrue(parsed.getVerificationMethod().contains("did:trusted:EU:issuer")); + Assertions.assertTrue(parsed.getVerificationMethod().contains("did:trusted:XY:issuer")); + + Assertions.assertEquals(2, parsed.getContext().size()); + Assertions.assertEquals("JsonWebSignature2020", parsed.getProof().getType()); + Assertions.assertTrue( + Instant.now().toEpochMilli() - parsed.getProof().getCreated().toInstant().toEpochMilli() < 10000); + Assertions.assertEquals("d0m4in", parsed.getProof().getDomain()); + Assertions.assertEquals(32, parsed.getProof().getNonce().length()); + Assertions.assertEquals("assertionMethod", parsed.getProof().getProofPurpose()); + Assertions.assertEquals("did:web:dummy.net", parsed.getProof().getVerificationMethod()); + Assertions.assertNotNull(parsed.getProof().getJws()); + Assertions.assertNotEquals("", parsed.getProof().getJws()); + + //JSON should start with "@context" due to https://www.w3.org/TR/json-ld11-streaming/#key-ordering-required + String json = JsonLDObject.fromJson(objectMapper.writeValueAsString(parsed)).toJson(); + String first10Characters = json.substring(0, Math.min(10, json.length())); + Assertions.assertTrue(first10Characters.contains("@context")); + } + + + private Object getVerificationMethodByKid(List verificationMethods, String kid) { + + return verificationMethods.stream() + .filter(entry -> entry instanceof LinkedHashMap) + .map(entry -> (LinkedHashMap) entry) + .filter(entry -> entry.get("id").equals(kid)) + .findFirst() + .orElseGet( + () -> Assertions.fail("Could not find VerificationMethod with KID " + kid)); + } + + private void assertVerificationMethod(Object in, String kid, X509Certificate dsc, X509Certificate csca, + String parentDidId) + throws CertificateEncodingException { + + LinkedHashMap jsonNode = (LinkedHashMap) in; + Assertions.assertEquals("JsonWebKey2020", jsonNode.get("type")); + Assertions.assertEquals(parentDidId, jsonNode.get("controller")); + Assertions.assertEquals(parentDidId + "#" + URLEncoder.encode(kid, StandardCharsets.UTF_8), + jsonNode.get("id")); + + LinkedHashMap publicKeyJwk = (LinkedHashMap) jsonNode.get("publicKeyJwk"); + + if (dsc.getPublicKey().getAlgorithm().equals(CertificateTestUtils.SignerType.EC.getSigningAlgorithm())) { + Assertions.assertEquals(((ECPublicKey) dsc.getPublicKey()).getW().getAffineX(), + new BigInteger(Base64.getDecoder().decode(publicKeyJwk.get("x").toString()))); + Assertions.assertEquals(((ECPublicKey) dsc.getPublicKey()).getW().getAffineY(), + new BigInteger(Base64.getDecoder().decode(publicKeyJwk.get("y").toString()))); + Assertions.assertEquals(CertificateTestUtils.SignerType.EC.getSigningAlgorithm(), + publicKeyJwk.get("kty").toString()); + Assertions.assertEquals("P-256", publicKeyJwk.get("crv").toString()); + } else { + Assertions.assertEquals(((RSAPublicKey) dsc.getPublicKey()).getPublicExponent(), + new BigInteger(Base64.getDecoder().decode(publicKeyJwk.get("e").toString()))); + Assertions.assertEquals(((RSAPublicKey) dsc.getPublicKey()).getModulus(), + new BigInteger(Base64.getDecoder().decode(publicKeyJwk.get("n").toString()))); + Assertions.assertEquals(CertificateTestUtils.SignerType.RSA.getSigningAlgorithm(), + publicKeyJwk.get("kty").toString()); + } + ArrayList x5c = ((ArrayList) publicKeyJwk.get("x5c")); + Assertions.assertEquals(Base64.getEncoder().encodeToString(dsc.getEncoded()), x5c.get(0)); + if (csca != null) { + Assertions.assertEquals(Base64.getEncoder().encodeToString(csca.getEncoded()), x5c.get(1)); + } + } + + @Getter + @Setter + public static class SignedDidTrustList extends DidTrustList { + + private LDProof proof; + + @Data + private static class LDProof { + + private String type; + + private ZonedDateTime created; + + private String verificationMethod; + + private String proofPurpose; + + private String jws; + + private String domain; + + private String nonce; + + } + } +} diff --git a/src/test/java/tng/trustnetwork/keydistribution/service/InfoServiceTest.java b/src/test/java/tng/trustnetwork/keydistribution/service/InfoServiceTest.java deleted file mode 100644 index 77eabde..0000000 --- a/src/test/java/tng/trustnetwork/keydistribution/service/InfoServiceTest.java +++ /dev/null @@ -1,75 +0,0 @@ -/*- - * ---license-start - * WorldHealthOrganization / tng-key-distribution - * --- - * Copyright (C) 2021 T-Systems International GmbH and all other contributors - * --- - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://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. - * ---license-end - */ - -package tng.trustnetwork.keydistribution.service; - -import java.util.List; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import eu.europa.ec.dgc.gateway.connector.DgcGatewayDownloadConnector; -import tng.trustnetwork.keydistribution.entity.InfoEntity; -import tng.trustnetwork.keydistribution.repository.InfoRepository; - -@SpringBootTest -class InfoServiceTest { - - @MockBean - DgcGatewayDownloadConnector dgcGatewayDownloadConnector; - - @Autowired - InfoRepository infoRepository; - - @Autowired - InfoService infoService; - - - - @BeforeEach - void clearRepositoryData() { - infoRepository.deleteAll(); - } - - - @Test - void saveInfo() throws Exception { - - infoService.setValueForKey("TestKey", "TestValue"); - - List entities = infoRepository.findAll(); - - Assertions.assertEquals(1, entities.size()); - Assertions.assertEquals("TestKey", entities.get(0).getIdentifierKey()); - Assertions.assertEquals("TestValue", entities.get(0).getValue()); - - } - - @Test - void getInfo() throws Exception { - - InfoEntity entity = new InfoEntity("TestKey", "TestValue"); - infoRepository.save(entity); - - Assertions.assertEquals("TestValue", infoService.getValueForKey("TestKey")); - } -} diff --git a/src/test/java/tng/trustnetwork/keydistribution/service/SignerCertificateDownloadServiceImplTest.java b/src/test/java/tng/trustnetwork/keydistribution/service/SignerCertificateDownloadServiceTest.java similarity index 53% rename from src/test/java/tng/trustnetwork/keydistribution/service/SignerCertificateDownloadServiceImplTest.java rename to src/test/java/tng/trustnetwork/keydistribution/service/SignerCertificateDownloadServiceTest.java index 5c02ebf..0828d20 100644 --- a/src/test/java/tng/trustnetwork/keydistribution/service/SignerCertificateDownloadServiceImplTest.java +++ b/src/test/java/tng/trustnetwork/keydistribution/service/SignerCertificateDownloadServiceTest.java @@ -1,27 +1,48 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + package tng.trustnetwork.keydistribution.service; import eu.europa.ec.dgc.gateway.connector.DgcGatewayDownloadConnector; import eu.europa.ec.dgc.gateway.connector.model.TrustListItem; -import tng.trustnetwork.keydistribution.entity.SignerInformationEntity; -import tng.trustnetwork.keydistribution.repository.SignerInformationRepository; -import tng.trustnetwork.keydistribution.testdata.SignerInformationTestHelper; import java.util.ArrayList; import java.util.List; +import eu.europa.ec.dgc.gateway.connector.model.TrustedCertificateTrustListItem; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; +import tng.trustnetwork.keydistribution.entity.SignerInformationEntity; +import tng.trustnetwork.keydistribution.repository.SignerInformationRepository; +import tng.trustnetwork.keydistribution.testdata.SignerInformationTestHelper; @SpringBootTest -class SignerCertificateDownloadServiceImplTest { +class SignerCertificateDownloadServiceTest { @MockBean DgcGatewayDownloadConnector dgcGatewayDownloadConnector; @Autowired - SignerCertificateDownloadServiceImpl signerCertificateDownloadService; + SignerCertificateDownloadService signerCertificateDownloadService; @Autowired SignerInformationRepository signerInformationRepository; @@ -31,20 +52,20 @@ class SignerCertificateDownloadServiceImplTest { @Test void downloadEmptyCertificatesList() { - ArrayList trustList = new ArrayList<>(); - Mockito.when(dgcGatewayDownloadConnector.getTrustedCertificates()).thenReturn(trustList); + ArrayList trustList = new ArrayList<>(); + Mockito.when(dgcGatewayDownloadConnector.getDdccTrustedCertificates()).thenReturn(trustList); signerCertificateDownloadService.downloadCertificates(); - List repositoryItems = signerInformationRepository.findAllByDeletedOrderByIdAsc(false); - Assertions.assertEquals(0, repositoryItems.size()); + List repositoryItems = signerInformationRepository.findAll(); + Assertions.assertTrue(repositoryItems.isEmpty()); } @Test void downloadCertificates() { - ArrayList trustList = new ArrayList<>(); - trustList.add(signerInformationTestHelper.createTrustListItem(SignerInformationTestHelper.TEST_CERT_1_STR)); - Mockito.when(dgcGatewayDownloadConnector.getTrustedCertificates()).thenReturn(trustList); + ArrayList trustList = new ArrayList<>(); + trustList.add(signerInformationTestHelper.createTrustedCertificateTrustListItem(SignerInformationTestHelper.TEST_CERT_1_STR)); + Mockito.when(dgcGatewayDownloadConnector.getDdccTrustedCertificates()).thenReturn(trustList); signerCertificateDownloadService.downloadCertificates(); diff --git a/src/test/java/tng/trustnetwork/keydistribution/service/SignerInformationServiceTest.java b/src/test/java/tng/trustnetwork/keydistribution/service/SignerInformationServiceTest.java index 1ea033a..d02052f 100644 --- a/src/test/java/tng/trustnetwork/keydistribution/service/SignerInformationServiceTest.java +++ b/src/test/java/tng/trustnetwork/keydistribution/service/SignerInformationServiceTest.java @@ -2,7 +2,7 @@ * ---license-start * WorldHealthOrganization / tng-key-distribution * --- - * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors * --- * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,30 +20,13 @@ package tng.trustnetwork.keydistribution.service; -import com.google.code.beanmatchers.BeanMatchers; import eu.europa.ec.dgc.gateway.connector.DgcGatewayDownloadConnector; -import eu.europa.ec.dgc.gateway.connector.model.TrustListItem; -import tng.trustnetwork.keydistribution.dto.TrustedIssuerDto; -import tng.trustnetwork.keydistribution.entity.SignerInformationEntity; -import tng.trustnetwork.keydistribution.repository.SignerInformationRepository; -import tng.trustnetwork.keydistribution.restapi.dto.DeltaListDto; -import tng.trustnetwork.keydistribution.testdata.SignerInformationTestHelper; -import java.time.ZonedDateTime; -import java.util.ArrayList; -import java.util.List; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; -import static com.google.code.beanmatchers.BeanMatchers.hasValidBeanConstructor; -import static com.google.code.beanmatchers.BeanMatchers.hasValidBeanEquals; -import static com.google.code.beanmatchers.BeanMatchers.hasValidBeanHashCode; -import static com.google.code.beanmatchers.BeanMatchers.hasValidBeanToString; -import static com.google.code.beanmatchers.BeanMatchers.hasValidGettersAndSetters; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.allOf; +import tng.trustnetwork.keydistribution.repository.SignerInformationRepository; +import tng.trustnetwork.keydistribution.testdata.SignerInformationTestHelper; @SpringBootTest class SignerInformationServiceTest { @@ -65,165 +48,4 @@ void clearRepositoryData() { signerInformationRepository.deleteAll(); } - - @Test - void updateEmptyRepositoryWithEmptyCertList() { - ArrayList trustList = new ArrayList<>(); - - signerInformationService.updateTrustedCertsList(trustList); - - List repositoryItems = signerInformationRepository.findAll(); - - Assertions.assertEquals(0, repositoryItems.size()); - - } - - @Test - void updateEmptyRepositoryWithOneCert() { - ArrayList trustList = new ArrayList<>(); - trustList.add(signerInformationTestHelper.createTrustListItem(SignerInformationTestHelper.TEST_CERT_1_STR)); - - signerInformationService.updateTrustedCertsList(trustList); - - List repositoryItems = signerInformationRepository.findAll(); - - Assertions.assertEquals(1, repositoryItems.size()); - - SignerInformationEntity repositoryItem = repositoryItems.get(0); - - Assertions.assertEquals(SignerInformationTestHelper.TEST_CERT_1_KID, repositoryItem.getKid()); - Assertions.assertEquals(SignerInformationTestHelper.TEST_CERT_1_STR, repositoryItem.getRawData()); - - } - - @Test - void updateEmptyRepositoryWithCerts() { - ArrayList trustList = new ArrayList<>(); - trustList.add(signerInformationTestHelper.createTrustListItem(SignerInformationTestHelper.TEST_CERT_1_STR)); - trustList.add(signerInformationTestHelper.createTrustListItem(SignerInformationTestHelper.TEST_CERT_2_STR)); - trustList.add(signerInformationTestHelper.createTrustListItem(SignerInformationTestHelper.TEST_CERT_3_STR)); - - signerInformationService.updateTrustedCertsList(trustList); - - List repositoryItems = signerInformationRepository.findAll(); - - Assertions.assertEquals(3, repositoryItems.size()); - - SignerInformationEntity repositoryItem = repositoryItems.get(0); - Assertions.assertEquals(SignerInformationTestHelper.TEST_CERT_1_KID, repositoryItem.getKid()); - Assertions.assertEquals(SignerInformationTestHelper.TEST_CERT_1_STR, repositoryItem.getRawData()); - repositoryItem = repositoryItems.get(1); - Assertions.assertEquals(SignerInformationTestHelper.TEST_CERT_2_KID, repositoryItem.getKid()); - Assertions.assertEquals(SignerInformationTestHelper.TEST_CERT_2_STR, repositoryItem.getRawData()); - repositoryItem = repositoryItems.get(2); - Assertions.assertEquals(SignerInformationTestHelper.TEST_CERT_3_KID, repositoryItem.getKid()); - Assertions.assertEquals(SignerInformationTestHelper.TEST_CERT_3_STR, repositoryItem.getRawData()); - - } - - @Test - void updateEmptyRepositoryWithSameCertsTwice() { - ArrayList trustList = new ArrayList<>(); - trustList.add(signerInformationTestHelper.createTrustListItem(SignerInformationTestHelper.TEST_CERT_1_STR)); - trustList.add(signerInformationTestHelper.createTrustListItem(SignerInformationTestHelper.TEST_CERT_2_STR)); - trustList.add(signerInformationTestHelper.createTrustListItem(SignerInformationTestHelper.TEST_CERT_3_STR)); - - signerInformationService.updateTrustedCertsList(trustList); - - List repositoryItems = signerInformationRepository.findAll(); - - Assertions.assertEquals(3, repositoryItems.size()); - - SignerInformationEntity repositoryItem0 = repositoryItems.get(0); - SignerInformationEntity repositoryItem1 = repositoryItems.get(1); - SignerInformationEntity repositoryItem2 = repositoryItems.get(2); - - signerInformationService.updateTrustedCertsList(trustList); - - repositoryItems = signerInformationRepository.findAll(); - - Assertions.assertEquals(3, repositoryItems.size()); - - SignerInformationEntity repositoryItem = repositoryItems.get(0); - Assertions.assertEquals(repositoryItem0, repositoryItem); - repositoryItem = repositoryItems.get(1); - Assertions.assertEquals(repositoryItem1, repositoryItem); - repositoryItem = repositoryItems.get(2); - Assertions.assertEquals(repositoryItem2, repositoryItem); - } - - @Test - void updateRepositoryWithOneNewCertAndOneRevoked() { - signerInformationTestHelper.insertCertString(SignerInformationTestHelper.TEST_CERT_1_STR); - signerInformationTestHelper.insertCertString(SignerInformationTestHelper.TEST_CERT_2_STR); - - - ArrayList trustList = new ArrayList<>(); - trustList.add(signerInformationTestHelper.createTrustListItem(SignerInformationTestHelper.TEST_CERT_2_STR)); - trustList.add(signerInformationTestHelper.createTrustListItem(SignerInformationTestHelper.TEST_CERT_3_STR)); - - signerInformationService.updateTrustedCertsList(trustList); - - List repositoryItems = signerInformationRepository.findAllByDeletedOrderByIdAsc(false); - - Assertions.assertEquals(2, repositoryItems.size()); - - SignerInformationEntity repositoryItem0 = repositoryItems.get(0); - SignerInformationEntity repositoryItem1 = repositoryItems.get(1); - - Assertions.assertEquals(SignerInformationTestHelper.TEST_CERT_2_KID, repositoryItem0.getKid()); - Assertions.assertEquals(SignerInformationTestHelper.TEST_CERT_2_STR, repositoryItem0.getRawData()); - Assertions.assertEquals(SignerInformationTestHelper.TEST_CERT_3_KID, repositoryItem1.getKid()); - Assertions.assertEquals(SignerInformationTestHelper.TEST_CERT_3_STR, repositoryItem1.getRawData()); - - } - - @Test - void updateRepositoryWithEmptyCertList() { - signerInformationTestHelper.insertCertString(SignerInformationTestHelper.TEST_CERT_1_STR); - signerInformationTestHelper.insertCertString(SignerInformationTestHelper.TEST_CERT_2_STR); - signerInformationTestHelper.insertCertString(SignerInformationTestHelper.TEST_CERT_3_STR); - - ArrayList trustList = new ArrayList<>(); - - signerInformationService.updateTrustedCertsList(trustList); - - List repositoryItems = signerInformationRepository.findAllByDeletedOrderByIdAsc(false); - - Assertions.assertEquals(0, repositoryItems.size()); - - } - - @Test - void dataTypeTests() { - - assertThat(DeltaListDto.class, allOf(hasValidBeanConstructor(), hasValidBeanEquals(), - hasValidGettersAndSetters(), hasValidBeanHashCode(), hasValidBeanToString())); - List updated = new ArrayList<>(); - updated.add("updated"); - List deleted = new ArrayList<>(); - deleted.add("deleted"); - DeltaListDto deltaListDto = new DeltaListDto(updated, deleted); - Assertions.assertEquals("updated",deltaListDto.getUpdated().get(0)); - Assertions.assertEquals("deleted",deltaListDto.getDeleted().get(0)); - deltaListDto.setDeleted(updated); - deltaListDto.setUpdated(deleted); - Assertions.assertEquals("deleted",deltaListDto.getUpdated().get(0)); - Assertions.assertEquals("updated",deltaListDto.getDeleted().get(0)); - - - TrustedIssuerDto issuer = new - TrustedIssuerDto("url", TrustedIssuerDto.UrlTypeDto.HTTP,"DE","TP1","PK1", - "JWKM","signature1", ZonedDateTime.now(),"name"); - Assertions.assertEquals("url",issuer.getUrl()); - issuer.setUrl("newUrl"); - Assertions.assertEquals("newUrl",issuer.getUrl()); - - BeanMatchers.registerValueGenerator(ZonedDateTime::now, ZonedDateTime.class); - - assertThat(TrustedIssuerDto.class, allOf(hasValidBeanConstructor(), hasValidBeanEquals(), - hasValidGettersAndSetters(), hasValidBeanHashCode(), hasValidBeanToString())); - - } - } diff --git a/src/test/java/tng/trustnetwork/keydistribution/service/TrustedIssuerDownloadServiceImplTest.java b/src/test/java/tng/trustnetwork/keydistribution/service/TrustedIssuerDownloadServiceTest.java similarity index 75% rename from src/test/java/tng/trustnetwork/keydistribution/service/TrustedIssuerDownloadServiceImplTest.java rename to src/test/java/tng/trustnetwork/keydistribution/service/TrustedIssuerDownloadServiceTest.java index cc1c449..ca412b7 100644 --- a/src/test/java/tng/trustnetwork/keydistribution/service/TrustedIssuerDownloadServiceImplTest.java +++ b/src/test/java/tng/trustnetwork/keydistribution/service/TrustedIssuerDownloadServiceTest.java @@ -1,24 +1,45 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + package tng.trustnetwork.keydistribution.service; +import eu.europa.ec.dgc.gateway.connector.DgcGatewayDownloadConnector; +import eu.europa.ec.dgc.gateway.connector.DgcGatewayTrustedIssuerDownloadConnector; +import eu.europa.ec.dgc.gateway.connector.model.TrustedIssuer; import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.TestPropertySource; -import eu.europa.ec.dgc.gateway.connector.DgcGatewayDownloadConnector; -import eu.europa.ec.dgc.gateway.connector.DgcGatewayTrustedIssuerDownloadConnector; -import eu.europa.ec.dgc.gateway.connector.model.TrustedIssuer; import tng.trustnetwork.keydistribution.entity.TrustedIssuerEntity; import tng.trustnetwork.keydistribution.repository.TrustedIssuerRepository; import tng.trustnetwork.keydistribution.testdata.TrustedIssuerTestHelper; @SpringBootTest @TestPropertySource(properties = {"dgc.trustedIssuerDownloader.enabled=true"}) -class TrustedIssuerDownloadServiceImplTest { +class TrustedIssuerDownloadServiceTest { @MockBean DgcGatewayDownloadConnector dgcGatewayDownloadConnectorMock; @@ -26,9 +47,8 @@ class TrustedIssuerDownloadServiceImplTest { @MockBean DgcGatewayTrustedIssuerDownloadConnector dgcGatewayDownloadConnector; - @Autowired - TrustedIssuerDownloadServiceImpl trustedIssuerDownloadService; + TrustedIssuerDownloadService trustedIssuerDownloadService; @Autowired TrustedIssuerRepository trustedIssuerRepository; @@ -36,6 +56,11 @@ class TrustedIssuerDownloadServiceImplTest { @Autowired TrustedIssuerTestHelper trustedIssuerTestHelper; + @BeforeEach + void cleanup() { + trustedIssuerRepository.deleteAll(); + } + @Test void downloadEmptyIssuerList() { ArrayList trustList = new ArrayList<>(); diff --git a/src/test/java/tng/trustnetwork/keydistribution/service/UniversalResolverServiceImplTest.java b/src/test/java/tng/trustnetwork/keydistribution/service/UniversalResolverServiceImplTest.java deleted file mode 100644 index 6218f91..0000000 --- a/src/test/java/tng/trustnetwork/keydistribution/service/UniversalResolverServiceImplTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package tng.trustnetwork.keydistribution.service; - -import eu.europa.ec.dgc.gateway.connector.DgcGatewayDownloadConnector; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import tng.trustnetwork.keydistribution.clients.UniversalResolverClient; -import tng.trustnetwork.keydistribution.model.DidDocument; -import tng.trustnetwork.keydistribution.model.DidDocumentUnmarshal; - -@SpringBootTest -class UniversalResolverServiceImplTest { - - @MockBean - private UniversalResolverClient universalResolverClient; - - @Autowired - private UniversalResolverService universalResolverService; - - @MockBean - DgcGatewayDownloadConnector dgcGatewayDownloadConnector; - - @Autowired - SignerCertificateDownloadServiceImpl signerCertificateDownloadService; - - @Test - void resolveDIDDocumentNotNull() { - - DidDocument didDocument = new DidDocument(); - Mockito.when(universalResolverClient.getDidDocument("did:web:tng-cdn-dev.who.int:trustlist")) - .thenReturn(didDocument); - DidDocumentUnmarshal didDocumentUnmarshal = - universalResolverService.universalResolverApiCall("did:web:tng-cdn-dev.who.int:trustlist"); - Assertions.assertNotNull(didDocumentUnmarshal); - - } - - @Test - void resolveDIDDocumentNull() { - - DidDocument didDocument = new DidDocument(); - Mockito.when(universalResolverClient.getDidDocument("test")).thenReturn(null); - DidDocumentUnmarshal didDocumentUnmarshal = universalResolverService.universalResolverApiCall("test"); - Assertions.assertNull(didDocumentUnmarshal); - - } - -} diff --git a/src/test/java/tng/trustnetwork/keydistribution/service/UniversalResolverServiceTest.java b/src/test/java/tng/trustnetwork/keydistribution/service/UniversalResolverServiceTest.java new file mode 100644 index 0000000..f177628 --- /dev/null +++ b/src/test/java/tng/trustnetwork/keydistribution/service/UniversalResolverServiceTest.java @@ -0,0 +1,121 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + +package tng.trustnetwork.keydistribution.service; + +import static org.mockito.Mockito.when; + +import com.fasterxml.jackson.core.JsonProcessingException; +import eu.europa.ec.dgc.gateway.connector.DgcGatewayDownloadConnector; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import tng.trustnetwork.keydistribution.clients.UniversalResolverClient; +import tng.trustnetwork.keydistribution.model.EcPublicKeyJwk; +import tng.trustnetwork.keydistribution.model.JwkVerificationMethod; + +@SpringBootTest +class UniversalResolverServiceTest { + + @MockBean + DgcGatewayDownloadConnector dgcGatewayDownloadConnector; + + @MockBean + UniversalResolverClient universalResolverClientMock; + + @Autowired + UniversalResolverService universalResolverService; + + private static final String testDidId = "did:web:did.actor:mike"; + + private static final String testDid = """ + { + "@context": [ + "https://www.w3.org/ns/did/v1", + { + "@base": "did:web:did.actor:mike", + "rating": "https://schema.org/Rating", + "publicAccess": "https://schema.org/publicAccess", + "additionalType": "https://schema.org/additionalType" + } + ], + "id": "did:web:did.actor:mike", + "rating": 4.5, + "publicAccess": true, + "additionalType": null, + "verificationMethod": [ + { + "id": "#g1", + "controller": "did:web:did.actor:mike", + "type": "JsonWebKey2020", + "publicKeyJwk": { + "kty": "EC", + "crv": "P-256", + "x": "xValue" + } + } + ], + "authentication": [ + "did:web:did.actor:mike#g1" + ], + "assertionMethod": [ + "did:web:did.actor:mike#g1" + ] + } + """; + + @Test + void itShouldReturnParsedDidDocument() throws JsonProcessingException { + + when(universalResolverClientMock.getDidDocument(testDidId)) + .thenReturn(testDid); + + UniversalResolverService.DidDocumentWithRawResponse response = + universalResolverService.universalResolverApiCall(testDidId); + + Assertions.assertEquals(testDid, response.raw()); + Assertions.assertEquals(testDidId, response.didDocument().getId()); + Assertions.assertEquals(1, response.didDocument().getVerificationMethod().size()); + Assertions.assertEquals(testDidId, response.didDocument().getVerificationMethod().get(0).getObjectValue().getController()); + Assertions.assertEquals("#g1", response.didDocument().getVerificationMethod().get(0).getObjectValue().getId()); + + Assertions.assertInstanceOf(JwkVerificationMethod.class, response.didDocument().getVerificationMethod().get(0).getObjectValue()); + JwkVerificationMethod jwkVerificationMethod = + (JwkVerificationMethod) response.didDocument().getVerificationMethod().get(0).getObjectValue(); + + Assertions.assertInstanceOf(EcPublicKeyJwk.class, jwkVerificationMethod.getPublicKeyJwk()); + EcPublicKeyJwk ecPublicKeyJwk = (EcPublicKeyJwk) jwkVerificationMethod.getPublicKeyJwk(); + + Assertions.assertEquals(EcPublicKeyJwk.Curve.P256 , ecPublicKeyJwk.getCrv()); + Assertions.assertEquals("xValue", ecPublicKeyJwk.getXvalue()); + } + + @Test + void itShouldThrowAnExceptionIfJsonIsInvalid() { + + when(universalResolverClientMock.getDidDocument(testDidId)) + .thenReturn("noValidJson"); + + Assertions.assertThrows(JsonProcessingException.class, () -> + universalResolverService.universalResolverApiCall(testDidId)); + } +} diff --git a/src/test/java/tng/trustnetwork/keydistribution/testdata/CertificateTestUtils.java b/src/test/java/tng/trustnetwork/keydistribution/testdata/CertificateTestUtils.java new file mode 100644 index 0000000..c33cf75 --- /dev/null +++ b/src/test/java/tng/trustnetwork/keydistribution/testdata/CertificateTestUtils.java @@ -0,0 +1,121 @@ +/*- + * ---license-start + * WorldHealthOrganization / tng-key-distribution + * --- + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://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. + * ---license-end + */ + +package tng.trustnetwork.keydistribution.testdata; + +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.RSAKeyGenParameterSpec; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Date; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x500.X500NameBuilder; +import org.bouncycastle.asn1.x509.BasicConstraints; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.X509ObjectIdentifiers; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; + +public class CertificateTestUtils { + + public static X509Certificate generateCertificate(KeyPair keyPair, String country, String commonName, + SignerType signerType) throws Exception { + Date validFrom = Date.from(Instant.now().minus(1, ChronoUnit.DAYS)); + Date validTo = Date.from(Instant.now().plus(365, ChronoUnit.DAYS)); + + return generateCertificate(keyPair, country, commonName, validFrom, validTo, signerType); + } + + public static X509Certificate generateCertificate(KeyPair keyPair, String country, String commonName, + X509Certificate ca, PrivateKey caKey, + SignerType signerType) throws Exception { + Date validFrom = Date.from(Instant.now().minus(1, ChronoUnit.DAYS)); + Date validTo = Date.from(Instant.now().plus(365, ChronoUnit.DAYS)); + + return generateCertificate(keyPair, country, commonName, validFrom, validTo, ca, caKey, signerType); + } + + public static X509Certificate generateCertificate(KeyPair keyPair, String country, String commonName, + Date validFrom, Date validTo, + SignerType signerType) throws Exception { + X500Name subject = new X500NameBuilder() + .addRDN(X509ObjectIdentifiers.countryName, country) + .addRDN(X509ObjectIdentifiers.commonName, commonName) + .build(); + + BigInteger certSerial = new BigInteger(Long.toString(System.currentTimeMillis())); + + ContentSigner contentSigner = new JcaContentSignerBuilder(signerType.signingMethod).build(keyPair.getPrivate()); + + JcaX509v3CertificateBuilder certBuilder = + new JcaX509v3CertificateBuilder(subject, certSerial, validFrom, validTo, subject, keyPair.getPublic()); + + BasicConstraints basicConstraints = new BasicConstraints(false); + certBuilder.addExtension(Extension.basicConstraints, true, basicConstraints); + + return new JcaX509CertificateConverter().getCertificate(certBuilder.build(contentSigner)); + } + + public static X509Certificate generateCertificate(KeyPair keyPair, String country, String commonName, + Date validFrom, Date validTo, X509Certificate ca, + PrivateKey caKey, SignerType signerType) throws Exception { + X500Name subject = new X500NameBuilder() + .addRDN(X509ObjectIdentifiers.countryName, country) + .addRDN(X509ObjectIdentifiers.commonName, commonName) + .build(); + + X500Name issuer = new X509CertificateHolder(ca.getEncoded()).getSubject(); + + BigInteger certSerial = new BigInteger(Long.toString(System.currentTimeMillis())); + + ContentSigner contentSigner = new JcaContentSignerBuilder(signerType.signingMethod).build(caKey); + + JcaX509v3CertificateBuilder certBuilder = + new JcaX509v3CertificateBuilder(issuer, certSerial, validFrom, validTo, subject, keyPair.getPublic()); + + BasicConstraints basicConstraints = new BasicConstraints(false); + certBuilder.addExtension(Extension.basicConstraints, true, basicConstraints); + + return new JcaX509CertificateConverter().getCertificate(certBuilder.build(contentSigner)); + } + + @RequiredArgsConstructor(access = AccessLevel.PRIVATE) + @Getter + public static class SignerType { + + private final String signingMethod; + private final String signingAlgorithm; + private final AlgorithmParameterSpec signingAlgorithmSpec; + + public static SignerType RSA = new SignerType("SHA256withRSA", "RSA", new RSAKeyGenParameterSpec(2048, BigInteger.valueOf(65537L))); + public static SignerType EC = new SignerType("SHA256withECDSA", "EC", new ECGenParameterSpec("secp256r1")); + } +} diff --git a/src/test/java/tng/trustnetwork/keydistribution/testdata/SignerInformationTestHelper.java b/src/test/java/tng/trustnetwork/keydistribution/testdata/SignerInformationTestHelper.java index 15a449d..c85cb81 100644 --- a/src/test/java/tng/trustnetwork/keydistribution/testdata/SignerInformationTestHelper.java +++ b/src/test/java/tng/trustnetwork/keydistribution/testdata/SignerInformationTestHelper.java @@ -2,7 +2,7 @@ * ---license-start * WorldHealthOrganization / tng-key-distribution * --- - * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors * --- * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,9 +21,8 @@ package tng.trustnetwork.keydistribution.testdata; import eu.europa.ec.dgc.gateway.connector.model.TrustListItem; +import eu.europa.ec.dgc.gateway.connector.model.TrustedCertificateTrustListItem; import eu.europa.ec.dgc.utils.CertificateUtils; -import tng.trustnetwork.keydistribution.entity.SignerInformationEntity; -import tng.trustnetwork.keydistribution.repository.SignerInformationRepository; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.security.cert.CertificateException; @@ -33,12 +32,13 @@ import java.util.Base64; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import tng.trustnetwork.keydistribution.entity.SignerInformationEntity; +import tng.trustnetwork.keydistribution.repository.SignerInformationRepository; @Service @RequiredArgsConstructor public class SignerInformationTestHelper { - public static final String TEST_CERT_1_STR = "MIICrDCCAZSgAwIBAgIEYH+7ujANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1l" + "ZGdjX2Rldl90ZXN0MB4XDTIxMDQyMTA1NDQyNloXDTIyMDQyMTA1NDQyNlowGDEW" @@ -58,41 +58,6 @@ public class SignerInformationTestHelper { public static final String TEST_CERT_1_KID = "8xYtW2837ac="; - public static final String TEST_CERT_2_STR = - "MIIBGzCBwqADAgECAgRggUObMAoGCCqGSM49BAMCMBYxFDASBgNVBAMMC2VkZ2Nf" - + "ZGV2X2VjMB4XDTIxMDQyMjA5MzYyN1oXDTIyMDQyMjA5MzYyN1owFjEUMBIGA1UE" - + "AwwLZWRnY19kZXZfZWMwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQVQc9JY190" - + "s/Jn0CBSq/AWuxmqUzRVu+AsCe6gfbqk3s0e4jonzp5v/5IMW/9t7v5Fu2ITMmOT" - + "VfKL1TuM+aixMAoGCCqGSM49BAMCA0gAMEUCIQCGWIk6ZET3afRxdpFVuXdrEYtF" - + "iR1MGDx4HweZfspjSgIgBdCJsT746/FI3euIbzKDoeY65m+Qx2/4Cd/vOayNbuw="; - - public static final String TEST_CERT_2_KID = "EzVuT0kOpJc="; - - public static final String TEST_CERT_3_STR = - "MIIDqDCCAhCgAwIBAgIEYIFDEjANBgkqhkiG9w0BAQsFADAWMRQwEgYDVQQDDAtl" - + "ZGdjX2Rldl9kZTAeFw0yMTA0MjIwOTM0MTBaFw0yMjA0MjIwOTM0MTBaMBYxFDAS" - + "BgNVBAMMC2VkZ2NfZGV2X2RlMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKC" - + "AYEAt1aoSm/JB7qth70XBPR4avb1wcKHpBLFBDZIZnHzKYWvIy6JIXgd342tK825" - + "0jOJ5UC1SVJdtAckWEkV5HYQ3qJ7qr6booEQzK64lLSk6oimjOnnIOFWEIrPPqW+" - + "nQFOyw96opf6ISiyVvUipJVFuQC2RE3Ci/yKGBO7LeMQi2FDw+edo4/HtsmJlkEz" - + "8JxnCniwjTRCnRGNAs7YXMlrwcCcyIarDtxbdbcwm/6WpuOnj8MzTAAUXQ+SeFOq" - + "MlvUosKxL34nJ7liHySu6uuGCopFSvuRh3yIuwAqeufGVKBfoiJkrtsn+AB/Q/kP" - + "XpPR7Dk2NybbJX3g+dh2ok08zpbVcYBRrtITXPZIQvLuZXMd1CUnNz0aOWNAxT6P" - + "v4R4ROavuQcjJR785mspCovqXCy8SpD4JHs+HxYqE7RTWzd3j4HmPf7NuWMnlH04" - + "J2h10V/EffHu65+wQ4s9dMCRLttOBScV6EAgRLoCt11tvc8XUxzI0yq17YntZDr2" - + "1SjJAgMBAAEwDQYJKoZIhvcNAQELBQADggGBABIWLWx/RQ3WQoHXmbLhkTTtM2b3" - + "Q/TZCXz5ZB89l/CrTeLQ+hy5pYv5HUTz00JnikyxbfVwsNhfVRMYm0NVJf6WWqHB" - + "OIk9MKAxksJ49QFHdL2sW4Vm5XhGy2FDaEgtx58q3koNHY9e5FyOcEZcXo2+eXKO" - + "bOsj80RJV5aj53SWY3Si+sq9iJMGYghskaEs/rnWn65ullbUKuC1+vkOV3qfFPKo" - + "CxeHlmGzdokRzbVKtXjDqb/edRX6I4k7laZ0+irFQqftvkaMHVEf13nXTIgQ9rpp" - + "+JQ0Y2pWSLPnWf/dah/D0/NmwI6E6V5+9U6i73RcalGw97gfyorMkYFFE8ByLdfp" - + "n76oTgJaXN/CQDLm2yzOX/ynt4t0ycqcVYrzewiKY2Fpnhao4U00vrh+0lwdUFr3" - + "jpOMeNg/2UDYhpWwWiT1ik+D6PSfKQ7Amuph6VcYEy/grQxNxPWcghoZSVKdXhOz" - + "6ggdK/eFNlO1aYj/DLxV3ZWcrAYk6dS4rnn8Ow=="; - - public static final String TEST_CERT_3_KID = "zoQi+KTb8LM="; - - private final SignerInformationRepository signerInformationRepository; private final CertificateUtils certificateUtils; private X509Certificate convertStringToX509Cert(String certificate) throws CertificateException { @@ -102,56 +67,7 @@ private X509Certificate convertStringToX509Cert(String certificate) throws Certi .generateCertificate(targetStream); } - public Long insertCertString(String certStr) { - String kid; - try { - kid = certificateUtils.getCertKid(convertStringToX509Cert(certStr)); - }catch (CertificateException e) { - kid = "kid_"+ ZonedDateTime.now(); - } - - SignerInformationEntity cert = new SignerInformationEntity( - null, - kid, - ZonedDateTime.now(), - certStr, - "de", - "thumbprint", - ZonedDateTime.now(), - false - ); - - signerInformationRepository.save(cert); - - return cert.getId(); - } - - public Long insertCertString(String certStr, String country, - String thumbprint, ZonedDateTime date, boolean deleted) { - String kid; - try { - kid = certificateUtils.getCertKid(convertStringToX509Cert(certStr)); - }catch (CertificateException e) { - kid = "kid_"+ ZonedDateTime.now(); - } - - SignerInformationEntity cert = new SignerInformationEntity( - null, - kid, - ZonedDateTime.now(), - certStr, - country, - thumbprint, - date, - deleted - ); - - signerInformationRepository.save(cert); - - return cert.getId(); - } - - public TrustListItem createTrustListItem(String certStr) { + public TrustedCertificateTrustListItem createTrustedCertificateTrustListItem(String certStr) { String kid; try { kid = certificateUtils.getCertKid(convertStringToX509Cert(certStr)); @@ -159,12 +75,11 @@ public TrustListItem createTrustListItem(String certStr) { kid = "kid_"+ ZonedDateTime.now(); } - TrustListItem item = new TrustListItem(); + //TrustListItem item = new TrustListItem(); + TrustedCertificateTrustListItem item = new TrustedCertificateTrustListItem(); item.setKid(kid); - item.setTimestamp(ZonedDateTime.now()); - item.setRawData(certStr); + item.setCertificate(certStr); return item; } - } diff --git a/src/test/java/tng/trustnetwork/keydistribution/testdata/TrustedIssuerTestHelper.java b/src/test/java/tng/trustnetwork/keydistribution/testdata/TrustedIssuerTestHelper.java index 0a9f103..b880bd2 100644 --- a/src/test/java/tng/trustnetwork/keydistribution/testdata/TrustedIssuerTestHelper.java +++ b/src/test/java/tng/trustnetwork/keydistribution/testdata/TrustedIssuerTestHelper.java @@ -2,7 +2,7 @@ * ---license-start * WorldHealthOrganization / tng-key-distribution * --- - * Copyright (C) 2021 T-Systems International GmbH and all other contributors + * Copyright (C) 2021 - 2024 T-Systems International GmbH and all other contributors * --- * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,16 +20,15 @@ package tng.trustnetwork.keydistribution.testdata; -import java.time.ZonedDateTime; -import java.util.ArrayList; -import java.util.List; - import eu.europa.ec.dgc.gateway.connector.model.TrustedIssuer; +import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import tng.trustnetwork.keydistribution.entity.TrustedIssuerEntity; import tng.trustnetwork.keydistribution.repository.TrustedIssuerRepository; -import lombok.RequiredArgsConstructor; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.List; @Service @RequiredArgsConstructor @@ -38,60 +37,20 @@ public class TrustedIssuerTestHelper { @Autowired TrustedIssuerRepository trustedIssuerRepository; - - public TrustedIssuerEntity getIssuer(int number) { - TrustedIssuerEntity issuer = new TrustedIssuerEntity(); - - switch (number) { - case 1: - issuer.setEtag("TestEtag"); - issuer.setCreatedAt(ZonedDateTime.parse("2022-04-04T02:21:00Z")); - issuer.setCountry("DE"); - issuer.setUrl("https://TestUrl.de"); - issuer.setName("example1.de"); - issuer.setUrlType(TrustedIssuerEntity.UrlType.HTTP); - issuer.setThumbprint("thumbprint1"); - issuer.setSslPublicKey("PublicKey1"); - issuer.setKeyStorageType("JWKS"); - issuer.setSignature("Signature1"); - return issuer; - - case 2: - issuer.setEtag("TestEtag"); - issuer.setCreatedAt(ZonedDateTime.parse("2022-04-03T03:33:00Z")); - issuer.setCountry("DE"); - issuer.setUrl("https://TestUrl2.de"); - issuer.setName("example2.de"); - issuer.setUrlType(TrustedIssuerEntity.UrlType.HTTP); - issuer.setThumbprint("thumbprint2"); - issuer.setSslPublicKey("PublicKey2"); - issuer.setKeyStorageType("JWKS"); - issuer.setSignature("Signature2"); - break; - default: - issuer.setEtag("TestEtag"); - issuer.setCreatedAt(ZonedDateTime.parse("2022-04-03T03:33:00Z")); - issuer.setCountry("DE"); - issuer.setUrl("https://TestUrlDefault.de"); - issuer.setName("exampleDefault.de"); - issuer.setUrlType(TrustedIssuerEntity.UrlType.HTTP); - issuer.setThumbprint("thumbprintDefault"); - issuer.setSslPublicKey("PublicKeyDefault"); - issuer.setKeyStorageType("JWKS"); - issuer.setSignature("SignatureDefault"); - break; - } - - return issuer; - + public TrustedIssuerEntity createTrustedIssuer(final String country) { + TrustedIssuerEntity trustedIssuer = new TrustedIssuerEntity(); + trustedIssuer.setUrl("did:trusted:" + country + ":issuer"); + trustedIssuer.setName("tiName"); + trustedIssuer.setCountry(country); + trustedIssuer.setUrlType(TrustedIssuerEntity.UrlType.DID); + trustedIssuer.setSslPublicKey("pubKey"); + trustedIssuer.setThumbprint("thumbprint"); + trustedIssuer.setKeyStorageType("JWKS"); + trustedIssuer.setSignature("sig"); + + return trustedIssuer; } - - public void insertTrustedIssuer(TrustedIssuerEntity issuer) { - trustedIssuerRepository.save(issuer); - } - - public List getTrustedIssuerList() { List list = new ArrayList<>(); @@ -101,38 +60,39 @@ public List getTrustedIssuerList() { issuer.setType(TrustedIssuer.UrlType.HTTP); issuer.setThumbprint("8e5b84a5c807f8661e470453119830f2ec27971fce4a3420bb744bad66e5bf4c"); issuer.setSslPublicKey("MHcCAQEEICdvyZFxcPenETpnkmMf8m7te73UE6olhUB72OpIuGRpoAoGCCqGSM49AwEHoUQDQgAE7ni62sNPT7" - + "02PoVkwd8+oCJMkDjht8gcFVGSgYNmjUFDXjKuLK/IVl87xQ5G8zNTbIMllwD1JJZB9LElhFb3JA=="); + + "02PoVkwd8+oCJMkDjht8gcFVGSgYNmjUFDXjKuLK/IVl87xQ5G8zNTbIMllwD1JJZB9LElhFb3JA=="); issuer.setKeyStorageType("JWKS"); - issuer.setSignature("MIAGCSqGSIb3DQEHAqCAMIACAQExDTALBglghkgBZQMEAgEwgAYJKoZIhvcNAQcBAACggDCCBX0wggNloAMCAQICF" - + "CfArZMSPZ2iPmF85n5LHsj4D5XgMA0GCSqGSIb3DQEBCwUAME4xCzAJBgNVBAYTAkVVMRcwFQYDVQQIDA5FdXJvcGVhbiBVbmlvbjEU" - + "MBIGA1UECgwLVHJ1c3RBbmNob3IxEDAOBgNVBAsMB1RTVCBFTlYwHhcNMjEwNDIyMDgxNTIyWhcNMzEwNDIwMDgxNTIyWjBOMQswCQY" - + "DVQQGEwJFVTEXMBUGA1UECAwORXVyb3BlYW4gVW5pb24xFDASBgNVBAoMC1RydXN0QW5jaG9yMRAwDgYDVQQLDAdUU1QgRU5WMIICIj" - + "ANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA13sh56S2sRAwnS4TCKci0UHGFC1/GxptcaAow2jznzRaJyz7k6oghedDzibFZREen" - + "g3cO+pw4XpNO8SiWK8w8fipE9TOkbWBNP8cij/yWj+jfyZvVCPY8eXyS5okzS2PNN2lPswdiB5m5BkuXcm8I8d0fgi4bTzT3lwtxlRo" - + "JZo6LVMFjI/sB3LTYsMiL/OnYozpQWf7Cd6wLJI3c9IiQWFH40dGFFwtdQifDWPjOj9iwMASeCarqtOpNhpkn1ZxCDmqPj1mPqreLdq" - + "2RCbzrdvuFRs8KsIrjzJFCcBACPzQeP0jFijPhMa9p8BLSwCrlZOz7OEASPqWDstOqBazTUYBvcwGnP2ZcBuXKUS+lN9V+r37J4ANb/" - + "OpM+iZuPUURxf7OxPa+0INauy6OD8018OleL4svS+8tQadT4G9Nbr/2JqFfqat0FVhaZxQHEyLgQdt70wX1BOctgbCKlGQKBuLMyvyT" - + "wUJ6Qd0IKxmzFbOVfe+AWHb+V+x8oBpAo+vhS6OCaFuB8dIma1pgf6JP6kfmBERvm8n7158q92ZfGebzhSDhbsuB6Gaj0Ew5qJ/kdzQ" - + "rZP5QywHZQ8mEum7JR8rygPEEXDRhdtn3CHIDWEt0we+hGU2GchHOrZwMenQKMdxWnNr5/4M6WobefnOk+t2t4aF1ceWd8nXvK2j1l8" - + "CAwEAAaNTMFEwHQYDVR0OBBYEFK9nb1NMVv4ZzXG7A2alSueXrLBQMB8GA1UdIwQYMBaAFK9nb1NMVv4ZzXG7A2alSueXrLBQMA8GA1" - + "UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAEujCHeHgcqBFeKvt9bAsEDB1QH19+kcd2TdW87GWlA+sYPM3ARwSy5E7JbYj" - + "yk0pZ/XbDi6qC+CE8OgOyWQaj9CELEZCktXZsdGvOs9dKJd5yf97CLDT9EMp2284Ek67VWp5wqqa1+B6xGTg5r8a0OCNrCR04siQNoQ" - + "3pq669hQfhmg5iR0sz4JZrgUL6LIukrd5b/kDvaP37xh8gUrYLX5ApdQFuX41FiP/zcwC4/LG4llsAfYw2lh9ZhXqj3VW8SCayYeJ/O" - + "ExQLM8sHCxJ5NMHoXEvlOjoz+X3/Jib7GHIb0z70EaA8BN6KQ8YPcm+U6sgrjsj501WNAz2GA7ji5Iv/Pet5HGZsYNsDYZSWspe5hbc" - + "Buc271sVbofLkIXxS8l1mVyhJYj4G+X2DWU3RDoQE+XN8wUdYXcrnKlpp8BKQTOxjofp5xnymCq5GXO50+K1C/tqHjCP1aiir2V1Sb1" - + "SumgFoJ10bJXCaqCtUX1/7U7f9lGLirAhgN26s4T13hp+8X1D2hMxfo0w/w90fvtcxfSxutoMwwyU917JtPO/8TA+rE07MbnS0SVsYI" - + "Pg+CVPBHV2jSa1ZVSSsVhJSteG6Hs971ci3kgo4rN/ukosBycylzjBLXBnWfWYAoMb3YoNs1jQJnSyll+N2WxX7vHkKwPrh7OpI9yh+" - + "IEOnYAAAxggMoMIIDJAIBATBmME4xCzAJBgNVBAYTAkVVMRcwFQYDVQQIDA5FdXJvcGVhbiBVbmlvbjEUMBIGA1UECgwLVHJ1c3RBbm" - + "Nob3IxEDAOBgNVBAsMB1RTVCBFTlYCFCfArZMSPZ2iPmF85n5LHsj4D5XgMAsGCWCGSAFlAwQCAaCBljAYBgkqhkiG9w0BCQMxCwYJK" - + "oZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yMjAzMjUxMTE3MzRaMCsGCSqGSIb3DQEJNDEeMBwwCwYJYIZIAWUDBAIBoQ0GCSqGSIb3" - + "DQEBCwUAMC8GCSqGSIb3DQEJBDEiBCDRX6mP3IuhUUd3UlbOhbuPYgXXjxeGv+F6IlfEC1aeRTANBgkqhkiG9w0BAQsFAASCAgC23Mz" - + "bNZgXilk+NjuGPfbqQM2veffsKdA0Ln89ODg7Bjtjc0UKTpIQj/o8K9xR/xLkANxM+jLr1v4ya7CUwG9fCde0lqxozSl/j4+P+9Ir82" - + "yTDO7AgT0tNpYI+Pa1NzIlRNgqiTVfEg+AmaKLHkg/SJaDa3KxMslkaeQrUwGqaWBLbaMjQFzk/S92s+uRl00At04peXClb87ml6qlO" - + "BEipjzpcmz/pJPXctBJ38rLSaWyId+Gi+2z5xyClP3N5xUBumVNJZQvkE21cxggUw9CF7m7TPl6O3+6pbkW5ZLrDPOYvGMVH2XYkIJN" - + "AsxEnJSOIEhCAF2PWaKQ5A2ioHOpEvO7Ao2XHxHYZviH66dibxz1tZKe+lxdn65wChfHimvgmu3qyEVjAW3DcHBK8Vs4vB5xdBcx9Q8" - + "1tES/w/Q5ML4rIXKHv6aWlg5cpLuxY6q/T39AxxHnn7CZfIhj+A7kFQGQzy98qRj/qUDgTGF2VoEVX5hDRpkINZhStsW5pTVWtppLVc" - + "CLn7L67FKp8pj8z1S5XY/5akbflY0NPy/a9u71aVHPA+O3RaOlNKG9ZzIKBjApdoDuEEabhwmUmqxbtPhKOSklhv0qOJ1rvuMZLCOha" - + "S1u3C1KyLok+6WI0oSr+hnLwzR69j9Mcfrq98HjvYpmZgSgOKaRe4XsKIBpNQAAAAAAAA=="); + issuer.setSignature(""" + MIAGCSqGSIb3DQEHAqCAMIACAQExDTALBglghkgBZQMEAgEwgAYJKoZIhvcNAQcBAACggDCCBX0wggNloAMCAQICF\ + CfArZMSPZ2iPmF85n5LHsj4D5XgMA0GCSqGSIb3DQEBCwUAME4xCzAJBgNVBAYTAkVVMRcwFQYDVQQIDA5FdXJvcGVhbiBVbmlvbjEU\ + MBIGA1UECgwLVHJ1c3RBbmNob3IxEDAOBgNVBAsMB1RTVCBFTlYwHhcNMjEwNDIyMDgxNTIyWhcNMzEwNDIwMDgxNTIyWjBOMQswCQY\ + DVQQGEwJFVTEXMBUGA1UECAwORXVyb3BlYW4gVW5pb24xFDASBgNVBAoMC1RydXN0QW5jaG9yMRAwDgYDVQQLDAdUU1QgRU5WMIICIj\ + ANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA13sh56S2sRAwnS4TCKci0UHGFC1/GxptcaAow2jznzRaJyz7k6oghedDzibFZREen\ + g3cO+pw4XpNO8SiWK8w8fipE9TOkbWBNP8cij/yWj+jfyZvVCPY8eXyS5okzS2PNN2lPswdiB5m5BkuXcm8I8d0fgi4bTzT3lwtxlRo\ + JZo6LVMFjI/sB3LTYsMiL/OnYozpQWf7Cd6wLJI3c9IiQWFH40dGFFwtdQifDWPjOj9iwMASeCarqtOpNhpkn1ZxCDmqPj1mPqreLdq\ + 2RCbzrdvuFRs8KsIrjzJFCcBACPzQeP0jFijPhMa9p8BLSwCrlZOz7OEASPqWDstOqBazTUYBvcwGnP2ZcBuXKUS+lN9V+r37J4ANb/\ + OpM+iZuPUURxf7OxPa+0INauy6OD8018OleL4svS+8tQadT4G9Nbr/2JqFfqat0FVhaZxQHEyLgQdt70wX1BOctgbCKlGQKBuLMyvyT\ + wUJ6Qd0IKxmzFbOVfe+AWHb+V+x8oBpAo+vhS6OCaFuB8dIma1pgf6JP6kfmBERvm8n7158q92ZfGebzhSDhbsuB6Gaj0Ew5qJ/kdzQ\ + rZP5QywHZQ8mEum7JR8rygPEEXDRhdtn3CHIDWEt0we+hGU2GchHOrZwMenQKMdxWnNr5/4M6WobefnOk+t2t4aF1ceWd8nXvK2j1l8\ + CAwEAAaNTMFEwHQYDVR0OBBYEFK9nb1NMVv4ZzXG7A2alSueXrLBQMB8GA1UdIwQYMBaAFK9nb1NMVv4ZzXG7A2alSueXrLBQMA8GA1\ + UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAEujCHeHgcqBFeKvt9bAsEDB1QH19+kcd2TdW87GWlA+sYPM3ARwSy5E7JbYj\ + yk0pZ/XbDi6qC+CE8OgOyWQaj9CELEZCktXZsdGvOs9dKJd5yf97CLDT9EMp2284Ek67VWp5wqqa1+B6xGTg5r8a0OCNrCR04siQNoQ\ + 3pq669hQfhmg5iR0sz4JZrgUL6LIukrd5b/kDvaP37xh8gUrYLX5ApdQFuX41FiP/zcwC4/LG4llsAfYw2lh9ZhXqj3VW8SCayYeJ/O\ + ExQLM8sHCxJ5NMHoXEvlOjoz+X3/Jib7GHIb0z70EaA8BN6KQ8YPcm+U6sgrjsj501WNAz2GA7ji5Iv/Pet5HGZsYNsDYZSWspe5hbc\ + Buc271sVbofLkIXxS8l1mVyhJYj4G+X2DWU3RDoQE+XN8wUdYXcrnKlpp8BKQTOxjofp5xnymCq5GXO50+K1C/tqHjCP1aiir2V1Sb1\ + SumgFoJ10bJXCaqCtUX1/7U7f9lGLirAhgN26s4T13hp+8X1D2hMxfo0w/w90fvtcxfSxutoMwwyU917JtPO/8TA+rE07MbnS0SVsYI\ + Pg+CVPBHV2jSa1ZVSSsVhJSteG6Hs971ci3kgo4rN/ukosBycylzjBLXBnWfWYAoMb3YoNs1jQJnSyll+N2WxX7vHkKwPrh7OpI9yh+\ + IEOnYAAAxggMoMIIDJAIBATBmME4xCzAJBgNVBAYTAkVVMRcwFQYDVQQIDA5FdXJvcGVhbiBVbmlvbjEUMBIGA1UECgwLVHJ1c3RBbm\ + Nob3IxEDAOBgNVBAsMB1RTVCBFTlYCFCfArZMSPZ2iPmF85n5LHsj4D5XgMAsGCWCGSAFlAwQCAaCBljAYBgkqhkiG9w0BCQMxCwYJK\ + oZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yMjAzMjUxMTE3MzRaMCsGCSqGSIb3DQEJNDEeMBwwCwYJYIZIAWUDBAIBoQ0GCSqGSIb3\ + DQEBCwUAMC8GCSqGSIb3DQEJBDEiBCDRX6mP3IuhUUd3UlbOhbuPYgXXjxeGv+F6IlfEC1aeRTANBgkqhkiG9w0BAQsFAASCAgC23Mz\ + bNZgXilk+NjuGPfbqQM2veffsKdA0Ln89ODg7Bjtjc0UKTpIQj/o8K9xR/xLkANxM+jLr1v4ya7CUwG9fCde0lqxozSl/j4+P+9Ir82\ + yTDO7AgT0tNpYI+Pa1NzIlRNgqiTVfEg+AmaKLHkg/SJaDa3KxMslkaeQrUwGqaWBLbaMjQFzk/S92s+uRl00At04peXClb87ml6qlO\ + BEipjzpcmz/pJPXctBJ38rLSaWyId+Gi+2z5xyClP3N5xUBumVNJZQvkE21cxggUw9CF7m7TPl6O3+6pbkW5ZLrDPOYvGMVH2XYkIJN\ + AsxEnJSOIEhCAF2PWaKQ5A2ioHOpEvO7Ao2XHxHYZviH66dibxz1tZKe+lxdn65wChfHimvgmu3qyEVjAW3DcHBK8Vs4vB5xdBcx9Q8\ + 1tES/w/Q5ML4rIXKHv6aWlg5cpLuxY6q/T39AxxHnn7CZfIhj+A7kFQGQzy98qRj/qUDgTGF2VoEVX5hDRpkINZhStsW5pTVWtppLVc\ + CLn7L67FKp8pj8z1S5XY/5akbflY0NPy/a9u71aVHPA+O3RaOlNKG9ZzIKBjApdoDuEEabhwmUmqxbtPhKOSklhv0qOJ1rvuMZLCOha\ + S1u3C1KyLok+6WI0oSr+hnLwzR69j9Mcfrq98HjvYpmZgSgOKaRe4XsKIBpNQAAAAAAAA=="""); issuer.setTimestamp(ZonedDateTime.parse("2022-03-25T12:14:49+01:00")); issuer.setName("example-de"); @@ -142,5 +102,4 @@ public List getTrustedIssuerList() { list.add(issuer); return list; } - } diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index bc800ba..ce649d3 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -1,6 +1,3 @@ -server: - port: 8080 - spring: application: name: tng-key-distribution @@ -9,12 +6,15 @@ spring: url: jdbc:h2:mem:dgc;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1; username: sa password: '' + jackson: + deserialization: + read-unknown-enum-values-using-default-value: true jpa: database-platform: org.hibernate.dialect.H2Dialect hibernate: - ddl-auto: create-drop + ddl-auto: validate liquibase: - change-log: classpath:db/changelog.xml + change-log: classpath:db/changelog.yaml h2: console: enabled: true @@ -35,28 +35,33 @@ dgc: gateway: connector: enabled: false -# endpoint: https://tng-dev.who.int -# proxy: -# enabled: false -# max-cache-age: 300 -# tls-trust-store: #CA of TNG Gateway of TLS certificate -# alias: tng-tls-server-certificate -# password: dgcg-p4ssw0rd -# path: certs/tng_tls_server_truststore.p12 -# tls-key-store: # client cert key pair of participant -# alias: clientcredentials -# password: dgcg-p4ssw0rd -# path: certs/tls_key_store.p12 -# trust-anchor: # TA pem file of TNG that was used for signing -# alias: trustanchor -# password: dgcg-p4ssw0rd -# path: certs/trustanchor_store.jks -springdoc: - api-docs: - path: /api/docs - swagger-ui: - path: /swagger - + did: + cron: "" + enableDidGeneration: true + didUploadProvider: dummy + didSigningProvider: dummy + git: + workdir: "" + prefix: "" + url: "" + pat: "" + ld-proof-verification-method: did:web:dummy.net + ld-proof-domain: d0m4in + did-id: did:web:abc + trust-list-path: trustlist + trust-list-ref-path: trustlist-ref + did-controller: did:web:def + trust-list-id-prefix: did:web:abc + trust-list-controller-prefix: did:web:abc + contextMapping: + "[https://www.w3.org/ns/did/v1]": did_v1.json + "[https://w3id.org/security/suites/jws-2020/v1]": jws-2020_v1.json + virtualCountries: + EU: XEU + group-deny-list: + - UPLOAD + group-name-mapping: + CSCA: CSA universal: resolver: "https://dev.uniresolver.io/1.0/identifiers"