From ea573b6816bcbdbf2b1a4416652e551517f893d2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Mar 2022 08:53:30 +0200 Subject: [PATCH 001/433] Bump actions/checkout from 2 to 3 (#1) Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yaml | 2 +- .github/workflows/draft-new-release.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index af70ed3ca..3da5b3d16 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -21,7 +21,7 @@ jobs: ############## - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: submodules: recursive - diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index c7c0c8900..56c1709e3 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -13,7 +13,7 @@ jobs: name: "Draft a new release" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Create release branch run: git checkout -b release/${{ github.event.inputs.version }} From 41dfaf265e5ef4ee898ffdd1692a155440beb8f0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 29 Mar 2022 08:55:55 +0200 Subject: [PATCH 002/433] Release version 0.0.1 (#2) * Bump actions/checkout from 2 to 3 (#1) Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Prepare release 0.0.1 Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: GitHub actions --- .github/workflows/build.yaml | 2 +- .github/workflows/draft-new-release.yaml | 2 +- CHANGELOG.md | 6 ++++++ deployment/helm/edc-controlplane/Chart.yaml | 4 ++-- deployment/helm/edc-dataplane/Chart.yaml | 4 ++-- edc-controlplane/edc-controlplane-cosmosdb/pom.xml | 2 +- edc-controlplane/pom.xml | 2 +- edc-dataplane/pom.xml | 2 +- pom.xml | 2 +- 9 files changed, 16 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index af70ed3ca..3da5b3d16 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -21,7 +21,7 @@ jobs: ############## - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: submodules: recursive - diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index c7c0c8900..56c1709e3 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -13,7 +13,7 @@ jobs: name: "Draft a new release" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Create release branch run: git checkout -b release/${{ github.event.inputs.version }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 11bddf32c..fe4d06868 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,3 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] + +## [0.0.1] - 2022-03-29 + +[Unreleased]: https://github.com/catenax-ng/catena-x-edc/compare/0.0.1...HEAD + +[0.0.1]: https://github.com/catenax-ng/catena-x-edc/compare/ea573b6816bcbdbf2b1a4416652e551517f893d2...0.0.1 diff --git a/deployment/helm/edc-controlplane/Chart.yaml b/deployment/helm/edc-controlplane/Chart.yaml index 46ed5fe88..27ed4840c 100644 --- a/deployment/helm/edc-controlplane/Chart.yaml +++ b/deployment/helm/edc-controlplane/Chart.yaml @@ -3,5 +3,5 @@ name: edc-controlplane description: EDC Control-Plane home: https://github.com/catenax-ng/catena-x-edc/deployment/helm/edc-controlplane type: application -appVersion: "0.0.3" -version: 0.0.3 +appVersion: "0.0.1" +version: 0.0.1 diff --git a/deployment/helm/edc-dataplane/Chart.yaml b/deployment/helm/edc-dataplane/Chart.yaml index 368af65e0..46ad4c61b 100644 --- a/deployment/helm/edc-dataplane/Chart.yaml +++ b/deployment/helm/edc-dataplane/Chart.yaml @@ -3,5 +3,5 @@ name: edc-dataplane description: EDC Control-Plane home: https://github.com/catenax-ng/catena-x-edc/deployment/helm/edc-dataplane type: application -appVersion: "0.0.3" -version: 0.0.3 +appVersion: "0.0.1" +version: 0.0.1 diff --git a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml index 2a0539fa3..4181857e8 100644 --- a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml +++ b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml @@ -5,7 +5,7 @@ net.catenax.edc edc-controlplane - 0.0.1-SNAPSHOT + 0.0.1 4.0.0 diff --git a/edc-controlplane/pom.xml b/edc-controlplane/pom.xml index ad344acb9..70b94117d 100644 --- a/edc-controlplane/pom.xml +++ b/edc-controlplane/pom.xml @@ -7,7 +7,7 @@ net.catenax.edc catena-x-edc-parent - 0.0.1-SNAPSHOT + 0.0.1 edc-controlplane diff --git a/edc-dataplane/pom.xml b/edc-dataplane/pom.xml index 1c5d3e3f8..686de3135 100644 --- a/edc-dataplane/pom.xml +++ b/edc-dataplane/pom.xml @@ -7,7 +7,7 @@ net.catenax.edc catena-x-edc-parent - 0.0.1-SNAPSHOT + 0.0.1 edc-dataplane jar diff --git a/pom.xml b/pom.xml index a3edf565d..13499c80a 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ net.catenax.edc catena-x-edc-parent - 0.0.1-SNAPSHOT + 0.0.1 pom From 72359983319dcb6c5b5a991c87d912761069d97d Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Tue, 29 Mar 2022 17:49:00 +0200 Subject: [PATCH 003/433] helm: add image pull secret template (#3) * helm: add image pull secret template Signed-off-by: Denis Neuling * helm: replace configuration properties delimiter by underscore Signed-off-by: Denis Neuling * helm: add config properties for cosmosdb based contract definition/negotiation store Signed-off-by: Denis Neuling --- .github/dependabot.yml | 2 +- .../edc-controlplane/templates/configmap.yaml | 8 +- .../templates/deployment.yaml | 5 + .../templates/imagepullsecret.yaml | 11 ++ deployment/helm/edc-controlplane/values.yaml | 109 +++++++++++++++++- .../edc-dataplane/templates/configmap.yaml | 8 +- .../edc-dataplane/templates/deployment.yaml | 5 + .../templates/imagepullsecret.yaml | 11 ++ deployment/helm/edc-dataplane/values.yaml | 109 +++++++++++++++++- 9 files changed, 263 insertions(+), 5 deletions(-) create mode 100644 deployment/helm/edc-controlplane/templates/imagepullsecret.yaml create mode 100644 deployment/helm/edc-dataplane/templates/imagepullsecret.yaml diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5ddf2f6a7..1e3ed44f7 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -31,7 +31,7 @@ updates: - package-ecosystem: "maven" target-branch: develop - directory: "edc-controlplane/edc-controlplane-cosmosdb/src/main/docker" + directory: / labels: - "dependabot" - "dependencies" diff --git a/deployment/helm/edc-controlplane/templates/configmap.yaml b/deployment/helm/edc-controlplane/templates/configmap.yaml index 37bfc3034..157f1d33f 100644 --- a/deployment/helm/edc-controlplane/templates/configmap.yaml +++ b/deployment/helm/edc-controlplane/templates/configmap.yaml @@ -6,4 +6,10 @@ metadata: {{- include "edc-controlplane.labels" . | nindent 4 }} data: configuration.properties: |- - {{ .Values.configuration.properties | nindent 4 }} + {{- range $k, $v := .Values.configuration.properties }} + {{- if $v }} + {{ $k | replace "_" "." }}={{ $v }} + {{- else }} + # {{ $k | replace "_" "." }}= + {{- end }} + {{- end }} diff --git a/deployment/helm/edc-controlplane/templates/deployment.yaml b/deployment/helm/edc-controlplane/templates/deployment.yaml index 641bd5b44..40112fd27 100644 --- a/deployment/helm/edc-controlplane/templates/deployment.yaml +++ b/deployment/helm/edc-controlplane/templates/deployment.yaml @@ -20,10 +20,15 @@ spec: labels: {{- include "edc-controlplane.selectorLabels" . | nindent 8 }} spec: + {{- if .Values.imagePullSecret.dockerconfigjson }} + imagePullSecrets: + - name: {{ include "edc-controlplane.fullname" . }}-imagepullsecret + {{- else }} {{- with .Values.imagePullSecrets }} imagePullSecrets: {{- toYaml . | nindent 8 }} {{- end }} + {{- end }} serviceAccountName: {{ include "edc-controlplane.serviceAccountName" . }} securityContext: {{- toYaml .Values.podSecurityContext | nindent 8 }} diff --git a/deployment/helm/edc-controlplane/templates/imagepullsecret.yaml b/deployment/helm/edc-controlplane/templates/imagepullsecret.yaml new file mode 100644 index 000000000..84ddc001d --- /dev/null +++ b/deployment/helm/edc-controlplane/templates/imagepullsecret.yaml @@ -0,0 +1,11 @@ +{{- if .Values.imagePullSecret.dockerconfigjson }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "edc-controlplane.fullname" . }}-imagepullsecret + labels: + {{- include "edc-controlplane.labels" . | nindent 4 }} +data: + .dockerconfigjson: {{ .Values.imagePullSecret.dockerconfigjson }} +type: kubernetes.io/dockerconfigjson +{{- end }} diff --git a/deployment/helm/edc-controlplane/values.yaml b/deployment/helm/edc-controlplane/values.yaml index 3b5967129..9ca688bb6 100644 --- a/deployment/helm/edc-controlplane/values.yaml +++ b/deployment/helm/edc-controlplane/values.yaml @@ -13,6 +13,9 @@ image: # Overrides the image tag whose default is the chart appVersion. tag: "" +imagePullSecret: + dockerconfigjson: "" + imagePullSecrets: [] nameOverride: "" fullnameOverride: "" @@ -85,4 +88,108 @@ tolerations: [] affinity: {} configuration: - properties: |- + properties: + edc_api_control_auth_apikey_key: "" + edc_api_control_auth_apikey_value: "" + edc_assetindex_cosmos_account-name: "" + edc_assetindex_cosmos_container-name: "" + edc_assetindex_cosmos_database-name: "" + edc_assetindex_cosmos_preferred-region: "" + edc_atomikos_checkpoint_interval: "" + edc_atomikos_directory: "" + edc_atomikos_logging: "" + edc_atomikos_threaded2pc: "" + edc_atomikos_timeout: "" + edc_aws_access_key: "" + edc_aws_provision_retry_retries_max: "" + edc_aws_provision_role_duration_session_max: "" + edc_aws_secret_access_key: "" + edc_blobstore_endpoint: "" + edc_contractdefinitionstore_cosmos_account-name: "" + edc_contractdefinitionstore_cosmos_container-name: "" + edc_contractdefinitionstore_cosmos_database-name: "" + edc_contractdefinitionstore_cosmos_preferred-region: "" + edc_contractnegotiationstore_cosmos_account-name: "" + edc_contractnegotiationstore_cosmos_container-name: "" + edc_contractnegotiationstore_cosmos_database-name: "" + edc_contractnegotiationstore_cosmos_preferred-region: "" + edc_controlplane_validation-endpoint: "" + edc_core_retry_backoff_max: "" + edc_core_retry_backoff_min: "" + edc_core_retry_retries_max: "" + edc_core_system_health_check_liveness-period: "" + edc_core_system_health_check_readiness-period: "" + edc_core_system_health_check_startup-period: "" + edc_core_system_health_check_threadpool-size: "" + edc_cosmos_partition-key: "" + edc_cosmos_query-metrics-enabled: "" + edc_dataplane_queue_capacity: "" + edc_dataplane_wait: "" + edc_dataplane_workers: "" + edc_datasource_contractdefinition_name: "" + edc_dpf_selector_url: "" + edc_events_topic_endpoint: "" + edc_events_topic_name: "" + edc_fs_config: "" + edc_hostname: "" + edc_identity_did_url: "" + edc_ids_catalog_id: "" + edc_ids_curator: "" + edc_ids_description: "" + edc_ids_endpoint: "" + edc_ids_id: "" + edc_ids_maintainer: "" + edc_ids_security_profile: "" + edc_ids_title: "" + edc_ids_validation_referringconnector: "" + edc_ion_crawler_did-type: "" + edc_ion_crawler_interval-minutes: "" + edc_ion_crawler_ion_url: "" + edc_keystore: "" + edc_keystore_password: "" + edc_metrics_enabled: "" + edc_metrics_executor_enabled: "" + edc_metrics_jersey_enabled: "" + edc_metrics_jetty_enabled: "" + edc_metrics_okhttp_enabled: "" + edc_metrics_system_enabled: "" + edc_negotiation_consumer_state-machine_batch-size: "" + edc_negotiation_provider_state-machine_batch-size: "" + edc_node_directory_cosmos_account_name: "" + edc_node_directory_cosmos_container_name: "" + edc_node_directory_cosmos_database_name: "" + edc_node_directory_cosmos_preferred_region: "" + edc_oauth_client_id: "" + edc_oauth_private_key_alias: "" + edc_oauth_provider_audience: "" + edc_oauth_provider_jwks_refresh: "" + edc_oauth_provider_jwks_url: "" + edc_oauth_public_key_alias: "" + edc_oauth_token_url: "" + edc_oauth_validation_nbf_leeway: "" + edc_public_key_alias: "" + edc_receiver_http_auth-code: "" + edc_receiver_http_auth-key: "" + edc_receiver_http_endpoint: "" + edc_transfer_dataplane_sync_endpoint: "" + edc_transfer_dataplane_sync_token_validity: "" + edc_transfer_dataplane_token_signer_privatekey_alias: "" + edc_transfer_functions_check_endpoint: "" + edc_transfer_functions_enabled_protocols: "" + edc_transfer_functions_transfer_endpoint: "" + edc_transfer-process-store_cosmos_account_name: "" + edc_transfer-process-store_cosmos_container-name: "" + edc_transfer-process-store_cosmos_preferred-region: "" + edc_transfer-process-store_database_name: "" + edc_transfer_state-machine_batch-size: "" + edc_vault: "" + edc_vault_certificate: "" + edc_vault_clientid: "" + edc_vault_clientsecret: "" + edc_vault_name: "" + edc_vault_tenantid: "" + edc_webdid_doh_url: "" + edc_web_rest_cors_enabled: "" + edc_web_rest_cors_headers: "" + edc_web_rest_cors_methods: "" + edc_web_rest_cors_origins: "" diff --git a/deployment/helm/edc-dataplane/templates/configmap.yaml b/deployment/helm/edc-dataplane/templates/configmap.yaml index 27a43a8b3..068895dbf 100644 --- a/deployment/helm/edc-dataplane/templates/configmap.yaml +++ b/deployment/helm/edc-dataplane/templates/configmap.yaml @@ -6,4 +6,10 @@ metadata: {{- include "edc-dataplane.labels" . | nindent 4 }} data: configuration.properties: |- - {{ .Values.configuration.properties | nindent 4 }} + {{- range $k, $v := .Values.configuration.properties }} + {{- if $v }} + {{ $k | replace "_" "." }}={{ $v }} + {{- else }} + # {{ $k | replace "_" "." }}= + {{- end }} + {{- end }} diff --git a/deployment/helm/edc-dataplane/templates/deployment.yaml b/deployment/helm/edc-dataplane/templates/deployment.yaml index d65f75f87..f8b1f2888 100644 --- a/deployment/helm/edc-dataplane/templates/deployment.yaml +++ b/deployment/helm/edc-dataplane/templates/deployment.yaml @@ -20,10 +20,15 @@ spec: labels: {{- include "edc-dataplane.selectorLabels" . | nindent 8 }} spec: + {{- if .Values.imagePullSecret.dockerconfigjson }} + imagePullSecrets: + - name: {{ include "edc-dataplane.fullname" . }}-imagepullsecret + {{- else }} {{- with .Values.imagePullSecrets }} imagePullSecrets: {{- toYaml . | nindent 8 }} {{- end }} + {{- end }} serviceAccountName: {{ include "edc-dataplane.serviceAccountName" . }} securityContext: {{- toYaml .Values.podSecurityContext | nindent 8 }} diff --git a/deployment/helm/edc-dataplane/templates/imagepullsecret.yaml b/deployment/helm/edc-dataplane/templates/imagepullsecret.yaml new file mode 100644 index 000000000..32f39ddbc --- /dev/null +++ b/deployment/helm/edc-dataplane/templates/imagepullsecret.yaml @@ -0,0 +1,11 @@ +{{- if .Values.imagePullSecret.dockerconfigjson }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "edc-dataplane.fullname" . }}-imagepullsecret + labels: + {{- include "edc-dataplane.labels" . | nindent 4 }} +data: + .dockerconfigjson: {{ .Values.imagePullSecret.dockerconfigjson }} +type: kubernetes.io/dockerconfigjson +{{- end }} diff --git a/deployment/helm/edc-dataplane/values.yaml b/deployment/helm/edc-dataplane/values.yaml index bedf48f9c..3cbb0b15e 100644 --- a/deployment/helm/edc-dataplane/values.yaml +++ b/deployment/helm/edc-dataplane/values.yaml @@ -10,6 +10,9 @@ image: # Overrides the image tag whose default is the chart appVersion. tag: "" +imagePullSecret: + dockerconfigjson: "" + imagePullSecrets: [] nameOverride: "" fullnameOverride: "" @@ -82,4 +85,108 @@ tolerations: [] affinity: {} configuration: - properties: |- + properties: + edc_api_control_auth_apikey_key: "" + edc_api_control_auth_apikey_value: "" + edc_assetindex_cosmos_account-name: "" + edc_assetindex_cosmos_container-name: "" + edc_assetindex_cosmos_database-name: "" + edc_assetindex_cosmos_preferred-region: "" + edc_atomikos_checkpoint_interval: "" + edc_atomikos_directory: "" + edc_atomikos_logging: "" + edc_atomikos_threaded2pc: "" + edc_atomikos_timeout: "" + edc_aws_access_key: "" + edc_aws_provision_retry_retries_max: "" + edc_aws_provision_role_duration_session_max: "" + edc_aws_secret_access_key: "" + edc_blobstore_endpoint: "" + edc_contractdefinitionstore_cosmos_account-name: "" + edc_contractdefinitionstore_cosmos_container-name: "" + edc_contractdefinitionstore_cosmos_database-name: "" + edc_contractdefinitionstore_cosmos_preferred-region: "" + edc_contractnegotiationstore_cosmos_account-name: "" + edc_contractnegotiationstore_cosmos_container-name: "" + edc_contractnegotiationstore_cosmos_database-name: "" + edc_contractnegotiationstore_cosmos_preferred-region: "" + edc_controlplane_validation-endpoint: "" + edc_core_retry_backoff_max: "" + edc_core_retry_backoff_min: "" + edc_core_retry_retries_max: "" + edc_core_system_health_check_liveness-period: "" + edc_core_system_health_check_readiness-period: "" + edc_core_system_health_check_startup-period: "" + edc_core_system_health_check_threadpool-size: "" + edc_cosmos_partition-key: "" + edc_cosmos_query-metrics-enabled: "" + edc_dataplane_queue_capacity: "" + edc_dataplane_wait: "" + edc_dataplane_workers: "" + edc_datasource_contractdefinition_name: "" + edc_dpf_selector_url: "" + edc_events_topic_endpoint: "" + edc_events_topic_name: "" + edc_fs_config: "" + edc_hostname: "" + edc_identity_did_url: "" + edc_ids_catalog_id: "" + edc_ids_curator: "" + edc_ids_description: "" + edc_ids_endpoint: "" + edc_ids_id: "" + edc_ids_maintainer: "" + edc_ids_security_profile: "" + edc_ids_title: "" + edc_ids_validation_referringconnector: "" + edc_ion_crawler_did-type: "" + edc_ion_crawler_interval-minutes: "" + edc_ion_crawler_ion_url: "" + edc_keystore: "" + edc_keystore_password: "" + edc_metrics_enabled: "" + edc_metrics_executor_enabled: "" + edc_metrics_jersey_enabled: "" + edc_metrics_jetty_enabled: "" + edc_metrics_okhttp_enabled: "" + edc_metrics_system_enabled: "" + edc_negotiation_consumer_state-machine_batch-size: "" + edc_negotiation_provider_state-machine_batch-size: "" + edc_node_directory_cosmos_account_name: "" + edc_node_directory_cosmos_container_name: "" + edc_node_directory_cosmos_database_name: "" + edc_node_directory_cosmos_preferred_region: "" + edc_oauth_client_id: "" + edc_oauth_private_key_alias: "" + edc_oauth_provider_audience: "" + edc_oauth_provider_jwks_refresh: "" + edc_oauth_provider_jwks_url: "" + edc_oauth_public_key_alias: "" + edc_oauth_token_url: "" + edc_oauth_validation_nbf_leeway: "" + edc_public_key_alias: "" + edc_receiver_http_auth-code: "" + edc_receiver_http_auth-key: "" + edc_receiver_http_endpoint: "" + edc_transfer_dataplane_sync_endpoint: "" + edc_transfer_dataplane_sync_token_validity: "" + edc_transfer_dataplane_token_signer_privatekey_alias: "" + edc_transfer_functions_check_endpoint: "" + edc_transfer_functions_enabled_protocols: "" + edc_transfer_functions_transfer_endpoint: "" + edc_transfer-process-store_cosmos_account_name: "" + edc_transfer-process-store_cosmos_container-name: "" + edc_transfer-process-store_cosmos_preferred-region: "" + edc_transfer-process-store_database_name: "" + edc_transfer_state-machine_batch-size: "" + edc_vault: "" + edc_vault_certificate: "" + edc_vault_clientid: "" + edc_vault_clientsecret: "" + edc_vault_name: "" + edc_vault_tenantid: "" + edc_webdid_doh_url: "" + edc_web_rest_cors_enabled: "" + edc_web_rest_cors_headers: "" + edc_web_rest_cors_methods: "" + edc_web_rest_cors_origins: "" From 4beae90d462453dbc5434e4e667b0a7967de6fe9 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Tue, 29 Mar 2022 18:19:21 +0200 Subject: [PATCH 004/433] edc: use ref 705a44712fecb9cf5ac18622311e6a2dbd86ffc3 (#4) Signed-off-by: Denis Neuling --- .github/workflows/build.yaml | 2 +- edc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 3da5b3d16..244b42a33 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -37,7 +37,7 @@ jobs: run: |- [ ! -d "edc" ] && git submodule add https://github.com/eclipse-dataspaceconnector/DataSpaceConnector.git edc git submodule update --init - git -C edc checkout d429b6cd5e81e0fc79e8f9a0cb1b31888d0900a4 + git -C edc checkout 705a44712fecb9cf5ac18622311e6a2dbd86ffc3 - name: Set up JDK 11 uses: actions/setup-java@v2 diff --git a/edc b/edc index d429b6cd5..705a44712 160000 --- a/edc +++ b/edc @@ -1 +1 @@ -Subproject commit d429b6cd5e81e0fc79e8f9a0cb1b31888d0900a4 +Subproject commit 705a44712fecb9cf5ac18622311e6a2dbd86ffc3 From 3990acdc2bae905fadd83343b7f0a2ba58818801 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Wed, 30 Mar 2022 12:31:34 +0200 Subject: [PATCH 005/433] Make docker images distroless (#5) Signed-off-by: Denis Neuling --- .../helm/edc-controlplane/templates/deployment.yaml | 6 ++---- .../helm/edc-dataplane/templates/deployment.yaml | 6 ++---- .../src/main/docker/Dockerfile | 13 ++----------- edc-dataplane/src/main/docker/Dockerfile | 13 ++----------- 4 files changed, 8 insertions(+), 30 deletions(-) diff --git a/deployment/helm/edc-controlplane/templates/deployment.yaml b/deployment/helm/edc-controlplane/templates/deployment.yaml index 40112fd27..18b519f5f 100644 --- a/deployment/helm/edc-controlplane/templates/deployment.yaml +++ b/deployment/helm/edc-controlplane/templates/deployment.yaml @@ -38,9 +38,6 @@ spec: {{- toYaml .Values.securityContext | nindent 12 }} image: "{{ .Values.image.repository }}{{- if .Values.flavor }}-{{ .Values.flavor }}{{- end -}}:{{ .Values.image.tag | default .Chart.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy }} - env: - - name: CONFIGURATION_PROPERTIES - value: "/etc/edc/configuration.properties" ports: - name: http containerPort: 80 @@ -57,7 +54,8 @@ spec: {{- toYaml .Values.resources | nindent 12 }} volumeMounts: - name: configuration - mountPath: /etc/edc + mountPath: /app/configuration.properties + subPath: configuration.properties volumes: - name: configuration configMap: diff --git a/deployment/helm/edc-dataplane/templates/deployment.yaml b/deployment/helm/edc-dataplane/templates/deployment.yaml index f8b1f2888..b0028afe0 100644 --- a/deployment/helm/edc-dataplane/templates/deployment.yaml +++ b/deployment/helm/edc-dataplane/templates/deployment.yaml @@ -38,9 +38,6 @@ spec: {{- toYaml .Values.securityContext | nindent 12 }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy }} - env: - - name: CONFIGURATION_PROPERTIES - value: "/etc/edc/configuration.properties" ports: - name: http containerPort: 80 @@ -57,7 +54,8 @@ spec: {{- toYaml .Values.resources | nindent 12 }} volumeMounts: - name: configuration - mountPath: /etc/edc + mountPath: /app/configuration.properties + subPath: configuration.properties volumes: - name: configuration configMap: diff --git a/edc-controlplane/edc-controlplane-cosmosdb/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-cosmosdb/src/main/docker/Dockerfile index f6df86226..71b56b37e 100644 --- a/edc-controlplane/edc-controlplane-cosmosdb/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-cosmosdb/src/main/docker/Dockerfile @@ -1,17 +1,8 @@ -FROM openjdk:11-jre-slim-buster +FROM gcr.io/distroless/java11-debian11 ARG JAR -ENV CONFIGURATION_PROPERTIES=/app/configuration.properties - -RUN useradd --create-home app - WORKDIR /app -USER app - COPY $JAR edc-controlplane.jar -ENTRYPOINT java \ - -Djava.security.edg=file:/dev/.urandom \ - -Dedc.fs.config="${CONFIGURATION_PROPERTIES}" \ - -jar edc-controlplane.jar \ No newline at end of file +CMD ["-Djava.security.edg=file:/dev/.urandom", "-Dedc.fs.config=/app/configuration.properties", "-jar", "edc-controlplane.jar"] diff --git a/edc-dataplane/src/main/docker/Dockerfile b/edc-dataplane/src/main/docker/Dockerfile index cd934c193..c6250c42d 100644 --- a/edc-dataplane/src/main/docker/Dockerfile +++ b/edc-dataplane/src/main/docker/Dockerfile @@ -1,17 +1,8 @@ -FROM openjdk:11-jre-slim-buster +FROM gcr.io/distroless/java11-debian11 ARG JAR -ENV CONFIGURATION_PROPERTIES=/app/configuration.properties - -RUN useradd --create-home app - WORKDIR /app -USER app - COPY $JAR edc-dataplane.jar -ENTRYPOINT java \ - -Djava.security.edg=file:/dev/.urandom \ - -Dedc.fs.config="${CONFIGURATION_PROPERTIES}" \ - -jar edc-dataplane.jar \ No newline at end of file +CMD ["-Djava.security.edg=file:/dev/.urandom", "-Dedc.fs.config=/app/configuration.properties", "-jar", "edc-dataplane.jar"] \ No newline at end of file From f44e5bd941baa02babf77068bde42fbdbe6762af Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Wed, 30 Mar 2022 15:16:45 +0200 Subject: [PATCH 006/433] Feature: classpath hardening (#6) control/data-plane classpath hardening * Collect all non-sample/launcher edc artifacts within pom's dependencyManagement * Revise control/data plane dependencies --- .../edc-controlplane-cosmosdb/pom.xml | 94 ++- edc-dataplane/pom.xml | 32 +- pom.xml | 536 +++++++++++++++++- 3 files changed, 625 insertions(+), 37 deletions(-) diff --git a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml index 327e0499e..f54c7dc3f 100644 --- a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml +++ b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml @@ -47,12 +47,19 @@ META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension + + + META-INF/services/org.eclipse.dataspaceconnector.spi.system.VaultExtension + + META-INF/services/org.eclipse.dataspaceconnector.spi.system.ConfigurationExtension + @@ -62,55 +69,65 @@ + - jakarta.ws.rs - jakarta.ws.rs-api - 3.0.0 + org.eclipse.dataspaceconnector + filesystem-configuration + + org.eclipse.dataspaceconnector - web-spi + azure-vault - + + + org.eclipse.dataspaceconnector - core-spi + control-api + org.eclipse.dataspaceconnector - transfer-process-store-memory + observability-api + + org.eclipse.dataspaceconnector - contractnegotiation-store-memory + ids-api-configuration org.eclipse.dataspaceconnector - ids + ids-api-multipart-dispatcher-v1 org.eclipse.dataspaceconnector - filesystem-configuration + ids-api-multipart-endpoint-v1 org.eclipse.dataspaceconnector - iam-mock + ids-api-transform-v1 org.eclipse.dataspaceconnector - control-api + ids-core org.eclipse.dataspaceconnector - http + ids-spi org.eclipse.dataspaceconnector - azure-vault + ids-token-validation + + org.eclipse.dataspaceconnector assetindex-cosmos @@ -127,6 +144,53 @@ org.eclipse.dataspaceconnector transfer-process-store-cosmos + + + org.eclipse.dataspaceconnector + policy-store-memory + + + + + org.eclipse.dataspaceconnector + core-base + + + org.eclipse.dataspaceconnector + core-boot + + + org.eclipse.dataspaceconnector + transfer + + + org.eclipse.dataspaceconnector + contract + + + + + org.eclipse.dataspaceconnector + iam-mock + + + + + org.eclipse.dataspaceconnector + http + + + + org.eclipse.dataspaceconnector + http-receiver + + + + + jakarta.ws.rs + jakarta.ws.rs-api + 3.0.0 + diff --git a/edc-dataplane/pom.xml b/edc-dataplane/pom.xml index 4f9c5f7fa..12bb2f997 100644 --- a/edc-dataplane/pom.xml +++ b/edc-dataplane/pom.xml @@ -37,12 +37,19 @@ META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension + + + META-INF/services/org.eclipse.dataspaceconnector.spi.system.VaultExtension + + META-INF/services/org.eclipse.dataspaceconnector.spi.system.ConfigurationExtension + @@ -52,37 +59,44 @@ + org.eclipse.dataspaceconnector - web-spi + filesystem-configuration + + org.eclipse.dataspaceconnector - core-base + data-plane-framework org.eclipse.dataspaceconnector - core-boot + data-plane-http org.eclipse.dataspaceconnector - http + data-plane-api org.eclipse.dataspaceconnector - data-plane-framework + observability-api + + org.eclipse.dataspaceconnector - data-plane-http + core-base org.eclipse.dataspaceconnector - data-plane-api + core-boot - + + + org.eclipse.dataspaceconnector - filesystem-configuration + http diff --git a/pom.xml b/pom.xml index 34d6dc55e..5440b06c3 100644 --- a/pom.xml +++ b/pom.xml @@ -108,6 +108,176 @@ + + org.eclipse.dataspaceconnector + api-configuration + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + api-core + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + asset-api + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + assetindex-cosmos + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + assetindex-memory + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + auth-spi + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + auth-tokenbased + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + aws-s3 + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + aws-test + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + azure-eventgrid + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + azure-eventgrid-config + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + azure-test + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + azure-vault + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + blob-core + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + blob-operator + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + blob-provision + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + blobstorage + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + catalog + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + catalog-cache + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + catalog-cache-store-memory + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + catalog-node-directory-memory + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + catalog-spi + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + common-util + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + contract + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + contractagreement-api + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + contractdefinition-api + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + contract-definition-store-cosmos + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + contractdefinition-store-memory + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + contractnegotiation-api + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + contract-negotiation-store-cosmos + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + contractnegotiation-store-memory + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + contract-spi + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + control-api + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + core + ${org.eclipse.dataspaceconnector.version} + org.eclipse.dataspaceconnector core-base @@ -120,7 +290,47 @@ org.eclipse.dataspaceconnector - http + core-micrometer + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + core-policy + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + core-spi + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + cosmos + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + cosmos-common + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + dataloading + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + data-management-api + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + data-plane-api + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + data-plane-azure-storage ${org.eclipse.dataspaceconnector.version} @@ -135,7 +345,62 @@ org.eclipse.dataspaceconnector - data-plane-api + data-plane-selector-client + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + data-plane-selector-spi + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + data-plane-selector-store + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + data-plane-spi + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + data-plane-transfer-core + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + data-plane-transfer-spi + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + data-plane-transfer-sync + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + decentralized-identity + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + did-document-store-memory + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + dummy-credentials-verifier + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + fcc-node-directory-cosmos + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + federated-catalog-spi ${org.eclipse.dataspaceconnector.version} @@ -145,27 +410,62 @@ org.eclipse.dataspaceconnector - web-spi + filesystem-vault ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector - core-spi + http ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector - core + http-receiver ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector - transfer-process-store-memory + iam-daps ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector - contractnegotiation-store-memory + iam-mock + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + identity-common-test + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + identity-did-core + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + identity-did-crypto + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + identity-did-service + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + identity-did-spi + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + identity-did-web + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + identity-hub-memory ${org.eclipse.dataspaceconnector.version} @@ -175,32 +475,222 @@ org.eclipse.dataspaceconnector - iam-mock + ids-api-configuration ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector - control-api + ids-api-multipart-dispatcher-v1 ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector - azure-vault + ids-api-multipart-endpoint-v1 ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector - assetindex-cosmos + ids-api-transform-v1 ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector - contract-definition-store-cosmos + ids-core ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector - contract-negotiation-store-cosmos + ids-policy + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + ids-spi + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + ids-token-validation + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + in-memory + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + jdk-logger-monitor + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + jersey + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + jersey-micrometer + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + jetty + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + jetty-micrometer + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + oauth2-core + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + oauth2-spi + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + observability-api + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + policydefinition-api + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + policy-engine + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + policy-evaluator + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + policy-spi + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + policy-store-memory + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + registration-service + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + registration-service-api + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + s3-core + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + s3-operator + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + s3-provision + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + spi + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + sql-common + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + sql-contractdefinition-store + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + sql-lease + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + sql-pool-apache-commons-pool + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + sql-transferprocess-store + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + state-machine-lib + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + token-generation-lib + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + token-validation-lib + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + transaction-atomikos + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + transaction-datasource-spi + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + transaction-local + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + transaction-spi + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + transfer + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + transfer-functions-core + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + transfer-functions-spi + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + transferprocess-api ${org.eclipse.dataspaceconnector.version} @@ -208,6 +698,26 @@ transfer-process-store-cosmos ${org.eclipse.dataspaceconnector.version} + + org.eclipse.dataspaceconnector + transfer-process-store-memory + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + transfer-spi + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + transport-spi + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + web-spi + ${org.eclipse.dataspaceconnector.version} + \ No newline at end of file From a671d61c1575f7722feee67c17c15325a314e80e Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Wed, 30 Mar 2022 15:28:58 +0200 Subject: [PATCH 007/433] Add minimal memory controlplane (#7) --- .github/dependabot.yml | 9 + .github/workflows/build.yaml | 35 +++ .../edc-controlplane-memory/pom.xml | 221 ++++++++++++++++++ .../src/main/docker/Dockerfile | 8 + .../controlplane/ControlPlaneController.java | 66 ++++++ .../controlplane/ControlPlaneExtension.java | 32 +++ ...spaceconnector.spi.system.ServiceExtension | 1 + edc-controlplane/pom.xml | 1 + 8 files changed, 373 insertions(+) create mode 100644 edc-controlplane/edc-controlplane-memory/pom.xml create mode 100644 edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile create mode 100644 edc-controlplane/edc-controlplane-memory/src/main/java/net/catenax/edc/controlplane/ControlPlaneController.java create mode 100644 edc-controlplane/edc-controlplane-memory/src/main/java/net/catenax/edc/controlplane/ControlPlaneExtension.java create mode 100644 edc-controlplane/edc-controlplane-memory/src/main/resources/META-INF/resources/org.eclipse.dataspaceconnector.spi.system.ServiceExtension diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 1e3ed44f7..8228e72c3 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -28,6 +28,15 @@ updates: - "docker" schedule: interval: "daily" + - + package-ecosystem: "docker" + target-branch: develop + directory: "edc-controlplane/edc-controlplane-memory/src/main/docker" + labels: + - "dependabot" + - "docker" + schedule: + interval: "daily" - package-ecosystem: "maven" target-branch: develop diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 244b42a33..488f14905 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -50,6 +50,41 @@ jobs: run: ./gradlew publishToMavenLocal working-directory: edc + ################################# + ### edc-controlplane-memory ### + ################################# + - + name: Build edc-controlplane-memory + run: |- + ./mvnw -s settings.xml -B -pl .,edc-controlplane/edc-controlplane-memory -am verify + env: + GITHUB_PACKAGE_USERNAME: ${{ github.actor }} + GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} + - + name: Docker Metadata + id: edc_controlplane_memory_meta + uses: docker/metadata-action@v3 + with: + images: | + ghcr.io/catenax-ng/catena-x-edc/edc-controlplane-memory + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{raw}} + type=match,pattern=\d.\d.\d + type=sha + - + name: Build Docker Image + uses: docker/build-push-action@v2 + with: + context: . + file: edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile + build-args: | + JAR=edc-controlplane/edc-controlplane-memory/target/edc-controlplane-memory.jar + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.edc_controlplane_memory_meta.outputs.tags }} + labels: ${{ steps.edc_controlplane_memory_meta.outputs.labels }} + ################################# ### edc-controlplane-cosmosdb ### ################################# diff --git a/edc-controlplane/edc-controlplane-memory/pom.xml b/edc-controlplane/edc-controlplane-memory/pom.xml new file mode 100644 index 000000000..edb17ee7c --- /dev/null +++ b/edc-controlplane/edc-controlplane-memory/pom.xml @@ -0,0 +1,221 @@ + + + + edc-controlplane + net.catenax.edc + 0.0.2-SNAPSHOT + + 4.0.0 + + edc-controlplane-memory + jar + + + + + org.apache.maven.plugins + maven-shade-plugin + + + + shade + + + ${project.artifactId} + true + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + org.eclipse.dataspaceconnector.boot.system.runtime.BaseRuntime + + + + + META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension + + + + + META-INF/services/org.eclipse.dataspaceconnector.spi.system.VaultExtension + + + + + META-INF/services/org.eclipse.dataspaceconnector.spi.system.ConfigurationExtension + + + + + + + + + + + + + + + org.eclipse.dataspaceconnector + filesystem-configuration + + + + + org.eclipse.dataspaceconnector + control-api + + + + org.eclipse.dataspaceconnector + observability-api + + + + + org.eclipse.dataspaceconnector + ids-api-configuration + + + org.eclipse.dataspaceconnector + ids-api-multipart-dispatcher-v1 + + + org.eclipse.dataspaceconnector + ids-api-multipart-endpoint-v1 + + + org.eclipse.dataspaceconnector + ids-api-transform-v1 + + + org.eclipse.dataspaceconnector + ids-core + + + org.eclipse.dataspaceconnector + ids-spi + + + org.eclipse.dataspaceconnector + ids-token-validation + + + + + org.eclipse.dataspaceconnector + assetindex-memory + + + org.eclipse.dataspaceconnector + contractdefinition-store-memory + + + org.eclipse.dataspaceconnector + contractnegotiation-store-memory + + + org.eclipse.dataspaceconnector + transfer-process-store-memory + + + + org.eclipse.dataspaceconnector + policy-store-memory + + + + + org.eclipse.dataspaceconnector + core-base + + + org.eclipse.dataspaceconnector + core-boot + + + org.eclipse.dataspaceconnector + transfer + + + org.eclipse.dataspaceconnector + contract + + + + + org.eclipse.dataspaceconnector + iam-mock + + + + + org.eclipse.dataspaceconnector + http + + + + org.eclipse.dataspaceconnector + http-receiver + + + + + jakarta.ws.rs + jakarta.ws.rs-api + 3.0.0 + + + + + + with-docker-image + + + + com.spotify + dockerfile-maven-plugin + + + default + package + + build + + + + + . + src/main/docker/Dockerfile + ${project.artifactId} + ${project.version} + + target/${project.artifactId}.jar + + + + + + + + \ No newline at end of file diff --git a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile new file mode 100644 index 000000000..71b56b37e --- /dev/null +++ b/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile @@ -0,0 +1,8 @@ +FROM gcr.io/distroless/java11-debian11 +ARG JAR + +WORKDIR /app + +COPY $JAR edc-controlplane.jar + +CMD ["-Djava.security.edg=file:/dev/.urandom", "-Dedc.fs.config=/app/configuration.properties", "-jar", "edc-controlplane.jar"] diff --git a/edc-controlplane/edc-controlplane-memory/src/main/java/net/catenax/edc/controlplane/ControlPlaneController.java b/edc-controlplane/edc-controlplane-memory/src/main/java/net/catenax/edc/controlplane/ControlPlaneController.java new file mode 100644 index 000000000..6394945a4 --- /dev/null +++ b/edc-controlplane/edc-controlplane-memory/src/main/java/net/catenax/edc/controlplane/ControlPlaneController.java @@ -0,0 +1,66 @@ +package net.catenax.edc.controlplane; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import java.util.Map; +import org.eclipse.dataspaceconnector.dataloading.AssetLoader; +import org.eclipse.dataspaceconnector.spi.contract.offer.store.ContractDefinitionStore; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.transfer.store.TransferProcessStore; +import org.eclipse.dataspaceconnector.spi.types.domain.DataAddress; +import org.eclipse.dataspaceconnector.spi.types.domain.asset.Asset; +import org.eclipse.dataspaceconnector.spi.types.domain.contract.offer.ContractDefinition; +import org.eclipse.dataspaceconnector.spi.types.domain.transfer.TransferProcess; + +@Consumes({MediaType.APPLICATION_JSON}) +@Produces({MediaType.APPLICATION_JSON}) +@Path("/") +public class ControlPlaneController { + private final Monitor monitor; + private final AssetLoader assetLoader; + private final ContractDefinitionStore contractDefinitionStore; + private final TransferProcessStore transferProcessStore; + + public ControlPlaneController( + Monitor monitor, + AssetLoader assetLoader, + ContractDefinitionStore contractDefinitionStore, + TransferProcessStore transferProcessStore) { + this.monitor = monitor; + this.assetLoader = assetLoader; + this.contractDefinitionStore = contractDefinitionStore; + this.transferProcessStore = transferProcessStore; + } + + // TODO: most of these api will be replaced by data management api + @Path("/assets") + @POST + public String createAsset(Map> properties) { + var assetProperties = properties.get("asset"); + var asset = Asset.Builder.newInstance().properties(assetProperties).build(); + + var dataAddressProperties = properties.get("dataAddress"); + var dataAddress = DataAddress.Builder.newInstance().properties(dataAddressProperties).build(); + monitor.debug("Create asset: " + asset.getId()); + assetLoader.accept(asset, dataAddress); + return asset.getId(); + } + + @Path("/contractdefinitions") + @POST + public void createContractDefinition(ContractDefinition definition) { + monitor.debug("Create contract definition: " + definition.getId()); + contractDefinitionStore.save(definition); + } + + @Path("/transfers/{id}") + @GET + public TransferProcess getTransferProcess(@PathParam("id") String id) { + return transferProcessStore.find(id); + } +} diff --git a/edc-controlplane/edc-controlplane-memory/src/main/java/net/catenax/edc/controlplane/ControlPlaneExtension.java b/edc-controlplane/edc-controlplane-memory/src/main/java/net/catenax/edc/controlplane/ControlPlaneExtension.java new file mode 100644 index 000000000..459ba1746 --- /dev/null +++ b/edc-controlplane/edc-controlplane-memory/src/main/java/net/catenax/edc/controlplane/ControlPlaneExtension.java @@ -0,0 +1,32 @@ +package net.catenax.edc.controlplane; + +import org.eclipse.dataspaceconnector.dataloading.AssetLoader; +import org.eclipse.dataspaceconnector.spi.WebService; +import org.eclipse.dataspaceconnector.spi.contract.offer.store.ContractDefinitionStore; +import org.eclipse.dataspaceconnector.spi.system.Inject; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtension; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; +import org.eclipse.dataspaceconnector.spi.transfer.store.TransferProcessStore; + +public class ControlPlaneExtension implements ServiceExtension { + + @Inject private WebService webService; + + @Inject private AssetLoader assetLoader; + + @Inject private ContractDefinitionStore contractDefinitionStore; + + @Inject private TransferProcessStore transferProcessStore; + + @Override + public String name() { + return "Control Plane"; + } + + @Override + public void initialize(ServiceExtensionContext context) { + webService.registerResource( + new ControlPlaneController( + context.getMonitor(), assetLoader, contractDefinitionStore, transferProcessStore)); + } +} diff --git a/edc-controlplane/edc-controlplane-memory/src/main/resources/META-INF/resources/org.eclipse.dataspaceconnector.spi.system.ServiceExtension b/edc-controlplane/edc-controlplane-memory/src/main/resources/META-INF/resources/org.eclipse.dataspaceconnector.spi.system.ServiceExtension new file mode 100644 index 000000000..af2b75359 --- /dev/null +++ b/edc-controlplane/edc-controlplane-memory/src/main/resources/META-INF/resources/org.eclipse.dataspaceconnector.spi.system.ServiceExtension @@ -0,0 +1 @@ +net.catenax.edc.controlplane.ControlPlaneExtension \ No newline at end of file diff --git a/edc-controlplane/pom.xml b/edc-controlplane/pom.xml index 29c807f9a..c1d6da782 100644 --- a/edc-controlplane/pom.xml +++ b/edc-controlplane/pom.xml @@ -15,5 +15,6 @@ edc-controlplane-cosmosdb + edc-controlplane-memory \ No newline at end of file From 95377b266f50ddec1ad116d7a67fcba12c7d85c4 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Wed, 30 Mar 2022 16:06:41 +0200 Subject: [PATCH 008/433] Bumb edc to 3305340d6a19ba919540d28a731b3cab6d23857a + enable image publication of develop branch (#8) --- .github/workflows/build.yaml | 15 ++++++++------- edc | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 488f14905..09c0b238f 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -5,6 +5,7 @@ on: push: branches: - main + - develop tags: - '[0-9]+.[0-9]+.[0-9]+' pull_request: @@ -37,7 +38,7 @@ jobs: run: |- [ ! -d "edc" ] && git submodule add https://github.com/eclipse-dataspaceconnector/DataSpaceConnector.git edc git submodule update --init - git -C edc checkout 705a44712fecb9cf5ac18622311e6a2dbd86ffc3 + git -C edc checkout 3305340d6a19ba919540d28a731b3cab6d23857a - name: Set up JDK 11 uses: actions/setup-java@v2 @@ -61,7 +62,7 @@ jobs: GITHUB_PACKAGE_USERNAME: ${{ github.actor }} GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} - - name: Docker Metadata + name: edc-controlplane-memory Docker Metadata id: edc_controlplane_memory_meta uses: docker/metadata-action@v3 with: @@ -74,7 +75,7 @@ jobs: type=match,pattern=\d.\d.\d type=sha - - name: Build Docker Image + name: Build edc-controlplane-memory Docker Image uses: docker/build-push-action@v2 with: context: . @@ -96,7 +97,7 @@ jobs: GITHUB_PACKAGE_USERNAME: ${{ github.actor }} GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} - - name: Docker Metadata + name: edc-controlplane-cosmosdb Docker Metadata id: edc_controlplane_cosmosdb_meta uses: docker/metadata-action@v3 with: @@ -109,7 +110,7 @@ jobs: type=match,pattern=\d.\d.\d type=sha - - name: Build Docker Image + name: Build edc-controlplane-cosmosdb Docker Image uses: docker/build-push-action@v2 with: context: . @@ -131,7 +132,7 @@ jobs: GITHUB_PACKAGE_USERNAME: ${{ github.actor }} GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} - - name: Docker Metadata + name: edc-dataplane Docker Metadata id: edc_dataplane_meta uses: docker/metadata-action@v3 with: @@ -144,7 +145,7 @@ jobs: type=match,pattern=\d.\d.\d type=sha - - name: Build Docker Image + name: Build edc-dataplane Docker Image uses: docker/build-push-action@v2 with: context: . diff --git a/edc b/edc index 705a44712..3305340d6 160000 --- a/edc +++ b/edc @@ -1 +1 @@ -Subproject commit 705a44712fecb9cf5ac18622311e6a2dbd86ffc3 +Subproject commit 3305340d6a19ba919540d28a731b3cab6d23857a From fdb35f98621af2eda814a0ae1943b023d4e93ba0 Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Wed, 30 Mar 2022 21:19:52 +0200 Subject: [PATCH 009/433] Update .gitignore & fix ServiceExtension directory (#9) * add '.vscode' to .gitignore * mv ServiceExtension file into META-INF/serices * add DS_STORE to .gitignore --- .gitignore | 4 ++++ ...org.eclipse.dataspaceconnector.spi.system.ServiceExtension | 0 2 files changed, 4 insertions(+) rename edc-controlplane/edc-controlplane-memory/src/main/resources/META-INF/{resources => services}/org.eclipse.dataspaceconnector.spi.system.ServiceExtension (100%) diff --git a/.gitignore b/.gitignore index 1e4b29306..cb53525fa 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,10 @@ .idea/**/usage.statistics.xml .idea/**/dictionaries .idea/**/shelf +.vscode/** + +# Macbooks +**/.DS_STORE # Generated files .idea/**/contentModel.xml diff --git a/edc-controlplane/edc-controlplane-memory/src/main/resources/META-INF/resources/org.eclipse.dataspaceconnector.spi.system.ServiceExtension b/edc-controlplane/edc-controlplane-memory/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension similarity index 100% rename from edc-controlplane/edc-controlplane-memory/src/main/resources/META-INF/resources/org.eclipse.dataspaceconnector.spi.system.ServiceExtension rename to edc-controlplane/edc-controlplane-memory/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension From d2bd19745273f4b5f83ab6a78bd8ce5cbf8ecd46 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Wed, 30 Mar 2022 22:08:11 +0200 Subject: [PATCH 010/433] Fix controlplane cosmosdb servicelocator (#11) Signed-off-by: Denis Neuling --- .../org.eclipse.dataspaceconnector.spi.system.ServiceExtension | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename edc-controlplane/edc-controlplane-cosmosdb/src/main/resources/META-INF/{resources => services}/org.eclipse.dataspaceconnector.spi.system.ServiceExtension (100%) diff --git a/edc-controlplane/edc-controlplane-cosmosdb/src/main/resources/META-INF/resources/org.eclipse.dataspaceconnector.spi.system.ServiceExtension b/edc-controlplane/edc-controlplane-cosmosdb/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension similarity index 100% rename from edc-controlplane/edc-controlplane-cosmosdb/src/main/resources/META-INF/resources/org.eclipse.dataspaceconnector.spi.system.ServiceExtension rename to edc-controlplane/edc-controlplane-cosmosdb/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension From 165090bc1c20849a93ef68bdbfe663ff13334c51 Mon Sep 17 00:00:00 2001 From: Dominik Pinsel Date: Thu, 31 Mar 2022 10:15:20 +0200 Subject: [PATCH 011/433] fix createAsset API in ControlPlaneController.java Signed-off-by: Dominik Pinsel --- .../controlplane/ControlPlaneController.java | 63 +++++++++++++++++-- 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/edc-controlplane/edc-controlplane-memory/src/main/java/net/catenax/edc/controlplane/ControlPlaneController.java b/edc-controlplane/edc-controlplane-memory/src/main/java/net/catenax/edc/controlplane/ControlPlaneController.java index 6394945a4..ab3209f30 100644 --- a/edc-controlplane/edc-controlplane-memory/src/main/java/net/catenax/edc/controlplane/ControlPlaneController.java +++ b/edc-controlplane/edc-controlplane-memory/src/main/java/net/catenax/edc/controlplane/ControlPlaneController.java @@ -19,7 +19,7 @@ @Consumes({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON}) -@Path("/") +@Path("/v1") public class ControlPlaneController { private final Monitor monitor; private final AssetLoader assetLoader; @@ -40,11 +40,11 @@ public ControlPlaneController( // TODO: most of these api will be replaced by data management api @Path("/assets") @POST - public String createAsset(Map> properties) { - var assetProperties = properties.get("asset"); + public String createAsset(AssetEntryDto assetEntry) { + var assetProperties = assetEntry.asset.properties; var asset = Asset.Builder.newInstance().properties(assetProperties).build(); - var dataAddressProperties = properties.get("dataAddress"); + var dataAddressProperties = assetEntry.dataAddress.properties; var dataAddress = DataAddress.Builder.newInstance().properties(dataAddressProperties).build(); monitor.debug("Create asset: " + asset.getId()); assetLoader.accept(asset, dataAddress); @@ -63,4 +63,59 @@ public void createContractDefinition(ContractDefinition definition) { public TransferProcess getTransferProcess(@PathParam("id") String id) { return transferProcessStore.find(id); } + + private static class AssetDto { + + public AssetDto() {} + + Map properties; + + public Map getProperties() { + return properties; + } + + public void setProperties(Map properties) { + this.properties = properties; + } + } + + private static class DataAddressDto { + + public DataAddressDto() {} + + Map properties; + + public Map getProperties() { + return properties; + } + + public void setProperties(Map properties) { + this.properties = properties; + } + } + + private static class AssetEntryDto { + + public AssetEntryDto() {} + + private AssetDto asset; + + private DataAddressDto dataAddress; + + public AssetDto getAsset() { + return asset; + } + + public void setAsset(AssetDto asset) { + this.asset = asset; + } + + public DataAddressDto getDataAddress() { + return dataAddress; + } + + public void setDataAddress(DataAddressDto dataAddress) { + this.dataAddress = dataAddress; + } + } } From a1da627107eabf99046ab7761c2528a835d20094 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Thu, 31 Mar 2022 12:08:28 +0200 Subject: [PATCH 012/433] Use azure bom for cosmosdb backend controlplane (#13) --- .../edc-controlplane-cosmosdb/pom.xml | 52 ++++++++++++++++++- .../edc-controlplane-memory/pom.xml | 1 + edc-dataplane/pom.xml | 13 ++++- pom.xml | 18 +++++++ 4 files changed, 81 insertions(+), 3 deletions(-) diff --git a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml index f54c7dc3f..d323b4646 100644 --- a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml +++ b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml @@ -29,6 +29,7 @@ *:* + module-info.class META-INF/*.SF META-INF/*.DSA META-INF/*.RSA @@ -59,7 +60,8 @@ META-INF/services/org.eclipse.dataspaceconnector.spi.system.ConfigurationExtension - + @@ -79,6 +81,16 @@ org.eclipse.dataspaceconnector azure-vault + + + com.azure + azure-security-keyvault-secrets + + + com.azure + azure-identity + + @@ -131,18 +143,42 @@ org.eclipse.dataspaceconnector assetindex-cosmos + + + com.azure + azure-cosmos + + org.eclipse.dataspaceconnector contract-definition-store-cosmos + + + com.azure + azure-cosmos + + org.eclipse.dataspaceconnector contract-negotiation-store-cosmos + + + com.azure + azure-cosmos + + org.eclipse.dataspaceconnector transfer-process-store-cosmos + + + com.azure + azure-cosmos + + @@ -189,7 +225,19 @@ jakarta.ws.rs jakarta.ws.rs-api - 3.0.0 + + + + com.azure + azure-cosmos + + + com.azure + azure-identity + + + com.azure + azure-security-keyvault-secrets diff --git a/edc-controlplane/edc-controlplane-memory/pom.xml b/edc-controlplane/edc-controlplane-memory/pom.xml index edb17ee7c..15f9991ae 100644 --- a/edc-controlplane/edc-controlplane-memory/pom.xml +++ b/edc-controlplane/edc-controlplane-memory/pom.xml @@ -29,6 +29,7 @@ *:* + module-info.class META-INF/*.SF META-INF/*.DSA META-INF/*.RSA diff --git a/edc-dataplane/pom.xml b/edc-dataplane/pom.xml index 12bb2f997..d14aefc77 100644 --- a/edc-dataplane/pom.xml +++ b/edc-dataplane/pom.xml @@ -25,6 +25,17 @@ ${project.artifactId} true + + + *:* + + module-info.class + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + @@ -49,7 +60,7 @@ META-INF/services/org.eclipse.dataspaceconnector.spi.system.ConfigurationExtension - + diff --git a/pom.xml b/pom.xml index 5440b06c3..d1f97369a 100644 --- a/pom.xml +++ b/pom.xml @@ -24,6 +24,8 @@ 1.31.3 0.0.1-SNAPSHOT + 1.2.0 + 3.0.0 @@ -108,6 +110,22 @@ + + + com.azure + azure-sdk-bom + ${com.azure.sdk.bom.version} + pom + import + + + + + jakarta.ws.rs + jakarta.ws.rs-api + ${jakarta.ws.rs.api.version} + + org.eclipse.dataspaceconnector api-configuration From 0f54cb727a02b272f08096b2deb236b9da079283 Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Tue, 5 Apr 2022 14:10:35 +0200 Subject: [PATCH 013/433] new aas controller extension (#14) --- .../edc-controlplane-cosmosdb/pom.xml | 36 ++++------ .../controlplane/ControlPlaneController.java | 66 ------------------- .../edc-controlplane-memory/pom.xml | 45 ++++++------- .../controlplane/ControlPlaneExtension.java | 32 --------- ...spaceconnector.spi.system.ServiceExtension | 1 - edc-extensions/aas-controller/README.md | 3 + edc-extensions/aas-controller/pom.xml | 43 ++++++++++++ .../controlplane/ControlPlaneController.java | 14 ++++ .../controlplane/ControlPlaneExtension.java | 14 ++++ ...spaceconnector.spi.system.ServiceExtension | 0 edc-extensions/pom.xml | 19 ++++++ pom.xml | 15 +++-- 12 files changed, 137 insertions(+), 151 deletions(-) delete mode 100644 edc-controlplane/edc-controlplane-cosmosdb/src/main/java/net/catenax/edc/controlplane/ControlPlaneController.java delete mode 100644 edc-controlplane/edc-controlplane-memory/src/main/java/net/catenax/edc/controlplane/ControlPlaneExtension.java delete mode 100644 edc-controlplane/edc-controlplane-memory/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension create mode 100644 edc-extensions/aas-controller/README.md create mode 100644 edc-extensions/aas-controller/pom.xml rename {edc-controlplane/edc-controlplane-memory => edc-extensions/aas-controller}/src/main/java/net/catenax/edc/controlplane/ControlPlaneController.java (90%) rename {edc-controlplane/edc-controlplane-cosmosdb => edc-extensions/aas-controller}/src/main/java/net/catenax/edc/controlplane/ControlPlaneExtension.java (75%) rename {edc-controlplane/edc-controlplane-cosmosdb => edc-extensions/aas-controller}/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension (100%) create mode 100644 edc-extensions/pom.xml diff --git a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml index d323b4646..c35e60ec8 100644 --- a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml +++ b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml @@ -1,7 +1,5 @@ - + net.catenax.edc edc-controlplane @@ -37,31 +35,26 @@ - + org.eclipse.dataspaceconnector.boot.system.runtime.BaseRuntime - + META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension - + META-INF/services/org.eclipse.dataspaceconnector.spi.system.VaultExtension - + META-INF/services/org.eclipse.dataspaceconnector.spi.system.ConfigurationExtension - + @@ -94,7 +87,7 @@ - + org.eclipse.dataspaceconnector control-api @@ -181,7 +174,8 @@ - + + org.eclipse.dataspaceconnector policy-store-memory @@ -211,22 +205,18 @@ - + + org.eclipse.dataspaceconnector http - + + org.eclipse.dataspaceconnector http-receiver - - - jakarta.ws.rs - jakarta.ws.rs-api - - com.azure azure-cosmos diff --git a/edc-controlplane/edc-controlplane-cosmosdb/src/main/java/net/catenax/edc/controlplane/ControlPlaneController.java b/edc-controlplane/edc-controlplane-cosmosdb/src/main/java/net/catenax/edc/controlplane/ControlPlaneController.java deleted file mode 100644 index 6394945a4..000000000 --- a/edc-controlplane/edc-controlplane-cosmosdb/src/main/java/net/catenax/edc/controlplane/ControlPlaneController.java +++ /dev/null @@ -1,66 +0,0 @@ -package net.catenax.edc.controlplane; - -import jakarta.ws.rs.Consumes; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.POST; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.core.MediaType; -import java.util.Map; -import org.eclipse.dataspaceconnector.dataloading.AssetLoader; -import org.eclipse.dataspaceconnector.spi.contract.offer.store.ContractDefinitionStore; -import org.eclipse.dataspaceconnector.spi.monitor.Monitor; -import org.eclipse.dataspaceconnector.spi.transfer.store.TransferProcessStore; -import org.eclipse.dataspaceconnector.spi.types.domain.DataAddress; -import org.eclipse.dataspaceconnector.spi.types.domain.asset.Asset; -import org.eclipse.dataspaceconnector.spi.types.domain.contract.offer.ContractDefinition; -import org.eclipse.dataspaceconnector.spi.types.domain.transfer.TransferProcess; - -@Consumes({MediaType.APPLICATION_JSON}) -@Produces({MediaType.APPLICATION_JSON}) -@Path("/") -public class ControlPlaneController { - private final Monitor monitor; - private final AssetLoader assetLoader; - private final ContractDefinitionStore contractDefinitionStore; - private final TransferProcessStore transferProcessStore; - - public ControlPlaneController( - Monitor monitor, - AssetLoader assetLoader, - ContractDefinitionStore contractDefinitionStore, - TransferProcessStore transferProcessStore) { - this.monitor = monitor; - this.assetLoader = assetLoader; - this.contractDefinitionStore = contractDefinitionStore; - this.transferProcessStore = transferProcessStore; - } - - // TODO: most of these api will be replaced by data management api - @Path("/assets") - @POST - public String createAsset(Map> properties) { - var assetProperties = properties.get("asset"); - var asset = Asset.Builder.newInstance().properties(assetProperties).build(); - - var dataAddressProperties = properties.get("dataAddress"); - var dataAddress = DataAddress.Builder.newInstance().properties(dataAddressProperties).build(); - monitor.debug("Create asset: " + asset.getId()); - assetLoader.accept(asset, dataAddress); - return asset.getId(); - } - - @Path("/contractdefinitions") - @POST - public void createContractDefinition(ContractDefinition definition) { - monitor.debug("Create contract definition: " + definition.getId()); - contractDefinitionStore.save(definition); - } - - @Path("/transfers/{id}") - @GET - public TransferProcess getTransferProcess(@PathParam("id") String id) { - return transferProcessStore.find(id); - } -} diff --git a/edc-controlplane/edc-controlplane-memory/pom.xml b/edc-controlplane/edc-controlplane-memory/pom.xml index 15f9991ae..f77527da2 100644 --- a/edc-controlplane/edc-controlplane-memory/pom.xml +++ b/edc-controlplane/edc-controlplane-memory/pom.xml @@ -1,7 +1,5 @@ - + edc-controlplane net.catenax.edc @@ -37,30 +35,26 @@ - + org.eclipse.dataspaceconnector.boot.system.runtime.BaseRuntime - + META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension - + META-INF/services/org.eclipse.dataspaceconnector.spi.system.VaultExtension - + META-INF/services/org.eclipse.dataspaceconnector.spi.system.ConfigurationExtension - + @@ -70,6 +64,12 @@ + + + net.catenax.edc.extensions + aas-controller + + org.eclipse.dataspaceconnector @@ -77,16 +77,15 @@ - + + org.eclipse.dataspaceconnector control-api - org.eclipse.dataspaceconnector observability-api @@ -140,7 +139,8 @@ transfer-process-store-memory - + + org.eclipse.dataspaceconnector policy-store-memory @@ -170,22 +170,17 @@ - + + org.eclipse.dataspaceconnector http - + + org.eclipse.dataspaceconnector http-receiver - - - - jakarta.ws.rs - jakarta.ws.rs-api - 3.0.0 - diff --git a/edc-controlplane/edc-controlplane-memory/src/main/java/net/catenax/edc/controlplane/ControlPlaneExtension.java b/edc-controlplane/edc-controlplane-memory/src/main/java/net/catenax/edc/controlplane/ControlPlaneExtension.java deleted file mode 100644 index 459ba1746..000000000 --- a/edc-controlplane/edc-controlplane-memory/src/main/java/net/catenax/edc/controlplane/ControlPlaneExtension.java +++ /dev/null @@ -1,32 +0,0 @@ -package net.catenax.edc.controlplane; - -import org.eclipse.dataspaceconnector.dataloading.AssetLoader; -import org.eclipse.dataspaceconnector.spi.WebService; -import org.eclipse.dataspaceconnector.spi.contract.offer.store.ContractDefinitionStore; -import org.eclipse.dataspaceconnector.spi.system.Inject; -import org.eclipse.dataspaceconnector.spi.system.ServiceExtension; -import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; -import org.eclipse.dataspaceconnector.spi.transfer.store.TransferProcessStore; - -public class ControlPlaneExtension implements ServiceExtension { - - @Inject private WebService webService; - - @Inject private AssetLoader assetLoader; - - @Inject private ContractDefinitionStore contractDefinitionStore; - - @Inject private TransferProcessStore transferProcessStore; - - @Override - public String name() { - return "Control Plane"; - } - - @Override - public void initialize(ServiceExtensionContext context) { - webService.registerResource( - new ControlPlaneController( - context.getMonitor(), assetLoader, contractDefinitionStore, transferProcessStore)); - } -} diff --git a/edc-controlplane/edc-controlplane-memory/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension b/edc-controlplane/edc-controlplane-memory/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension deleted file mode 100644 index af2b75359..000000000 --- a/edc-controlplane/edc-controlplane-memory/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension +++ /dev/null @@ -1 +0,0 @@ -net.catenax.edc.controlplane.ControlPlaneExtension \ No newline at end of file diff --git a/edc-extensions/aas-controller/README.md b/edc-extensions/aas-controller/README.md new file mode 100644 index 000000000..3ff0d0cbf --- /dev/null +++ b/edc-extensions/aas-controller/README.md @@ -0,0 +1,3 @@ +# Asset Administration Shell Controller + +This EDC extension implements a custom controller für the 'Asset Administration Shell'-Wrapper. \ No newline at end of file diff --git a/edc-extensions/aas-controller/pom.xml b/edc-extensions/aas-controller/pom.xml new file mode 100644 index 000000000..172faa38d --- /dev/null +++ b/edc-extensions/aas-controller/pom.xml @@ -0,0 +1,43 @@ + + + 4.0.0 + + + net.catenax.edc.extensions + edc-extensions + 0.0.2-SNAPSHOT + + + aas-controller + jar + + + + + org.eclipse.dataspaceconnector + web-spi + + + org.eclipse.dataspaceconnector + transfer-spi + + + org.eclipse.dataspaceconnector + contract-spi + + + org.eclipse.dataspaceconnector + contract-spi + + + org.eclipse.dataspaceconnector + dataloading + + + + + jakarta.ws.rs + jakarta.ws.rs-api + + + \ No newline at end of file diff --git a/edc-controlplane/edc-controlplane-memory/src/main/java/net/catenax/edc/controlplane/ControlPlaneController.java b/edc-extensions/aas-controller/src/main/java/net/catenax/edc/controlplane/ControlPlaneController.java similarity index 90% rename from edc-controlplane/edc-controlplane-memory/src/main/java/net/catenax/edc/controlplane/ControlPlaneController.java rename to edc-extensions/aas-controller/src/main/java/net/catenax/edc/controlplane/ControlPlaneController.java index ab3209f30..20d347265 100644 --- a/edc-controlplane/edc-controlplane-memory/src/main/java/net/catenax/edc/controlplane/ControlPlaneController.java +++ b/edc-extensions/aas-controller/src/main/java/net/catenax/edc/controlplane/ControlPlaneController.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 Daimler TSS GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Daimler TSS GmbH - Initial API and Implementation + * + */ + package net.catenax.edc.controlplane; import jakarta.ws.rs.Consumes; diff --git a/edc-controlplane/edc-controlplane-cosmosdb/src/main/java/net/catenax/edc/controlplane/ControlPlaneExtension.java b/edc-extensions/aas-controller/src/main/java/net/catenax/edc/controlplane/ControlPlaneExtension.java similarity index 75% rename from edc-controlplane/edc-controlplane-cosmosdb/src/main/java/net/catenax/edc/controlplane/ControlPlaneExtension.java rename to edc-extensions/aas-controller/src/main/java/net/catenax/edc/controlplane/ControlPlaneExtension.java index 459ba1746..f0f37820d 100644 --- a/edc-controlplane/edc-controlplane-cosmosdb/src/main/java/net/catenax/edc/controlplane/ControlPlaneExtension.java +++ b/edc-extensions/aas-controller/src/main/java/net/catenax/edc/controlplane/ControlPlaneExtension.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 Daimler TSS GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Daimler TSS GmbH - Initial API and Implementation + * + */ + package net.catenax.edc.controlplane; import org.eclipse.dataspaceconnector.dataloading.AssetLoader; diff --git a/edc-controlplane/edc-controlplane-cosmosdb/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension b/edc-extensions/aas-controller/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension similarity index 100% rename from edc-controlplane/edc-controlplane-cosmosdb/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension rename to edc-extensions/aas-controller/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension diff --git a/edc-extensions/pom.xml b/edc-extensions/pom.xml new file mode 100644 index 000000000..8f5fa495e --- /dev/null +++ b/edc-extensions/pom.xml @@ -0,0 +1,19 @@ + + + 4.0.0 + + + net.catenax.edc + catena-x-edc-parent + 0.0.2-SNAPSHOT + + + net.catenax.edc.extensions + edc-extensions + pom + + + aas-controller + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index d1f97369a..ec90f0ec6 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,5 @@ - + 4.0.0 net.catenax.edc @@ -10,6 +8,7 @@ pom + edc-extensions edc-controlplane edc-dataplane @@ -110,6 +109,14 @@ + + + net.catenax.edc.extensions + aas-controller + ${project.version} + + + com.azure @@ -125,7 +132,6 @@ jakarta.ws.rs-api ${jakarta.ws.rs.api.version} - org.eclipse.dataspaceconnector api-configuration @@ -736,6 +742,7 @@ web-spi ${org.eclipse.dataspaceconnector.version} + \ No newline at end of file From ab7a2f21b8e5cce5b545f7eaee68822bc47e95f4 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Tue, 5 Apr 2022 14:50:51 +0200 Subject: [PATCH 014/433] Add default config for Opentelemetry + logging (#15) --- .../edc-controlplane/templates/configmap.yaml | 16 ++++++++++++++ .../templates/deployment.yaml | 13 +++++++++++ .../edc-controlplane/templates/service.yaml | 4 ++++ deployment/helm/edc-controlplane/values.yaml | 14 ++++++++++++ .../edc-dataplane/templates/configmap.yaml | 16 ++++++++++++++ .../edc-dataplane/templates/deployment.yaml | 13 +++++++++++ .../helm/edc-dataplane/templates/service.yaml | 4 ++++ deployment/helm/edc-dataplane/values.yaml | 14 ++++++++++++ .../edc-controlplane-cosmosdb/pom.xml | 22 +++++++++++++++++++ .../src/main/docker/Dockerfile | 17 +++++++++++++- .../edc-controlplane-memory/pom.xml | 22 +++++++++++++++++++ .../src/main/docker/Dockerfile | 17 +++++++++++++- edc-dataplane/pom.xml | 22 +++++++++++++++++++ edc-dataplane/src/main/docker/Dockerfile | 17 +++++++++++++- 14 files changed, 208 insertions(+), 3 deletions(-) diff --git a/deployment/helm/edc-controlplane/templates/configmap.yaml b/deployment/helm/edc-controlplane/templates/configmap.yaml index 157f1d33f..437884924 100644 --- a/deployment/helm/edc-controlplane/templates/configmap.yaml +++ b/deployment/helm/edc-controlplane/templates/configmap.yaml @@ -13,3 +13,19 @@ data: # {{ $k | replace "_" "." }}= {{- end }} {{- end }} + opentelemetry.properties: |- + {{- range $k, $v := .Values.opentelemetry.properties }} + {{- if $v }} + {{ $k | replace "_" "." }}={{ $v }} + {{- else }} + # {{ $k | replace "_" "." }}= + {{- end }} + {{- end }} + logging.properties: |- + {{- range $k, $v := .Values.logging.properties }} + {{- if $v }} + {{ $k | replace "_" "." }}={{ $v }} + {{- else }} + # {{ $k | replace "_" "." }}= + {{- end }} + {{- end }} diff --git a/deployment/helm/edc-controlplane/templates/deployment.yaml b/deployment/helm/edc-controlplane/templates/deployment.yaml index 18b519f5f..ae261cd75 100644 --- a/deployment/helm/edc-controlplane/templates/deployment.yaml +++ b/deployment/helm/edc-controlplane/templates/deployment.yaml @@ -42,6 +42,9 @@ spec: - name: http containerPort: 80 protocol: TCP + - name: metrics + containerPort: 9090 + protocol: TCP livenessProbe: httpGet: path: / @@ -56,6 +59,12 @@ spec: - name: configuration mountPath: /app/configuration.properties subPath: configuration.properties + - name: configuration + mountPath: /app/opentelemetry.properties + subPath: opentelemetry.properties + - name: configuration + mountPath: /app/logging.properties + subPath: logging.properties volumes: - name: configuration configMap: @@ -63,6 +72,10 @@ spec: items: - key: configuration.properties path: configuration.properties + - key: opentelemetry.properties + path: opentelemetry.properties + - key: logging.properties + path: logging.properties {{- with .Values.nodeSelector }} nodeSelector: {{- toYaml . | nindent 8 }} diff --git a/deployment/helm/edc-controlplane/templates/service.yaml b/deployment/helm/edc-controlplane/templates/service.yaml index 2d1004219..8417e50a1 100644 --- a/deployment/helm/edc-controlplane/templates/service.yaml +++ b/deployment/helm/edc-controlplane/templates/service.yaml @@ -11,5 +11,9 @@ spec: targetPort: http protocol: TCP name: http + - port: 9090 + targetPort: metrics + protocol: TCP + name: metrics selector: {{- include "edc-controlplane.selectorLabels" . | nindent 4 }} diff --git a/deployment/helm/edc-controlplane/values.yaml b/deployment/helm/edc-controlplane/values.yaml index 9ca688bb6..fb56eda05 100644 --- a/deployment/helm/edc-controlplane/values.yaml +++ b/deployment/helm/edc-controlplane/values.yaml @@ -87,6 +87,20 @@ tolerations: [] affinity: {} +logging: + properties: + _level: INFO + org_eclipse_dataspaceconnector_level: ALL + handlers: java.util.logging.ConsoleHandler + java_util_logging_ConsoleHandler_formatter: java.util.logging.SimpleFormatter + java_util_logging_ConsoleHandler_level: ALL + java_util_logging_SimpleFormatter_format: "[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n" + +opentelemetry: + properties: + otel_javaagent_enabled: "true" + otel_javaagent_debug: "false" + configuration: properties: edc_api_control_auth_apikey_key: "" diff --git a/deployment/helm/edc-dataplane/templates/configmap.yaml b/deployment/helm/edc-dataplane/templates/configmap.yaml index 068895dbf..9e27ef2eb 100644 --- a/deployment/helm/edc-dataplane/templates/configmap.yaml +++ b/deployment/helm/edc-dataplane/templates/configmap.yaml @@ -13,3 +13,19 @@ data: # {{ $k | replace "_" "." }}= {{- end }} {{- end }} + opentelemetry.properties: |- + {{- range $k, $v := .Values.opentelemetry.properties }} + {{- if $v }} + {{ $k | replace "_" "." }}={{ $v }} + {{- else }} + # {{ $k | replace "_" "." }}= + {{- end }} + {{- end }} + logging.properties: |- + {{- range $k, $v := .Values.logging.properties }} + {{- if $v }} + {{ $k | replace "_" "." }}={{ $v }} + {{- else }} + # {{ $k | replace "_" "." }}= + {{- end }} + {{- end }} \ No newline at end of file diff --git a/deployment/helm/edc-dataplane/templates/deployment.yaml b/deployment/helm/edc-dataplane/templates/deployment.yaml index b0028afe0..12cf71b6f 100644 --- a/deployment/helm/edc-dataplane/templates/deployment.yaml +++ b/deployment/helm/edc-dataplane/templates/deployment.yaml @@ -42,6 +42,9 @@ spec: - name: http containerPort: 80 protocol: TCP + - name: metrics + containerPort: 9090 + protocol: TCP livenessProbe: httpGet: path: / @@ -56,6 +59,12 @@ spec: - name: configuration mountPath: /app/configuration.properties subPath: configuration.properties + - name: configuration + mountPath: /app/opentelemetry.properties + subPath: opentelemetry.properties + - name: configuration + mountPath: /app/logging.properties + subPath: logging.properties volumes: - name: configuration configMap: @@ -63,6 +72,10 @@ spec: items: - key: configuration.properties path: configuration.properties + - key: opentelemetry.properties + path: opentelemetry.properties + - key: logging.properties + path: logging.properties {{- with .Values.nodeSelector }} nodeSelector: {{- toYaml . | nindent 8 }} diff --git a/deployment/helm/edc-dataplane/templates/service.yaml b/deployment/helm/edc-dataplane/templates/service.yaml index 24903794e..3fe961c5a 100644 --- a/deployment/helm/edc-dataplane/templates/service.yaml +++ b/deployment/helm/edc-dataplane/templates/service.yaml @@ -11,5 +11,9 @@ spec: targetPort: http protocol: TCP name: http + - port: 9090 + targetPort: metrics + protocol: TCP + name: metrics selector: {{- include "edc-dataplane.selectorLabels" . | nindent 4 }} diff --git a/deployment/helm/edc-dataplane/values.yaml b/deployment/helm/edc-dataplane/values.yaml index 3cbb0b15e..a8f62a9b1 100644 --- a/deployment/helm/edc-dataplane/values.yaml +++ b/deployment/helm/edc-dataplane/values.yaml @@ -84,6 +84,20 @@ tolerations: [] affinity: {} +logging: + properties: + _level: INFO + org_eclipse_dataspaceconnector_level: ALL + handlers: java.util.logging.ConsoleHandler + java_util_logging_ConsoleHandler_formatter: java.util.logging.SimpleFormatter + java_util_logging_ConsoleHandler_level: ALL + java_util_logging_SimpleFormatter_format: "[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n" + +opentelemetry: + properties: + otel_javaagent_enabled: "true" + otel_javaagent_debug: "false" + configuration: properties: edc_api_control_auth_apikey_key: "" diff --git a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml index c35e60ec8..a02b004f7 100644 --- a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml +++ b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml @@ -204,6 +204,28 @@ iam-mock + + + org.eclipse.dataspaceconnector + core-micrometer + + + org.eclipse.dataspaceconnector + jersey-micrometer + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + jetty-micrometer + ${org.eclipse.dataspaceconnector.version} + + + + + org.eclipse.dataspaceconnector + jdk-logger-monitor + + diff --git a/edc-controlplane/edc-controlplane-cosmosdb/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-cosmosdb/src/main/docker/Dockerfile index 71b56b37e..773b1e06c 100644 --- a/edc-controlplane/edc-controlplane-cosmosdb/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-cosmosdb/src/main/docker/Dockerfile @@ -1,8 +1,23 @@ +FROM alpine:3.14 as otel + +ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" + +RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar + FROM gcr.io/distroless/java11-debian11 ARG JAR WORKDIR /app +COPY --from=otel /tmp/opentelemetry-javaagent.jar . COPY $JAR edc-controlplane.jar -CMD ["-Djava.security.edg=file:/dev/.urandom", "-Dedc.fs.config=/app/configuration.properties", "-jar", "edc-controlplane.jar"] +CMD ["-javaagent:/app/opentelemetry-javaagent.jar", \ + "-Dedc.fs.config=/app/configuration.properties", \ + "-Djava.util.logging.config.file=/app/logging.properties", \ + "-Dotel.javaagent.configuration-file=/app/opentelemetry.properties", \ + "-Dotel.metrics.exporter=prometheus", \ + "-Dotel.exporter.prometheus.port=9090", \ + "-Djava.security.edg=file:/dev/.urandom", \ + "-jar", \ + "edc-controlplane.jar"] diff --git a/edc-controlplane/edc-controlplane-memory/pom.xml b/edc-controlplane/edc-controlplane-memory/pom.xml index f77527da2..bdc798ef7 100644 --- a/edc-controlplane/edc-controlplane-memory/pom.xml +++ b/edc-controlplane/edc-controlplane-memory/pom.xml @@ -169,6 +169,28 @@ iam-mock + + + org.eclipse.dataspaceconnector + core-micrometer + + + org.eclipse.dataspaceconnector + jersey-micrometer + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + jetty-micrometer + ${org.eclipse.dataspaceconnector.version} + + + + + org.eclipse.dataspaceconnector + jdk-logger-monitor + + diff --git a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile index 71b56b37e..773b1e06c 100644 --- a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile @@ -1,8 +1,23 @@ +FROM alpine:3.14 as otel + +ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" + +RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar + FROM gcr.io/distroless/java11-debian11 ARG JAR WORKDIR /app +COPY --from=otel /tmp/opentelemetry-javaagent.jar . COPY $JAR edc-controlplane.jar -CMD ["-Djava.security.edg=file:/dev/.urandom", "-Dedc.fs.config=/app/configuration.properties", "-jar", "edc-controlplane.jar"] +CMD ["-javaagent:/app/opentelemetry-javaagent.jar", \ + "-Dedc.fs.config=/app/configuration.properties", \ + "-Djava.util.logging.config.file=/app/logging.properties", \ + "-Dotel.javaagent.configuration-file=/app/opentelemetry.properties", \ + "-Dotel.metrics.exporter=prometheus", \ + "-Dotel.exporter.prometheus.port=9090", \ + "-Djava.security.edg=file:/dev/.urandom", \ + "-jar", \ + "edc-controlplane.jar"] diff --git a/edc-dataplane/pom.xml b/edc-dataplane/pom.xml index d14aefc77..484849f9f 100644 --- a/edc-dataplane/pom.xml +++ b/edc-dataplane/pom.xml @@ -104,6 +104,28 @@ core-boot + + + org.eclipse.dataspaceconnector + core-micrometer + + + org.eclipse.dataspaceconnector + jersey-micrometer + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + jetty-micrometer + ${org.eclipse.dataspaceconnector.version} + + + + + org.eclipse.dataspaceconnector + jdk-logger-monitor + + org.eclipse.dataspaceconnector diff --git a/edc-dataplane/src/main/docker/Dockerfile b/edc-dataplane/src/main/docker/Dockerfile index c6250c42d..a798d93ee 100644 --- a/edc-dataplane/src/main/docker/Dockerfile +++ b/edc-dataplane/src/main/docker/Dockerfile @@ -1,8 +1,23 @@ +FROM alpine:3.14 as otel + +ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" + +RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar + FROM gcr.io/distroless/java11-debian11 ARG JAR WORKDIR /app +COPY --from=otel /tmp/opentelemetry-javaagent.jar . COPY $JAR edc-dataplane.jar -CMD ["-Djava.security.edg=file:/dev/.urandom", "-Dedc.fs.config=/app/configuration.properties", "-jar", "edc-dataplane.jar"] \ No newline at end of file +CMD ["-javaagent:/app/opentelemetry-javaagent.jar", \ + "-Dedc.fs.config=/app/configuration.properties", \ + "-Djava.util.logging.config.file=/app/logging.properties", \ + "-Dotel.javaagent.configuration-file=/app/opentelemetry.properties", \ + "-Dotel.metrics.exporter=prometheus", \ + "-Dotel.exporter.prometheus.port=9090", \ + "-Djava.security.edg=file:/dev/.urandom", \ + "-jar", \ + "edc-dataplane.jar"] \ No newline at end of file From 9f4a4bc86dbfa9833908c78cf7719611b2b310c5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Apr 2022 16:27:48 +0200 Subject: [PATCH 015/433] Bump alpine (#17) Bumps alpine from 3.14 to 3.15.4. --- .../edc-controlplane-cosmosdb/src/main/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-controlplane/edc-controlplane-cosmosdb/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-cosmosdb/src/main/docker/Dockerfile index 773b1e06c..79440ed5d 100644 --- a/edc-controlplane/edc-controlplane-cosmosdb/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-cosmosdb/src/main/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.14 as otel +FROM alpine:3.15.4 as otel ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" From fa3fb9f8156946f3b594f847eab2d2cb0c83b84e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Apr 2022 17:01:38 +0200 Subject: [PATCH 016/433] Bump alpine from 3.14 to 3.15.4 in /edc-dataplane/src/main/docker (#16) Bumps alpine from 3.14 to 3.15.4. --- edc-dataplane/src/main/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-dataplane/src/main/docker/Dockerfile b/edc-dataplane/src/main/docker/Dockerfile index a798d93ee..d36dc2636 100644 --- a/edc-dataplane/src/main/docker/Dockerfile +++ b/edc-dataplane/src/main/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.14 as otel +FROM alpine:3.15.4 as otel ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" From c1405d3cc9d460339ebd0e6bc8e5294a0e60f15f Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Wed, 6 Apr 2022 16:41:36 +0200 Subject: [PATCH 017/433] Add controlplane-postgres flavor (#10) --- .github/workflows/build.yaml | 38 ++- deployment/helm/edc-controlplane/values.yaml | 17 +- deployment/helm/edc-dataplane/values.yaml | 17 +- edc | 2 +- .../src/main/docker/Dockerfile | 2 +- .../edc-controlplane-postgresql/pom.xml | 272 ++++++++++++++++++ .../src/main/docker/Dockerfile | 23 ++ edc-controlplane/pom.xml | 1 + edc-extensions/pom.xml | 1 + edc-extensions/postgresql-migration/pom.xml | 44 +++ .../AbstractPostgresqlMigrationExtension.java | 89 ++++++ .../AssetPostgresqlMigrationExtension.java | 15 + ...efinitionPostgresqlMigrationExtension.java | 19 ++ ...gotiationPostgresqlMigrationExtension.java | 19 ++ .../DriverManagerConnectionFactory.java | 27 ++ .../PolicyPostgresqlMigrationExtension.java | 17 ++ ...erProcessPostgresqlMigrationExtension.java | 19 ++ ...spaceconnector.spi.system.ServiceExtension | 18 ++ .../V0_0_1__Init_Asset_Database_Schema.sql | 55 ++++ ...nit_ContractDefinition_Database_Schema.sql | 24 ++ ...it_ContractNegotiation_Database_Schema.sql | 82 ++++++ .../V0_0_1__Init_Policy_Database_Schema.sql | 40 +++ ...__Init_TransferProcess_Database_Schema.sql | 91 ++++++ pom.xml | 53 +++- 24 files changed, 971 insertions(+), 14 deletions(-) create mode 100644 edc-controlplane/edc-controlplane-postgresql/pom.xml create mode 100644 edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile create mode 100644 edc-extensions/postgresql-migration/pom.xml create mode 100644 edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/AbstractPostgresqlMigrationExtension.java create mode 100644 edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/AssetPostgresqlMigrationExtension.java create mode 100644 edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/ContractDefinitionPostgresqlMigrationExtension.java create mode 100644 edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/ContractNegotiationPostgresqlMigrationExtension.java create mode 100644 edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/DriverManagerConnectionFactory.java create mode 100644 edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/PolicyPostgresqlMigrationExtension.java create mode 100644 edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/TransferProcessPostgresqlMigrationExtension.java create mode 100644 edc-extensions/postgresql-migration/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension create mode 100644 edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/asset/V0_0_1__Init_Asset_Database_Schema.sql create mode 100644 edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractdefinition/V0_0_1__Init_ContractDefinition_Database_Schema.sql create mode 100644 edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractnegotiation/V0_0_1__Init_ContractNegotiation_Database_Schema.sql create mode 100644 edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/policy/V0_0_1__Init_Policy_Database_Schema.sql create mode 100644 edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/transferprocess/V0_0_1__Init_TransferProcess_Database_Schema.sql diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 09c0b238f..eea160dd1 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -38,7 +38,8 @@ jobs: run: |- [ ! -d "edc" ] && git submodule add https://github.com/eclipse-dataspaceconnector/DataSpaceConnector.git edc git submodule update --init - git -C edc checkout 3305340d6a19ba919540d28a731b3cab6d23857a + git -C edc fetch --all + git -C edc checkout febacc631b4c07aaec2e76ff8a81e8564a99090d - name: Set up JDK 11 uses: actions/setup-java@v2 @@ -121,6 +122,41 @@ jobs: tags: ${{ steps.edc_controlplane_cosmosdb_meta.outputs.tags }} labels: ${{ steps.edc_controlplane_cosmosdb_meta.outputs.labels }} + ################################# + ### edc-controlplane-postgresql ### + ################################# + - + name: Build edc-controlplane-postgresql + run: |- + ./mvnw -s settings.xml -B -pl .,edc-controlplane/edc-controlplane-postgresql -am verify + env: + GITHUB_PACKAGE_USERNAME: ${{ github.actor }} + GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} + - + name: edc-controlplane-postgresql Docker Metadata + id: edc_controlplane_postgresql_meta + uses: docker/metadata-action@v3 + with: + images: | + ghcr.io/catenax-ng/catena-x-edc/edc-controlplane-postgresql + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{raw}} + type=match,pattern=\d.\d.\d + type=sha + - + name: Build edc-controlplane-postgresql Docker Image + uses: docker/build-push-action@v2 + with: + context: . + file: edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile + build-args: | + JAR=edc-controlplane/edc-controlplane-postgresql/target/edc-controlplane-postgresql.jar + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.edc_controlplane_postgresql_meta.outputs.tags }} + labels: ${{ steps.edc_controlplane_postgresql_meta.outputs.labels }} + ##################### ### edc-dataplane ### ##################### diff --git a/deployment/helm/edc-controlplane/values.yaml b/deployment/helm/edc-controlplane/values.yaml index fb56eda05..cb5585765 100644 --- a/deployment/helm/edc-controlplane/values.yaml +++ b/deployment/helm/edc-controlplane/values.yaml @@ -140,7 +140,22 @@ configuration: edc_dataplane_queue_capacity: "" edc_dataplane_wait: "" edc_dataplane_workers: "" - edc_datasource_contractdefinition_name: "" + edc_datasource_asset_name: "default" + edc_datasource_contractdefinition_name: "default" + edc_datasource_contractnegotiation_name: "default" + edc_datasource_policy_name: "default" + edc_datasource_transferprocess_name: "default" + edc_datasource_default_pool_maxIdleConnections: "" + edc_datasource_default_pool_maxTotalConnections: "" + edc_datasource_default_pool_minIdleConnections: "" + edc_datasource_default_pool_testConnectionOnBorrow: "" + edc_datasource_default_pool_testConnectionOnCreate: "" + edc_datasource_default_pool_testConnectionOnReturn: "" + edc_datasource_default_pool_testConnectionWhileIdle: "" + edc_datasource_default_pool_testQuery: "" + edc_datasource_default_url: "" + edc_datasource_default_user: "" + edc_datasource_default_password: "" edc_dpf_selector_url: "" edc_events_topic_endpoint: "" edc_events_topic_name: "" diff --git a/deployment/helm/edc-dataplane/values.yaml b/deployment/helm/edc-dataplane/values.yaml index a8f62a9b1..b979f43a2 100644 --- a/deployment/helm/edc-dataplane/values.yaml +++ b/deployment/helm/edc-dataplane/values.yaml @@ -137,7 +137,22 @@ configuration: edc_dataplane_queue_capacity: "" edc_dataplane_wait: "" edc_dataplane_workers: "" - edc_datasource_contractdefinition_name: "" + edc_datasource_asset_name: "default" + edc_datasource_contractdefinition_name: "default" + edc_datasource_contractnegotiation_name: "default" + edc_datasource_policy_name: "default" + edc_datasource_transferprocess_name: "default" + edc_datasource_default_pool_maxIdleConnections: "" + edc_datasource_default_pool_maxTotalConnections: "" + edc_datasource_default_pool_minIdleConnections: "" + edc_datasource_default_pool_testConnectionOnBorrow: "" + edc_datasource_default_pool_testConnectionOnCreate: "" + edc_datasource_default_pool_testConnectionOnReturn: "" + edc_datasource_default_pool_testConnectionWhileIdle: "" + edc_datasource_default_pool_testQuery: "" + edc_datasource_default_url: "" + edc_datasource_default_user: "" + edc_datasource_default_password: "" edc_dpf_selector_url: "" edc_events_topic_endpoint: "" edc_events_topic_name: "" diff --git a/edc b/edc index 3305340d6..febacc631 160000 --- a/edc +++ b/edc @@ -1 +1 @@ -Subproject commit 3305340d6a19ba919540d28a731b3cab6d23857a +Subproject commit febacc631b4c07aaec2e76ff8a81e8564a99090d diff --git a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile index 773b1e06c..79440ed5d 100644 --- a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.14 as otel +FROM alpine:3.15.4 as otel ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" diff --git a/edc-controlplane/edc-controlplane-postgresql/pom.xml b/edc-controlplane/edc-controlplane-postgresql/pom.xml new file mode 100644 index 000000000..262148bac --- /dev/null +++ b/edc-controlplane/edc-controlplane-postgresql/pom.xml @@ -0,0 +1,272 @@ + + + + edc-controlplane + net.catenax.edc + 0.0.2-SNAPSHOT + + 4.0.0 + + edc-controlplane-postgresql + jar + + + + + org.apache.maven.plugins + maven-shade-plugin + + + + shade + + + ${project.artifactId} + true + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + org.eclipse.dataspaceconnector.boot.system.runtime.BaseRuntime + + + + + META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension + + + + + META-INF/services/org.eclipse.dataspaceconnector.spi.system.VaultExtension + + + + + META-INF/services/org.eclipse.dataspaceconnector.spi.system.ConfigurationExtension + + + + + + + + + + + + + + + net.catenax.edc.extensions + aas-controller + + + + + net.catenax.edc.extensions + postgresql-migration + + + + + org.eclipse.dataspaceconnector + filesystem-configuration + + + + + org.eclipse.dataspaceconnector + azure-vault + + + + + org.eclipse.dataspaceconnector + control-api + + + + org.eclipse.dataspaceconnector + observability-api + + + + + org.eclipse.dataspaceconnector + ids-api-configuration + + + org.eclipse.dataspaceconnector + ids-api-multipart-dispatcher-v1 + + + org.eclipse.dataspaceconnector + ids-api-multipart-endpoint-v1 + + + org.eclipse.dataspaceconnector + ids-api-transform-v1 + + + org.eclipse.dataspaceconnector + ids-core + + + org.eclipse.dataspaceconnector + ids-spi + + + org.eclipse.dataspaceconnector + ids-token-validation + + + + + org.eclipse.dataspaceconnector + sql-asset-index + + + org.eclipse.dataspaceconnector + sql-contractdefinition-store + + + org.eclipse.dataspaceconnector + sql-contractnegotiation-store + + + org.eclipse.dataspaceconnector + sql-transferprocess-store + + + org.eclipse.dataspaceconnector + sql-policy-store + + + + + org.eclipse.dataspaceconnector + core-base + + + org.eclipse.dataspaceconnector + core-boot + + + org.eclipse.dataspaceconnector + transfer + + + org.eclipse.dataspaceconnector + contract + + + + + org.eclipse.dataspaceconnector + iam-mock + + + + + org.eclipse.dataspaceconnector + core-micrometer + + + org.eclipse.dataspaceconnector + jersey-micrometer + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + jetty-micrometer + ${org.eclipse.dataspaceconnector.version} + + + + + org.eclipse.dataspaceconnector + jdk-logger-monitor + + + + + org.eclipse.dataspaceconnector + http + + + + org.eclipse.dataspaceconnector + sql-pool-apache-commons-pool + + + org.eclipse.dataspaceconnector + transaction-local + + + + org.eclipse.dataspaceconnector + http-receiver + + + + + org.postgresql + postgresql + + + jakarta.ws.rs + jakarta.ws.rs-api + + + + + + with-docker-image + + + + com.spotify + dockerfile-maven-plugin + + + default + package + + build + + + + + . + src/main/docker/Dockerfile + ${project.artifactId} + ${project.version} + + target/${project.artifactId}.jar + + + + + + + + \ No newline at end of file diff --git a/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile new file mode 100644 index 000000000..79440ed5d --- /dev/null +++ b/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile @@ -0,0 +1,23 @@ +FROM alpine:3.15.4 as otel + +ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" + +RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar + +FROM gcr.io/distroless/java11-debian11 +ARG JAR + +WORKDIR /app + +COPY --from=otel /tmp/opentelemetry-javaagent.jar . +COPY $JAR edc-controlplane.jar + +CMD ["-javaagent:/app/opentelemetry-javaagent.jar", \ + "-Dedc.fs.config=/app/configuration.properties", \ + "-Djava.util.logging.config.file=/app/logging.properties", \ + "-Dotel.javaagent.configuration-file=/app/opentelemetry.properties", \ + "-Dotel.metrics.exporter=prometheus", \ + "-Dotel.exporter.prometheus.port=9090", \ + "-Djava.security.edg=file:/dev/.urandom", \ + "-jar", \ + "edc-controlplane.jar"] diff --git a/edc-controlplane/pom.xml b/edc-controlplane/pom.xml index c1d6da782..ee196417b 100644 --- a/edc-controlplane/pom.xml +++ b/edc-controlplane/pom.xml @@ -16,5 +16,6 @@ edc-controlplane-cosmosdb edc-controlplane-memory + edc-controlplane-postgresql \ No newline at end of file diff --git a/edc-extensions/pom.xml b/edc-extensions/pom.xml index 8f5fa495e..194abb15b 100644 --- a/edc-extensions/pom.xml +++ b/edc-extensions/pom.xml @@ -14,6 +14,7 @@ aas-controller + postgresql-migration \ No newline at end of file diff --git a/edc-extensions/postgresql-migration/pom.xml b/edc-extensions/postgresql-migration/pom.xml new file mode 100644 index 000000000..00ffa0ff1 --- /dev/null +++ b/edc-extensions/postgresql-migration/pom.xml @@ -0,0 +1,44 @@ + + + + edc-extensions + net.catenax.edc.extensions + 0.0.2-SNAPSHOT + + 4.0.0 + + postgresql-migration + jar + + + + org.eclipse.dataspaceconnector + transaction-datasource-spi + + + org.eclipse.dataspaceconnector + transaction-spi + + + + + org.eclipse.dataspaceconnector + sql-asset-index + compile + + + + + org.eclipse.dataspaceconnector + sql-common + + + + + org.flywaydb + flyway-core + + + \ No newline at end of file diff --git a/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/AbstractPostgresqlMigrationExtension.java b/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/AbstractPostgresqlMigrationExtension.java new file mode 100644 index 000000000..871725d5d --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/AbstractPostgresqlMigrationExtension.java @@ -0,0 +1,89 @@ +package net.catenax.edc.postgresql.migration; + +import java.util.Objects; +import java.util.Properties; +import org.eclipse.dataspaceconnector.spi.persistence.EdcPersistenceException; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtension; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; +import org.eclipse.dataspaceconnector.spi.system.configuration.Config; +import org.eclipse.dataspaceconnector.sql.datasource.ConnectionFactoryDataSource; +import org.flywaydb.core.Flyway; +import org.flywaydb.core.api.MigrationVersion; +import org.flywaydb.core.api.output.MigrateResult; + +abstract class AbstractPostgresqlMigrationExtension implements ServiceExtension { + protected abstract String getDataSourceNameConfigurationKey(); + + protected abstract String getSubsystemName(); + + private static final String EDC_DATASOURCE_PREFIX = "edc.datasource"; + private static final String MIGRATION_LOCATION_BASE = + String.format( + "classpath:%s", + AbstractPostgresqlMigrationExtension.class.getPackageName().replaceAll("\\.", "/")); + + @Override + public void initialize(final ServiceExtensionContext context) { + final String subSystemName = Objects.requireNonNull(getSubsystemName()); + + final String dataSourceName = + context.getConfig().getString(getDataSourceNameConfigurationKey(), null); + if (dataSourceName == null) { + return; + } + + boolean enabled = + context + .getConfig() + .getBoolean( + String.format("net.catenax.edc.postgresql.migration.%s.enabled", subSystemName), + true); + + if (!enabled) { + return; + } + + Config datasourceConfiguration = + context.getConfig(String.join(".", EDC_DATASOURCE_PREFIX, dataSourceName)); + + final String jdbcUrl = Objects.requireNonNull(datasourceConfiguration.getString("url")); + final Properties jdbcProperties = new Properties(); + jdbcProperties.putAll(datasourceConfiguration.getRelativeEntries()); + + final DriverManagerConnectionFactory driverManagerConnectionFactory = + new DriverManagerConnectionFactory(jdbcUrl, jdbcProperties); + final ConnectionFactoryDataSource dataSource = + new ConnectionFactoryDataSource(driverManagerConnectionFactory); + + final String schemaHistoryTableName = getSchemaHistoryTableName(subSystemName); + final String migrationsLocation = getMigrationsLocation(); + + final Flyway flyway = + Flyway.configure() + .baselineVersion(MigrationVersion.fromVersion("0.0.0")) + .failOnMissingLocations(true) + .dataSource(dataSource) + .table(schemaHistoryTableName) + .locations(migrationsLocation) + .load(); + + flyway.baseline(); + + final MigrateResult migrateResult = flyway.migrate(); + + if (!migrateResult.success) { + throw new EdcPersistenceException( + String.format( + "Migrating DataSource %s for subsystem %s failed: %s", + dataSourceName, subSystemName, String.join(", ", migrateResult.warnings))); + } + } + + private String getMigrationsLocation() { + return String.join("/", MIGRATION_LOCATION_BASE, getSubsystemName()); + } + + private String getSchemaHistoryTableName(final String subSystemName) { + return String.format("flyway_schema_history_%s", subSystemName); + } +} diff --git a/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/AssetPostgresqlMigrationExtension.java b/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/AssetPostgresqlMigrationExtension.java new file mode 100644 index 000000000..8f1d41702 --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/AssetPostgresqlMigrationExtension.java @@ -0,0 +1,15 @@ +package net.catenax.edc.postgresql.migration; + +import org.eclipse.dataspaceconnector.sql.asset.index.ConfigurationKeys; + +public class AssetPostgresqlMigrationExtension extends AbstractPostgresqlMigrationExtension { + private static final String NAME_SUBSYSTEM = "asset"; + + protected String getDataSourceNameConfigurationKey() { + return ConfigurationKeys.DATASOURCE_SETTING_NAME; + } + + protected String getSubsystemName() { + return NAME_SUBSYSTEM; + } +} diff --git a/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/ContractDefinitionPostgresqlMigrationExtension.java b/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/ContractDefinitionPostgresqlMigrationExtension.java new file mode 100644 index 000000000..f2c0c40e1 --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/ContractDefinitionPostgresqlMigrationExtension.java @@ -0,0 +1,19 @@ +package net.catenax.edc.postgresql.migration; + +import org.eclipse.dataspaceconnector.spi.EdcSetting; + +public class ContractDefinitionPostgresqlMigrationExtension + extends AbstractPostgresqlMigrationExtension { + private static final String NAME_SUBSYSTEM = "contractdefinition"; + + @EdcSetting + private static final String DATASOURCE_SETTING_NAME = "edc.datasource.contractdefinition.name"; + + protected String getDataSourceNameConfigurationKey() { + return DATASOURCE_SETTING_NAME; + } + + protected String getSubsystemName() { + return NAME_SUBSYSTEM; + } +} diff --git a/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/ContractNegotiationPostgresqlMigrationExtension.java b/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/ContractNegotiationPostgresqlMigrationExtension.java new file mode 100644 index 000000000..9856bacaa --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/ContractNegotiationPostgresqlMigrationExtension.java @@ -0,0 +1,19 @@ +package net.catenax.edc.postgresql.migration; + +import org.eclipse.dataspaceconnector.spi.EdcSetting; + +public class ContractNegotiationPostgresqlMigrationExtension + extends AbstractPostgresqlMigrationExtension { + private static final String NAME_SUBSYSTEM = "contractnegotiation"; + + @EdcSetting + private static final String DATASOURCE_SETTING_NAME = "edc.datasource.contractnegotiation.name"; + + protected String getDataSourceNameConfigurationKey() { + return DATASOURCE_SETTING_NAME; + } + + protected String getSubsystemName() { + return NAME_SUBSYSTEM; + } +} diff --git a/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/DriverManagerConnectionFactory.java b/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/DriverManagerConnectionFactory.java new file mode 100644 index 000000000..1c203e61c --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/DriverManagerConnectionFactory.java @@ -0,0 +1,27 @@ +package net.catenax.edc.postgresql.migration; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.util.Objects; +import java.util.Properties; +import org.eclipse.dataspaceconnector.spi.persistence.EdcPersistenceException; +import org.eclipse.dataspaceconnector.sql.ConnectionFactory; + +class DriverManagerConnectionFactory implements ConnectionFactory { + private final String jdbcUrl; + private final Properties properties; + + public DriverManagerConnectionFactory(final String jdbcUrl, final Properties properties) { + this.jdbcUrl = Objects.requireNonNull(jdbcUrl); + this.properties = Objects.requireNonNull(properties); + } + + @Override + public Connection create() { + try { + return DriverManager.getConnection(jdbcUrl, properties); + } catch (Exception exception) { + throw new EdcPersistenceException(exception.getMessage(), exception); + } + } +} diff --git a/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/PolicyPostgresqlMigrationExtension.java b/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/PolicyPostgresqlMigrationExtension.java new file mode 100644 index 000000000..24c77971c --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/PolicyPostgresqlMigrationExtension.java @@ -0,0 +1,17 @@ +package net.catenax.edc.postgresql.migration; + +import org.eclipse.dataspaceconnector.spi.EdcSetting; + +public class PolicyPostgresqlMigrationExtension extends AbstractPostgresqlMigrationExtension { + private static final String NAME_SUBSYSTEM = "policy"; + + @EdcSetting private static final String DATASOURCE_SETTING_NAME = "edc.datasource.policy.name"; + + protected String getDataSourceNameConfigurationKey() { + return DATASOURCE_SETTING_NAME; + } + + protected String getSubsystemName() { + return NAME_SUBSYSTEM; + } +} diff --git a/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/TransferProcessPostgresqlMigrationExtension.java b/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/TransferProcessPostgresqlMigrationExtension.java new file mode 100644 index 000000000..784b1d238 --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/TransferProcessPostgresqlMigrationExtension.java @@ -0,0 +1,19 @@ +package net.catenax.edc.postgresql.migration; + +import org.eclipse.dataspaceconnector.spi.EdcSetting; + +public class TransferProcessPostgresqlMigrationExtension + extends AbstractPostgresqlMigrationExtension { + private static final String NAME_SUBSYSTEM = "transferprocess"; + + @EdcSetting + private static final String DATASOURCE_SETTING_NAME = "edc.datasource.transferprocess.name"; + + protected String getDataSourceNameConfigurationKey() { + return DATASOURCE_SETTING_NAME; + } + + protected String getSubsystemName() { + return NAME_SUBSYSTEM; + } +} diff --git a/edc-extensions/postgresql-migration/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension b/edc-extensions/postgresql-migration/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension new file mode 100644 index 000000000..aa8b6b5b1 --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension @@ -0,0 +1,18 @@ +# +# Copyright (c) 2022 Daimler TSS GmbH +# +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# +# Contributors: +# Daimler TSS GmbH - Initial ServiceExtension file +# +# +net.catenax.edc.postgresql.migration.AssetPostgresqlMigrationExtension +net.catenax.edc.postgresql.migration.ContractDefinitionPostgresqlMigrationExtension +net.catenax.edc.postgresql.migration.ContractNegotiationPostgresqlMigrationExtension +net.catenax.edc.postgresql.migration.PolicyPostgresqlMigrationExtension +net.catenax.edc.postgresql.migration.TransferProcessPostgresqlMigrationExtension diff --git a/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/asset/V0_0_1__Init_Asset_Database_Schema.sql b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/asset/V0_0_1__Init_Asset_Database_Schema.sql new file mode 100644 index 000000000..1a4e86f7f --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/asset/V0_0_1__Init_Asset_Database_Schema.sql @@ -0,0 +1,55 @@ +-- +-- Copyright (c) 2022 Daimler TSS GmbH +-- +-- This program and the accompanying materials are made available under the +-- terms of the Apache License, Version 2.0 which is available at +-- https://www.apache.org/licenses/LICENSE-2.0 +-- +-- SPDX-License-Identifier: Apache-2.0 +-- +-- Contributors: +-- Daimler TSS GmbH - Initial Database Schema +-- + +-- +-- table: edc_asset +-- +CREATE TABLE IF NOT EXISTS edc_asset +( + asset_id VARCHAR(255) NOT NULL, + PRIMARY KEY (asset_id) +); + +-- +-- table: edc_asset_dataaddress +-- +CREATE TABLE IF NOT EXISTS edc_asset_dataaddress +( + asset_id VARCHAR(255) NOT NULL, + properties TEXT NOT NULL, + PRIMARY KEY (asset_id), + FOREIGN KEY (asset_id) REFERENCES edc_asset (asset_id) ON DELETE CASCADE +); +COMMENT ON COLUMN edc_asset_dataaddress.properties is 'DataAddress properties serialized as JSON'; + +-- +-- table: edc_asset_property +-- +CREATE TABLE IF NOT EXISTS edc_asset_property +( + asset_id VARCHAR(255) NOT NULL, + property_name VARCHAR(255) NOT NULL, + property_value TEXT NOT NULL, + property_type VARCHAR(255) NOT NULL, + PRIMARY KEY (asset_id, property_name), + FOREIGN KEY (asset_id) REFERENCES edc_asset (asset_id) ON DELETE CASCADE +); +COMMENT ON COLUMN edc_asset_property.property_name IS + 'Asset property key'; +COMMENT ON COLUMN edc_asset_property.property_value IS + 'Asset property value'; +COMMENT ON COLUMN edc_asset_property.property_type IS + 'Asset property class name'; + +CREATE INDEX IF NOT EXISTS idx_edc_asset_property_value + ON edc_asset_property (property_name, property_value); \ No newline at end of file diff --git a/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractdefinition/V0_0_1__Init_ContractDefinition_Database_Schema.sql b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractdefinition/V0_0_1__Init_ContractDefinition_Database_Schema.sql new file mode 100644 index 000000000..f147f6499 --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractdefinition/V0_0_1__Init_ContractDefinition_Database_Schema.sql @@ -0,0 +1,24 @@ +-- +-- Copyright (c) 2022 Daimler TSS GmbH +-- +-- This program and the accompanying materials are made available under the +-- terms of the Apache License, Version 2.0 which is available at +-- https://www.apache.org/licenses/LICENSE-2.0 +-- +-- SPDX-License-Identifier: Apache-2.0 +-- +-- Contributors: +-- Daimler TSS GmbH - Initial Database Schema +-- + +-- +-- table: edc_contract_definitions +-- +CREATE TABLE IF NOT EXISTS edc_contract_definitions +( + contract_definition_id VARCHAR(255) NOT NULL, + access_policy TEXT NOT NULL, + contract_policy TEXT NOT NULL, + selector_expression TEXT NOT NULL, + PRIMARY KEY (contract_definition_id) +); diff --git a/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractnegotiation/V0_0_1__Init_ContractNegotiation_Database_Schema.sql b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractnegotiation/V0_0_1__Init_ContractNegotiation_Database_Schema.sql new file mode 100644 index 000000000..2eaf02e13 --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractnegotiation/V0_0_1__Init_ContractNegotiation_Database_Schema.sql @@ -0,0 +1,82 @@ +-- +-- Copyright (c) 2022 Daimler TSS GmbH +-- +-- This program and the accompanying materials are made available under the +-- terms of the Apache License, Version 2.0 which is available at +-- https://www.apache.org/licenses/LICENSE-2.0 +-- +-- SPDX-License-Identifier: Apache-2.0 +-- +-- Contributors: +-- Daimler TSS GmbH - Initial Database Schema +-- + +-- +-- table: edc_lease +-- +CREATE TABLE IF NOT EXISTS edc_lease +( + leased_by VARCHAR(255) NOT NULL, + leased_at BIGINT, + lease_duration INTEGER DEFAULT 60000 NOT NULL, + lease_id VARCHAR(255) NOT NULL + CONSTRAINT lease_pk + PRIMARY KEY +); +COMMENT ON COLUMN edc_lease.leased_at IS 'posix timestamp of lease'; +COMMENT ON COLUMN edc_lease.lease_duration IS 'duration of lease in milliseconds'; + +CREATE UNIQUE INDEX lease_lease_id_uindex + ON edc_lease (lease_id); + +-- +-- table: edc_contract_agreement +-- +CREATE TABLE IF NOT EXISTS edc_contract_agreement +( + id VARCHAR NOT NULL + CONSTRAINT contract_agreement_pk PRIMARY KEY, + provider_agent_id VARCHAR(255), + consumer_agent_id VARCHAR(255), + signing_date BIGINT, + start_date BIGINT, + end_date INTEGER, + asset_id VARCHAR(255) NOT NULL, + policy_id VARCHAR(255), + serialized_policy TEXT +); + +-- +-- table: edc_contract_negotiation +-- +CREATE TABLE IF NOT EXISTS edc_contract_negotiation +( + id VARCHAR(255) NOT NULL + CONSTRAINT contract_negotiation_pk PRIMARY KEY, + correlation_id VARCHAR(255) NOT NULL, + counterparty_id VARCHAR(255) NOT NULL, + counterparty_address VARCHAR(255) NOT NULL, + protocol VARCHAR(255) DEFAULT 'ids-multipart':: CHARACTER VARYING NOT NULL, + type INTEGER DEFAULT 0 NOT NULL, + state INTEGER DEFAULT 0 NOT NULL, + state_count INTEGER DEFAULT 0, + state_timestamp BIGINT, + error_detail TEXT, + contract_agreement_id TEXT + CONSTRAINT contract_negotiation_contract_agreement_id_fk REFERENCES edc_contract_agreement, + contract_offers TEXT, + trace_context TEXT, + lease_id VARCHAR(255) + CONSTRAINT contract_negotiation_lease_lease_id_fk REFERENCES edc_lease ON DELETE SET NULL +); +COMMENT ON COLUMN edc_contract_negotiation.contract_agreement_id IS 'ContractAgreement serialized as JSON'; +COMMENT ON COLUMN edc_contract_negotiation.contract_offers IS 'List serialized as JSON'; +COMMENT ON COLUMN edc_contract_negotiation.trace_context IS 'Map serialized as JSON'; + +CREATE INDEX IF NOT EXISTS contract_negotiation_correlationid_index + ON edc_contract_negotiation (correlation_id); +CREATE UNIQUE INDEX IF NOT EXISTS contract_negotiation_id_uindex + ON edc_contract_negotiation (id); +CREATE UNIQUE INDEX IF NOT EXISTS contract_agreement_id_uindex + ON edc_contract_agreement (id); + diff --git a/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/policy/V0_0_1__Init_Policy_Database_Schema.sql b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/policy/V0_0_1__Init_Policy_Database_Schema.sql new file mode 100644 index 000000000..9c8ebfc74 --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/policy/V0_0_1__Init_Policy_Database_Schema.sql @@ -0,0 +1,40 @@ +-- +-- Copyright (c) 2022 ZF Friedrichshafen AG and others +-- +-- This program and the accompanying materials are made available under the +-- terms of the Apache License, Version 2.0 which is available at +-- https://www.apache.org/licenses/LICENSE-2.0 +-- +-- SPDX-License-Identifier: Apache-2.0 +-- +-- Contributors: +-- ZF Friedrichshafen AG - Initial SQL Query +-- Daimler TSS GmbH - Value range modifications +-- + +-- +-- table: edc_policies +-- +CREATE TABLE IF NOT EXISTS edc_policies +( + policy_id VARCHAR(255) NOT NULL, + permissions TEXT, + prohibitions TEXT, + duties TEXT, + extensible_properties TEXT, + inherits_from VARCHAR(255), + assigner VARCHAR(255), + assignee VARCHAR(255), + target VARCHAR(255), + policy_type VARCHAR(255) NOT NULL, + PRIMARY KEY (policy_id) +); + +COMMENT ON COLUMN edc_policies.permissions IS 'Java List serialized as JSON'; +COMMENT ON COLUMN edc_policies.prohibitions IS 'Java List serialized as JSON'; +COMMENT ON COLUMN edc_policies.duties IS 'Java List serialized as JSON'; +COMMENT ON COLUMN edc_policies.extensible_properties IS 'Java Map serialized as JSON'; +COMMENT ON COLUMN edc_policies.policy_type IS 'Java PolicyType serialized as JSON'; + +CREATE UNIQUE INDEX IF NOT EXISTS edc_policies_id_uindex + ON edc_policies (policy_id); diff --git a/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/transferprocess/V0_0_1__Init_TransferProcess_Database_Schema.sql b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/transferprocess/V0_0_1__Init_TransferProcess_Database_Schema.sql new file mode 100644 index 000000000..f80c0df09 --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/transferprocess/V0_0_1__Init_TransferProcess_Database_Schema.sql @@ -0,0 +1,91 @@ +-- +-- Copyright (c) 2022 Daimler TSS GmbH +-- +-- This program and the accompanying materials are made available under the +-- terms of the Apache License, Version 2.0 which is available at +-- https://www.apache.org/licenses/LICENSE-2.0 +-- +-- SPDX-License-Identifier: Apache-2.0 +-- +-- Contributors: +-- Daimler TSS GmbH - Rewrite to be SQL Init Schema +-- + +-- +-- table: edc_lease +-- +CREATE TABLE IF NOT EXISTS edc_lease +( + leased_by VARCHAR(255) NOT NULL, + leased_at BIGINT, + lease_duration INTEGER DEFAULT 60000 NOT NULL, + lease_id VARCHAR(255) NOT NULL + CONSTRAINT lease_pk + PRIMARY KEY +); +COMMENT ON COLUMN edc_lease.leased_at IS 'posix timestamp of lease'; +COMMENT ON COLUMN edc_lease.lease_duration IS 'duration of lease in milliseconds'; + +CREATE UNIQUE INDEX IF NOT EXISTS lease_lease_id_uindex + ON edc_lease (lease_id); + +-- +-- table: edc_transfer_process +-- +CREATE TABLE IF NOT EXISTS edc_transfer_process +( + id VARCHAR(255) NOT NULL + CONSTRAINT transfer_process_pk + PRIMARY KEY, + type VARCHAR(255) NOT NULL, + state INTEGER NOT NULL, + state_count INTEGER DEFAULT 0 NOT NULL, + state_time_stamp BIGINT, + trace_context TEXT, + error_detail TEXT, + resource_manifest TEXT, + provisioned_resource_set TEXT, + lease_id VARCHAR(255) + CONSTRAINT transfer_process_lease_lease_id_fk + REFERENCES edc_lease + ON DELETE SET NULL +); +COMMENT ON COLUMN edc_transfer_process.trace_context IS 'Java Map serialized as JSON'; +COMMENT ON COLUMN edc_transfer_process.resource_manifest IS 'java ResourceManifest serialized as JSON'; +COMMENT ON COLUMN edc_transfer_process.provisioned_resource_set IS 'ProvisionedResourceSet serialized as JSON'; + +CREATE UNIQUE INDEX IF NOT EXISTS transfer_process_id_uindex + ON edc_transfer_process (id); + +-- +-- table: edc_data_request +-- +CREATE TABLE IF NOT EXISTS edc_data_request +( + id VARCHAR(255) NOT NULL + CONSTRAINT data_request_pk + PRIMARY KEY, + process_id VARCHAR(255) NOT NULL, + connector_address VARCHAR(255) NOT NULL, + protocol VARCHAR(255) NOT NULL, + connector_id VARCHAR(255), + asset_id VARCHAR(255) NOT NULL, + contract_id VARCHAR(255) NOT NULL, + data_destination TEXT NOT NULL, + managed_resources BOOLEAN DEFAULT TRUE, + properties TEXT, + transfer_type TEXT, + transfer_process_id VARCHAR(255) NOT NULL + CONSTRAINT data_request_transfer_process_id_fk + REFERENCES edc_transfer_process + ON UPDATE RESTRICT ON DELETE CASCADE +); +COMMENT ON COLUMN edc_data_request.data_destination IS 'DataAddress serialized as JSON'; +COMMENT ON COLUMN edc_data_request.properties IS 'java Map serialized as JSON'; +COMMENT ON COLUMN edc_data_request.transfer_type IS 'TransferType serialized as JSON'; + +CREATE UNIQUE INDEX IF NOT EXISTS data_request_id_uindex + ON edc_data_request (id); +CREATE UNIQUE INDEX IF NOT EXISTS lease_lease_id_uindex + ON edc_lease (lease_id); + diff --git a/pom.xml b/pom.xml index ec90f0ec6..498b3c201 100644 --- a/pom.xml +++ b/pom.xml @@ -9,6 +9,7 @@ edc-extensions + edc-controlplane edc-dataplane @@ -25,6 +26,8 @@ 0.0.1-SNAPSHOT 1.2.0 3.0.0 + 42.2.6 + 8.5.5 @@ -115,23 +118,30 @@ aas-controller ${project.version} - - - - com.azure - azure-sdk-bom - ${com.azure.sdk.bom.version} - pom - import + net.catenax.edc.extensions + postgresql-migration + ${project.version} - + jakarta.ws.rs jakarta.ws.rs-api ${jakarta.ws.rs.api.version} + + org.postgresql + postgresql + ${org.postgresql.version} + + + org.flywaydb + flyway-core + ${org.flywaydb.version} + + + org.eclipse.dataspaceconnector api-configuration @@ -637,6 +647,12 @@ spi ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + sql-asset-index + ${org.eclipse.dataspaceconnector.version} + org.eclipse.dataspaceconnector sql-common @@ -662,6 +678,17 @@ sql-transferprocess-store ${org.eclipse.dataspaceconnector.version} + + org.eclipse.dataspaceconnector + sql-contractnegotiation-store + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + sql-policy-store + ${org.eclipse.dataspaceconnector.version} + + org.eclipse.dataspaceconnector state-machine-lib @@ -743,6 +770,14 @@ ${org.eclipse.dataspaceconnector.version} + + + com.azure + azure-sdk-bom + ${com.azure.sdk.bom.version} + pom + import + \ No newline at end of file From 9ec41197864912acf3d6cd54a5c8db4444ed0969 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Mon, 11 Apr 2022 12:57:40 +0200 Subject: [PATCH 018/433] Use edc release milestone-3 (#19) --- .github/workflows/build.yaml | 2 +- edc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index eea160dd1..dcd9c68ce 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -39,7 +39,7 @@ jobs: [ ! -d "edc" ] && git submodule add https://github.com/eclipse-dataspaceconnector/DataSpaceConnector.git edc git submodule update --init git -C edc fetch --all - git -C edc checkout febacc631b4c07aaec2e76ff8a81e8564a99090d + git -C edc checkout milestone-3 - name: Set up JDK 11 uses: actions/setup-java@v2 diff --git a/edc b/edc index febacc631..70ff63d97 160000 --- a/edc +++ b/edc @@ -1 +1 @@ -Subproject commit febacc631b4c07aaec2e76ff8a81e8564a99090d +Subproject commit 70ff63d9752d25e754af32502487eb1ce707a238 From 98327fb7ca5a56bacc44af4c3863297bf4e8a57e Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Mon, 11 Apr 2022 13:07:27 +0200 Subject: [PATCH 019/433] Use DAPS as IAM system (#20) --- edc-controlplane/edc-controlplane-cosmosdb/pom.xml | 6 +++++- edc-controlplane/edc-controlplane-memory/pom.xml | 6 +++++- edc-controlplane/edc-controlplane-postgresql/pom.xml | 6 +++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml index a02b004f7..e85084b26 100644 --- a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml +++ b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml @@ -201,7 +201,11 @@ org.eclipse.dataspaceconnector - iam-mock + oauth2-core + + + org.eclipse.dataspaceconnector + iam-daps diff --git a/edc-controlplane/edc-controlplane-memory/pom.xml b/edc-controlplane/edc-controlplane-memory/pom.xml index bdc798ef7..fe8a305d1 100644 --- a/edc-controlplane/edc-controlplane-memory/pom.xml +++ b/edc-controlplane/edc-controlplane-memory/pom.xml @@ -166,7 +166,11 @@ org.eclipse.dataspaceconnector - iam-mock + oauth2-core + + + org.eclipse.dataspaceconnector + iam-daps diff --git a/edc-controlplane/edc-controlplane-postgresql/pom.xml b/edc-controlplane/edc-controlplane-postgresql/pom.xml index 262148bac..f8054c578 100644 --- a/edc-controlplane/edc-controlplane-postgresql/pom.xml +++ b/edc-controlplane/edc-controlplane-postgresql/pom.xml @@ -182,7 +182,11 @@ org.eclipse.dataspaceconnector - iam-mock + oauth2-core + + + org.eclipse.dataspaceconnector + iam-daps From bf51291bc26e84a22d1cb27c61a511425c489c86 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Mon, 11 Apr 2022 13:17:44 +0200 Subject: [PATCH 020/433] Add BPN validation (#21) --- .../edc-controlplane-cosmosdb/pom.xml | 10 ++ .../edc-controlplane-memory/pom.xml | 8 +- .../edc-controlplane-postgresql/pom.xml | 8 +- .../business-partner-validation/README.md | 147 ++++++++++++++++ .../business-partner-validation/pom.xml | 43 +++++ .../BusinessPartnerValidationExtension.java | 74 ++++++++ .../AbstractBusinessPartnerValidation.java | 92 ++++++++++ .../BusinessPartnerDutyFunction.java | 35 ++++ .../BusinessPartnerPermissionFunction.java | 36 ++++ .../BusinessPartnerProhibitionFunction.java | 36 ++++ ...spaceconnector.spi.system.ServiceExtension | 15 ++ ...usinessPartnerValidationExtensionTest.java | 95 +++++++++++ ...AbstractBusinessPartnerValidationTest.java | 158 ++++++++++++++++++ edc-extensions/pom.xml | 1 + pom.xml | 53 +++++- 15 files changed, 805 insertions(+), 6 deletions(-) create mode 100644 edc-extensions/business-partner-validation/README.md create mode 100644 edc-extensions/business-partner-validation/pom.xml create mode 100644 edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/BusinessPartnerValidationExtension.java create mode 100644 edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java create mode 100644 edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/BusinessPartnerDutyFunction.java create mode 100644 edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/BusinessPartnerPermissionFunction.java create mode 100644 edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/BusinessPartnerProhibitionFunction.java create mode 100644 edc-extensions/business-partner-validation/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension create mode 100644 edc-extensions/business-partner-validation/src/test/java/net/catenax/edc/validation/businesspartner/BusinessPartnerValidationExtensionTest.java create mode 100644 edc-extensions/business-partner-validation/src/test/java/net/catenax/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidationTest.java diff --git a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml index e85084b26..0a19f3166 100644 --- a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml +++ b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml @@ -64,6 +64,16 @@ + + + net.catenax.edc.extensions + aas-controller + + + net.catenax.edc.extensions + business-partner-validation + + org.eclipse.dataspaceconnector diff --git a/edc-controlplane/edc-controlplane-memory/pom.xml b/edc-controlplane/edc-controlplane-memory/pom.xml index fe8a305d1..af7f3940a 100644 --- a/edc-controlplane/edc-controlplane-memory/pom.xml +++ b/edc-controlplane/edc-controlplane-memory/pom.xml @@ -64,11 +64,15 @@ - + net.catenax.edc.extensions aas-controller + + net.catenax.edc.extensions + business-partner-validation + @@ -78,7 +82,6 @@ - org.eclipse.dataspaceconnector control-api @@ -191,6 +194,7 @@ + org.eclipse.dataspaceconnector jdk-logger-monitor diff --git a/edc-controlplane/edc-controlplane-postgresql/pom.xml b/edc-controlplane/edc-controlplane-postgresql/pom.xml index f8054c578..4f52b6511 100644 --- a/edc-controlplane/edc-controlplane-postgresql/pom.xml +++ b/edc-controlplane/edc-controlplane-postgresql/pom.xml @@ -69,13 +69,15 @@ - + net.catenax.edc.extensions aas-controller - - + + net.catenax.edc.extensions + business-partner-validation + net.catenax.edc.extensions postgresql-migration diff --git a/edc-extensions/business-partner-validation/README.md b/edc-extensions/business-partner-validation/README.md new file mode 100644 index 000000000..9940e6570 --- /dev/null +++ b/edc-extensions/business-partner-validation/README.md @@ -0,0 +1,147 @@ +# Token Claim Validation Extension + +Using the Token Claim Validation Extension it's possible to add configurable validation against `Participants` in +the `ContractDefinition.AccessPolicy`. + +**Why only AccessPolicy?** Because if a custom validation would be used in the `ContractPolicy`, it would be necessary +to send it to the other connector. But nether is it possible to send a generic constraint to other connectors using IDS, +nor is it possible for another connector to enforce a configurable constraint reliable. Hence, the limit +to `AccessPolicy`. + +A custom validation against a `Participant` claim can be done in three steps: + +1. Include this extension +2. Add setting `edc.policy.validation..claim=` +3. Use the claim-validation in a `AtomicConstraint`. + +`AtomicConstraint` example: + +```json +{ + "leftExpression": { + "value": "" + }, + "rightExpression": { + "value": "" + }, + "operator": "" +} +``` + +**Operators** + +- `EQ` is _true_ when the content of a claim is equal to the right value of the `Constraint`. If the claim is not part + of the token or different it is _false_. +- `NEQ` is _true_ when the content of a claim is **not** equal to the right value of the `Constraint`. If the claim is + not part of the token or equal it is _false_. +- `IN` is _true_ when the right value of the `Constraint` is contained in the content of a claim. If the claim is not + part of the token or the right value of the `Constraint` is not part of the claim it is _false_. + +**Please note** that this extension only supports `Constraints` that are part of a `Permission`. + +## Example + +### 1. Include the extension + +#### Gradle KTS + +Project +```bash +implementation(project(":extensions:policy:token-claim-validation")) +``` + +Package +```bash +implementation("org.eclipse.dataspaceconnector:policy-token-claim-validation:0.0.1-SNAPSHOT") +``` + +#### Maven + +```xml + + + org.eclipse.dataspaceconnector + policy-token-claim-validation + 0.0.1-SNAPSHOT + +``` + +### 2. Add setting to the connector configuration + +In this example the IDS Security Profile of the DAPS token should be validated. + +This is how a DAPS token looks like: + +```json +{ + "typ": "JWT", + "kid": "default", + "alg": "RS256" +} +. +{ + "scopes": [ + "idsc:IDS_CONNECTOR_ATTRIBUTES_ALL" + ], + "aud": "idsc:IDS_CONNECTORS_ALL", + "iss": "https://daps.aisec.fraunhofer.de", + "nbf": 1632982369, + "iat": 1632982369, + "jti": "OTAyNTE1OTMzNTczMDgyMzUxNg==", + "exp": 1632985969, + "securityProfile": "idsc:TRUST_SECURITY_PROFILE", + "referringConnector": "http://consumer-core.demo", + "@type": "ids:DatPayload", + "@context": "https://w3id.org/idsa/contexts/context.jsonld", + "transportCertsSha256": "c15e6558088dbfef215a43d2507bbd124f44fb8facd561c14561a2c1a669d0e0", + "sub": "A5:0C:A5:F0:84:D9:90:BB:BC:D9:57:3A:04:C8:7F:93:ED:97:A2:52:keyid:CB:8C:C7:B6:85:79:A8:23:A6:CB:15:AB:17:50:2F:E6:65:43:5D:E8" +} +. + +``` + +Create a new EDC validation function by adding this to the settings: + +```bash +edc.policy.validation.ids-security-profile.claim=securityProfile +``` + +### 3. Use the custom constraint validation in a `ContractDefinition` + +`ContractDefinition` as JSON (simplified): + +```json +{ + "id": "98c9c8b4-532f-45d3-a737-7f5a56c15b3e", + "accessPolicy": { + "uid": "5cc32689-f258-44f6-8821-acf230804a41", + "permissions": [ + { + "target": "a4be1bbb-9610-4a2b-af29-50bb7e9a2fbc", + "action": { + "type": "USE" + }, + "constraints": [ + { + "leftExpression": { + "value": "ids-security-profile" + }, + "rightExpression": { + "value": "idsc:TRUST_SECURITY_PROFILE" + }, + "operator": "EQ" + } + ], + "duties": [] + } + ], + "prohibitions": [], + "obligations": [] + }, + "contractPolicy": { + }, + "selectorExpression": { + "criteria": [] + } +} +``` diff --git a/edc-extensions/business-partner-validation/pom.xml b/edc-extensions/business-partner-validation/pom.xml new file mode 100644 index 000000000..a5c4a79de --- /dev/null +++ b/edc-extensions/business-partner-validation/pom.xml @@ -0,0 +1,43 @@ + + + 4.0.0 + + + net.catenax.edc.extensions + edc-extensions + 0.0.2-SNAPSHOT + + business-partner-validation + jar + + + + + org.eclipse.dataspaceconnector + policy-spi + + + + + org.junit.jupiter + junit-jupiter-engine + + + org.junit.jupiter + junit-jupiter-api + + + org.junit.jupiter + junit-jupiter-params + + + org.junit.platform + junit-platform-suite + + + org.mockito + mockito-all + + + + \ No newline at end of file diff --git a/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/BusinessPartnerValidationExtension.java b/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/BusinessPartnerValidationExtension.java new file mode 100644 index 000000000..48aca1b33 --- /dev/null +++ b/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/BusinessPartnerValidationExtension.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.validation.businesspartner; + +import static org.eclipse.dataspaceconnector.spi.policy.PolicyEngine.ALL_SCOPES; + +import net.catenax.edc.validation.businesspartner.functions.BusinessPartnerDutyFunction; +import net.catenax.edc.validation.businesspartner.functions.BusinessPartnerPermissionFunction; +import net.catenax.edc.validation.businesspartner.functions.BusinessPartnerProhibitionFunction; +import org.eclipse.dataspaceconnector.policy.model.Duty; +import org.eclipse.dataspaceconnector.policy.model.Permission; +import org.eclipse.dataspaceconnector.policy.model.Prohibition; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.policy.PolicyEngine; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtension; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; + +public class BusinessPartnerValidationExtension implements ServiceExtension { + + /** + * The key for business partner numbers constraints. Must be used as left operand when declaring + * constraints. + * + *

Example: + * + *

+   * {
+   *     "constraint": {
+   *         "leftOperand": "BusinessPartner",
+   *         "operator": "EQ",
+   *         "rightOperand": "BPNLCDQ90000X42KU"
+   *     }
+   * }
+   * 
+ */ + private static final String BUSINESS_PARTNER_CONSTRAINT_KEY = "BusinessPartner"; + + @Override + public String name() { + return "Business Partner Validation Extension"; + } + + @Override + public void initialize(ServiceExtensionContext context) { + + final Monitor monitor = context.getMonitor(); + final PolicyEngine policyEngine = context.getService(PolicyEngine.class); + + final BusinessPartnerDutyFunction dutyFunction = new BusinessPartnerDutyFunction(monitor); + final BusinessPartnerPermissionFunction permissionFunction = + new BusinessPartnerPermissionFunction(monitor); + final BusinessPartnerProhibitionFunction prohibitionFunction = + new BusinessPartnerProhibitionFunction(monitor); + + policyEngine.registerFunction( + ALL_SCOPES, Duty.class, BUSINESS_PARTNER_CONSTRAINT_KEY, dutyFunction); + policyEngine.registerFunction( + ALL_SCOPES, Permission.class, BUSINESS_PARTNER_CONSTRAINT_KEY, permissionFunction); + policyEngine.registerFunction( + ALL_SCOPES, Prohibition.class, BUSINESS_PARTNER_CONSTRAINT_KEY, prohibitionFunction); + } +} diff --git a/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java b/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java new file mode 100644 index 000000000..c776e6042 --- /dev/null +++ b/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.validation.businesspartner.functions; + +import java.util.Map; +import java.util.Objects; +import org.eclipse.dataspaceconnector.policy.model.Operator; +import org.eclipse.dataspaceconnector.spi.agent.ParticipantAgent; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.policy.PolicyContext; + +/** + * Abstract class for BusinessPartnerNumber validation. This class may be inherited from the EDC + * policy enforcing functions for duties, permissions and prohibitions. + */ +public abstract class AbstractBusinessPartnerValidation { + + private final Monitor monitor; + + protected AbstractBusinessPartnerValidation(Monitor monitor) { + this.monitor = Objects.requireNonNull(monitor); + } + + /** + * Name of the claim that contains the Business Partner Number. + * + *

Please note: At the time of writing (April 2022) the business partner + * number is part of the 'referringConnector' claim in the IDS DAT token. This will probably + * change for the next release. + */ + private static final String BUSINESS_PARTNER_NUMBER_CLAIM_KEY = "referringConnector"; + + /** + * Evaluation funtion to decide whether a claim belongs to a specific business partner. + * + * @param operator operator of the constraint + * @param rightValue right value fo the constraint, that contains the business partner number + * (e.g. BPNLCDQ90000X42KU) + * @param claims claims of the participant / business partner + * @return true if claims are from the constrained business partner + */ + protected boolean evaluate( + final Operator operator, final Object rightValue, final PolicyContext policyContext) { + + if (policyContext.hasProblems() && policyContext.getProblems().size() > 0) { + String problems = String.join(", ", policyContext.getProblems()); + String logMessage = + String.format( + "BusinessPartnerNumberValidation: Rejecting PolicyContext with problems. Problems: %s", + problems); + monitor.debug(logMessage); + return false; + } + + final ParticipantAgent participantAgent = policyContext.getParticipantAgent(); + final Map claims = participantAgent.getClaims(); + if (!claims.containsKey(BUSINESS_PARTNER_NUMBER_CLAIM_KEY)) { + return false; + } + + if (operator != Operator.EQ) { + throw new UnsupportedOperationException( + "Operator for BusinessPartnerNumber must always be 'EQ'"); + } + + if (!(rightValue instanceof String)) { + throw new UnsupportedOperationException( + "Right value of BusinessPartnerNumber constraint must be of type 'String'"); + } + + String claimValue = claims.get(BUSINESS_PARTNER_NUMBER_CLAIM_KEY); + + // At the time of writing the business partner number is part of the + // 'referingConnector' claim, which contains a connector URL. + // As the CX projects are not further alligned about the URL formatting, the + // enforcement can only be done by checking whether the URL _contains_ the + // number. + return claimValue.contains((String) rightValue); + } +} diff --git a/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/BusinessPartnerDutyFunction.java b/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/BusinessPartnerDutyFunction.java new file mode 100644 index 000000000..dcf0c5050 --- /dev/null +++ b/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/BusinessPartnerDutyFunction.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.validation.businesspartner.functions; + +import org.eclipse.dataspaceconnector.policy.model.Duty; +import org.eclipse.dataspaceconnector.policy.model.Operator; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.policy.AtomicConstraintFunction; +import org.eclipse.dataspaceconnector.spi.policy.PolicyContext; + +/** AtomicConstraintFunction to validate business partner numbers for edc duties. */ +public class BusinessPartnerDutyFunction extends AbstractBusinessPartnerValidation + implements AtomicConstraintFunction { + + public BusinessPartnerDutyFunction(Monitor monitor) { + super(monitor); + } + + @Override + public boolean evaluate(Operator operator, Object rightValue, Duty rule, PolicyContext context) { + return evaluate(operator, rightValue, context); + } +} diff --git a/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/BusinessPartnerPermissionFunction.java b/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/BusinessPartnerPermissionFunction.java new file mode 100644 index 000000000..a12544248 --- /dev/null +++ b/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/BusinessPartnerPermissionFunction.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.validation.businesspartner.functions; + +import org.eclipse.dataspaceconnector.policy.model.Operator; +import org.eclipse.dataspaceconnector.policy.model.Permission; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.policy.AtomicConstraintFunction; +import org.eclipse.dataspaceconnector.spi.policy.PolicyContext; + +/** AtomicConstraintFunction to validate business partner numbers for edc permissions. */ +public class BusinessPartnerPermissionFunction extends AbstractBusinessPartnerValidation + implements AtomicConstraintFunction { + + public BusinessPartnerPermissionFunction(Monitor monitor) { + super(monitor); + } + + @Override + public boolean evaluate( + Operator operator, Object rightValue, Permission rule, PolicyContext context) { + return evaluate(operator, rightValue, context); + } +} diff --git a/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/BusinessPartnerProhibitionFunction.java b/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/BusinessPartnerProhibitionFunction.java new file mode 100644 index 000000000..faeb4a358 --- /dev/null +++ b/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/BusinessPartnerProhibitionFunction.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.validation.businesspartner.functions; + +import org.eclipse.dataspaceconnector.policy.model.Operator; +import org.eclipse.dataspaceconnector.policy.model.Prohibition; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.policy.AtomicConstraintFunction; +import org.eclipse.dataspaceconnector.spi.policy.PolicyContext; + +/** AtomicConstraintFunction to validate business partner numbers for edc prohibitions. */ +public class BusinessPartnerProhibitionFunction extends AbstractBusinessPartnerValidation + implements AtomicConstraintFunction { + + public BusinessPartnerProhibitionFunction(Monitor monitor) { + super(monitor); + } + + @Override + public boolean evaluate( + Operator operator, Object rightValue, Prohibition rule, PolicyContext context) { + return evaluate(operator, rightValue, context); + } +} diff --git a/edc-extensions/business-partner-validation/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension b/edc-extensions/business-partner-validation/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension new file mode 100644 index 000000000..609f8ca1b --- /dev/null +++ b/edc-extensions/business-partner-validation/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension @@ -0,0 +1,15 @@ +# +# Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +# +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# +# Contributors: +# Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation +# +# + +net.catenax.edc.validation.businesspartner.BusinessPartnerValidationExtension diff --git a/edc-extensions/business-partner-validation/src/test/java/net/catenax/edc/validation/businesspartner/BusinessPartnerValidationExtensionTest.java b/edc-extensions/business-partner-validation/src/test/java/net/catenax/edc/validation/businesspartner/BusinessPartnerValidationExtensionTest.java new file mode 100644 index 000000000..6cfe5c0f0 --- /dev/null +++ b/edc-extensions/business-partner-validation/src/test/java/net/catenax/edc/validation/businesspartner/BusinessPartnerValidationExtensionTest.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.validation.businesspartner; + +import org.eclipse.dataspaceconnector.policy.model.Duty; +import org.eclipse.dataspaceconnector.policy.model.Permission; +import org.eclipse.dataspaceconnector.policy.model.Prohibition; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.policy.PolicyEngine; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class BusinessPartnerValidationExtensionTest { + + private static final String BUSINESS_PARTNER_CONSTRAINT_KEY = "BusinessPartner"; + + private BusinessPartnerValidationExtension extension; + + // mocks + private ServiceExtensionContext serviceExtensionContext; + private PolicyEngine policyEngine; + + @BeforeEach + public void setup() { + + policyEngine = Mockito.mock(PolicyEngine.class); + + final Monitor monitor = Mockito.mock(Monitor.class); + serviceExtensionContext = Mockito.mock(ServiceExtensionContext.class); + + Mockito.when(serviceExtensionContext.getMonitor()).thenReturn(monitor); + Mockito.when(serviceExtensionContext.getService(PolicyEngine.class)).thenReturn(policyEngine); + + extension = new BusinessPartnerValidationExtension(); + } + + @Test + public void testRegisterDutyFunction() { + + // invoke + extension.initialize(serviceExtensionContext); + + // verify + Mockito.verify(policyEngine, Mockito.times(1)) + .registerFunction( + Mockito.anyString(), + Mockito.eq(Duty.class), + Mockito.eq(BUSINESS_PARTNER_CONSTRAINT_KEY), + Mockito.any()); + } + + @Test + public void testRegisterPermissionFunction() { + + // invoke + extension.initialize(serviceExtensionContext); + + // verify + Mockito.verify(policyEngine, Mockito.times(1)) + .registerFunction( + Mockito.anyString(), + Mockito.eq(Permission.class), + Mockito.eq(BUSINESS_PARTNER_CONSTRAINT_KEY), + Mockito.any()); + } + + @Test + public void testRegisterProhibitionFunction() { + + // invoke + extension.initialize(serviceExtensionContext); + + // verify + Mockito.verify(policyEngine, Mockito.times(1)) + .registerFunction( + Mockito.anyString(), + Mockito.eq(Prohibition.class), + Mockito.eq(BUSINESS_PARTNER_CONSTRAINT_KEY), + Mockito.any()); + } +} diff --git a/edc-extensions/business-partner-validation/src/test/java/net/catenax/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidationTest.java b/edc-extensions/business-partner-validation/src/test/java/net/catenax/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidationTest.java new file mode 100644 index 000000000..8bf70c247 --- /dev/null +++ b/edc-extensions/business-partner-validation/src/test/java/net/catenax/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidationTest.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.validation.businesspartner.functions; + +import java.util.Collections; +import java.util.List; +import org.eclipse.dataspaceconnector.policy.model.Operator; +import org.eclipse.dataspaceconnector.spi.agent.ParticipantAgent; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.policy.PolicyContext; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.Mockito; + +public class AbstractBusinessPartnerValidationTest { + + private AbstractBusinessPartnerValidation validation; + + // mocks + private Monitor monitor; + private PolicyContext policyContext; + private ParticipantAgent participantAgent; + + @BeforeEach + public void BeforeEach() { + this.monitor = Mockito.mock(Monitor.class); + this.policyContext = Mockito.mock(PolicyContext.class); + this.participantAgent = Mockito.mock(ParticipantAgent.class); + + Mockito.when(policyContext.getParticipantAgent()).thenReturn(participantAgent); + + validation = new AbstractBusinessPartnerValidation(monitor) {}; + } + + @ParameterizedTest + @EnumSource(Operator.class) + public void testThrowsOnUnsupportedOperations(Operator operator) { + + if (operator == Operator.EQ) { // only allowed operator + return; + } + + // prepare + prepareContextProblems(null); + prepareBusinessPartnerClaim("yes"); + + // invoke & assert + Assertions.assertThrows( + UnsupportedOperationException.class, + () -> validation.evaluate(operator, "null", policyContext)); + } + + @Test + public void testThrowsOnUnsupportedRightValue() { + + // prepare + prepareContextProblems(null); + prepareBusinessPartnerClaim("yes"); + + // invoke & assert + Assertions.assertThrows( + UnsupportedOperationException.class, + () -> validation.evaluate(Operator.EQ, 1, policyContext)); + Assertions.assertThrows( + UnsupportedOperationException.class, + () -> validation.evaluate(Operator.EQ, new Object(), policyContext)); + } + + @Test + public void testValidationFailsWhenClaimMissing() { + + // prepare + prepareContextProblems(null); + + // invoke + final boolean isValid = validation.evaluate(Operator.EQ, "foo", policyContext); + + // assert + Assertions.assertFalse(isValid); + } + + @Test + public void testValidationSuccedesWhenClaimContainsNumber() { + + // prepare + prepareContextProblems(null); + + // prepare equals + prepareBusinessPartnerClaim("foo"); + final boolean isEqualsTrue = validation.evaluate(Operator.EQ, "foo", policyContext); + + // prepare contains + prepareBusinessPartnerClaim("foobar"); + final boolean isContainedTrue = validation.evaluate(Operator.EQ, "foo", policyContext); + + // assert + Assertions.assertTrue(isEqualsTrue); + Assertions.assertTrue(isContainedTrue); + } + + @Test + public void testValidationWhenParticipantHasProblems() { + + // prepare + prepareContextProblems(Collections.singletonList("big problem")); + prepareBusinessPartnerClaim("foo"); + + // invoke + final boolean isValid = validation.evaluate(Operator.EQ, "foo", policyContext); + + // Mockito.verify(monitor.debug(Mockito.anyString()); + Assertions.assertFalse(isValid); + } + + @Test + public void testValidationWhenParticipantIsValid() { + + // prepare + prepareContextProblems(null); + prepareBusinessPartnerClaim("foo"); + + // invoke + final boolean isContainedTrue = validation.evaluate(Operator.EQ, "foo", policyContext); + + // Mockito.verify(monitor.debug(Mockito.anyString()); + Assertions.assertTrue(isContainedTrue); + } + + private void prepareContextProblems(List problems) { + Mockito.when(policyContext.getProblems()).thenReturn(problems); + + if (problems == null || problems.isEmpty()) { + Mockito.when(policyContext.hasProblems()).thenReturn(false); + } else { + Mockito.when(policyContext.hasProblems()).thenReturn(true); + } + } + + private void prepareBusinessPartnerClaim(String businessPartnerNumber) { + Mockito.when(participantAgent.getClaims()) + .thenReturn(Collections.singletonMap("referringConnector", businessPartnerNumber)); + } +} diff --git a/edc-extensions/pom.xml b/edc-extensions/pom.xml index 194abb15b..e42822784 100644 --- a/edc-extensions/pom.xml +++ b/edc-extensions/pom.xml @@ -13,6 +13,7 @@ pom + business-partner-validation aas-controller postgresql-migration diff --git a/pom.xml b/pom.xml index 498b3c201..37ab4fc93 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,6 @@ edc-extensions - edc-controlplane edc-dataplane @@ -28,6 +27,10 @@ 3.0.0 42.2.6 8.5.5 + 5.8.2 + 1.8.2 + 1.10.19 + 1.1.0 @@ -99,6 +102,17 @@ + + maven-surefire-plugin + 2.19.1 + + + org.junit.platform + junit-platform-surefire-provider + ${org.junit.platform.surefire.provider.version} + + + @@ -118,6 +132,11 @@ aas-controller ${project.version} + + net.catenax.edc.extensions + business-partner-validation + ${project.version} + net.catenax.edc.extensions postgresql-migration @@ -778,6 +797,38 @@ pom import + + + + org.junit.jupiter + junit-jupiter-engine + ${junit.jupiter.version} + test + + + org.junit.jupiter + junit-jupiter-api + ${junit.jupiter.version} + test + + + org.junit.jupiter + junit-jupiter-params + ${junit.jupiter.version} + test + + + org.junit.platform + junit-platform-suite + ${junit.platform.version} + test + + + org.mockito + mockito-all + ${mockito.version} + test + \ No newline at end of file From 1af1d065cf463afe262688f2e6ba1be3f8dca42f Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Tue, 12 Apr 2022 08:15:28 +0200 Subject: [PATCH 021/433] Rework edc data/control-plane svc/ingress endpoints (#23) * Rework edc data/control-plane svc/ingress endpoints --- .../edc-controlplane/templates/configmap.yaml | 35 +- .../templates/deployment.yaml | 38 +- .../helm/edc-controlplane/templates/hpa.yaml | 1 + .../templates/imagepullsecret.yaml | 1 + .../edc-controlplane/templates/ingress.yaml | 60 +-- .../edc-controlplane/templates/service.yaml | 21 +- .../templates/serviceaccount.yaml | 1 + deployment/helm/edc-controlplane/values.yaml | 413 +++++++++++------- deployment/helm/edc-dataplane/Chart.yaml | 2 +- .../edc-dataplane/templates/configmap.yaml | 33 +- .../edc-dataplane/templates/deployment.yaml | 33 +- .../helm/edc-dataplane/templates/hpa.yaml | 1 + .../templates/imagepullsecret.yaml | 1 + .../helm/edc-dataplane/templates/ingress.yaml | 60 +-- .../helm/edc-dataplane/templates/service.yaml | 17 +- .../templates/serviceaccount.yaml | 1 + deployment/helm/edc-dataplane/values.yaml | 404 ++++++++++------- .../edc-controlplane-cosmosdb/pom.xml | 2 - .../edc-controlplane-memory/pom.xml | 22 +- .../edc-controlplane-postgresql/pom.xml | 2 - edc-dataplane/pom.xml | 2 - 21 files changed, 711 insertions(+), 439 deletions(-) diff --git a/deployment/helm/edc-controlplane/templates/configmap.yaml b/deployment/helm/edc-controlplane/templates/configmap.yaml index 437884924..8da172467 100644 --- a/deployment/helm/edc-controlplane/templates/configmap.yaml +++ b/deployment/helm/edc-controlplane/templates/configmap.yaml @@ -1,3 +1,4 @@ +--- apiVersion: v1 kind: ConfigMap metadata: @@ -6,26 +7,18 @@ metadata: {{- include "edc-controlplane.labels" . | nindent 4 }} data: configuration.properties: |- - {{- range $k, $v := .Values.configuration.properties }} - {{- if $v }} - {{ $k | replace "_" "." }}={{ $v }} - {{- else }} - # {{ $k | replace "_" "." }}= - {{- end }} - {{- end }} + web.http.default.port={{ .Values.edc.endpoints.default.port }} + web.http.default.path={{ .Values.edc.endpoints.default.path }} + web.http.data.port={{ .Values.edc.endpoints.data.port }} + web.http.data.path={{ .Values.edc.endpoints.data.path }} + web.http.control.port={{ .Values.edc.endpoints.control.port }} + web.http.control.path={{ .Values.edc.endpoints.control.path }} + web.http.ids.port={{ .Values.edc.endpoints.ids.port }} + web.http.ids.path={{ .Values.edc.endpoints.ids.path }} + {{- .Values.configuration.properties | nindent 4 }} + opentelemetry.properties: |- - {{- range $k, $v := .Values.opentelemetry.properties }} - {{- if $v }} - {{ $k | replace "_" "." }}={{ $v }} - {{- else }} - # {{ $k | replace "_" "." }}= - {{- end }} - {{- end }} + {{- .Values.opentelemetry.properties | nindent 4 }} + logging.properties: |- - {{- range $k, $v := .Values.logging.properties }} - {{- if $v }} - {{ $k | replace "_" "." }}={{ $v }} - {{- else }} - # {{ $k | replace "_" "." }}= - {{- end }} - {{- end }} + {{- .Values.logging.properties | nindent 4 }} diff --git a/deployment/helm/edc-controlplane/templates/deployment.yaml b/deployment/helm/edc-controlplane/templates/deployment.yaml index ae261cd75..94d02c83f 100644 --- a/deployment/helm/edc-controlplane/templates/deployment.yaml +++ b/deployment/helm/edc-controlplane/templates/deployment.yaml @@ -1,3 +1,4 @@ +--- apiVersion: apps/v1 kind: Deployment metadata: @@ -36,23 +37,44 @@ spec: - name: {{ .Chart.Name }} securityContext: {{- toYaml .Values.securityContext | nindent 12 }} - image: "{{ .Values.image.repository }}{{- if .Values.flavor }}-{{ .Values.flavor }}{{- end -}}:{{ .Values.image.tag | default .Chart.AppVersion }}" + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy }} ports: - - name: http - containerPort: 80 + - name: default + containerPort: {{ .Values.edc.endpoints.default.port }} + protocol: TCP + - name: control + containerPort: {{ .Values.edc.endpoints.control.port }} + protocol: TCP + - name: data + containerPort: {{ .Values.edc.endpoints.data.port }} + protocol: TCP + - name: ids + containerPort: {{ .Values.edc.endpoints.ids.port }} protocol: TCP - name: metrics - containerPort: 9090 + containerPort: {{ .Values.edc.endpoints.metrics.port }} protocol: TCP + {{- if .Values.livenessProbe.enabled }} livenessProbe: httpGet: - path: / - port: http + path: {{ .Values.edc.endpoints.default.path }}/check/liveness + port: default + {{- end }} + {{- if .Values.readinessProbe.enabled }} readinessProbe: httpGet: - path: / - port: http + path: {{ .Values.edc.endpoints.default.path }}/check/readiness + port: default + {{- end }} + {{- if .Values.startupProbe.enabled }} + startupProbe: + httpGet: + path: {{ .Values.edc.endpoints.default.path }}/check/startup + port: default + failureThreshold: 12 + initialDelaySeconds: 10 + {{- end }} resources: {{- toYaml .Values.resources | nindent 12 }} volumeMounts: diff --git a/deployment/helm/edc-controlplane/templates/hpa.yaml b/deployment/helm/edc-controlplane/templates/hpa.yaml index 5af9fb7fc..0881a3ce2 100644 --- a/deployment/helm/edc-controlplane/templates/hpa.yaml +++ b/deployment/helm/edc-controlplane/templates/hpa.yaml @@ -1,4 +1,5 @@ {{- if .Values.autoscaling.enabled }} +--- apiVersion: autoscaling/v2beta1 kind: HorizontalPodAutoscaler metadata: diff --git a/deployment/helm/edc-controlplane/templates/imagepullsecret.yaml b/deployment/helm/edc-controlplane/templates/imagepullsecret.yaml index 84ddc001d..663efd28a 100644 --- a/deployment/helm/edc-controlplane/templates/imagepullsecret.yaml +++ b/deployment/helm/edc-controlplane/templates/imagepullsecret.yaml @@ -1,4 +1,5 @@ {{- if .Values.imagePullSecret.dockerconfigjson }} +--- apiVersion: v1 kind: Secret metadata: diff --git a/deployment/helm/edc-controlplane/templates/ingress.yaml b/deployment/helm/edc-controlplane/templates/ingress.yaml index 0e15b7baa..c9014fbbd 100644 --- a/deployment/helm/edc-controlplane/templates/ingress.yaml +++ b/deployment/helm/edc-controlplane/templates/ingress.yaml @@ -1,6 +1,6 @@ {{- if .Values.ingress.enabled -}} +--- {{- $fullName := include "edc-controlplane.fullname" . -}} -{{- $svcPort := .Values.service.port -}} {{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} @@ -18,44 +18,50 @@ metadata: name: {{ $fullName }} labels: {{- include "edc-controlplane.labels" . | nindent 4 }} - {{- with .Values.ingress.annotations }} annotations: + {{- with .Values.ingress.annotations }} {{- toYaml . | nindent 4 }} - {{- end }} + {{- end }} + {{- if .Values.ingress.certManager -}} + {{- if .Values.ingress.certManager.issuer -}} + cert-manager.io/issuer: {{ .Values.ingress.certManager.issuer }} + {{- end }} + {{- if .Values.ingress.certManager.clusterIssuer -}} + cert-manager.io/cluster-issuer: {{ .Values.ingress.certManager.clusterIssuer }} + {{- end }} + {{- end }} spec: {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} ingressClassName: {{ .Values.ingress.className }} {{- end }} + {{- if .Values.ingress.hostname }} {{- if .Values.ingress.tls }} tls: - {{- range .Values.ingress.tls }} - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} + - {{ .Values.ingress.hostname }} + secretName: {{ $fullName }}-tls {{- end }} rules: - {{- range .Values.ingress.hosts }} - - host: {{ .host | quote }} + - host: {{ .Values.ingress.hostname }} http: paths: - {{- range .paths }} - - path: {{ .path }} - {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} - pathType: {{ .pathType }} - {{- end }} - backend: - {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} - service: - name: {{ $fullName }} - port: - number: {{ $svcPort }} - {{- else }} - serviceName: {{ $fullName }} - servicePort: {{ $svcPort }} - {{- end }} + {{ range $name, $mapping := .Values.edc.endpoints }} + {{ if $mapping.ingress -}} + - path: {{ $mapping.path }} + {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + pathType: Prefix {{- end }} - {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ $fullName }} + port: + number: {{ $mapping.port }} + {{- else }} + serviceName: {{ $fullName }} + servicePort: {{ $mapping.port }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} {{- end }} diff --git a/deployment/helm/edc-controlplane/templates/service.yaml b/deployment/helm/edc-controlplane/templates/service.yaml index 8417e50a1..020a48026 100644 --- a/deployment/helm/edc-controlplane/templates/service.yaml +++ b/deployment/helm/edc-controlplane/templates/service.yaml @@ -1,3 +1,4 @@ +--- apiVersion: v1 kind: Service metadata: @@ -7,11 +8,23 @@ metadata: spec: type: {{ .Values.service.type }} ports: - - port: {{ .Values.service.port }} - targetPort: http + - port: {{ .Values.edc.endpoints.default.port }} + targetPort: default protocol: TCP - name: http - - port: 9090 + name: default + - port: {{ .Values.edc.endpoints.control.port }} + targetPort: control + protocol: TCP + name: control + - port: {{ .Values.edc.endpoints.data.port }} + targetPort: data + protocol: TCP + name: data + - port: {{ .Values.edc.endpoints.ids.port }} + targetPort: ids + protocol: TCP + name: ids + - port: {{ .Values.edc.endpoints.metrics.port }} targetPort: metrics protocol: TCP name: metrics diff --git a/deployment/helm/edc-controlplane/templates/serviceaccount.yaml b/deployment/helm/edc-controlplane/templates/serviceaccount.yaml index 5602d2204..66568118b 100644 --- a/deployment/helm/edc-controlplane/templates/serviceaccount.yaml +++ b/deployment/helm/edc-controlplane/templates/serviceaccount.yaml @@ -1,4 +1,5 @@ {{- if .Values.serviceAccount.create -}} +--- apiVersion: v1 kind: ServiceAccount metadata: diff --git a/deployment/helm/edc-controlplane/values.yaml b/deployment/helm/edc-controlplane/values.yaml index cb5585765..fd5ff04c5 100644 --- a/deployment/helm/edc-controlplane/values.yaml +++ b/deployment/helm/edc-controlplane/values.yaml @@ -4,29 +4,49 @@ replicaCount: 1 -# Flavor of edc-controlplane. values: [cosmosdb] -flavor: "" - image: - repository: ghcr.io/catenax-ng/catena-x-edc/edc-controlplane + ## + ## Which derivate of the edc controlplane to use. + ## One of: + ## * ghcr.io/catenax-ng/catena-x-edc/edc-controlplane-memory + ## * ghcr.io/catenax-ng/catena-x-edc/edc-controlplane-postgresql + ## * ghcr.io/catenax-ng/catena-x-edc/edc-controlplane-cosmosdb + ## + repository: ghcr.io/catenax-ng/catena-x-edc/edc-controlplane-memory pullPolicy: IfNotPresent - # Overrides the image tag whose default is the chart appVersion. + ## + ## Overrides the image tag whose default is the chart appVersion. + ## tag: "" +## +## Image pull secret to create to obtain the container image +## Note: 'imagePullSecret.dockerconfigjson' takes precedence if configured together with 'imagePullSecrets' +## imagePullSecret: dockerconfigjson: "" +## +## Existing image pull secret to use to obtain the container image +## imagePullSecrets: [] + nameOverride: "" fullnameOverride: "" serviceAccount: - # Specifies whether a service account should be created + ## + ## Specifies whether a service account should be created per release + ## create: true - # Annotations to add to the service account + ## + ## 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 + ## + ## 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: {} @@ -42,25 +62,100 @@ securityContext: {} # runAsNonRoot: true # runAsUser: 1000 +## +## Whether to enable kubernetes liveness-probes +## +livenessProbe: + enabled: true + +## +## Whether to enable kubernetes readiness-probes +## +readinessProbe: + enabled: true + +## +## Whether to enable kubernetes startup-probes +## +startupProbe: + enabled: true + +## +## EDC endpoints exposed by the conrol-plane +## +edc: + endpoints: + # Default api exposing health checks etc + default: + port: "8080" + path: /api + ingress: false + # Data management API + data: + port: "8181" + path: /data + ingress: true + # Control API + control: + port: "9999" + path: /api/control + ingress: true + # IDS endpoints + ids: + port: "8282" + path: /api/v1/ids + ingress: true + # Prometheus endpoint + metrics: + port: "9090" + path: /metrics + ingress: false + +## +## Service to expose the running application on a set of Pods as a network service. +## service: type: ClusterIP - port: 80 +## +## Ingress declaration to publicly expose the network service. +## ingress: - enabled: false + ## + ## Set to true to enable ingress record generation + ## + enabled: true + ## + ## Defines the ingress class to use https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class + ## className: "" + ## + ## En + ## + tls: false + + ## + ## Adds cert-manager annotations to the ingress + ## + certManager: + ## + ## If preset enables certificate generation via cert-manager namespace scoped issuer + ## + issuer: "" + ## + ## If preset enables certificate generation via cert-manager cluster-wide issuer + ## + clusterIssuer: "" + + ## + ## Additional ingress annotations to add + ## annotations: {} - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" - hosts: - - host: controlplane-example.local - paths: - - path: / - pathType: ImplementationSpecific - tls: [] - # - secretName: chart-example-tls - # hosts: - # - chart-example.local + + ## + ## The hostname to be used to precisely map incoming traffic onto the underlying network service + ## + hostname: "edc-controlplane.local" resources: {} # We usually recommend not to specify default resources and to leave this as a conscious @@ -87,138 +182,152 @@ tolerations: [] affinity: {} +## +## EDC logging.properties configuring the java.util.logging subsystem +## logging: - properties: - _level: INFO - org_eclipse_dataspaceconnector_level: ALL - handlers: java.util.logging.ConsoleHandler - java_util_logging_ConsoleHandler_formatter: java.util.logging.SimpleFormatter - java_util_logging_ConsoleHandler_level: ALL - java_util_logging_SimpleFormatter_format: "[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n" + properties: |- + .level=INFO + org.eclipse.dataspaceconnector.level=ALL + handlers=java.util.logging.ConsoleHandler + java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter + java.util.logging.ConsoleHandler.level=ALL + java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n +## +## opentelemetry.properties configuring the opentelemetry agent +## +## See https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/ for further configuration options +## opentelemetry: - properties: - otel_javaagent_enabled: "true" - otel_javaagent_debug: "false" + properties: |- + otel.javaagent.enabled=true + otel.javaagent.debug=false +## +## EDC configuration.properties +## +## Consult the eclipse-dataspaceconnector documentation for further details: https://github.com/eclipse-dataspaceconnector/DataSpaceConnector +## configuration: - properties: - edc_api_control_auth_apikey_key: "" - edc_api_control_auth_apikey_value: "" - edc_assetindex_cosmos_account-name: "" - edc_assetindex_cosmos_container-name: "" - edc_assetindex_cosmos_database-name: "" - edc_assetindex_cosmos_preferred-region: "" - edc_atomikos_checkpoint_interval: "" - edc_atomikos_directory: "" - edc_atomikos_logging: "" - edc_atomikos_threaded2pc: "" - edc_atomikos_timeout: "" - edc_aws_access_key: "" - edc_aws_provision_retry_retries_max: "" - edc_aws_provision_role_duration_session_max: "" - edc_aws_secret_access_key: "" - edc_blobstore_endpoint: "" - edc_contractdefinitionstore_cosmos_account-name: "" - edc_contractdefinitionstore_cosmos_container-name: "" - edc_contractdefinitionstore_cosmos_database-name: "" - edc_contractdefinitionstore_cosmos_preferred-region: "" - edc_contractnegotiationstore_cosmos_account-name: "" - edc_contractnegotiationstore_cosmos_container-name: "" - edc_contractnegotiationstore_cosmos_database-name: "" - edc_contractnegotiationstore_cosmos_preferred-region: "" - edc_controlplane_validation-endpoint: "" - edc_core_retry_backoff_max: "" - edc_core_retry_backoff_min: "" - edc_core_retry_retries_max: "" - edc_core_system_health_check_liveness-period: "" - edc_core_system_health_check_readiness-period: "" - edc_core_system_health_check_startup-period: "" - edc_core_system_health_check_threadpool-size: "" - edc_cosmos_partition-key: "" - edc_cosmos_query-metrics-enabled: "" - edc_dataplane_queue_capacity: "" - edc_dataplane_wait: "" - edc_dataplane_workers: "" - edc_datasource_asset_name: "default" - edc_datasource_contractdefinition_name: "default" - edc_datasource_contractnegotiation_name: "default" - edc_datasource_policy_name: "default" - edc_datasource_transferprocess_name: "default" - edc_datasource_default_pool_maxIdleConnections: "" - edc_datasource_default_pool_maxTotalConnections: "" - edc_datasource_default_pool_minIdleConnections: "" - edc_datasource_default_pool_testConnectionOnBorrow: "" - edc_datasource_default_pool_testConnectionOnCreate: "" - edc_datasource_default_pool_testConnectionOnReturn: "" - edc_datasource_default_pool_testConnectionWhileIdle: "" - edc_datasource_default_pool_testQuery: "" - edc_datasource_default_url: "" - edc_datasource_default_user: "" - edc_datasource_default_password: "" - edc_dpf_selector_url: "" - edc_events_topic_endpoint: "" - edc_events_topic_name: "" - edc_fs_config: "" - edc_hostname: "" - edc_identity_did_url: "" - edc_ids_catalog_id: "" - edc_ids_curator: "" - edc_ids_description: "" - edc_ids_endpoint: "" - edc_ids_id: "" - edc_ids_maintainer: "" - edc_ids_security_profile: "" - edc_ids_title: "" - edc_ids_validation_referringconnector: "" - edc_ion_crawler_did-type: "" - edc_ion_crawler_interval-minutes: "" - edc_ion_crawler_ion_url: "" - edc_keystore: "" - edc_keystore_password: "" - edc_metrics_enabled: "" - edc_metrics_executor_enabled: "" - edc_metrics_jersey_enabled: "" - edc_metrics_jetty_enabled: "" - edc_metrics_okhttp_enabled: "" - edc_metrics_system_enabled: "" - edc_negotiation_consumer_state-machine_batch-size: "" - edc_negotiation_provider_state-machine_batch-size: "" - edc_node_directory_cosmos_account_name: "" - edc_node_directory_cosmos_container_name: "" - edc_node_directory_cosmos_database_name: "" - edc_node_directory_cosmos_preferred_region: "" - edc_oauth_client_id: "" - edc_oauth_private_key_alias: "" - edc_oauth_provider_audience: "" - edc_oauth_provider_jwks_refresh: "" - edc_oauth_provider_jwks_url: "" - edc_oauth_public_key_alias: "" - edc_oauth_token_url: "" - edc_oauth_validation_nbf_leeway: "" - edc_public_key_alias: "" - edc_receiver_http_auth-code: "" - edc_receiver_http_auth-key: "" - edc_receiver_http_endpoint: "" - edc_transfer_dataplane_sync_endpoint: "" - edc_transfer_dataplane_sync_token_validity: "" - edc_transfer_dataplane_token_signer_privatekey_alias: "" - edc_transfer_functions_check_endpoint: "" - edc_transfer_functions_enabled_protocols: "" - edc_transfer_functions_transfer_endpoint: "" - edc_transfer-process-store_cosmos_account_name: "" - edc_transfer-process-store_cosmos_container-name: "" - edc_transfer-process-store_cosmos_preferred-region: "" - edc_transfer-process-store_database_name: "" - edc_transfer_state-machine_batch-size: "" - edc_vault: "" - edc_vault_certificate: "" - edc_vault_clientid: "" - edc_vault_clientsecret: "" - edc_vault_name: "" - edc_vault_tenantid: "" - edc_webdid_doh_url: "" - edc_web_rest_cors_enabled: "" - edc_web_rest_cors_headers: "" - edc_web_rest_cors_methods: "" - edc_web_rest_cors_origins: "" + properties: |- + # edc.api.control.auth.apikey.key= + # edc.api.control.auth.apikey.value= + # edc.assetindex.cosmos.account-name= + # edc.assetindex.cosmos.container-name= + # edc.assetindex.cosmos.database-name= + # edc.assetindex.cosmos.preferred-region= + # edc.atomikos.checkpoint.interval= + # edc.atomikos.directory= + # edc.atomikos.logging= + # edc.atomikos.threaded2pc= + # edc.atomikos.timeout= + # edc.aws.access.key= + # edc.aws.provision.retry.retries.max= + # edc.aws.provision.role.duration.session.max= + # edc.aws.secret.access.key= + # edc.blobstore.endpoint= + # edc.contractdefinitionstore.cosmos.account-name= + # edc.contractdefinitionstore.cosmos.container-name= + # edc.contractdefinitionstore.cosmos.database-name= + # edc.contractdefinitionstore.cosmos.preferred-region= + # edc.contractnegotiationstore.cosmos.account-name= + # edc.contractnegotiationstore.cosmos.container-name= + # edc.contractnegotiationstore.cosmos.database-name= + # edc.contractnegotiationstore.cosmos.preferred-region= + # edc.controlplane.validation-endpoint= + # edc.core.retry.backoff.max= + # edc.core.retry.backoff.min= + # edc.core.retry.retries.max= + # edc.core.system.health.check.liveness-period= + # edc.core.system.health.check.readiness-period= + # edc.core.system.health.check.startup-period= + # edc.core.system.health.check.threadpool-size= + # edc.cosmos.partition-key= + # edc.cosmos.query-metrics-enabled= + # edc.dataplane.queue.capacity= + # edc.dataplane.wait= + # edc.dataplane.workers= + # edc.datasource.asset.name="default" + # edc.datasource.contractdefinition.name="default" + # edc.datasource.contractnegotiation.name="default" + # edc.datasource.policy.name="default" + # edc.datasource.transferprocess.name="default" + # edc.datasource.default.pool.maxIdleConnections= + # edc.datasource.default.pool.maxTotalConnections= + # edc.datasource.default.pool.minIdleConnections= + # edc.datasource.default.pool.testConnectionOnBorrow= + # edc.datasource.default.pool.testConnectionOnCreate= + # edc.datasource.default.pool.testConnectionOnReturn= + # edc.datasource.default.pool.testConnectionWhileIdle= + # edc.datasource.default.pool.testQuery= + # edc.datasource.default.url= + # edc.datasource.default.user= + # edc.datasource.default.password= + # edc.dpf.selector.url= + # edc.events.topic.endpoint= + # edc.events.topic.name= + # edc.fs.config= + # edc.hostname= + # edc.identity.did.url= + # edc.ids.catalog.id= + # edc.ids.curator= + # edc.ids.description= + # edc.ids.endpoint= + # edc.ids.id= + # edc.ids.maintainer= + # edc.ids.security.profile= + # edc.ids.title= + # edc.ids.validation.referringconnector= + # edc.ion.crawler.did-type= + # edc.ion.crawler.interval-minutes= + # edc.ion.crawler.ion.url= + # edc.keystore= + # edc.keystore.password= + # edc.metrics.enabled= + # edc.metrics.executor.enabled= + # edc.metrics.jersey.enabled= + # edc.metrics.jetty.enabled= + # edc.metrics.okhttp.enabled= + # edc.metrics.system.enabled= + # edc.negotiation.consumer.state-machine.batch-size= + # edc.negotiation.provider.state-machine.batch-size= + # edc.node.directory.cosmos.account.name= + # edc.node.directory.cosmos.container.name= + # edc.node.directory.cosmos.database.name= + # edc.node.directory.cosmos.preferred.region= + # edc.oauth.client.id= + # edc.oauth.private.key.alias= + # edc.oauth.provider.audience= + # edc.oauth.provider.jwks.refresh= + # edc.oauth.provider.jwks.url= + # edc.oauth.public.key.alias= + # edc.oauth.token.url= + # edc.oauth.validation.nbf.leeway= + # edc.public.key.alias= + # edc.receiver.http.auth-code= + # edc.receiver.http.auth-key= + # edc.receiver.http.endpoint= + # edc.transfer.dataplane.sync.endpoint= + # edc.transfer.dataplane.sync.token.validity= + # edc.transfer.dataplane.token.signer.privatekey.alias= + # edc.transfer.functions.check.endpoint= + # edc.transfer.functions.enabled.protocols= + # edc.transfer.functions.transfer.endpoint= + # edc.transfer-process-store.cosmos.account.name= + # edc.transfer-process-store.cosmos.container-name= + # edc.transfer-process-store.cosmos.preferred-region= + # edc.transfer-process-store.database.name= + # edc.transfer.state-machine.batch-size= + # edc.vault= + # edc.vault.certificate= + # edc.vault.clientid= + # edc.vault.clientsecret= + # edc.vault.name= + # edc.vault.tenantid= + # edc.webdid.doh.url= + # edc.web.rest.cors.enabled= + # edc.web.rest.cors.headers= + # edc.web.rest.cors.methods= + # edc.web.rest.cors.origins= + diff --git a/deployment/helm/edc-dataplane/Chart.yaml b/deployment/helm/edc-dataplane/Chart.yaml index 46ad4c61b..7aa03c35a 100644 --- a/deployment/helm/edc-dataplane/Chart.yaml +++ b/deployment/helm/edc-dataplane/Chart.yaml @@ -1,6 +1,6 @@ apiVersion: v2 name: edc-dataplane -description: EDC Control-Plane +description: EDC Data-Plane home: https://github.com/catenax-ng/catena-x-edc/deployment/helm/edc-dataplane type: application appVersion: "0.0.1" diff --git a/deployment/helm/edc-dataplane/templates/configmap.yaml b/deployment/helm/edc-dataplane/templates/configmap.yaml index 9e27ef2eb..5b32349ca 100644 --- a/deployment/helm/edc-dataplane/templates/configmap.yaml +++ b/deployment/helm/edc-dataplane/templates/configmap.yaml @@ -1,3 +1,4 @@ +--- apiVersion: v1 kind: ConfigMap metadata: @@ -6,26 +7,16 @@ metadata: {{- include "edc-dataplane.labels" . | nindent 4 }} data: configuration.properties: |- - {{- range $k, $v := .Values.configuration.properties }} - {{- if $v }} - {{ $k | replace "_" "." }}={{ $v }} - {{- else }} - # {{ $k | replace "_" "." }}= - {{- end }} - {{- end }} + web.http.default.port={{ .Values.edc.endpoints.default.port }} + web.http.default.path={{ .Values.edc.endpoints.default.path }} + web.http.public.port={{ .Values.edc.endpoints.public.port }} + web.http.public.path={{ .Values.edc.endpoints.public.path }} + web.http.control.port={{ .Values.edc.endpoints.control.port }} + web.http.control.path={{ .Values.edc.endpoints.control.path }} + {{- .Values.configuration.properties | nindent 4 }} + opentelemetry.properties: |- - {{- range $k, $v := .Values.opentelemetry.properties }} - {{- if $v }} - {{ $k | replace "_" "." }}={{ $v }} - {{- else }} - # {{ $k | replace "_" "." }}= - {{- end }} - {{- end }} + {{- .Values.opentelemetry.properties | nindent 4 }} + logging.properties: |- - {{- range $k, $v := .Values.logging.properties }} - {{- if $v }} - {{ $k | replace "_" "." }}={{ $v }} - {{- else }} - # {{ $k | replace "_" "." }}= - {{- end }} - {{- end }} \ No newline at end of file + {{- .Values.logging.properties | nindent 4 }} diff --git a/deployment/helm/edc-dataplane/templates/deployment.yaml b/deployment/helm/edc-dataplane/templates/deployment.yaml index 12cf71b6f..ee632461e 100644 --- a/deployment/helm/edc-dataplane/templates/deployment.yaml +++ b/deployment/helm/edc-dataplane/templates/deployment.yaml @@ -1,3 +1,4 @@ +--- apiVersion: apps/v1 kind: Deployment metadata: @@ -39,20 +40,38 @@ spec: image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy }} ports: - - name: http - containerPort: 80 + - name: default + containerPort: {{ .Values.edc.endpoints.default.port }} + protocol: TCP + - name: control + containerPort: {{ .Values.edc.endpoints.control.port }} + protocol: TCP + - name: public + containerPort: {{ .Values.edc.endpoints.public.port }} protocol: TCP - name: metrics - containerPort: 9090 + containerPort: {{ .Values.edc.endpoints.metrics.port }} protocol: TCP + {{- if .Values.livenessProbe.enabled }} livenessProbe: httpGet: - path: / - port: http + path: {{ .Values.edc.endpoints.default.path }}/check/liveness + port: default + {{- end }} + {{- if .Values.readinessProbe.enabled }} readinessProbe: httpGet: - path: / - port: http + path: {{ .Values.edc.endpoints.default.path }}/check/readiness + port: default + {{- end }} + {{- if .Values.startupProbe.enabled }} + startupProbe: + httpGet: + path: {{ .Values.edc.endpoints.default.path }}/check/startup + port: default + failureThreshold: 12 + initialDelaySeconds: 10 + {{- end }} resources: {{- toYaml .Values.resources | nindent 12 }} volumeMounts: diff --git a/deployment/helm/edc-dataplane/templates/hpa.yaml b/deployment/helm/edc-dataplane/templates/hpa.yaml index c903077c5..3a481bccf 100644 --- a/deployment/helm/edc-dataplane/templates/hpa.yaml +++ b/deployment/helm/edc-dataplane/templates/hpa.yaml @@ -1,4 +1,5 @@ {{- if .Values.autoscaling.enabled }} +--- apiVersion: autoscaling/v2beta1 kind: HorizontalPodAutoscaler metadata: diff --git a/deployment/helm/edc-dataplane/templates/imagepullsecret.yaml b/deployment/helm/edc-dataplane/templates/imagepullsecret.yaml index 32f39ddbc..45b7332f1 100644 --- a/deployment/helm/edc-dataplane/templates/imagepullsecret.yaml +++ b/deployment/helm/edc-dataplane/templates/imagepullsecret.yaml @@ -1,4 +1,5 @@ {{- if .Values.imagePullSecret.dockerconfigjson }} +--- apiVersion: v1 kind: Secret metadata: diff --git a/deployment/helm/edc-dataplane/templates/ingress.yaml b/deployment/helm/edc-dataplane/templates/ingress.yaml index ef93f367e..58174d98f 100644 --- a/deployment/helm/edc-dataplane/templates/ingress.yaml +++ b/deployment/helm/edc-dataplane/templates/ingress.yaml @@ -1,6 +1,6 @@ {{- if .Values.ingress.enabled -}} +--- {{- $fullName := include "edc-dataplane.fullname" . -}} -{{- $svcPort := .Values.service.port -}} {{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} @@ -18,44 +18,50 @@ metadata: name: {{ $fullName }} labels: {{- include "edc-dataplane.labels" . | nindent 4 }} - {{- with .Values.ingress.annotations }} annotations: + {{- with .Values.ingress.annotations }} {{- toYaml . | nindent 4 }} - {{- end }} + {{- end }} + {{- if .Values.ingress.certManager -}} + {{- if .Values.ingress.certManager.issuer -}} + cert-manager.io/issuer: {{ .Values.ingress.certManager.issuer }} + {{- end }} + {{- if .Values.ingress.certManager.clusterIssuer -}} + cert-manager.io/cluster-issuer: {{ .Values.ingress.certManager.clusterIssuer }} + {{- end }} + {{- end }} spec: {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} ingressClassName: {{ .Values.ingress.className }} {{- end }} + {{- if .Values.ingress.hostname }} {{- if .Values.ingress.tls }} tls: - {{- range .Values.ingress.tls }} - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} + - {{ .Values.ingress.hostname }} + secretName: {{ $fullName }}-tls {{- end }} rules: - {{- range .Values.ingress.hosts }} - - host: {{ .host | quote }} + - host: {{ .Values.ingress.hostname }} http: paths: - {{- range .paths }} - - path: {{ .path }} - {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} - pathType: {{ .pathType }} - {{- end }} - backend: - {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} - service: - name: {{ $fullName }} - port: - number: {{ $svcPort }} - {{- else }} - serviceName: {{ $fullName }} - servicePort: {{ $svcPort }} - {{- end }} + {{ range $name, $mapping := .Values.edc.endpoints }} + {{ if $mapping.ingress -}} + - path: {{ $mapping.path }} + {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + pathType: Prefix {{- end }} - {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ $fullName }} + port: + number: {{ $mapping.port }} + {{- else }} + serviceName: {{ $fullName }} + servicePort: {{ $mapping.port }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} {{- end }} diff --git a/deployment/helm/edc-dataplane/templates/service.yaml b/deployment/helm/edc-dataplane/templates/service.yaml index 3fe961c5a..a29e2c125 100644 --- a/deployment/helm/edc-dataplane/templates/service.yaml +++ b/deployment/helm/edc-dataplane/templates/service.yaml @@ -1,3 +1,4 @@ +--- apiVersion: v1 kind: Service metadata: @@ -7,11 +8,19 @@ metadata: spec: type: {{ .Values.service.type }} ports: - - port: {{ .Values.service.port }} - targetPort: http + - port: {{ .Values.edc.endpoints.default.port }} + targetPort: default protocol: TCP - name: http - - port: 9090 + name: default + - port: {{ .Values.edc.endpoints.control.port }} + targetPort: control + protocol: TCP + name: control + - port: {{ .Values.edc.endpoints.public.port }} + targetPort: public + protocol: TCP + name: public + - port: {{ .Values.edc.endpoints.metrics.port }} targetPort: metrics protocol: TCP name: metrics diff --git a/deployment/helm/edc-dataplane/templates/serviceaccount.yaml b/deployment/helm/edc-dataplane/templates/serviceaccount.yaml index 1d46ec16a..7fbe537fb 100644 --- a/deployment/helm/edc-dataplane/templates/serviceaccount.yaml +++ b/deployment/helm/edc-dataplane/templates/serviceaccount.yaml @@ -1,4 +1,5 @@ {{- if .Values.serviceAccount.create -}} +--- apiVersion: v1 kind: ServiceAccount metadata: diff --git a/deployment/helm/edc-dataplane/values.yaml b/deployment/helm/edc-dataplane/values.yaml index b979f43a2..f709f6775 100644 --- a/deployment/helm/edc-dataplane/values.yaml +++ b/deployment/helm/edc-dataplane/values.yaml @@ -5,31 +5,50 @@ replicaCount: 1 image: + ## + ## Which edc-dataplane container image to use. + ## repository: ghcr.io/catenax-ng/catena-x-edc/edc-dataplane pullPolicy: IfNotPresent - # Overrides the image tag whose default is the chart appVersion. + ## + ## Overrides the image tag whose default is the chart appVersion. + ## tag: "" +## +## Image pull secret to create to obtain the container image +## Note: 'imagePullSecret.dockerconfigjson' takes precedence if configured together with 'imagePullSecrets' +## imagePullSecret: dockerconfigjson: "" +## +## Existing image pull secret to use to obtain the container image +## imagePullSecrets: [] + nameOverride: "" fullnameOverride: "" serviceAccount: - # Specifies whether a service account should be created + ## + ## Specifies whether a service account should be created per release + ## create: true - # Annotations to add to the service account + ## + ## 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 + ## + ## 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 +# fsGroup: 2000 securityContext: {} # capabilities: @@ -37,27 +56,97 @@ securityContext: {} # - ALL # readOnlyRootFilesystem: true # runAsNonRoot: true - # runAsUser: 1000 +# runAsUser: 1000 + +## +## Whether to enable kubernetes liveness-probes +## +livenessProbe: + enabled: true + +## +## Whether to enable kubernetes readiness-probes +## +readinessProbe: + enabled: true + +## +## Whether to enable kubernetes startup-probes +## +startupProbe: + enabled: true + +## +## EDC endpoints exposed by the data-plane +## +edc: + endpoints: + # Default api exposing health checks etc + default: + port: "8080" + path: /api + ingress: false + # Control API + control: + port: "9999" + path: /api/control + ingress: true + # Public endpoint for data transfer + public: + port: "8185" + path: /api/public + ingress: true + # Prometheus endpoint + metrics: + port: "9090" + path: /metrics + ingress: false +## +## Service to expose the running application on a set of Pods as a network service. +## service: type: ClusterIP - port: 80 +## +## Ingress declaration to publicly expose the network service. +## ingress: - enabled: false + ## + ## Set to true to enable ingress record generation + ## + enabled: true + ## + ## Defines the ingress class to use https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class + ## className: "" + ## + ## En + ## + tls: false + + ## + ## Adds cert-manager annotations to the ingress + ## + certManager: + ## + ## If preset enables certificate generation via cert-manager namespace scoped issuer + ## + issuer: "" + ## + ## If preset enables certificate generation via cert-manager cluster-wide issuer + ## + clusterIssuer: "" + + ## + ## Additional ingress annotations to add + ## annotations: {} - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" - hosts: - - host: dataplane-example.local - paths: - - path: / - pathType: ImplementationSpecific - tls: [] - # - secretName: chart-example-tls - # hosts: - # - chart-example.local + + ## + ## The hostname to be used to precisely map incoming traffic onto the underlying network service + ## + hostname: "edc-dataplane.local" resources: {} # We usually recommend not to specify default resources and to leave this as a conscious @@ -69,7 +158,7 @@ resources: {} # memory: 128Mi # requests: # cpu: 100m - # memory: 128Mi +# memory: 128Mi autoscaling: enabled: false @@ -84,138 +173,151 @@ tolerations: [] affinity: {} +## +## EDC logging.properties configuring the java.util.logging subsystem +## logging: - properties: - _level: INFO - org_eclipse_dataspaceconnector_level: ALL - handlers: java.util.logging.ConsoleHandler - java_util_logging_ConsoleHandler_formatter: java.util.logging.SimpleFormatter - java_util_logging_ConsoleHandler_level: ALL - java_util_logging_SimpleFormatter_format: "[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n" + properties: |- + .level=INFO + org.eclipse.dataspaceconnector.level=ALL + handlers=java.util.logging.ConsoleHandler + java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter + java.util.logging.ConsoleHandler.level=ALL + java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n +## +## opentelemetry.properties configuring the opentelemetry agent +## +## See https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/ for further configuration options +## opentelemetry: - properties: - otel_javaagent_enabled: "true" - otel_javaagent_debug: "false" + properties: |- + otel.javaagent.enabled=true + otel.javaagent.debug=false +## +## EDC configuration.properties +## +## Consult the eclipse-dataspaceconnector documentation for further details: https://github.com/eclipse-dataspaceconnector/DataSpaceConnector +## configuration: - properties: - edc_api_control_auth_apikey_key: "" - edc_api_control_auth_apikey_value: "" - edc_assetindex_cosmos_account-name: "" - edc_assetindex_cosmos_container-name: "" - edc_assetindex_cosmos_database-name: "" - edc_assetindex_cosmos_preferred-region: "" - edc_atomikos_checkpoint_interval: "" - edc_atomikos_directory: "" - edc_atomikos_logging: "" - edc_atomikos_threaded2pc: "" - edc_atomikos_timeout: "" - edc_aws_access_key: "" - edc_aws_provision_retry_retries_max: "" - edc_aws_provision_role_duration_session_max: "" - edc_aws_secret_access_key: "" - edc_blobstore_endpoint: "" - edc_contractdefinitionstore_cosmos_account-name: "" - edc_contractdefinitionstore_cosmos_container-name: "" - edc_contractdefinitionstore_cosmos_database-name: "" - edc_contractdefinitionstore_cosmos_preferred-region: "" - edc_contractnegotiationstore_cosmos_account-name: "" - edc_contractnegotiationstore_cosmos_container-name: "" - edc_contractnegotiationstore_cosmos_database-name: "" - edc_contractnegotiationstore_cosmos_preferred-region: "" - edc_controlplane_validation-endpoint: "" - edc_core_retry_backoff_max: "" - edc_core_retry_backoff_min: "" - edc_core_retry_retries_max: "" - edc_core_system_health_check_liveness-period: "" - edc_core_system_health_check_readiness-period: "" - edc_core_system_health_check_startup-period: "" - edc_core_system_health_check_threadpool-size: "" - edc_cosmos_partition-key: "" - edc_cosmos_query-metrics-enabled: "" - edc_dataplane_queue_capacity: "" - edc_dataplane_wait: "" - edc_dataplane_workers: "" - edc_datasource_asset_name: "default" - edc_datasource_contractdefinition_name: "default" - edc_datasource_contractnegotiation_name: "default" - edc_datasource_policy_name: "default" - edc_datasource_transferprocess_name: "default" - edc_datasource_default_pool_maxIdleConnections: "" - edc_datasource_default_pool_maxTotalConnections: "" - edc_datasource_default_pool_minIdleConnections: "" - edc_datasource_default_pool_testConnectionOnBorrow: "" - edc_datasource_default_pool_testConnectionOnCreate: "" - edc_datasource_default_pool_testConnectionOnReturn: "" - edc_datasource_default_pool_testConnectionWhileIdle: "" - edc_datasource_default_pool_testQuery: "" - edc_datasource_default_url: "" - edc_datasource_default_user: "" - edc_datasource_default_password: "" - edc_dpf_selector_url: "" - edc_events_topic_endpoint: "" - edc_events_topic_name: "" - edc_fs_config: "" - edc_hostname: "" - edc_identity_did_url: "" - edc_ids_catalog_id: "" - edc_ids_curator: "" - edc_ids_description: "" - edc_ids_endpoint: "" - edc_ids_id: "" - edc_ids_maintainer: "" - edc_ids_security_profile: "" - edc_ids_title: "" - edc_ids_validation_referringconnector: "" - edc_ion_crawler_did-type: "" - edc_ion_crawler_interval-minutes: "" - edc_ion_crawler_ion_url: "" - edc_keystore: "" - edc_keystore_password: "" - edc_metrics_enabled: "" - edc_metrics_executor_enabled: "" - edc_metrics_jersey_enabled: "" - edc_metrics_jetty_enabled: "" - edc_metrics_okhttp_enabled: "" - edc_metrics_system_enabled: "" - edc_negotiation_consumer_state-machine_batch-size: "" - edc_negotiation_provider_state-machine_batch-size: "" - edc_node_directory_cosmos_account_name: "" - edc_node_directory_cosmos_container_name: "" - edc_node_directory_cosmos_database_name: "" - edc_node_directory_cosmos_preferred_region: "" - edc_oauth_client_id: "" - edc_oauth_private_key_alias: "" - edc_oauth_provider_audience: "" - edc_oauth_provider_jwks_refresh: "" - edc_oauth_provider_jwks_url: "" - edc_oauth_public_key_alias: "" - edc_oauth_token_url: "" - edc_oauth_validation_nbf_leeway: "" - edc_public_key_alias: "" - edc_receiver_http_auth-code: "" - edc_receiver_http_auth-key: "" - edc_receiver_http_endpoint: "" - edc_transfer_dataplane_sync_endpoint: "" - edc_transfer_dataplane_sync_token_validity: "" - edc_transfer_dataplane_token_signer_privatekey_alias: "" - edc_transfer_functions_check_endpoint: "" - edc_transfer_functions_enabled_protocols: "" - edc_transfer_functions_transfer_endpoint: "" - edc_transfer-process-store_cosmos_account_name: "" - edc_transfer-process-store_cosmos_container-name: "" - edc_transfer-process-store_cosmos_preferred-region: "" - edc_transfer-process-store_database_name: "" - edc_transfer_state-machine_batch-size: "" - edc_vault: "" - edc_vault_certificate: "" - edc_vault_clientid: "" - edc_vault_clientsecret: "" - edc_vault_name: "" - edc_vault_tenantid: "" - edc_webdid_doh_url: "" - edc_web_rest_cors_enabled: "" - edc_web_rest_cors_headers: "" - edc_web_rest_cors_methods: "" - edc_web_rest_cors_origins: "" + properties: |- + # edc.api.control.auth.apikey.key= + # edc.api.control.auth.apikey.value= + # edc.assetindex.cosmos.account-name= + # edc.assetindex.cosmos.container-name= + # edc.assetindex.cosmos.database-name= + # edc.assetindex.cosmos.preferred-region= + # edc.atomikos.checkpoint.interval= + # edc.atomikos.directory= + # edc.atomikos.logging= + # edc.atomikos.threaded2pc= + # edc.atomikos.timeout= + # edc.aws.access.key= + # edc.aws.provision.retry.retries.max= + # edc.aws.provision.role.duration.session.max= + # edc.aws.secret.access.key= + # edc.blobstore.endpoint= + # edc.contractdefinitionstore.cosmos.account-name= + # edc.contractdefinitionstore.cosmos.container-name= + # edc.contractdefinitionstore.cosmos.database-name= + # edc.contractdefinitionstore.cosmos.preferred-region= + # edc.contractnegotiationstore.cosmos.account-name= + # edc.contractnegotiationstore.cosmos.container-name= + # edc.contractnegotiationstore.cosmos.database-name= + # edc.contractnegotiationstore.cosmos.preferred-region= + # edc.controlplane.validation-endpoint= + # edc.core.retry.backoff.max= + # edc.core.retry.backoff.min= + # edc.core.retry.retries.max= + # edc.core.system.health.check.liveness-period= + # edc.core.system.health.check.readiness-period= + # edc.core.system.health.check.startup-period= + # edc.core.system.health.check.threadpool-size= + # edc.cosmos.partition-key= + # edc.cosmos.query-metrics-enabled= + # edc.dataplane.queue.capacity= + # edc.dataplane.wait= + # edc.dataplane.workers= + # edc.datasource.asset.name="default" + # edc.datasource.contractdefinition.name="default" + # edc.datasource.contractnegotiation.name="default" + # edc.datasource.policy.name="default" + # edc.datasource.transferprocess.name="default" + # edc.datasource.default.pool.maxIdleConnections= + # edc.datasource.default.pool.maxTotalConnections= + # edc.datasource.default.pool.minIdleConnections= + # edc.datasource.default.pool.testConnectionOnBorrow= + # edc.datasource.default.pool.testConnectionOnCreate= + # edc.datasource.default.pool.testConnectionOnReturn= + # edc.datasource.default.pool.testConnectionWhileIdle= + # edc.datasource.default.pool.testQuery= + # edc.datasource.default.url= + # edc.datasource.default.user= + # edc.datasource.default.password= + # edc.dpf.selector.url= + # edc.events.topic.endpoint= + # edc.events.topic.name= + # edc.fs.config= + # edc.hostname= + # edc.identity.did.url= + # edc.ids.catalog.id= + # edc.ids.curator= + # edc.ids.description= + # edc.ids.endpoint= + # edc.ids.id= + # edc.ids.maintainer= + # edc.ids.security.profile= + # edc.ids.title= + # edc.ids.validation.referringconnector= + # edc.ion.crawler.did-type= + # edc.ion.crawler.interval-minutes= + # edc.ion.crawler.ion.url= + # edc.keystore= + # edc.keystore.password= + # edc.metrics.enabled= + # edc.metrics.executor.enabled= + # edc.metrics.jersey.enabled= + # edc.metrics.jetty.enabled= + # edc.metrics.okhttp.enabled= + # edc.metrics.system.enabled= + # edc.negotiation.consumer.state-machine.batch-size= + # edc.negotiation.provider.state-machine.batch-size= + # edc.node.directory.cosmos.account.name= + # edc.node.directory.cosmos.container.name= + # edc.node.directory.cosmos.database.name= + # edc.node.directory.cosmos.preferred.region= + # edc.oauth.client.id= + # edc.oauth.private.key.alias= + # edc.oauth.provider.audience= + # edc.oauth.provider.jwks.refresh= + # edc.oauth.provider.jwks.url= + # edc.oauth.public.key.alias= + # edc.oauth.token.url= + # edc.oauth.validation.nbf.leeway= + # edc.public.key.alias= + # edc.receiver.http.auth-code= + # edc.receiver.http.auth-key= + # edc.receiver.http.endpoint= + # edc.transfer.dataplane.sync.endpoint= + # edc.transfer.dataplane.sync.token.validity= + # edc.transfer.dataplane.token.signer.privatekey.alias= + # edc.transfer.functions.check.endpoint= + # edc.transfer.functions.enabled.protocols= + # edc.transfer.functions.transfer.endpoint= + # edc.transfer-process-store.cosmos.account.name= + # edc.transfer-process-store.cosmos.container-name= + # edc.transfer-process-store.cosmos.preferred-region= + # edc.transfer-process-store.database.name= + # edc.transfer.state-machine.batch-size= + # edc.vault= + # edc.vault.certificate= + # edc.vault.clientid= + # edc.vault.clientsecret= + # edc.vault.name= + # edc.vault.tenantid= + # edc.webdid.doh.url= + # edc.web.rest.cors.enabled= + # edc.web.rest.cors.headers= + # edc.web.rest.cors.methods= + # edc.web.rest.cors.origins= diff --git a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml index 0a19f3166..bc02bb1cd 100644 --- a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml +++ b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml @@ -226,12 +226,10 @@ org.eclipse.dataspaceconnector jersey-micrometer - ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector jetty-micrometer - ${org.eclipse.dataspaceconnector.version} diff --git a/edc-controlplane/edc-controlplane-memory/pom.xml b/edc-controlplane/edc-controlplane-memory/pom.xml index af7f3940a..0e4f457c9 100644 --- a/edc-controlplane/edc-controlplane-memory/pom.xml +++ b/edc-controlplane/edc-controlplane-memory/pom.xml @@ -167,14 +167,18 @@ - - org.eclipse.dataspaceconnector - oauth2-core - - - org.eclipse.dataspaceconnector - iam-daps - + + + + + + + + + + org.eclipse.dataspaceconnector + iam-mock + @@ -184,12 +188,10 @@ org.eclipse.dataspaceconnector jersey-micrometer - ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector jetty-micrometer - ${org.eclipse.dataspaceconnector.version} diff --git a/edc-controlplane/edc-controlplane-postgresql/pom.xml b/edc-controlplane/edc-controlplane-postgresql/pom.xml index 4f52b6511..465116d1a 100644 --- a/edc-controlplane/edc-controlplane-postgresql/pom.xml +++ b/edc-controlplane/edc-controlplane-postgresql/pom.xml @@ -199,12 +199,10 @@ org.eclipse.dataspaceconnector jersey-micrometer - ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector jetty-micrometer - ${org.eclipse.dataspaceconnector.version} diff --git a/edc-dataplane/pom.xml b/edc-dataplane/pom.xml index 484849f9f..77e682eda 100644 --- a/edc-dataplane/pom.xml +++ b/edc-dataplane/pom.xml @@ -112,12 +112,10 @@ org.eclipse.dataspaceconnector jersey-micrometer - ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector jetty-micrometer - ${org.eclipse.dataspaceconnector.version} From 6255843d1b261963c7c700a1c58f79e11b163242 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Apr 2022 08:16:01 +0200 Subject: [PATCH 022/433] Bump actions/setup-java from 2 to 3.1.1 (#22) --- .github/workflows/build.yaml | 2 +- .github/workflows/draft-new-release.yaml | 2 +- .github/workflows/publish-new-release.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index dcd9c68ce..639ffd918 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -42,7 +42,7 @@ jobs: git -C edc checkout milestone-3 - name: Set up JDK 11 - uses: actions/setup-java@v2 + uses: actions/setup-java@v3.1.1 with: java-version: '11' distribution: 'adopt' diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index 56c1709e3..c498a7dd8 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -29,7 +29,7 @@ jobs: git config user.email noreply@github.com - name: Set up JDK 11 - uses: actions/setup-java@v2 + uses: actions/setup-java@v3.1.1 with: java-version: '11' distribution: 'adopt' diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index 734302121..ac5fc29ee 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -61,7 +61,7 @@ jobs: prerelease: false - name: Set up JDK 11 - uses: actions/setup-java@v2 + uses: actions/setup-java@v3.1.1 with: java-version: '11' distribution: 'adopt' From 216221ba5402d88d07a6587e175ca0444e3894a5 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Tue, 12 Apr 2022 08:17:48 +0200 Subject: [PATCH 023/433] Add issue templates (#24) --- .github/ISSUE_TEMPLATE/bug_report.md | 35 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 23 +++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..c0f8fe3b0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,35 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' + +--- + +## Describe the bug +_A clear and concise description of what the bug is._ + +### To Reproduce +_Steps to reproduce the behavior:_ +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +### Expected behavior +_A clear and concise description of what you expected to happen._ + +### Screenshots/Error Messages +_If applicable, add screenshots and/or error messages to help explain your problem._ + +## Context Informations +_Add any other context about the probleme here._ + +- Used version: [e.g. Commit Hash] +- OS: [e.g. Mac OS (M1), Windows, Linux] +- Docker Version: [e.g. 20.10.12] +- `java --version`: + +## Possible Implementation +_You already know the root cause of the erroneous state and how to fix it? Feel free to share your thoughts._ diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..1b6f25b87 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,23 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: enhancement +assignees: '' + +--- + +_If you are missing a feature or have an idea how to improve this project that should first be +discussed, please feel free to open up a [discussion](https://github.com/catenax-ng/catena-x-edc/discussions/categories/ideas)._ + +**Is your feature request related to a problem? Please describe.** +_A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]_ + +**Describe the solution you'd like** +_A clear and concise description of what you want to happen._ + +**Describe alternatives you've considered** +_A clear and concise description of any alternative solutions or features you've considered._ + +**Additional context** +_Add any other context or screenshots about the feature request here._ From 24d5944a4ded1e42804d471ae298a4594549be54 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Tue, 12 Apr 2022 08:21:08 +0200 Subject: [PATCH 024/433] Add controlplane-postgresql dependabot section (#25) --- .github/dependabot.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 8228e72c3..062340eb8 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -37,6 +37,15 @@ updates: - "docker" schedule: interval: "daily" + - + package-ecosystem: "docker" + target-branch: develop + directory: "edc-controlplane/edc-controlplane-postgresql/src/main/docker" + labels: + - "dependabot" + - "docker" + schedule: + interval: "daily" - package-ecosystem: "maven" target-branch: develop From 4d7c275b4775ada2f5e111733cbcf75e5d3dbd6d Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Tue, 12 Apr 2022 10:08:29 +0200 Subject: [PATCH 025/433] Add missing license headers (#26) --- edc-controlplane/edc-controlplane-cosmosdb/pom.xml | 13 +++++++++++++ .../src/main/docker/Dockerfile | 12 ++++++++++++ edc-controlplane/edc-controlplane-memory/pom.xml | 13 +++++++++++++ .../src/main/docker/Dockerfile | 12 ++++++++++++ .../edc-controlplane-postgresql/pom.xml | 13 +++++++++++++ .../src/main/docker/Dockerfile | 12 ++++++++++++ edc-controlplane/pom.xml | 13 +++++++++++++ edc-dataplane/pom.xml | 13 +++++++++++++ edc-dataplane/src/main/docker/Dockerfile | 12 ++++++++++++ edc-extensions/aas-controller/pom.xml | 13 +++++++++++++ .../edc/controlplane/ControlPlaneController.java | 4 ++-- .../edc/controlplane/ControlPlaneExtension.java | 4 ++-- ....dataspaceconnector.spi.system.ServiceExtension | 13 +++++++++++++ edc-extensions/business-partner-validation/pom.xml | 13 +++++++++++++ ....dataspaceconnector.spi.system.ServiceExtension | 3 +-- edc-extensions/pom.xml | 13 +++++++++++++ edc-extensions/postgresql-migration/pom.xml | 13 +++++++++++++ .../AbstractPostgresqlMigrationExtension.java | 14 ++++++++++++++ .../AssetPostgresqlMigrationExtension.java | 14 ++++++++++++++ ...ractDefinitionPostgresqlMigrationExtension.java | 14 ++++++++++++++ ...actNegotiationPostgresqlMigrationExtension.java | 14 ++++++++++++++ .../migration/DriverManagerConnectionFactory.java | 14 ++++++++++++++ .../PolicyPostgresqlMigrationExtension.java | 14 ++++++++++++++ ...ransferProcessPostgresqlMigrationExtension.java | 14 ++++++++++++++ ....dataspaceconnector.spi.system.ServiceExtension | 4 ++-- .../asset/V0_0_1__Init_Asset_Database_Schema.sql | 4 ++-- ..._1__Init_ContractDefinition_Database_Schema.sql | 4 ++-- ...1__Init_ContractNegotiation_Database_Schema.sql | 4 ++-- ...0_0_1__Init_TransferProcess_Database_Schema.sql | 4 ++-- pom.xml | 13 +++++++++++++ 30 files changed, 304 insertions(+), 16 deletions(-) diff --git a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml index bc02bb1cd..c6e06ef3c 100644 --- a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml +++ b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml @@ -1,4 +1,17 @@ + net.catenax.edc diff --git a/edc-controlplane/edc-controlplane-cosmosdb/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-cosmosdb/src/main/docker/Dockerfile index 79440ed5d..6236f2dbf 100644 --- a/edc-controlplane/edc-controlplane-cosmosdb/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-cosmosdb/src/main/docker/Dockerfile @@ -1,3 +1,15 @@ +# +# Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +# +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# +# Contributors: +# Mercedes-Benz Tech Innovation GmbH - Initial Dockerfile +# FROM alpine:3.15.4 as otel ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" diff --git a/edc-controlplane/edc-controlplane-memory/pom.xml b/edc-controlplane/edc-controlplane-memory/pom.xml index 0e4f457c9..852ab4c8c 100644 --- a/edc-controlplane/edc-controlplane-memory/pom.xml +++ b/edc-controlplane/edc-controlplane-memory/pom.xml @@ -1,4 +1,17 @@ + edc-controlplane diff --git a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile index 79440ed5d..6236f2dbf 100644 --- a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile @@ -1,3 +1,15 @@ +# +# Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +# +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# +# Contributors: +# Mercedes-Benz Tech Innovation GmbH - Initial Dockerfile +# FROM alpine:3.15.4 as otel ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" diff --git a/edc-controlplane/edc-controlplane-postgresql/pom.xml b/edc-controlplane/edc-controlplane-postgresql/pom.xml index 465116d1a..ac6b44ad6 100644 --- a/edc-controlplane/edc-controlplane-postgresql/pom.xml +++ b/edc-controlplane/edc-controlplane-postgresql/pom.xml @@ -1,4 +1,17 @@ + diff --git a/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile index 79440ed5d..6236f2dbf 100644 --- a/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile @@ -1,3 +1,15 @@ +# +# Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +# +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# +# Contributors: +# Mercedes-Benz Tech Innovation GmbH - Initial Dockerfile +# FROM alpine:3.15.4 as otel ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" diff --git a/edc-controlplane/pom.xml b/edc-controlplane/pom.xml index ee196417b..403363d19 100644 --- a/edc-controlplane/pom.xml +++ b/edc-controlplane/pom.xml @@ -1,4 +1,17 @@ + diff --git a/edc-dataplane/pom.xml b/edc-dataplane/pom.xml index 77e682eda..99cefa340 100644 --- a/edc-dataplane/pom.xml +++ b/edc-dataplane/pom.xml @@ -1,4 +1,17 @@ + diff --git a/edc-dataplane/src/main/docker/Dockerfile b/edc-dataplane/src/main/docker/Dockerfile index d36dc2636..e18b6adc0 100644 --- a/edc-dataplane/src/main/docker/Dockerfile +++ b/edc-dataplane/src/main/docker/Dockerfile @@ -1,3 +1,15 @@ +# +# Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +# +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# +# Contributors: +# Mercedes-Benz Tech Innovation GmbH - Initial Dockerfile +# FROM alpine:3.15.4 as otel ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" diff --git a/edc-extensions/aas-controller/pom.xml b/edc-extensions/aas-controller/pom.xml index 172faa38d..eda62f3d6 100644 --- a/edc-extensions/aas-controller/pom.xml +++ b/edc-extensions/aas-controller/pom.xml @@ -1,4 +1,17 @@ + 4.0.0 diff --git a/edc-extensions/aas-controller/src/main/java/net/catenax/edc/controlplane/ControlPlaneController.java b/edc-extensions/aas-controller/src/main/java/net/catenax/edc/controlplane/ControlPlaneController.java index 20d347265..f8f510e74 100644 --- a/edc-extensions/aas-controller/src/main/java/net/catenax/edc/controlplane/ControlPlaneController.java +++ b/edc-extensions/aas-controller/src/main/java/net/catenax/edc/controlplane/ControlPlaneController.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Daimler TSS GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH * * This program and the accompanying materials are made available under the * terms of the Apache License, Version 2.0 which is available at @@ -8,7 +8,7 @@ * SPDX-License-Identifier: Apache-2.0 * * Contributors: - * Daimler TSS GmbH - Initial API and Implementation + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation * */ diff --git a/edc-extensions/aas-controller/src/main/java/net/catenax/edc/controlplane/ControlPlaneExtension.java b/edc-extensions/aas-controller/src/main/java/net/catenax/edc/controlplane/ControlPlaneExtension.java index f0f37820d..4e5791c26 100644 --- a/edc-extensions/aas-controller/src/main/java/net/catenax/edc/controlplane/ControlPlaneExtension.java +++ b/edc-extensions/aas-controller/src/main/java/net/catenax/edc/controlplane/ControlPlaneExtension.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Daimler TSS GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH * * This program and the accompanying materials are made available under the * terms of the Apache License, Version 2.0 which is available at @@ -8,7 +8,7 @@ * SPDX-License-Identifier: Apache-2.0 * * Contributors: - * Daimler TSS GmbH - Initial API and Implementation + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation * */ diff --git a/edc-extensions/aas-controller/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension b/edc-extensions/aas-controller/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension index af2b75359..80375f38d 100644 --- a/edc-extensions/aas-controller/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension +++ b/edc-extensions/aas-controller/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension @@ -1 +1,14 @@ +# +# Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +# +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# +# Contributors: +# Mercedes-Benz Tech Innovation GmbH - Initial ServiceExtension file +# +# net.catenax.edc.controlplane.ControlPlaneExtension \ No newline at end of file diff --git a/edc-extensions/business-partner-validation/pom.xml b/edc-extensions/business-partner-validation/pom.xml index a5c4a79de..90aa72ff7 100644 --- a/edc-extensions/business-partner-validation/pom.xml +++ b/edc-extensions/business-partner-validation/pom.xml @@ -1,4 +1,17 @@ + 4.0.0 diff --git a/edc-extensions/business-partner-validation/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension b/edc-extensions/business-partner-validation/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension index 609f8ca1b..226b41ab3 100644 --- a/edc-extensions/business-partner-validation/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension +++ b/edc-extensions/business-partner-validation/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension @@ -8,8 +8,7 @@ # SPDX-License-Identifier: Apache-2.0 # # Contributors: -# Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation +# Mercedes-Benz Tech Innovation GmbH - Initial ServiceExtension file # # - net.catenax.edc.validation.businesspartner.BusinessPartnerValidationExtension diff --git a/edc-extensions/pom.xml b/edc-extensions/pom.xml index e42822784..d311294eb 100644 --- a/edc-extensions/pom.xml +++ b/edc-extensions/pom.xml @@ -1,4 +1,17 @@ + 4.0.0 diff --git a/edc-extensions/postgresql-migration/pom.xml b/edc-extensions/postgresql-migration/pom.xml index 00ffa0ff1..48338ba2c 100644 --- a/edc-extensions/postgresql-migration/pom.xml +++ b/edc-extensions/postgresql-migration/pom.xml @@ -1,4 +1,17 @@ + diff --git a/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/AbstractPostgresqlMigrationExtension.java b/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/AbstractPostgresqlMigrationExtension.java index 871725d5d..dbb17465a 100644 --- a/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/AbstractPostgresqlMigrationExtension.java +++ b/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/AbstractPostgresqlMigrationExtension.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + package net.catenax.edc.postgresql.migration; import java.util.Objects; diff --git a/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/AssetPostgresqlMigrationExtension.java b/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/AssetPostgresqlMigrationExtension.java index 8f1d41702..44368a069 100644 --- a/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/AssetPostgresqlMigrationExtension.java +++ b/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/AssetPostgresqlMigrationExtension.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial implementation + * + */ + package net.catenax.edc.postgresql.migration; import org.eclipse.dataspaceconnector.sql.asset.index.ConfigurationKeys; diff --git a/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/ContractDefinitionPostgresqlMigrationExtension.java b/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/ContractDefinitionPostgresqlMigrationExtension.java index f2c0c40e1..1834cde8f 100644 --- a/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/ContractDefinitionPostgresqlMigrationExtension.java +++ b/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/ContractDefinitionPostgresqlMigrationExtension.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial implementation + * + */ + package net.catenax.edc.postgresql.migration; import org.eclipse.dataspaceconnector.spi.EdcSetting; diff --git a/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/ContractNegotiationPostgresqlMigrationExtension.java b/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/ContractNegotiationPostgresqlMigrationExtension.java index 9856bacaa..8c732742f 100644 --- a/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/ContractNegotiationPostgresqlMigrationExtension.java +++ b/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/ContractNegotiationPostgresqlMigrationExtension.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial implementation + * + */ + package net.catenax.edc.postgresql.migration; import org.eclipse.dataspaceconnector.spi.EdcSetting; diff --git a/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/DriverManagerConnectionFactory.java b/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/DriverManagerConnectionFactory.java index 1c203e61c..4e7c96220 100644 --- a/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/DriverManagerConnectionFactory.java +++ b/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/DriverManagerConnectionFactory.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial implementation + * + */ + package net.catenax.edc.postgresql.migration; import java.sql.Connection; diff --git a/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/PolicyPostgresqlMigrationExtension.java b/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/PolicyPostgresqlMigrationExtension.java index 24c77971c..9e3b64ff9 100644 --- a/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/PolicyPostgresqlMigrationExtension.java +++ b/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/PolicyPostgresqlMigrationExtension.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial implementation + * + */ + package net.catenax.edc.postgresql.migration; import org.eclipse.dataspaceconnector.spi.EdcSetting; diff --git a/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/TransferProcessPostgresqlMigrationExtension.java b/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/TransferProcessPostgresqlMigrationExtension.java index 784b1d238..4ef0d97f4 100644 --- a/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/TransferProcessPostgresqlMigrationExtension.java +++ b/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/TransferProcessPostgresqlMigrationExtension.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial implementation + * + */ + package net.catenax.edc.postgresql.migration; import org.eclipse.dataspaceconnector.spi.EdcSetting; diff --git a/edc-extensions/postgresql-migration/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension b/edc-extensions/postgresql-migration/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension index aa8b6b5b1..fc52160ad 100644 --- a/edc-extensions/postgresql-migration/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension +++ b/edc-extensions/postgresql-migration/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension @@ -1,5 +1,5 @@ # -# Copyright (c) 2022 Daimler TSS GmbH +# Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH # # This program and the accompanying materials are made available under the # terms of the Apache License, Version 2.0 which is available at @@ -8,7 +8,7 @@ # SPDX-License-Identifier: Apache-2.0 # # Contributors: -# Daimler TSS GmbH - Initial ServiceExtension file +# Mercedes-Benz Tech Innovation GmbH - Initial ServiceExtension file # # net.catenax.edc.postgresql.migration.AssetPostgresqlMigrationExtension diff --git a/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/asset/V0_0_1__Init_Asset_Database_Schema.sql b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/asset/V0_0_1__Init_Asset_Database_Schema.sql index 1a4e86f7f..6f2d87bd2 100644 --- a/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/asset/V0_0_1__Init_Asset_Database_Schema.sql +++ b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/asset/V0_0_1__Init_Asset_Database_Schema.sql @@ -1,5 +1,5 @@ -- --- Copyright (c) 2022 Daimler TSS GmbH +-- Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH -- -- This program and the accompanying materials are made available under the -- terms of the Apache License, Version 2.0 which is available at @@ -8,7 +8,7 @@ -- SPDX-License-Identifier: Apache-2.0 -- -- Contributors: --- Daimler TSS GmbH - Initial Database Schema +-- Mercedes-Benz Tech Innovation GmbH - Initial Database Schema -- -- diff --git a/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractdefinition/V0_0_1__Init_ContractDefinition_Database_Schema.sql b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractdefinition/V0_0_1__Init_ContractDefinition_Database_Schema.sql index f147f6499..90b36b8d9 100644 --- a/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractdefinition/V0_0_1__Init_ContractDefinition_Database_Schema.sql +++ b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractdefinition/V0_0_1__Init_ContractDefinition_Database_Schema.sql @@ -1,5 +1,5 @@ -- --- Copyright (c) 2022 Daimler TSS GmbH +-- Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH -- -- This program and the accompanying materials are made available under the -- terms of the Apache License, Version 2.0 which is available at @@ -8,7 +8,7 @@ -- SPDX-License-Identifier: Apache-2.0 -- -- Contributors: --- Daimler TSS GmbH - Initial Database Schema +-- Mercedes-Benz Tech Innovation GmbH - Initial Database Schema -- -- diff --git a/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractnegotiation/V0_0_1__Init_ContractNegotiation_Database_Schema.sql b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractnegotiation/V0_0_1__Init_ContractNegotiation_Database_Schema.sql index 2eaf02e13..9ec1b59d3 100644 --- a/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractnegotiation/V0_0_1__Init_ContractNegotiation_Database_Schema.sql +++ b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractnegotiation/V0_0_1__Init_ContractNegotiation_Database_Schema.sql @@ -1,5 +1,5 @@ -- --- Copyright (c) 2022 Daimler TSS GmbH +-- Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH -- -- This program and the accompanying materials are made available under the -- terms of the Apache License, Version 2.0 which is available at @@ -8,7 +8,7 @@ -- SPDX-License-Identifier: Apache-2.0 -- -- Contributors: --- Daimler TSS GmbH - Initial Database Schema +-- Mercedes-Benz Tech Innovation GmbH - Initial Database Schema -- -- diff --git a/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/transferprocess/V0_0_1__Init_TransferProcess_Database_Schema.sql b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/transferprocess/V0_0_1__Init_TransferProcess_Database_Schema.sql index f80c0df09..f587632ef 100644 --- a/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/transferprocess/V0_0_1__Init_TransferProcess_Database_Schema.sql +++ b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/transferprocess/V0_0_1__Init_TransferProcess_Database_Schema.sql @@ -1,5 +1,5 @@ -- --- Copyright (c) 2022 Daimler TSS GmbH +-- Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH -- -- This program and the accompanying materials are made available under the -- terms of the Apache License, Version 2.0 which is available at @@ -8,7 +8,7 @@ -- SPDX-License-Identifier: Apache-2.0 -- -- Contributors: --- Daimler TSS GmbH - Rewrite to be SQL Init Schema +-- Mercedes-Benz Tech Innovation GmbH - Rewrite to be SQL Init Schema -- -- diff --git a/pom.xml b/pom.xml index 37ab4fc93..f181ade56 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,17 @@ + 4.0.0 From e3d612f6c5139551feda75bb668a53f3496246ca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Apr 2022 16:22:40 +0200 Subject: [PATCH 026/433] Bump mikefarah/yq from 4.24.2 to 4.24.4 (#28) Bumps [mikefarah/yq](https://github.com/mikefarah/yq) from 4.24.2 to 4.24.4. --- .github/workflows/draft-new-release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index c498a7dd8..569fb24e1 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -43,7 +43,7 @@ jobs: GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} - name: Bump version in deployment/helm - uses: mikefarah/yq@v4.24.2 + uses: mikefarah/yq@v4.24.4 with: cmd: |- find deployment/helm -name Chart.yaml | xargs -n1 yq -i '.appVersion = "${{ github.event.inputs.version }}" | .version = "${{ github.event.inputs.version }}"' From 70a3ad5ea6ff32b6115e6094d87bdc92b4533e68 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Thu, 14 Apr 2022 15:24:30 +0200 Subject: [PATCH 027/433] Add initial documentation (#27) --- .github/workflows/build.yaml | 2 +- .github/workflows/draft-new-release.yaml | 2 +- README.md | 45 +- deployment/helm/edc-controlplane/README.md | 1 + .../edc-controlplane/templates/configmap.yaml | 2 + .../templates/deployment.yaml | 3 + deployment/helm/edc-controlplane/values.yaml | 7 +- deployment/helm/edc-dataplane/README.md | 1 + deployment/helm/edc-dataplane/values.yaml | 2 +- edc | 2 +- edc-controlplane/README.md | 526 ++++++++++++++++++ .../edc-controlplane-cosmosdb/README.md | 173 ++++++ .../edc-controlplane-cosmosdb/pom.xml | 24 +- .../edc-controlplane-memory/README.md | 125 +++++ .../edc-controlplane-memory/pom.xml | 66 ++- .../edc-controlplane-postgresql/README.md | 181 ++++++ .../edc-controlplane-postgresql/pom.xml | 46 +- edc-dataplane/README.md | 94 ++++ edc-dataplane/pom.xml | 44 +- edc-extensions/README.md | 1 + edc-extensions/aas-controller/README.md | 2 +- .../business-partner-validation/README.md | 177 ++---- edc-extensions/postgresql-migration/README.md | 9 + 23 files changed, 1378 insertions(+), 157 deletions(-) create mode 100644 deployment/helm/edc-controlplane/README.md create mode 100644 deployment/helm/edc-dataplane/README.md create mode 100644 edc-controlplane/README.md create mode 100644 edc-controlplane/edc-controlplane-cosmosdb/README.md create mode 100644 edc-controlplane/edc-controlplane-memory/README.md create mode 100644 edc-controlplane/edc-controlplane-postgresql/README.md create mode 100644 edc-dataplane/README.md create mode 100644 edc-extensions/README.md create mode 100644 edc-extensions/postgresql-migration/README.md diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 639ffd918..e5c4b1cd5 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -39,7 +39,7 @@ jobs: [ ! -d "edc" ] && git submodule add https://github.com/eclipse-dataspaceconnector/DataSpaceConnector.git edc git submodule update --init git -C edc fetch --all - git -C edc checkout milestone-3 + git -C edc checkout 045c9a6c1f8dcde78dfcb0480cf49643cdb65401 - name: Set up JDK 11 uses: actions/setup-java@v3.1.1 diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index 569fb24e1..c498a7dd8 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -43,7 +43,7 @@ jobs: GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} - name: Bump version in deployment/helm - uses: mikefarah/yq@v4.24.4 + uses: mikefarah/yq@v4.24.2 with: cmd: |- find deployment/helm -name Chart.yaml | xargs -n1 yq -i '.appVersion = "${{ github.event.inputs.version }}" | .version = "${{ github.event.inputs.version }}"' diff --git a/README.md b/README.md index 1c988c6e1..f16a5c5aa 100644 --- a/README.md +++ b/README.md @@ -1 +1,44 @@ -Catena-X specific edc apps +# Catena-X specific edc apps + +This project provides pre-built Control-Plane and Data-Plane [docker](https://www.docker.com/) images and [helm](https://helm.sh/) charts of the [Eclipse DataSpaceConnector Project](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector). + +## Inventory + +The eclipse data space connector is split up into Control-Plane and Data-Plane, whereas the Control-Plane functions as administration layer +and has responsibility of resource management, contract negotiation and administer data transfer. +The Data-Plane does the heavy lifting of transferring and receiving data streams. + +Depending on your environment there are different derivatives of the control-plane prepared: + +* [edc-controlplane-cosmosdb](edc-controlplane/edc-controlplane-cosmosdb) +* [edc-controlplane-memory](edc-controlplane/edc-controlplane-memory) +* [edc-controlplane-postgresql](edc-controlplane/edc-controlplane-postgresql) + +Derivatives of the Data-Plane can be found here + +* [edc-dataplane](edc-dataplane) + +## Prerequisites + +#### EDC artifacts + +Since the [EDC](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) does +not yet publish artifacts to a maven repository, which this project relies on, it needs +to be built upfront to be used: + +```shell +# Init / update git-submodule +[ ! -d "edc" ] && git submodule add https://github.com/eclipse-dataspaceconnector/DataSpaceConnector.git edc +git submodule update --init +git -C edc fetch --all +git -C edc checkout milestone-3 + +# build edc 0.0.1-SNAPSHOT artifacts +cd edc && ./gradlew publishToMavenLocal +``` + +## Build + +```shell +./mvnw package -Pwith-docker-image +``` diff --git a/deployment/helm/edc-controlplane/README.md b/deployment/helm/edc-controlplane/README.md new file mode 100644 index 000000000..489210dba --- /dev/null +++ b/deployment/helm/edc-controlplane/README.md @@ -0,0 +1 @@ +# edc-controlplane Helm Chart \ No newline at end of file diff --git a/deployment/helm/edc-controlplane/templates/configmap.yaml b/deployment/helm/edc-controlplane/templates/configmap.yaml index 8da172467..34a745bfa 100644 --- a/deployment/helm/edc-controlplane/templates/configmap.yaml +++ b/deployment/helm/edc-controlplane/templates/configmap.yaml @@ -11,6 +11,8 @@ data: web.http.default.path={{ .Values.edc.endpoints.default.path }} web.http.data.port={{ .Values.edc.endpoints.data.port }} web.http.data.path={{ .Values.edc.endpoints.data.path }} + web.http.validation.port={{ .Values.edc.endpoints.validation.port }} + web.http.validation.path={{ .Values.edc.endpoints.validation.path }} web.http.control.port={{ .Values.edc.endpoints.control.port }} web.http.control.path={{ .Values.edc.endpoints.control.path }} web.http.ids.port={{ .Values.edc.endpoints.ids.port }} diff --git a/deployment/helm/edc-controlplane/templates/deployment.yaml b/deployment/helm/edc-controlplane/templates/deployment.yaml index 94d02c83f..0d403d251 100644 --- a/deployment/helm/edc-controlplane/templates/deployment.yaml +++ b/deployment/helm/edc-controlplane/templates/deployment.yaml @@ -49,6 +49,9 @@ spec: - name: data containerPort: {{ .Values.edc.endpoints.data.port }} protocol: TCP + - name: validation + containerPort: {{ .Values.edc.endpoints.validation.port }} + protocol: TCP - name: ids containerPort: {{ .Values.edc.endpoints.ids.port }} protocol: TCP diff --git a/deployment/helm/edc-controlplane/values.yaml b/deployment/helm/edc-controlplane/values.yaml index fd5ff04c5..11dc2d8a2 100644 --- a/deployment/helm/edc-controlplane/values.yaml +++ b/deployment/helm/edc-controlplane/values.yaml @@ -95,10 +95,15 @@ edc: port: "8181" path: /data ingress: true + # Validation API + validation: + port: "8182" + path: /validation + ingress: false # Control API control: port: "9999" - path: /api/control + path: /api/controlplane/control ingress: true # IDS endpoints ids: diff --git a/deployment/helm/edc-dataplane/README.md b/deployment/helm/edc-dataplane/README.md new file mode 100644 index 000000000..acc4aa7c8 --- /dev/null +++ b/deployment/helm/edc-dataplane/README.md @@ -0,0 +1 @@ +# edc-dataplane Helm Chart \ No newline at end of file diff --git a/deployment/helm/edc-dataplane/values.yaml b/deployment/helm/edc-dataplane/values.yaml index f709f6775..6d4b9a4b5 100644 --- a/deployment/helm/edc-dataplane/values.yaml +++ b/deployment/helm/edc-dataplane/values.yaml @@ -89,7 +89,7 @@ edc: # Control API control: port: "9999" - path: /api/control + path: /api/dataplane/control ingress: true # Public endpoint for data transfer public: diff --git a/edc b/edc index 70ff63d97..045c9a6c1 160000 --- a/edc +++ b/edc @@ -1 +1 @@ -Subproject commit 70ff63d9752d25e754af32502487eb1ce707a238 +Subproject commit 045c9a6c1f8dcde78dfcb0480cf49643cdb65401 diff --git a/edc-controlplane/README.md b/edc-controlplane/README.md new file mode 100644 index 000000000..01023daa6 --- /dev/null +++ b/edc-controlplane/README.md @@ -0,0 +1,526 @@ +# Control Plane + +The Eclipse Dataspace Connector consists of a **Control Plan** and a **Data Plane** Application. +While the **Data Plane** handles the actual Data Transfer, the **Control Plane** is responsible for: + +- Resource Management (e.g. Assets, Policies & Contract Definitions CRUD) +- Contract Offering & Contract Negotiation +- Data Transfer Coordination / Management + +# Control Plane Setup + +This chapter is about integration the Control Plane with the Azure KeyVault and IDS DAPS. + +## Azure Key Vault Setup + +The Eclipse Dataspace Connector requires a key vault, where it can store and retrieve secrets and certificates.
+At the time of writing the only key vault, the EDC is supporting, is the Azure Key vault. + +### 1. Register a new App + +In the Azure Portal: + +1. Open **App registrations** page and create a new app +2. Choose a unique name and click _register_ +3. The new App has a **Client ID** (also called Application ID). This ID must be configured in the connector + setting `edc.vault.clientid` + +For further information have a look at the official documentation
+https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app + +### 2. Create App Secret + +In the Azure Portal: + +1. Open the page of the newly created app +2. On the left side select _certificates & secrets_ +3. Create a new _client secret_ +4. Add the secret value to the connector setting `edc.vault.clientsecret` + +For further information have a look at the official documentation
+https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app#add-credentials + +### 3. Create Azure Key Vault + +In the Azure Portal: + +1. Open **Key vaults** page and create a new Azure Key Vault +2. Fill out the mandatory fields, choose a unique key vault name and click _review + create_ +3. The chosen name must be configured in the connector setting `edc.vault.name` +4. The directory ID of the key vault (also called tenant ID) must be configured in the `edc.vault.tenantid` + +For further information have a look at the official documentation
+https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app + +### 4. Provide the newly created App access to the Key Vault + +In the Azure Portal: + +1. Open the page of the newly created key vault +2. On the left side select _access policies_ +3. Create new _access policy_ and select the appropriate permissions +5. Under _select principal_ add the newly created app from step 1 +6. Click _add_ + +For further information have a look at the official documentation
+https://docs.microsoft.com/en-us/azure/key-vault/general/assign-access-policy?tabs=azure-portal + +### 5. Summary + +The complete Azure Key Vault configuration in the EDC should look something like this + +```properties +edc.vault.tenantid= +edc.vault.clientid= +edc.vault.clientsecret= +edc.vault.name= +``` + +Please note that the key vault could also be configured using the `edc.vault.certificate`, which is not covered by this +documentation. + +## Connector Certificate Setup + +The connector needs it's own certificate / private key in the key vault, so that it is able to encrypt and decrypt data. +Therefore, generate a PEM file that contains the private key and the certificate and put it into the key vault. + +```bash +# Generate PKCS8 Key +openssl genpkey -out con_key.pem -algorithm RSA -pkeyopt rsa_keygen_bits:2048 + +# Generate Certificate +openssl req -config cert.conf -new -x509 -key con_key.pem -nodes -days 365 -out con_cert.pem + +# Create Cert+Key PEM file +cat con_cert.pem >certkey.pem +cat con_key.pem >>certkey.pem +``` + +Then in the Azure Portal: + +1. Open the page of the newly created key vault +2. On the left side select _certificate_ and click _generate/import_ +3. Select certificate creation method _import_, choose a unique name and upload _certkey.pem_ file into the value +4. The certificate name must be configured in the `edc.public.key.alias` + +## IDS DAPS Setup + +The Eclipse Dataspace Connector is able to retrieve an identity token from the IDS DAPS. This token is part of all IDS +messages. + +The DAPS application requires a certificate from the Eclipse Dataspace Connector. This certificate may then be used by +the EDC connector to prove its identity and retrieve its identity token. + +When writing this guidance these step were tested out using the open source omejdn DAPS of the Fraunhofer +AISEC ([GitHub](https://github.com/International-Data-Spaces-Association/omejdn-daps)). + +### 1. Key / Certificate Generation + +In the first step generate a PKSC8 Key and the corresponding certificate. + +```bash +# Private Key +openssl genpkey -out key.pem -algorithm RSA -pkeyopt rsa_keygen_bits:2048 +``` + +````bash +# Certificate +openssl req -new -x509 -key key.pem -nodes -days 365 -out cert.pem +```` + +### 2. Certificate Setup + +#### 2.1. Certificate Setup - DAPS + +Each connector client must be registered in the DAPS. Send the certificate to the DAPS maintainers. After the DAPS has +created a new client, configure the corresponding client ID in `edc.oauth.client.id`. + +#### 2.2 Certificate Setup - Azure Key Vault + +##### 2.2.1 Format Certificate + +The certificate must also be stored as _secret_ in the Azure Key Vault. But before that, all newline and space +characters must be removed. + +```bash +# Format Certificate +cat cert.pem | sed "s/[[:space:]]*//" | tr -d \\n >cert.txt +``` + +#### 2.2.2 Create Key Vault Secret + +In the Azure Portal: + +1. Open the page of the newly created key vault +2. On the left side select _secret_ and click _generate/import_ to create a new secret +3. Select upload options _manual_, choose a unique name and copy the content of the _cert.txt_ file into the value +4. The secret name must be configured in the `edc.oauth.public.key.alias` + +### 3. Private Key Setup + +#### 3.1 Format Key + +Before storing the key as _secret_ in the Azure Key Vault format it. The PCKS8 identifying lines must be removed with +all spaces and newlines, so that only the key itself remains. + +```bash +# Format Key +cat key.pem | sed "s/^-----BEGIN PRIVATE KEY-----//" | sed "s/-----END PRIVATE KEY-----$//" | sed "s/[[:space:]]*//" | tr -d \\n >key.txt +``` + +#### 3.2 Create Key Vault Secret + +In the Azure Portal: + +1. Open the page of the newly created key vault +2. On the left side select _secret_ and click _generate/import_ to create a new secret +3. Select upload options _manual_, choose a unique name and copy the content of the _key.txt_ file into the value. +4. The secret name must be configured in the `edc.oauth.private.key.alias` + +### 4. DAPS Setup/Configuration + +The these properties should be requested from the DAPS maintainer: + +- DAPS Token URL must be configured in `edc.oauth.token.url` +- DAPS JWKS URL must be configured in `edc.oauth.provider.jwks.url` +- Token Audience must be configured in `edc.oauth.provider.audience` + +### Summary + +The EDC DAPS configuration could look like this: + +```properties +edc.oauth.token.url=http://localhost:4567/token +edc.oauth.client.id= +edc.oauth.provider.audience= +edc.oauth.provider.jwks.url=http://localhost:4567/.well-known/jwks.json +``` + +And for the OAUTH extensions there should the following properties set: + +```properties +edc.oauth.public.key.alias= +edc.oauth.private.key.alias= +``` + +# Short Overview of the EDC Domain + +This chapter gives a short overview of the EDC domain. The idea is to get a basic understanding of the domain objects and their roles.
+The complete EDC documentation can be found in the official open source [EDC GitHub Repository](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector). + +---- + +**Please note** +
+If you have already used the Fraunhofer Dataspace Connector, you are probably familiar with the IDS Domain Model. Don't confuse the IDS Domain Model with the EDC Domain Model. +The terms that are used in both models are pretty similar, but often don't represent the same thing. In the context of the EDC documentation it’s always safe to assume that the EDC Domain Model is in place, as the IDS model is only used when two Eclipse Dataspace COnnectors are exchanging messages. + +---- + +## Contract Offer Exchange + +In the EDC it’s not possible to create a _ContractOffer_ directly. The _ContractOffer_ is generated on the fly when +another connector asks about the _ContractOfferCatalog_. A _ContractOffer_ will only ge persistent when it becomes part of a +_ContractNegotiation_. + +A _ContractDefinition_ defines how many _ContractOffers_ should be generated and how the _Policy_ should +look like. + +The _ContractDefinition_ consists of a + +- _ContractPolicy_, that describes in EDC ODRL terms how the policy for a _ContractOffer_ should look like. +- _AccessPolicy_, that describes in EDC ODRL terms who is able to see a _ContractOffer_. But the content of this policy + will not be part of the _ContractOffer_. +- _AssetSelector_, that defines for which _Assets_ a _ContractOffer_ should be generated. An _Asset_ describes the data + itself that may be offered/transferred and is comparable to the IDS triplet of IDS-Resource, IDS-Representation, IDS + Artifact. + +So the ContractDefinition looks somewhat like this: + + + +![test](http://www.plantuml.com/plantuml/png/PSvD2a9130FWVKyn-tU99-fUU2SOEbKAOqUQ28fuTnL_DYxpGK9ci2RFnowYlG9bFO9PbHlRUpXzHBd9j30z3iMRJBlHNQ-bgXhm3Z_KJ_dBAy2uM3VboEtbb0Qy5l57SXUHsQ8zhpm0) + +When another connector asks the EDC about its _ContractOffers_ it: + +- Checks the connector identity +- Finds all the _ContractDefinitions_ that have a passing AccessPolicy +- Finds for each passing _ContractDefinition_ the corresponding _Assets_ +- Generates a new _ContractOffer_ for each _Asset_. The policy of the _ContractOffer_ is described in the ContractPolicy of the _ContractDefinition_. +- Maps the content of the _ContractOffer_ into the IDS domain and sends an IDS-ContractOffers to the other connector. +- The other connector then maps the IDS-ContractOffers back into its EDC domain and can then processes the _ContractOffer_. + +# Data Management API + +The documentation of the Data Management API can be found in the official open +source [EDC GitHub Repository](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector). + +The complete Eclipse Dataspace Connector API is described in the [EDC Open API Specification](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/blob/main/resources/openapi/openapi.yaml). Please be aware that this specification contains all APIs, that are implemented in the open source repository. The extensions, that implement those APIs, might not be part of the Control- and/or Data-Plane applications in this repository. Additionally, depending on the extension configuration, the documented paths might be only reachable using the configured ports. + +## Contract Offer Exchange + +---- + +**Please note**
+This chapter showcases the contract offer exchange between two connectors. It should function as starting point when working the Eclipse Dataspace Connector API. For a more detailed explanation of the various topics, that are touched in this section, please consolidate the official documentation in the [EDC GitHub Repository](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector). + +---- + +As described in the chapter about the EDC domain, the following resources must be created at the data provider: + +- **Asset** (& DataAddress), describing the data and how it can be transferred +- **Policy**, as Contract- and/or AccessPolicy of the _ContractDefinition_ +- **ContractDefinition**, for the contract offer generation + +### 0. Calling the Data Management API + +The Data Management API is secured with an API key. The value of this key can be configured in `edc.api.auth.key` and +should then be passed in the header as `X-API-Key: `. +Additionally, most or all of the API methods accept only JSON content, therefore adding `Content-Type: application/json` +to the header for most of the calls is recommended. + +### 1. Create Asset using Data Mgmt API + +#### Bash Script + +```bash +# Variables (please update before running the script) +__connectorUrl=http://localhost:8181 +__dataMgmtPath=data-mgmt +__apiKey=X-Api-Key +__apiKeyValue=pwd +__assetId=1 +__assetDescription="Demo Asset" +__assetDataEndpoint=https://github.com/eclipse-dataspaceconnector + +__asset="{ + \"asset\": { + \"properties\": { + \"asset:prop:id\": \"$__assetId\", + \"asset:prop:description\": \"$__assetDescription\" + } + }, + \"dataAddress\": { + \"properties\": { + \"type\": \"HttpProxy\", + \"endpoint\": \"$__assetDataEndpoint\" + } + } + }" + +# Call Data Management API +curl -X POST "$__connectorUrl/$__dataMgmtPath/assets" --header "$__apiKey: $__apiKeyValue" --header "Content-Type: application/json" --data "$__asset" +``` + +#### Bash Parameters + +| Name | Description | +| -------------------- | ----------------------------------------------------------------------------------------- | +| $__connectorUrl | URL of the Connector with the Data Management API port configured in `web.http.data.port` | +| $__dataMgmtPath | Path of the Data Management API as configured in `web.http.data.path` | +| $__apiKey | Should always be _X-Api-Key_ for the Data Management API | +| $__apiKeyValue | The API Key Value as configured in `edc.api.auth.key` | +| $__assetId | Unique identifier of the asset | +| $__assetDescription | Description of the asset | +| $__assetDataEndpoint | Endpoint that might be used when data is transferred. Irrelevant in this context / sample | + +#### Control Call + +Get Asset + +```bash +curl -X GET "$__connectorUrl/$__dataMgmtPath/assets/$__assetId" --header "$__apiKey: $__apiKeyValue" --header "Content-Type: application/json" | jq +``` + +### 2. Create Policy + +**Please be aware that the following policy make the data offer public for everyone and should be used with caution outside of this showcase!** + +Create a policy that can be used by the __ContractDefinition__. As the same policy may be used as contract- and access policy of the ContractDefinition, creating only one policy for both cases is totally fine for this demo. + +#### Bash Script + +```bash +# Variables +__connectorUrl=http://localhost:8181 +__dataMgmtPath=data-mgmt +__apiKey=X-Api-Key +__apiKeyValue=pwd +__policyId=1 + +__publicPolicy=" +{ + \"uid\": \"$__policyId\", + \"prohibitions\": [], + \"obligations\": [], + \"permissions\": [ + { + \"edctype\": \"dataspaceconnector:permission\", + \"action\": { + \"type\": \"USE\" + }, + } + ] +}" + +# Call Data Mgmt API +curl -X POST "$__connectorUrl/$__dataMgmtPath/policies" --header "$__apiKey: $__apiKeyValue" --header "Content-Type: application/json" --data "$__publicPolicy" +``` + +#### Bash Parameters + +| Name | Description | +| --------------- | ----------------------------------------------------------------------------------------- | +| $__connectorUrl | URL of the Connector with the Data Management API port configured in `web.http.data.port` | +| $__dataMgmtPath | Path of the Data Management API as configured in `web.http.data.path` | +| $__apiKey | Should always be _X-Api-Key_ for the Data Management API | +| $__apiKeyValue | The API Key Value as configured in `edc.api.auth.key` | +| $__policyId | Unique identifier of the policy. | + +#### Control Call + +Get Policy + +```bash +curl -X GET "$__connectorUrl/$__dataMgmtPath/policies/$__policyId" --header "$__apiKey: $__apiKeyValue" --header "Content-Type: application/json" | jq +``` + +### 3. Create Contract Definition + +The following uses the previously created public policy make the data offer available for everyone. + +#### Bash Script + +```bash +# Variables +__connectorUrl=http://localhost:8181 +__dataMgmtPath=data-mgmt +__apiKey=X-Api-Key +__apiKeyValue=pwd +__contractDefinitionId=1 +__policyId=1 +__assetId=1 + +__publicContractDefinition=" + { + \"id\": \"$__contractDefinitionId\", + \"accessPolicyId\": \"$__policyId\", + \"contractPolicyId\": \"$__policyId\", + \"criteria\": [ + { + \"left\": \"asset:prop:id\", + \"op\": \"=\", + \"right\": \"$__assetId\" + } + ] + }" + +# Call Data Mgmt API +curl -X POST "$__connectorUrl/$__dataMgmtPath/policies" --header "$__apiKey: $__apiKeyValue" --header "Content-Type: application/json" --data "$__publicContractDefinition" +``` + +#### Bash Parameters + +| Name | Description | +| ----------------------- | ----------------------------------------------------------------------------------------- | +| $__connectorUrl | URL of the Connector with the Data Management API port configured in `web.http.data.port` | +| $__dataMgmtPath | Path of the Data Management API as configured in `web.http.data.path` | +| $__apiKey | Should always be _X-Api-Key_ for the Data Management API | +| $__apiKeyValue | The API Key Value as configured in `edc.api.auth.key` | +| $__contractDefinitionId | Unique identifier of the contract definition. | +| $__policyId | Unique identifier of the policy. Must be the same ID as in step 2. | +| $__assetId | Unique identifier of the asset. Must be the same ID as in step 1. | + +#### Control Call + +Get Contract Definition + +```bash +curl -X GET "$__connectorUrl/$__dataMgmtPath/policies/$__policyId" --header "$__apiKey: $__apiKeyValue" --header "Content-Type: application/json" | jq +``` + +### 4. Get Contract Offer Catalog + +The last call is not (yet) part of the Data Management API. Instead, the deprecated Control API is used. The extension +for the control API is part of the Catena-X images and usable. + +---- + +**Please Note** + +Don't confuse the deprecated Control API with another Control API of the connector, that is not deprecated. + +---- + +#### Bash Script + +```bash +# Variables +__connectorUrl=http://localhost:8181 +__targetConnectorUrl=http://localhost:9292 +__targetConnectorIdsPath=api/v1/ids +__defaultApiPath=api +__apiKey=X-Api-Key +__apiKeyValue=pwd +__contractDefinitionId=1 +__policyId=1 +__assetId=1 + +__publicContractDefinition=" + { + \"id\": \"$__contractDefinitionId\", + \"accessPolicyId\": \"$__policyId\", + \"contractPolicyId\": \"$__policyId\", + \"criteria\": [ + { + \"left\": \"asset:prop:id\", + \"op\": \"=\", + \"right\": \"$__assetId\" + } + ] + }" + +# Call Control API +curl -G -X GET $__connectorUrl/$__defaultApiPath/control/catalog --header "$__apiKey: $__apiKeyValue" --data-urlencode "provider=$__targetConnectorUrl/$__targetConnectorIdsPath/data" --header "Content-Type: application/json" -s | jq +``` + +#### Bash Parameters + +| Name | Description | +| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | +| $__connectorUrl | URL of the Connector with the Control API port configured in `web.http.default.port` | +| $__defaultApiPath | Path of the Control API as configured in `web.http.default.path` | +| $__apiKey | The API Key as configured in `edc.api.control.auth.apikey.key` | +| $__apiKeyValue | The API Key Value as configured in `edc.api.control.auth.apikey.value` | +| $__targetConnectorUrl | URL of the Connector of the target connector with the IDS API port configured in `web.http.ids.port`(in the configuration of the other connector) | +| $__targetConnectorIdsPath | The IDS Path as configured in `web.http.ids.path` (in the configuration of the other connector) | + +# Known Control Plane Issues + +Please have look at all the open issues in the open source repository. The list below might not be maintained well and +only contains the most important issues. +EDC Github Repository https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues + +### All Contract Offers and their data is public available + +https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1137 + +### Contract Negotiation not working + +https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1140 + +### Missing Input validation in the Data Management API + +https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1111 diff --git a/edc-controlplane/edc-controlplane-cosmosdb/README.md b/edc-controlplane/edc-controlplane-cosmosdb/README.md new file mode 100644 index 000000000..645e9506d --- /dev/null +++ b/edc-controlplane/edc-controlplane-cosmosdb/README.md @@ -0,0 +1,173 @@ +# EDC Control-Plane backed by [Azure CosmosDB](https://docs.microsoft.com/en-us/azure/cosmos-db/introduction) + +### Building + +```shell +./mvnw -pl .,edc-controlplane/edc-controlplane-cosmosdb -am package -Pwith-docker-image +``` + +### Configuration + +Listed below are configuration keys needed to get the `edc-controlplane-cosmosdb` up and running. +Details regarding each configuration property can be found at the [documentary section of the EDC](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/docs). + +| Key | Required | Example | Description | +|--- |--- |--- |--- | +| web.http.default.port | X | 8080 | | +| web.http.default.path | X | /api | | +| web.http.data.port | X | 8181 | | +| web.http.data.path | X | | | +| web.http.validation.port | X | 8182 | | +| web.http.validation.path | X | /validation | | +| web.http.control.port | X | 9999 | | +| web.http.control.path | X | /api/controlplane/control | | +| web.http.ids.port | X | 8282 | | +| web.http.ids.path | X | /api/v1/ids | | +| edc.receiver.http.endpoint | X | http://backend-service | | +| edc.ids.title | | Eclipse Dataspace Connector | | +| edc.ids.description | | Eclipse Dataspace Connector | | +| edc.ids.id | | urn:connector:edc | | +| edc.ids.security.profile | | base | | +| edc.ids.endpoint | | http://localhost:8282/api/v1/ids | | +| edc.ids.maintainer | | http://localhost | | +| edc.ids.curator | | http://localhost | | +| edc.ids.catalog.id | | urn:catalog:default | | +| ids.webhook.address | | http://localhost:8282/api/v1/ids | | +| edc.api.control.auth.apikey.key | | X-Api-Key | | +| edc.api.control.auth.apikey.value | | super-strong-api-key | | +| edc.hostname | | localhost | | +| edc.oauth.token.url | X | https://daps.catena-x.net | | +| edc.oauth.public.key.alias | X | key-to-daps-certificate-in-keyvault | | +| edc.oauth.private.key.alias | X | key-to-private-key-in-keyvault | | +| edc.oauth.client.id | X | daps-oauth-client-id | | +| edc.vault.clientid | X | 00000000-1111-2222-3333-444444444444 | | +| edc.vault.tenantid | X | 55555555-6666-7777-8888-999999999999 | | +| edc.vault.name | X | my-vault-name | | +| edc.vault.clientsecret | X | 34-chars-secret | | +| edc.assetindex.cosmos.account-name | X | cosmosdb-assetindex-account-name | | +| edc.assetindex.cosmos.database-name | X | asset-index | | +| edc.assetindex.cosmos.preferred-region | X | westeurope | | +| edc.assetindex.cosmos.container-name | X | cosmosdb-assetindex-container-name | | +| edc.contractdefinitionstore.cosmos.account-name | X | cosmosdb-contractdefinitionstore-account-name | | +| edc.contractdefinitionstore.cosmos.database-name | X | contract-definition-store | | +| edc.contractdefinitionstore.cosmos.preferred-region | X | westeurope | | +| edc.contractdefinitionstore.cosmos.container-name | X | cosmosdb-contractdefinitionstore-container-name | | +| edc.contractnegotiationstore.cosmos.account-name | X | cosmosdb-contractnegotiationstore-account-name | | +| edc.contractnegotiationstore.cosmos.database-name | X | contract-negotiation-store | | +| edc.contractnegotiationstore.cosmos.preferred-region | X | westeurope | | +| edc.contractnegotiationstore.cosmos.container-name | X | cosmosdb-contractnegotiationstore-container-name | | +| edc.transfer-process-store.cosmos.account.name | X | cosmosdb-contractnegotiationstore-account-name | | +| edc.transfer-process-store.database.name | X | transfer-process-store | | +| edc.transfer-process-store.cosmos.preferred-region | X | westeurope | | +| edc.transfer-process-store.cosmos.container-name | X | cosmosdb-transfer-process-store-container-name | | +| edc.transfer.dataplane.sync.endpoint | X | | | +| edc.transfer.dataplane.token.signer.privatekey.alias | X | | | + +#### Example configuration.properties + +JDK properties-style configuration of the EDC Control-Plane is expected to be mounted to `/app/configuration.properties` within the container. + +```shell +# Create configuration.properties +export CONFIGURATION_PROPERTIES_FILE=$(mktemp /tmp/configuration.properties.XXXXXX) +cat << 'EOF' > ${CONFIGURATION_PROPERTIES_FILE} + +web.http.default.port=8080 +web.http.default.path=/api +web.http.data.port=8181 +web.http.data.path=/data +web.http.validation.port=8182 +web.http.validation.path=/validation +web.http.control.port=9999 +web.http.control.path=/api/controlplane/control +web.http.ids.port=8282 +web.http.ids.path=/api/v1/ids + +edc.receiver.http.endpoint=http://backend-service + +edc.ids.title=Eclipse Dataspace Connector +edc.ids.description=Eclipse Dataspace Connector +edc.ids.id=urn:connector:edc +edc.ids.security.profile=base +edc.ids.endpoint=http://localhost:8282/api/v1/ids +edc.ids.maintainer=http://localhost +edc.ids.curator=http://localhost +edc.ids.catalog.id=urn:catalog:default +ids.webhook.address=http://localhost:8282/api/v1/ids + +edc.api.control.auth.apikey.key=X-Api-Key +edc.api.control.auth.apikey.value=pass + +edc.hostname=localhost + +# OAuth / DAPS related configuration +edc.oauth.token.url=https://daps.catena-x.net +edc.oauth.public.key.alias=key-to-daps-certificate-in-keyvault +edc.oauth.private.key.alias=key-to-private-key-in-keyvault +edc.oauth.client.id=daps-oauth-client-id + +# Azure vault related configuration +edc.vault.clientid=00000000-1111-2222-3333-444444444444 +edc.vault.tenantid=55555555-6666-7777-8888-999999999999 +edc.vault.name=my-vault-name +edc.vault.clientsecret=34-chars-secret + +# Control- / Data- Plane configuration +edc.transfer.dataplane.sync.endpoint=http://dataplane-public-endpoint/public +edc.transfer.dataplane.token.signer.privatekey.alias=azure-vault-token-signer-private-key + +# Azure CosmosDB related configuration +edc.assetindex.cosmos.account-name=cosmosdb-assetindex-account-name +edc.assetindex.cosmos.database-name=asset-index +edc.assetindex.cosmos.preferred-region=westeurope +edc.assetindex.cosmos.container-name=cosmosdb-assetindex-container-name +edc.contractdefinitionstore.cosmos.account-name=cosmosdb-contractdefinitionstore-account-name +edc.contractdefinitionstore.cosmos.database-name=contract-definition-store +edc.contractdefinitionstore.cosmos.preferred-region=westeurope +edc.contractdefinitionstore.cosmos.container-name=cosmosdb-contractdefinitionstore-container-name +edc.contractnegotiationstore.cosmos.account-name=cosmosdb-contractnegotiationstore-account-name +edc.contractnegotiationstore.cosmos.database-name=contract-negotiation-store +edc.contractnegotiationstore.cosmos.preferred-region=westeurope +edc.contractnegotiationstore.cosmos.container-name=cosmosdb-contractnegotiationstore-container-name +edc.transfer-process-store.cosmos.account.name=cosmosdb-contractnegotiationstore-account-name +edc.transfer-process-store.database.name=transfer-process-store +edc.transfer-process-store.cosmos.preferred-region=westeurope +edc.transfer-process-store.cosmos.container-name=cosmosdb-transfer-process-store-container-name + +EOF +``` + +#### Example logging.properties +```shell +# Create logging.properties +export LOGGING_PROPERTIES_FILE=$(mktemp /tmp/logging.properties.XXXXXX) +cat << 'EOF' > ${LOGGING_PROPERTIES_FILE} +.level=INFO +org.eclipse.dataspaceconnector.level=ALL +handlers=java.util.logging.ConsoleHandler +java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter +java.util.logging.ConsoleHandler.level=ALL +java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n +EOF +``` + +#### Example opentelemetry.properties +```shell +# Create opentelemetry.properties +export OPENTELEMETRY_PROPERTIES_FILE=$(mktemp /tmp/opentelemetry.properties.XXXXXX) +cat << 'EOF' > ${OPENTELEMETRY_PROPERTIES_FILE} +otel.javaagent.enabled=true +otel.javaagent.debug=false +EOF +``` + +### Running + +```shell +docker run \ + -p 8080:8080 -p 8181:8181 -p 8182:8182 -p 8282:8282 -p 9090:9090 -p 9999:9999 \ + -v ${CONFIGURATION_PROPERTIES_FILE:-/dev/null}:/app/configuration.properties \ + -v ${LOGGING_PROPERTIES_FILE:-/dev/null}:/app/logging.properties \ + -v ${OPENTELEMETRY_PROPERTIES_FILE:-/dev/null}:/app/opentelemetry.properties \ + -i edc-controlplane-cosmosdb:latest +``` \ No newline at end of file diff --git a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml index c6e06ef3c..a374ea421 100644 --- a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml +++ b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml @@ -221,6 +221,12 @@ contract
+ + + org.eclipse.dataspaceconnector + data-plane-transfer-sync + + org.eclipse.dataspaceconnector @@ -264,6 +270,7 @@ http-receiver + com.azure azure-cosmos @@ -288,18 +295,31 @@ dockerfile-maven-plugin - default + ${project.artifactId}:${project.version} package build + tag + + + ${project.version} + + + + ${project.artifactId}:latest + package + + tag + + latest + . src/main/docker/Dockerfile ${project.artifactId} - ${project.version} target/${project.artifactId}.jar diff --git a/edc-controlplane/edc-controlplane-memory/README.md b/edc-controlplane/edc-controlplane-memory/README.md new file mode 100644 index 000000000..71c60d167 --- /dev/null +++ b/edc-controlplane/edc-controlplane-memory/README.md @@ -0,0 +1,125 @@ +# EDC Control-Plane backed by In-Memory Stores + +### Building + +```shell +./mvnw -pl .,edc-controlplane/edc-controlplane-memory -am package -Pwith-docker-image +``` + +### Configuration (configuration.properties) + +Listed below are configuration keys needed to get the `edc-controlplane-memory` up and running. +Details regarding each configuration property can be found at the [documentary section of the EDC](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/docs). + +| Key | Required | Example | Description | +|--- |--- |--- |--- | +| web.http.default.port | X | 8080 | | +| web.http.default.path | X | /api | | +| web.http.data.port | X | 8181 | | +| web.http.data.path | X | /data | | +| web.http.validation.port | X | 8182 | | +| web.http.validation.path | X | /validation | | +| web.http.control.port | X | 9999 | | +| web.http.control.path | X | /api/controlplane/control | | +| web.http.ids.port | X | 8282 | | +| web.http.ids.path | X | /api/v1/ids | | +| edc.receiver.http.endpoint | X | http://backend-service | | +| edc.ids.title | | Eclipse Dataspace Connector | | +| edc.ids.description | | Eclipse Dataspace Connector | | +| edc.ids.id | | urn:connector:edc | | +| edc.ids.security.profile | | base | | +| edc.ids.endpoint | | http://localhost:8282/api/v1/ids | | +| edc.ids.maintainer | | http://localhost | | +| edc.ids.curator | | http://localhost | | +| edc.ids.catalog.id | | urn:catalog:default | | +| ids.webhook.address | | http://localhost:8282/api/v1/ids | | +| edc.api.control.auth.apikey.key | | X-Api-Key | | +| edc.api.control.auth.apikey.value | | super-strong-api-key | | +| edc.hostname | | localhost | | +| edc.transfer.dataplane.sync.endpoint | X | | | +| edc.transfer.dataplane.token.signer.privatekey.alias | X | | | + +#### Example configuration.properties + +JDK properties-style configuration of the EDC Control-Plane is expected to be mounted to `/app/configuration.properties` within the container. + +```shell +# Create configuration.properties +export CONFIGURATION_PROPERTIES_FILE=$(mktemp /tmp/configuration.properties.XXXXXX) +cat << 'EOF' > ${CONFIGURATION_PROPERTIES_FILE} + +web.http.default.port=8080 +web.http.default.path=/api +web.http.data.port=8181 +web.http.data.path=/data +web.http.validation.port=8182 +web.http.validation.path=/validation +web.http.control.port=9999 +web.http.control.path=/api/controlplane/control +web.http.ids.port=8282 +web.http.ids.path=/api/v1/ids + +edc.receiver.http.endpoint=http://backend-service + +edc.ids.title=Eclipse Dataspace Connector +edc.ids.description=Eclipse Dataspace Connector +edc.ids.id=urn:connector:edc +edc.ids.security.profile=base +edc.ids.endpoint=http://localhost:8282/api/v1/ids +edc.ids.maintainer=http://localhost +edc.ids.curator=http://localhost +edc.ids.catalog.id=urn:catalog:default +ids.webhook.address=http://localhost:8282/api/v1/ids + +edc.api.control.auth.apikey.key=X-Api-Key +edc.api.control.auth.apikey.value=pass + +edc.hostname=localhost + + +# Azure vault related configuration +edc.vault.clientid=00000000-1111-2222-3333-444444444444 +edc.vault.tenantid=55555555-6666-7777-8888-999999999999 +edc.vault.name=my-vault-name +edc.vault.clientsecret=34-chars-secret + +# Control- / Data- Plane configuration +edc.transfer.dataplane.sync.endpoint=http://dataplane-public-endpoint/public +edc.transfer.dataplane.token.signer.privatekey.alias=azure-vault-token-signer-private-key +EOF +``` + +#### Example logging.properties +```shell +# Create logging.properties +export LOGGING_PROPERTIES_FILE=$(mktemp /tmp/logging.properties.XXXXXX) +cat << 'EOF' > ${LOGGING_PROPERTIES_FILE} +.level=INFO +org.eclipse.dataspaceconnector.level=ALL +handlers=java.util.logging.ConsoleHandler +java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter +java.util.logging.ConsoleHandler.level=ALL +java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n +EOF +``` + +#### Example opentelemetry.properties +```shell +# Create opentelemetry.properties +export OPENTELEMETRY_PROPERTIES_FILE=$(mktemp /tmp/opentelemetry.properties.XXXXXX) +cat << 'EOF' > ${OPENTELEMETRY_PROPERTIES_FILE} +otel.javaagent.enabled=true +otel.javaagent.debug=false +EOF +``` + +### Running + +```shell +docker run \ + -p 8080:8080 -p 8181:8181 -p 8182:8182 -p 8282:8282 -p 9090:9090 -p 9999:9999 \ + -v ${CONFIGURATION_PROPERTIES_FILE:-/dev/null}:/app/configuration.properties \ + -v ${LOGGING_PROPERTIES_FILE:-/dev/null}:/app/logging.properties \ + -v ${OPENTELEMETRY_PROPERTIES_FILE:-/dev/null}:/app/opentelemetry.properties \ + -i edc-controlplane-memory:latest +``` \ No newline at end of file diff --git a/edc-controlplane/edc-controlplane-memory/pom.xml b/edc-controlplane/edc-controlplane-memory/pom.xml index 852ab4c8c..75604176e 100644 --- a/edc-controlplane/edc-controlplane-memory/pom.xml +++ b/edc-controlplane/edc-controlplane-memory/pom.xml @@ -93,6 +93,22 @@ filesystem-configuration + + + org.eclipse.dataspaceconnector + azure-vault + + + com.azure + azure-security-keyvault-secrets + + + com.azure + azure-identity + + + + org.eclipse.dataspaceconnector @@ -179,19 +195,17 @@ contract + + + org.eclipse.dataspaceconnector + data-plane-transfer-sync + + - - - - - - - - - - org.eclipse.dataspaceconnector - iam-mock - + + org.eclipse.dataspaceconnector + iam-mock + @@ -226,8 +240,19 @@ org.eclipse.dataspaceconnector http-receiver + + + + com.azure + azure-identity + + + com.azure + azure-security-keyvault-secrets + + with-docker-image @@ -238,18 +263,31 @@ dockerfile-maven-plugin - default + ${project.artifactId}:${project.version} package build + tag + + + ${project.version} + + + + ${project.artifactId}:latest + package + + tag + + latest + . src/main/docker/Dockerfile ${project.artifactId} - ${project.version} target/${project.artifactId}.jar diff --git a/edc-controlplane/edc-controlplane-postgresql/README.md b/edc-controlplane/edc-controlplane-postgresql/README.md new file mode 100644 index 000000000..e0c648c0e --- /dev/null +++ b/edc-controlplane/edc-controlplane-postgresql/README.md @@ -0,0 +1,181 @@ +# EDC Control-Plane backed by [Postgresql](https://www.postgresql.org/) + +### Building + +```shell +./mvnw -pl .,edc-controlplane/edc-controlplane-postgresql -am package -Pwith-docker-image +``` + +### Configuration + +Listed below are configuration keys needed to get the `edc-controlplane-postgresql` up and running. +Details regarding each configuration property can be found at the [documentary section of the EDC](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/docs). + +| Key | Required | Example | Description | +|--- |--- |--- |--- | +| web.http.default.port | X | 8080 | | +| web.http.default.path | X | /api | | +| web.http.data.port | X | 8181 | | +| web.http.data.path | X | | | +| web.http.validation.port | X | 8182 | | +| web.http.validation.path | X | /validation | | +| web.http.control.port | X | 9999 | | +| web.http.control.path | X | /api/controlplane/control | | +| web.http.ids.port | X | 8282 | | +| web.http.ids.path | X | /api/v1/ids | | +| edc.receiver.http.endpoint | X | http://backend-service | | +| edc.ids.title | | Eclipse Dataspace Connector | | +| edc.ids.description | | Eclipse Dataspace Connector | | +| edc.ids.id | | urn:connector:edc | | +| edc.ids.security.profile | | base | | +| edc.ids.endpoint | | http://localhost:8282/api/v1/ids | | +| edc.ids.maintainer | | http://localhost | | +| edc.ids.curator | | http://localhost | | +| edc.ids.catalog.id | | urn:catalog:default | | +| ids.webhook.address | | http://localhost:8282/api/v1/ids | | +| edc.api.control.auth.apikey.key | | X-Api-Key | | +| edc.api.control.auth.apikey.value | | super-strong-api-key | | +| edc.hostname | | localhost | | +| edc.oauth.token.url | X | https://daps.catena-x.net | | +| edc.oauth.public.key.alias | X | key-to-daps-certificate-in-keyvault | | +| edc.oauth.private.key.alias | X | key-to-private-key-in-keyvault | | +| edc.oauth.client.id | X | daps-oauth-client-id | | +| edc.vault.clientid | X | 00000000-1111-2222-3333-444444444444 | | +| edc.vault.tenantid | X | 55555555-6666-7777-8888-999999999999 | | +| edc.vault.name | X | my-vault-name | | +| edc.vault.clientsecret | X | 34-chars-secret | | +| edc.datasource.asset.name | X | asset | | +| edc.datasource.asset.url | X | jdbc:postgresql://postgres.svc.cluster.local:5432/edc_asset_db | | +| edc.datasource.asset.user | X | username | | +| edc.datasource.asset.password | X | password | | +| edc.datasource.contractdefinition.name | X | contractdefinition | | +| edc.datasource.contractdefinition.url | X | jdbc:postgresql://postgres.svc.cluster.local:5432/edc_contractdefinition_db | | +| edc.datasource.contractdefinition.user | X | username | | +| edc.datasource.contractdefinition.password | X | password | | +| edc.datasource.contractnegotiation.name | X | contractnegotiation | | +| edc.datasource.contractnegotiation.url | X | jdbc:postgresql://postgres.svc.cluster.local:5432/edc_contractnegotiation_db | | +| edc.datasource.contractnegotiation.user | X | username | | +| edc.datasource.contractnegotiation.password | X | password | | +| edc.datasource.policy.name | X | policy | | +| edc.datasource.policy.url | X | jdbc:postgresql://postgres.svc.cluster.local:5432/edc_policy_db | | +| edc.datasource.policy.user | X | username | | +| edc.datasource.policy.password | X | password | | +| edc.datasource.transferprocess.name | X | transferprocess | | +| edc.datasource.transferprocess.url | X | jdbc:postgresql://postgres.svc.cluster.local:5432/edc_transferprocess_db | | +| edc.datasource.transferprocess.user | X | username | | +| edc.datasource.transferprocess.password | X | password | | +| edc.transfer.dataplane.sync.endpoint | X | | | +| edc.transfer.dataplane.token.signer.privatekey.alias | X | | | + +#### Example configuration.properties + +JDK properties-style configuration of the EDC Control-Plane is expected to be mounted to `/app/configuration.properties` within the container. + +```shell +# Create configuration.properties +export CONFIGURATION_PROPERTIES_FILE=$(mktemp /tmp/configuration.properties.XXXXXX) +cat << 'EOF' > ${CONFIGURATION_PROPERTIES_FILE} + +web.http.default.port=8080 +web.http.default.path=/api +web.http.data.port=8181 +web.http.data.path=/data +web.http.validation.port=8182 +web.http.validation.path=/validation +web.http.control.port=9999 +web.http.control.path=/api/controlplane/control +web.http.ids.port=8282 +web.http.ids.path=/api/v1/ids + +edc.receiver.http.endpoint=http://backend-service + +edc.ids.title=Eclipse Dataspace Connector +edc.ids.description=Eclipse Dataspace Connector +edc.ids.id=urn:connector:edc +edc.ids.security.profile=base +edc.ids.endpoint=http://localhost:8282/api/v1/ids +edc.ids.maintainer=http://localhost +edc.ids.curator=http://localhost +edc.ids.catalog.id=urn:catalog:default +ids.webhook.address=http://localhost:8282/api/v1/ids + +edc.api.control.auth.apikey.key=X-Api-Key +edc.api.control.auth.apikey.value=pass + +edc.hostname=localhost + +# OAuth / DAPS related configuration +edc.oauth.token.url=https://daps.catena-x.net +edc.oauth.public.key.alias=key-to-daps-certificate-in-keyvault +edc.oauth.private.key.alias=key-to-private-key-in-keyvault +edc.oauth.client.id=daps-oauth-client-id + +# Azure vault related configuration +edc.vault.clientid=00000000-1111-2222-3333-444444444444 +edc.vault.tenantid=55555555-6666-7777-8888-999999999999 +edc.vault.name=my-vault-name +edc.vault.clientsecret=34-chars-secret + +# Control- / Data- Plane configuration +edc.transfer.dataplane.sync.endpoint=http://dataplane-public-endpoint/public +edc.transfer.dataplane.token.signer.privatekey.alias=azure-vault-token-signer-private-key + +# Postgresql related configuration +edc.datasource.asset.name=asset +edc.datasource.asset.url=jdbc:postgresql://postgres.svc.cluster.local:5432/edc_asset +edc.datasource.asset.user=user +edc.datasource.asset.password=pass +edc.datasource.contractdefinition.name=contractdefinition +edc.datasource.contractdefinition.url=jdbc:postgresql://postgres.svc.cluster.local:5432/edc_contractdefinition +edc.datasource.contractdefinition.user=user +edc.datasource.contractdefinition.password=pass +edc.datasource.contractnegotiation.name=contractnegotiation +edc.datasource.contractnegotiation.url=jdbc:postgresql://postgres.svc.cluster.local:5432/edc_contractnegotiation +edc.datasource.contractnegotiation.user=user +edc.datasource.contractnegotiation.password=pass +edc.datasource.policy.name=policy +edc.datasource.policy.url=jdbc:postgresql://postgres.svc.cluster.local:5432/edc_policy +edc.datasource.policy.user=user +edc.datasource.policy.password=pass +edc.datasource.transferprocess.name=transferprocess +edc.datasource.transferprocess.url=jdbc:postgresql://postgres.svc.cluster.local:5432/edc_transferprocess +edc.datasource.transferprocess.user=user +edc.datasource.transferprocess.password=pass + +EOF +``` + +#### Example logging.properties +```shell +# Create logging.properties +export LOGGING_PROPERTIES_FILE=$(mktemp /tmp/logging.properties.XXXXXX) +cat << 'EOF' > ${LOGGING_PROPERTIES_FILE} +.level=INFO +org.eclipse.dataspaceconnector.level=ALL +handlers=java.util.logging.ConsoleHandler +java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter +java.util.logging.ConsoleHandler.level=ALL +java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n +EOF +``` + +#### Example opentelemetry.properties +```shell +# Create opentelemetry.properties +export OPENTELEMETRY_PROPERTIES_FILE=$(mktemp /tmp/opentelemetry.properties.XXXXXX) +cat << 'EOF' > ${OPENTELEMETRY_PROPERTIES_FILE} +otel.javaagent.enabled=true +otel.javaagent.debug=false +EOF +``` + +### Running + +```shell +docker run \ + -p 8080:8080 -p 8181:8181 -p 8182:8182 -p 8282:8282 -p 9090:9090 -p 9999:9999 \ + -v ${CONFIGURATION_PROPERTIES_FILE:-/dev/null}:/app/configuration.properties \ + -v ${LOGGING_PROPERTIES_FILE:-/dev/null}:/app/logging.properties \ + -v ${OPENTELEMETRY_PROPERTIES_FILE:-/dev/null}:/app/opentelemetry.properties \ + -i edc-controlplane-postgresql:latest +``` \ No newline at end of file diff --git a/edc-controlplane/edc-controlplane-postgresql/pom.xml b/edc-controlplane/edc-controlplane-postgresql/pom.xml index ac6b44ad6..fa8440ee8 100644 --- a/edc-controlplane/edc-controlplane-postgresql/pom.xml +++ b/edc-controlplane/edc-controlplane-postgresql/pom.xml @@ -106,6 +106,16 @@ org.eclipse.dataspaceconnector azure-vault + + + com.azure + azure-security-keyvault-secrets + + + com.azure + azure-identity + + @@ -194,6 +204,12 @@ contract + + + org.eclipse.dataspaceconnector + data-plane-transfer-sync + + org.eclipse.dataspaceconnector @@ -246,15 +262,20 @@ - org.postgresql - postgresql + com.azure + azure-identity + + + com.azure + azure-security-keyvault-secrets - jakarta.ws.rs - jakarta.ws.rs-api + org.postgresql + postgresql + with-docker-image @@ -265,18 +286,31 @@ dockerfile-maven-plugin - default + ${project.artifactId}:${project.version} package build + tag + + + ${project.version} + + + + ${project.artifactId}:latest + package + + tag + + latest + . src/main/docker/Dockerfile ${project.artifactId} - ${project.version} target/${project.artifactId}.jar diff --git a/edc-dataplane/README.md b/edc-dataplane/README.md new file mode 100644 index 000000000..76cb68245 --- /dev/null +++ b/edc-dataplane/README.md @@ -0,0 +1,94 @@ +# EDC Data-Plane + +### Building + +```shell +./mvnw -pl .,edc-dataplane -am package -Pwith-docker-image +``` + +### Configuration + +Listed below are configuration keys needed to get the `edc-dataplane` up and running. +Details regarding each configuration property can be found at the [documentary section of the EDC](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/docs). + +| Key | Required | Example | Description | +|--- |--- |--- |--- | +| web.http.default.port | X | 8080 | | +| web.http.default.path | X | /api | | +| web.http.public.port | X | 8181 | | +| web.http.public.path | X | | | +| web.http.control.port | X | 9999 | | +| web.http.control.path | X | /api/controlplane/control | | +| edc.receiver.http.endpoint | X | http://backend-service | | +| edc.hostname | | localhost | | +| edc.oauth.client.id | X | daps-oauth-client-id | | +| edc.vault.clientid | X | 00000000-1111-2222-3333-444444444444 | | +| edc.vault.tenantid | X | 55555555-6666-7777-8888-999999999999 | | +| edc.vault.name | X | my-vault-name | | +| edc.vault.clientsecret | X | 34-chars-secret | | +| edc.controlplane.validation-endpoint | X | http://controlplane:8182/validation | | + +#### Example configuration.properties + +JDK properties-style configuration of the EDC Control-Plane is expected to be mounted to `/app/configuration.properties` within the container. + +```shell +# Create configuration.properties +export CONFIGURATION_PROPERTIES_FILE=$(mktemp /tmp/configuration.properties.XXXXXX) +cat << 'EOF' > ${CONFIGURATION_PROPERTIES_FILE} + +web.http.default.port=8080 +web.http.default.path=/api +web.http.public.port=8185 +web.http.public.path=/public +web.http.control.port=9999 +web.http.control.path=/api/dataplane/control + +# Validation endpoint of controlplane +edc.controlplane.validation-endpoint=http://controlplane:8182/validation + +# EDC hostname +edc.hostname=localhost + +# Azure vault related configuration +edc.vault.clientid=00000000-1111-2222-3333-444444444444 +edc.vault.tenantid=55555555-6666-7777-8888-999999999999 +edc.vault.name=my-vault-name +edc.vault.clientsecret=34-chars-secret +EOF +``` + +#### Example logging.properties +```shell +# Create logging.properties +export LOGGING_PROPERTIES_FILE=$(mktemp /tmp/logging.properties.XXXXXX) +cat << 'EOF' > ${LOGGING_PROPERTIES_FILE} +.level=INFO +org.eclipse.dataspaceconnector.level=ALL +handlers=java.util.logging.ConsoleHandler +java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter +java.util.logging.ConsoleHandler.level=ALL +java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n +EOF +``` + +#### Example opentelemetry.properties +```shell +# Create opentelemetry.properties +export OPENTELEMETRY_PROPERTIES_FILE=$(mktemp /tmp/opentelemetry.properties.XXXXXX) +cat << 'EOF' > ${OPENTELEMETRY_PROPERTIES_FILE} +otel.javaagent.enabled=true +otel.javaagent.debug=false +EOF +``` + +### Running + +```shell +docker run \ + -p 8080:8080 -p 8185:8185 -p 9999:9999 -p 9090:9090 \ + -v ${CONFIGURATION_PROPERTIES_FILE:-/dev/null}:/app/configuration.properties \ + -v ${LOGGING_PROPERTIES_FILE:-/dev/null}:/app/logging.properties \ + -v ${OPENTELEMETRY_PROPERTIES_FILE:-/dev/null}:/app/opentelemetry.properties \ + -i edc-dataplane:latest +``` \ No newline at end of file diff --git a/edc-dataplane/pom.xml b/edc-dataplane/pom.xml index 99cefa340..da50aa477 100644 --- a/edc-dataplane/pom.xml +++ b/edc-dataplane/pom.xml @@ -89,6 +89,22 @@ filesystem-configuration + + + org.eclipse.dataspaceconnector + azure-vault + + + com.azure + azure-security-keyvault-secrets + + + com.azure + azure-identity + + + + org.eclipse.dataspaceconnector @@ -142,8 +158,19 @@ org.eclipse.dataspaceconnector http + + + + com.azure + azure-identity + + + com.azure + azure-security-keyvault-secrets + + with-docker-image @@ -154,18 +181,31 @@ dockerfile-maven-plugin - default + ${project.artifactId}:${project.version} package build + tag + + + ${project.version} + + + + ${project.artifactId}:latest + package + + tag + + latest + . src/main/docker/Dockerfile ${project.artifactId} - ${project.version} target/${project.artifactId}.jar diff --git a/edc-extensions/README.md b/edc-extensions/README.md new file mode 100644 index 000000000..cbbaa5684 --- /dev/null +++ b/edc-extensions/README.md @@ -0,0 +1 @@ +# edc-extensions \ No newline at end of file diff --git a/edc-extensions/aas-controller/README.md b/edc-extensions/aas-controller/README.md index 3ff0d0cbf..f32ad27e5 100644 --- a/edc-extensions/aas-controller/README.md +++ b/edc-extensions/aas-controller/README.md @@ -1,3 +1,3 @@ # Asset Administration Shell Controller -This EDC extension implements a custom controller für the 'Asset Administration Shell'-Wrapper. \ No newline at end of file +This EDC extension implements a custom controller for the 'Asset Administration Shell'-Wrapper. \ No newline at end of file diff --git a/edc-extensions/business-partner-validation/README.md b/edc-extensions/business-partner-validation/README.md index 9940e6570..420c25033 100644 --- a/edc-extensions/business-partner-validation/README.md +++ b/edc-extensions/business-partner-validation/README.md @@ -1,147 +1,72 @@ -# Token Claim Validation Extension +# Business Partner Validation Extension -Using the Token Claim Validation Extension it's possible to add configurable validation against `Participants` in -the `ContractDefinition.AccessPolicy`. +Using the Business Partner Validation Extension it's possible to add configurable validation against +Catena-X `Participants` in the `ContractDefinition.AccessPolicy`. -**Why only AccessPolicy?** Because if a custom validation would be used in the `ContractPolicy`, it would be necessary -to send it to the other connector. But nether is it possible to send a generic constraint to other connectors using IDS, -nor is it possible for another connector to enforce a configurable constraint reliable. Hence, the limit -to `AccessPolicy`. +**Why only AccessPolicy?** Because when a custom validation is used in the `ContractPolicy`, it is necessary +to send it to the other connector. But nether is it possible to send a generic constraint using the IDS Protocol, +nor is it possible for another connector to enforce a generic constraint reliable. Hence, the limit +to `AccessPolicy`. This limitation is not technically enforceable, therefore adding Business Partner constraints to the +contract policy simply won't work. -A custom validation against a `Participant` claim can be done in three steps: +This extension is already included in all the Catena-X control-planes and can be used accordingly. +It is recommended to have a basic understanding of the EDC contract/policy domain before using this extension. The +corresponding documentation can +be found in the [EDC GitHub Repository](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector). For a +simplified overview of the EDC domain please have a look at the Catena-X Control Plane documentation. -1. Include this extension -2. Add setting `edc.policy.validation..claim=` -3. Use the claim-validation in a `AtomicConstraint`. - -`AtomicConstraint` example: +Example of business partner constraint: ```json { "leftExpression": { - "value": "" + "value": "BusinessPartner" }, "rightExpression": { - "value": "" + "value": "BPNLCDQ90000X42KU" }, - "operator": "" + "operator": "EQ" } ``` -**Operators** - -- `EQ` is _true_ when the content of a claim is equal to the right value of the `Constraint`. If the claim is not part - of the token or different it is _false_. -- `NEQ` is _true_ when the content of a claim is **not** equal to the right value of the `Constraint`. If the claim is - not part of the token or equal it is _false_. -- `IN` is _true_ when the right value of the `Constraint` is contained in the content of a claim. If the claim is not - part of the token or the right value of the `Constraint` is not part of the claim it is _false_. - -**Please note** that this extension only supports `Constraints` that are part of a `Permission`. - -## Example - -### 1. Include the extension - -#### Gradle KTS - -Project -```bash -implementation(project(":extensions:policy:token-claim-validation")) -``` - -Package -```bash -implementation("org.eclipse.dataspaceconnector:policy-token-claim-validation:0.0.1-SNAPSHOT") -``` - -#### Maven - -```xml - - - org.eclipse.dataspaceconnector - policy-token-claim-validation - 0.0.1-SNAPSHOT - -``` - -### 2. Add setting to the connector configuration +The `leftExpression` must always contain 'BusinessPartner', so that the policy functions of this extension are invoked. +Additionally, the only `operator` that is supported by these policy functions is 'EQ'. Finally, the `rightExpression` +must contain +the Business Partner Number. -In this example the IDS Security Profile of the DAPS token should be validated. - -This is how a DAPS token looks like: +The most simple BPN policy would allow the usage of certain data to a single Business Partner. An example `Policy` is +shown below. In this example the `edctype` properties are added, so that this policy may even be sent to the Data +Management API. ```json { - "typ": "JWT", - "kid": "default", - "alg": "RS256" -} -. -{ - "scopes": [ - "idsc:IDS_CONNECTOR_ATTRIBUTES_ALL" - ], - "aud": "idsc:IDS_CONNECTORS_ALL", - "iss": "https://daps.aisec.fraunhofer.de", - "nbf": 1632982369, - "iat": 1632982369, - "jti": "OTAyNTE1OTMzNTczMDgyMzUxNg==", - "exp": 1632985969, - "securityProfile": "idsc:TRUST_SECURITY_PROFILE", - "referringConnector": "http://consumer-core.demo", - "@type": "ids:DatPayload", - "@context": "https://w3id.org/idsa/contexts/context.jsonld", - "transportCertsSha256": "c15e6558088dbfef215a43d2507bbd124f44fb8facd561c14561a2c1a669d0e0", - "sub": "A5:0C:A5:F0:84:D9:90:BB:BC:D9:57:3A:04:C8:7F:93:ED:97:A2:52:keyid:CB:8C:C7:B6:85:79:A8:23:A6:CB:15:AB:17:50:2F:E6:65:43:5D:E8" + "uid": "", + "prohibitions": [], + "obligations": [], + "permissions": [ + { + "edctype": "dataspaceconnector:permission", + "action": { + "type": "USE" + }, + "constraints": [ + { + "edctype": "AtomicConstraint", + "leftExpression": { + "edctype": "dataspaceconnector:literalexpression", + "value": "BusinessPartnerNumber" + }, + "rightExpression": { + "edctype": "dataspaceconnector:literalexpression", + "value": "" + }, + "operator": "EQ" + } + ] + } + ] } -. - ``` -Create a new EDC validation function by adding this to the settings: - -```bash -edc.policy.validation.ids-security-profile.claim=securityProfile -``` - -### 3. Use the custom constraint validation in a `ContractDefinition` - -`ContractDefinition` as JSON (simplified): - -```json -{ - "id": "98c9c8b4-532f-45d3-a737-7f5a56c15b3e", - "accessPolicy": { - "uid": "5cc32689-f258-44f6-8821-acf230804a41", - "permissions": [ - { - "target": "a4be1bbb-9610-4a2b-af29-50bb7e9a2fbc", - "action": { - "type": "USE" - }, - "constraints": [ - { - "leftExpression": { - "value": "ids-security-profile" - }, - "rightExpression": { - "value": "idsc:TRUST_SECURITY_PROFILE" - }, - "operator": "EQ" - } - ], - "duties": [] - } - ], - "prohibitions": [], - "obligations": [] - }, - "contractPolicy": { - }, - "selectorExpression": { - "criteria": [] - } -} -``` +The business partner number of another connector is part of the DAPS token. Once a BPN constraint is used in an access +policy the connector checks the token before sending out contract offers. \ No newline at end of file diff --git a/edc-extensions/postgresql-migration/README.md b/edc-extensions/postgresql-migration/README.md new file mode 100644 index 000000000..d96c2af5e --- /dev/null +++ b/edc-extensions/postgresql-migration/README.md @@ -0,0 +1,9 @@ +# Postgresql SQL Migration Extension + +This extension applies SQL migrations to + +* the asset-index +* the contract-definition store +* contract-negotiation store +* policy store +* transfer-process store From 91d0e1a0f4acd04a4fb6c9e0075230809efe0e7b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Apr 2022 15:39:38 +0200 Subject: [PATCH 028/433] Bump mikefarah/yq from 4.24.2 to 4.24.5 (#30) Bumps [mikefarah/yq](https://github.com/mikefarah/yq) from 4.24.2 to 4.24.5. - [Release notes](https://github.com/mikefarah/yq/releases) - [Changelog](https://github.com/mikefarah/yq/blob/master/release_notes.txt) - [Commits](https://github.com/mikefarah/yq/compare/v4.24.2...v4.24.5) --- updated-dependencies: - dependency-name: mikefarah/yq dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/draft-new-release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index c498a7dd8..ce5bc7c51 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -43,7 +43,7 @@ jobs: GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} - name: Bump version in deployment/helm - uses: mikefarah/yq@v4.24.2 + uses: mikefarah/yq@v4.24.5 with: cmd: |- find deployment/helm -name Chart.yaml | xargs -n1 yq -i '.appVersion = "${{ github.event.inputs.version }}" | .version = "${{ github.event.inputs.version }}"' From c42534e0cf68635e99111b26799bf880f0007c0c Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Wed, 20 Apr 2022 10:32:31 +0200 Subject: [PATCH 029/433] Rename project to product-edc (#32) --- .github/workflows/build.yaml | 8 ++++---- CHANGELOG.md | 4 ++-- deployment/helm/edc-controlplane/Chart.yaml | 2 +- deployment/helm/edc-controlplane/values.yaml | 8 ++++---- deployment/helm/edc-dataplane/Chart.yaml | 2 +- deployment/helm/edc-dataplane/values.yaml | 2 +- edc-controlplane/pom.xml | 2 +- edc-dataplane/pom.xml | 2 +- edc-extensions/pom.xml | 2 +- pom.xml | 6 +++--- 10 files changed, 19 insertions(+), 19 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index e5c4b1cd5..edc9d44ae 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -68,7 +68,7 @@ jobs: uses: docker/metadata-action@v3 with: images: | - ghcr.io/catenax-ng/catena-x-edc/edc-controlplane-memory + ghcr.io/catenax-ng/product-edc/edc-controlplane-memory tags: | type=ref,event=branch type=ref,event=pr @@ -103,7 +103,7 @@ jobs: uses: docker/metadata-action@v3 with: images: | - ghcr.io/catenax-ng/catena-x-edc/edc-controlplane-cosmosdb + ghcr.io/catenax-ng/product-edc/edc-controlplane-cosmosdb tags: | type=ref,event=branch type=ref,event=pr @@ -138,7 +138,7 @@ jobs: uses: docker/metadata-action@v3 with: images: | - ghcr.io/catenax-ng/catena-x-edc/edc-controlplane-postgresql + ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql tags: | type=ref,event=branch type=ref,event=pr @@ -173,7 +173,7 @@ jobs: uses: docker/metadata-action@v3 with: images: | - ghcr.io/catenax-ng/catena-x-edc/edc-dataplane + ghcr.io/catenax-ng/product-edc/edc-dataplane tags: | type=ref,event=branch type=ref,event=pr diff --git a/CHANGELOG.md b/CHANGELOG.md index fe4d06868..ee13d7841 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.0.1] - 2022-03-29 -[Unreleased]: https://github.com/catenax-ng/catena-x-edc/compare/0.0.1...HEAD +[Unreleased]: https://github.com/catenax-ng/product-edc/compare/0.0.1...HEAD -[0.0.1]: https://github.com/catenax-ng/catena-x-edc/compare/ea573b6816bcbdbf2b1a4416652e551517f893d2...0.0.1 +[0.0.1]: https://github.com/catenax-ng/product-edc/compare/ea573b6816bcbdbf2b1a4416652e551517f893d2...0.0.1 diff --git a/deployment/helm/edc-controlplane/Chart.yaml b/deployment/helm/edc-controlplane/Chart.yaml index 27ed4840c..e10cf6dc0 100644 --- a/deployment/helm/edc-controlplane/Chart.yaml +++ b/deployment/helm/edc-controlplane/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v2 name: edc-controlplane description: EDC Control-Plane -home: https://github.com/catenax-ng/catena-x-edc/deployment/helm/edc-controlplane +home: https://github.com/catenax-ng/product-edc/deployment/helm/edc-controlplane type: application appVersion: "0.0.1" version: 0.0.1 diff --git a/deployment/helm/edc-controlplane/values.yaml b/deployment/helm/edc-controlplane/values.yaml index 11dc2d8a2..03a7d17a7 100644 --- a/deployment/helm/edc-controlplane/values.yaml +++ b/deployment/helm/edc-controlplane/values.yaml @@ -8,11 +8,11 @@ image: ## ## Which derivate of the edc controlplane to use. ## One of: - ## * ghcr.io/catenax-ng/catena-x-edc/edc-controlplane-memory - ## * ghcr.io/catenax-ng/catena-x-edc/edc-controlplane-postgresql - ## * ghcr.io/catenax-ng/catena-x-edc/edc-controlplane-cosmosdb + ## * ghcr.io/catenax-ng/product-edc/edc-controlplane-memory + ## * ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql + ## * ghcr.io/catenax-ng/product-edc/edc-controlplane-cosmosdb ## - repository: ghcr.io/catenax-ng/catena-x-edc/edc-controlplane-memory + repository: ghcr.io/catenax-ng/product-edc/edc-controlplane-memory pullPolicy: IfNotPresent ## ## Overrides the image tag whose default is the chart appVersion. diff --git a/deployment/helm/edc-dataplane/Chart.yaml b/deployment/helm/edc-dataplane/Chart.yaml index 7aa03c35a..feaed85d3 100644 --- a/deployment/helm/edc-dataplane/Chart.yaml +++ b/deployment/helm/edc-dataplane/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v2 name: edc-dataplane description: EDC Data-Plane -home: https://github.com/catenax-ng/catena-x-edc/deployment/helm/edc-dataplane +home: https://github.com/catenax-ng/product-edc/deployment/helm/edc-dataplane type: application appVersion: "0.0.1" version: 0.0.1 diff --git a/deployment/helm/edc-dataplane/values.yaml b/deployment/helm/edc-dataplane/values.yaml index 6d4b9a4b5..6129ea6a4 100644 --- a/deployment/helm/edc-dataplane/values.yaml +++ b/deployment/helm/edc-dataplane/values.yaml @@ -8,7 +8,7 @@ image: ## ## Which edc-dataplane container image to use. ## - repository: ghcr.io/catenax-ng/catena-x-edc/edc-dataplane + repository: ghcr.io/catenax-ng/product-edc/edc-dataplane pullPolicy: IfNotPresent ## ## Overrides the image tag whose default is the chart appVersion. diff --git a/edc-controlplane/pom.xml b/edc-controlplane/pom.xml index 403363d19..f348fe98b 100644 --- a/edc-controlplane/pom.xml +++ b/edc-controlplane/pom.xml @@ -19,7 +19,7 @@ net.catenax.edc - catena-x-edc-parent + product-edc-parent 0.0.2-SNAPSHOT diff --git a/edc-dataplane/pom.xml b/edc-dataplane/pom.xml index da50aa477..25dfe6672 100644 --- a/edc-dataplane/pom.xml +++ b/edc-dataplane/pom.xml @@ -19,7 +19,7 @@ net.catenax.edc - catena-x-edc-parent + product-edc-parent 0.0.2-SNAPSHOT edc-dataplane diff --git a/edc-extensions/pom.xml b/edc-extensions/pom.xml index d311294eb..a607c5bb1 100644 --- a/edc-extensions/pom.xml +++ b/edc-extensions/pom.xml @@ -17,7 +17,7 @@ net.catenax.edc - catena-x-edc-parent + product-edc-parent 0.0.2-SNAPSHOT diff --git a/pom.xml b/pom.xml index f181ade56..508ec35ad 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ 4.0.0 net.catenax.edc - catena-x-edc-parent + product-edc-parent 0.0.2-SNAPSHOT pom @@ -50,7 +50,7 @@ github-catenax-ng Catena-X NG: Github Packages - https://maven.pkg.github.com/catenax-ng/catena-x-edc + https://maven.pkg.github.com/catenax-ng/product-edc
@@ -76,7 +76,7 @@ github-catenax-ng Catena-X NG: Github Packages - https://maven.pkg.github.com/catenax-ng/catena-x-edc + https://maven.pkg.github.com/catenax-ng/product-edc From dda4589979c58f86b46a448fe6e1d20979a5ab75 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Mon, 25 Apr 2022 11:21:00 +0200 Subject: [PATCH 030/433] Bump edc to 79b58b8 (#33) Signed-off-by: Denis Neuling --- .github/workflows/build.yaml | 2 +- edc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index edc9d44ae..912a5c4a5 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -39,7 +39,7 @@ jobs: [ ! -d "edc" ] && git submodule add https://github.com/eclipse-dataspaceconnector/DataSpaceConnector.git edc git submodule update --init git -C edc fetch --all - git -C edc checkout 045c9a6c1f8dcde78dfcb0480cf49643cdb65401 + git -C edc checkout 79b58b8a8fe7b9183f3e678bd880db3c6069f97d - name: Set up JDK 11 uses: actions/setup-java@v3.1.1 diff --git a/edc b/edc index 045c9a6c1..79b58b8a8 160000 --- a/edc +++ b/edc @@ -1 +1 @@ -Subproject commit 045c9a6c1f8dcde78dfcb0480cf49643cdb65401 +Subproject commit 79b58b8a8fe7b9183f3e678bd880db3c6069f97d From 03c1454fd6eebc75deb955a56fc2b4d501882d28 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Tue, 26 Apr 2022 14:50:10 +0200 Subject: [PATCH 031/433] Extend helm notes (#35) --- .github/workflows/helm-lint.yaml | 57 ++++++++++++++ chart_schema.yaml | 38 ++++++++++ ct.yaml | 4 + deployment/helm/README.md | 5 ++ deployment/helm/edc-controlplane/Chart.yaml | 2 + .../helm/edc-controlplane/templates/NOTES.txt | 74 +++++++++++++++---- .../templates/deployment.yaml | 6 +- .../edc-controlplane/templates/ingress.yaml | 20 +++-- deployment/helm/edc-controlplane/values.yaml | 6 +- deployment/helm/edc-dataplane/Chart.yaml | 2 + .../helm/edc-dataplane/templates/NOTES.txt | 64 ++++++++++++---- .../edc-dataplane/templates/deployment.yaml | 6 +- .../helm/edc-dataplane/templates/ingress.yaml | 20 +++-- deployment/helm/edc-dataplane/values.yaml | 15 ++-- lintconf.yaml | 42 +++++++++++ 15 files changed, 293 insertions(+), 68 deletions(-) create mode 100644 .github/workflows/helm-lint.yaml create mode 100644 chart_schema.yaml create mode 100644 ct.yaml create mode 100644 deployment/helm/README.md create mode 100644 lintconf.yaml diff --git a/.github/workflows/helm-lint.yaml b/.github/workflows/helm-lint.yaml new file mode 100644 index 000000000..8d1dcbb3c --- /dev/null +++ b/.github/workflows/helm-lint.yaml @@ -0,0 +1,57 @@ +--- +name: "Lint helm charts" + +on: + push: + branches: + - main + - develop + tags: + - '[0-9]+.[0-9]+.[0-9]+' + pull_request: + branches: + - '*' + +jobs: + helm-lint: + runs-on: ubuntu-latest + steps: + ############## + ### Set-Up ### + ############## + - + name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - + name: helm (setup) + uses: azure/setup-helm@v1 + with: + version: v3.8.1 + - + name: python (setup) + uses: actions/setup-python@v2 + with: + python-version: 3.7 + - + name: chart-testing (setup) + uses: helm/chart-testing-action@v2.2.1 + ##################### + ### Chart Testing ### + ##################### + - + name: chart-testing (list-changed) + id: list-changed + run: | + changed=$(ct list-changed --config ct.yaml --target-branch develop) + if [[ -n "$changed" ]]; then + echo "::set-output name=changed::true" + fi + - + name: chart-testing (lint) + if: steps.list-changed.outputs.changed == 'true' + run: | + ct lint \ + --config ct.yaml \ + --all diff --git a/chart_schema.yaml b/chart_schema.yaml new file mode 100644 index 000000000..6581ef7e8 --- /dev/null +++ b/chart_schema.yaml @@ -0,0 +1,38 @@ +--- +name: str() +home: str(required=False) +version: str() +apiVersion: str() +appVersion: any(str(), num(), required=False) +description: str(required=False) +keywords: list(str(), required=False) +sources: list(str(), required=False) +maintainers: list(include('maintainer'), required=False) +dependencies: list(include('dependency'), required=False) +icon: str(required=False) +engine: str(required=False) +condition: str(required=False) +tags: str(required=False) +deprecated: bool(required=False) +kubeVersion: str(required=False) +annotations: map(str(), str(), required=False) +type: str(required=False) +--- +maintainer: + name: str(required=False) + email: str(required=False) + url: str(required=False) +--- +dependency: + name: str() + version: str() + repository: str(required=False) + condition: str(required=False) + tags: list(str(), required=False) + enabled: bool(required=False) + import-values: any(list(str()), list(include('import-value')), required=False) + alias: str(required=False) +--- +import-value: + child: str() + parent: str() diff --git a/ct.yaml b/ct.yaml new file mode 100644 index 000000000..4c716ff7d --- /dev/null +++ b/ct.yaml @@ -0,0 +1,4 @@ +--- +validate-maintainers: false +chart-dirs: + - deployment/helm diff --git a/deployment/helm/README.md b/deployment/helm/README.md new file mode 100644 index 000000000..c5f541187 --- /dev/null +++ b/deployment/helm/README.md @@ -0,0 +1,5 @@ +# Chart Linting + +Chart linting is performed using [helm's CT tool](https://github.com/helm/chart-testing). + +Configuration files for [CT](../../ct.yaml), [Yamale](../../chart_schema.yaml) and [Yamllint](../../lintconf.yaml) have been provided. diff --git a/deployment/helm/edc-controlplane/Chart.yaml b/deployment/helm/edc-controlplane/Chart.yaml index e10cf6dc0..0661b813a 100644 --- a/deployment/helm/edc-controlplane/Chart.yaml +++ b/deployment/helm/edc-controlplane/Chart.yaml @@ -1,3 +1,4 @@ +--- apiVersion: v2 name: edc-controlplane description: EDC Control-Plane @@ -5,3 +6,4 @@ home: https://github.com/catenax-ng/product-edc/deployment/helm/edc-controlplane type: application appVersion: "0.0.1" version: 0.0.1 +maintainers: [] diff --git a/deployment/helm/edc-controlplane/templates/NOTES.txt b/deployment/helm/edc-controlplane/templates/NOTES.txt index b7cbf4507..fd365cb24 100644 --- a/deployment/helm/edc-controlplane/templates/NOTES.txt +++ b/deployment/helm/edc-controlplane/templates/NOTES.txt @@ -1,22 +1,66 @@ -1. Get the application URL by running these commands: + +CHART NAME: {{ .Chart.Name }} +CHART VERSION: {{ .Chart.Version }} +APP VERSION: {{ .Chart.AppVersion }} + +Logs can be accessed by running this command: + + kubectl logs --tail 100 -f \ + --namespace {{ .Release.Namespace }} + -l "app.kubernetes.io/name={{ include "edc-controlplane.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" + {{- if .Values.ingress.enabled }} -{{- range $host := .Values.ingress.hosts }} - {{- range .paths }} - http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + +Following ingress URLS are available: + {{- range $name, $mapping := .Values.edc.endpoints }} + {{- if $mapping.ingress }} + Visit http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $.Values.ingress.hostname }}{{ $mapping.path }} to access the {{ $name }} api {{- end }} -{{- end }} + {{- end }} + {{- else if contains "NodePort" .Values.service.type }} - export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "edc-controlplane.fullname" . }}) +Get the application URLs by running these commands: export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") - echo http://$NODE_IP:$NODE_PORT -{{- else if contains "LoadBalancer" .Values.service.type }} - NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "edc-controlplane.fullname" . }}' - export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "edc-controlplane.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") - echo http://$SERVICE_IP:{{ .Values.service.port }} + + export NODE_PORT_DEFAULT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "edc-controlplane.fullname" . }}}") + export NODE_PORT_DATA=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[1].nodePort}" services {{ include "edc-controlplane.fullname" . }}}") + export NODE_PORT_VALIDATION=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[2].nodePort}" services {{ include "edc-controlplane.fullname" . }}}") + export NODE_PORT_CONTROL=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[3].nodePort}" services {{ include "edc-controlplane.fullname" . }}}") + export NODE_PORT_IDS=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[4].nodePort}" services {{ include "edc-controlplane.fullname" . }}}") + export NODE_PORT_METRICS=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[5].nodePort}" services {{ include "edc-controlplane.fullname" . }}}") + + echo "Visit http://$NODE_IP:$NODE_PORT_DEFAULT to access the default api" + echo "Visit http://$NODE_IP:$NODE_PORT_DATA to access the data management api" + echo "Visit http://$NODE_IP:$NODE_PORT_VALIDATION to access the data transfer validation api" + echo "Visit http://$NODE_IP:$NODE_PORT_CONTROL to access the control api" + echo "Visit http://$NODE_IP:$NODE_PORT_IDS to access the IDS api" + echo "Visit http://$NODE_IP:$NODE_PORT_METRICS to access the metrics api" + {{- else if contains "ClusterIP" .Values.service.type }} +Get the application URL by running these commands: + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "edc-controlplane.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") - export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") - echo "Visit http://127.0.0.1:8080 to use your application" - kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT + + export CONTAINER_PORT_DEFAULT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + export CONTAINER_PORT_DATA=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[1].containerPort}") + export CONTAINER_PORT_VALIDATION=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[2].containerPort}") + export CONTAINER_PORT_CONTROL=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[3].containerPort}") + export CONTAINER_PORT_IDS=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[4].containerPort}") + export CONTAINER_PORT_METRICS=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[5].containerPort}") + + echo "Visit http://127.0.0.1:8080 to access the default api" + echo "Visit http://127.0.0.1:8182 to access the data management api" + echo "Visit http://127.0.0.1:8182 to access the data transfer validation api" + echo "Visit http://127.0.0.1:9999 to access the control api" + echo "Visit http://127.0.0.1:8282 to access the IDS api" + echo "Visit http://127.0.0.1:9090 to access the metrics api" + + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME \ + 8080:$CONTAINER_PORT_DEFAULT \ + 8182:$CONTAINER_PORT_DATA \ + 8182:$CONTAINER_PORT_VALIDATION \ + 9999:$CONTAINER_PORT_CONTROL \ + 8282:$CONTAINER_PORT_IDS \ + 9090:$CONTAINER_PORT_METRICS + {{- end }} diff --git a/deployment/helm/edc-controlplane/templates/deployment.yaml b/deployment/helm/edc-controlplane/templates/deployment.yaml index 0d403d251..99824c75e 100644 --- a/deployment/helm/edc-controlplane/templates/deployment.yaml +++ b/deployment/helm/edc-controlplane/templates/deployment.yaml @@ -43,15 +43,15 @@ spec: - name: default containerPort: {{ .Values.edc.endpoints.default.port }} protocol: TCP - - name: control - containerPort: {{ .Values.edc.endpoints.control.port }} - protocol: TCP - name: data containerPort: {{ .Values.edc.endpoints.data.port }} protocol: TCP - name: validation containerPort: {{ .Values.edc.endpoints.validation.port }} protocol: TCP + - name: control + containerPort: {{ .Values.edc.endpoints.control.port }} + protocol: TCP - name: ids containerPort: {{ .Values.edc.endpoints.ids.port }} protocol: TCP diff --git a/deployment/helm/edc-controlplane/templates/ingress.yaml b/deployment/helm/edc-controlplane/templates/ingress.yaml index c9014fbbd..b62ee2481 100644 --- a/deployment/helm/edc-controlplane/templates/ingress.yaml +++ b/deployment/helm/edc-controlplane/templates/ingress.yaml @@ -1,4 +1,4 @@ -{{- if .Values.ingress.enabled -}} +{{- if .Values.ingress.enabled }} --- {{- $fullName := include "edc-controlplane.fullname" . -}} {{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} @@ -6,11 +6,11 @@ {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} {{- end }} {{- end }} -{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion }} apiVersion: networking.k8s.io/v1 -{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion }} apiVersion: networking.k8s.io/v1beta1 -{{- else -}} +{{- else }} apiVersion: extensions/v1beta1 {{- end }} kind: Ingress @@ -22,11 +22,11 @@ metadata: {{- with .Values.ingress.annotations }} {{- toYaml . | nindent 4 }} {{- end }} - {{- if .Values.ingress.certManager -}} - {{- if .Values.ingress.certManager.issuer -}} + {{- if .Values.ingress.certManager }} + {{- if .Values.ingress.certManager.issuer }} cert-manager.io/issuer: {{ .Values.ingress.certManager.issuer }} {{- end }} - {{- if .Values.ingress.certManager.clusterIssuer -}} + {{- if .Values.ingress.certManager.clusterIssuer }} cert-manager.io/cluster-issuer: {{ .Values.ingress.certManager.clusterIssuer }} {{- end }} {{- end }} @@ -45,12 +45,10 @@ spec: - host: {{ .Values.ingress.hostname }} http: paths: - {{ range $name, $mapping := .Values.edc.endpoints }} - {{ if $mapping.ingress -}} + {{- range $name, $mapping := .Values.edc.endpoints }} + {{- if $mapping.ingress }} - path: {{ $mapping.path }} - {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} pathType: Prefix - {{- end }} backend: {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} service: diff --git a/deployment/helm/edc-controlplane/values.yaml b/deployment/helm/edc-controlplane/values.yaml index 03a7d17a7..3ba6adc41 100644 --- a/deployment/helm/edc-controlplane/values.yaml +++ b/deployment/helm/edc-controlplane/values.yaml @@ -1,3 +1,4 @@ +--- # Default values for edc-controlplane. # This is a YAML-formatted file. # Declare variables to be passed into your templates. @@ -129,13 +130,13 @@ ingress: ## ## Set to true to enable ingress record generation ## - enabled: true + enabled: false ## ## Defines the ingress class to use https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class ## className: "" ## - ## En + ## Enables TLS on the ingress resource ## tls: false @@ -335,4 +336,3 @@ configuration: # edc.web.rest.cors.headers= # edc.web.rest.cors.methods= # edc.web.rest.cors.origins= - diff --git a/deployment/helm/edc-dataplane/Chart.yaml b/deployment/helm/edc-dataplane/Chart.yaml index feaed85d3..82559407b 100644 --- a/deployment/helm/edc-dataplane/Chart.yaml +++ b/deployment/helm/edc-dataplane/Chart.yaml @@ -1,3 +1,4 @@ +--- apiVersion: v2 name: edc-dataplane description: EDC Data-Plane @@ -5,3 +6,4 @@ home: https://github.com/catenax-ng/product-edc/deployment/helm/edc-dataplane type: application appVersion: "0.0.1" version: 0.0.1 +maintainers: [] diff --git a/deployment/helm/edc-dataplane/templates/NOTES.txt b/deployment/helm/edc-dataplane/templates/NOTES.txt index f0cb7547c..5197611e0 100644 --- a/deployment/helm/edc-dataplane/templates/NOTES.txt +++ b/deployment/helm/edc-dataplane/templates/NOTES.txt @@ -1,22 +1,56 @@ -1. Get the application URL by running these commands: + +CHART NAME: {{ .Chart.Name }} +CHART VERSION: {{ .Chart.Version }} +APP VERSION: {{ .Chart.AppVersion }} + +Logs can be accessed by running this command: + + kubectl logs --tail 100 -f \ + --namespace {{ .Release.Namespace }} + -l "app.kubernetes.io/name={{ include "edc-dataplane.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" + {{- if .Values.ingress.enabled }} -{{- range $host := .Values.ingress.hosts }} - {{- range .paths }} - http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + +Following ingress URLS are available: + {{- range $name, $mapping := .Values.edc.endpoints }} + {{- if $mapping.ingress }} + Visit http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $.Values.ingress.hostname }}{{ $mapping.path }} to access the {{ $name }} api {{- end }} -{{- end }} + {{- end }} + {{- else if contains "NodePort" .Values.service.type }} - export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "edc-dataplane.fullname" . }}) +Get the application URLs by running these commands: export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") - echo http://$NODE_IP:$NODE_PORT -{{- else if contains "LoadBalancer" .Values.service.type }} - NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "edc-dataplane.fullname" . }}' - export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "edc-dataplane.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") - echo http://$SERVICE_IP:{{ .Values.service.port }} + + export NODE_PORT_DEFAULT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "edc-dataplane.fullname" . }}}") + export NODE_PORT_PUBLIC=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[1].nodePort}" services {{ include "edc-dataplane.fullname" . }}}") + export NODE_PORT_CONTROL=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[2].nodePort}" services {{ include "edc-dataplane.fullname" . }}}") + export NODE_PORT_METRICS=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[3].nodePort}" services {{ include "edc-dataplane.fullname" . }}}") + + echo "Visit http://$NODE_IP:$NODE_PORT_DEFAULT to access the default api" + echo "Visit http://$NODE_IP:$NODE_PORT_PUBLIC to access the public data transfer api" + echo "Visit http://$NODE_IP:$NODE_PORT_CONTROL to access the control api" + echo "Visit http://$NODE_IP:$NODE_PORT_METRICS to access the metrics api" + {{- else if contains "ClusterIP" .Values.service.type }} +Get the application URL by running these commands: + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "edc-dataplane.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") - export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") - echo "Visit http://127.0.0.1:8080 to use your application" - kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT + + export CONTAINER_PORT_DEFAULT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + export CONTAINER_PORT_PUBLIC=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[1].containerPort}") + export CONTAINER_PORT_CONTROL=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[2].containerPort}") + export CONTAINER_PORT_METRICS=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[3].containerPort}") + + echo "Visit http://127.0.0.1:8080 to access the default api" + echo "Visit http://127.0.0.1:8185 to access the public data transfer api" + echo "Visit http://127.0.0.1:9999 to access the control api" + echo "Visit http://127.0.0.1:9090 to access the metrics api" + + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME \ + 8080:$CONTAINER_PORT_DEFAULT \ + 8185:$CONTAINER_PORT_PUBLIC \ + 9999:$CONTAINER_PORT_CONTROL \ + 9090:$CONTAINER_PORT_METRICS + {{- end }} diff --git a/deployment/helm/edc-dataplane/templates/deployment.yaml b/deployment/helm/edc-dataplane/templates/deployment.yaml index ee632461e..86a5eec9a 100644 --- a/deployment/helm/edc-dataplane/templates/deployment.yaml +++ b/deployment/helm/edc-dataplane/templates/deployment.yaml @@ -43,12 +43,12 @@ spec: - name: default containerPort: {{ .Values.edc.endpoints.default.port }} protocol: TCP - - name: control - containerPort: {{ .Values.edc.endpoints.control.port }} - protocol: TCP - name: public containerPort: {{ .Values.edc.endpoints.public.port }} protocol: TCP + - name: control + containerPort: {{ .Values.edc.endpoints.control.port }} + protocol: TCP - name: metrics containerPort: {{ .Values.edc.endpoints.metrics.port }} protocol: TCP diff --git a/deployment/helm/edc-dataplane/templates/ingress.yaml b/deployment/helm/edc-dataplane/templates/ingress.yaml index 58174d98f..cdcafb45a 100644 --- a/deployment/helm/edc-dataplane/templates/ingress.yaml +++ b/deployment/helm/edc-dataplane/templates/ingress.yaml @@ -1,4 +1,4 @@ -{{- if .Values.ingress.enabled -}} +{{- if .Values.ingress.enabled }} --- {{- $fullName := include "edc-dataplane.fullname" . -}} {{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} @@ -6,11 +6,11 @@ {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} {{- end }} {{- end }} -{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion }} apiVersion: networking.k8s.io/v1 -{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion }} apiVersion: networking.k8s.io/v1beta1 -{{- else -}} +{{- else }} apiVersion: extensions/v1beta1 {{- end }} kind: Ingress @@ -22,11 +22,11 @@ metadata: {{- with .Values.ingress.annotations }} {{- toYaml . | nindent 4 }} {{- end }} - {{- if .Values.ingress.certManager -}} - {{- if .Values.ingress.certManager.issuer -}} + {{- if .Values.ingress.certManager }} + {{- if .Values.ingress.certManager.issuer }} cert-manager.io/issuer: {{ .Values.ingress.certManager.issuer }} {{- end }} - {{- if .Values.ingress.certManager.clusterIssuer -}} + {{- if .Values.ingress.certManager.clusterIssuer }} cert-manager.io/cluster-issuer: {{ .Values.ingress.certManager.clusterIssuer }} {{- end }} {{- end }} @@ -45,12 +45,10 @@ spec: - host: {{ .Values.ingress.hostname }} http: paths: - {{ range $name, $mapping := .Values.edc.endpoints }} - {{ if $mapping.ingress -}} + {{- range $name, $mapping := .Values.edc.endpoints }} + {{- if $mapping.ingress }} - path: {{ $mapping.path }} - {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} pathType: Prefix - {{- end }} backend: {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} service: diff --git a/deployment/helm/edc-dataplane/values.yaml b/deployment/helm/edc-dataplane/values.yaml index 6129ea6a4..08cb3c9de 100644 --- a/deployment/helm/edc-dataplane/values.yaml +++ b/deployment/helm/edc-dataplane/values.yaml @@ -1,3 +1,4 @@ +--- # Default values for edc-dataplane. # This is a YAML-formatted file. # Declare variables to be passed into your templates. @@ -86,16 +87,16 @@ edc: port: "8080" path: /api ingress: false - # Control API - control: - port: "9999" - path: /api/dataplane/control - ingress: true # Public endpoint for data transfer public: port: "8185" path: /api/public ingress: true + # Control API + control: + port: "9999" + path: /api/dataplane/control + ingress: true # Prometheus endpoint metrics: port: "9090" @@ -115,13 +116,13 @@ ingress: ## ## Set to true to enable ingress record generation ## - enabled: true + enabled: false ## ## Defines the ingress class to use https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class ## className: "" ## - ## En + ## Enables TLS on the ingress resource ## tls: false diff --git a/lintconf.yaml b/lintconf.yaml new file mode 100644 index 000000000..8fd25b1f8 --- /dev/null +++ b/lintconf.yaml @@ -0,0 +1,42 @@ +--- +rules: + braces: + min-spaces-inside: 0 + max-spaces-inside: 0 + min-spaces-inside-empty: -1 + max-spaces-inside-empty: -1 + brackets: + min-spaces-inside: 0 + max-spaces-inside: 0 + min-spaces-inside-empty: -1 + max-spaces-inside-empty: -1 + colons: + max-spaces-before: 0 + max-spaces-after: 1 + commas: + max-spaces-before: 0 + min-spaces-after: 1 + max-spaces-after: 1 + comments: + require-starting-space: true + min-spaces-from-content: 2 + document-end: disable + document-start: true # No --- to start a file + empty-lines: + max: 2 + max-start: 0 + max-end: 0 + hyphens: + max-spaces-after: 1 + indentation: + spaces: consistent + indent-sequences: whatever # - list indentation will handle both indentation and without + check-multi-line-strings: false + key-duplicates: enable + line-length: disable # Lines can be any length + new-line-at-end-of-file: enable + new-lines: + type: unix + trailing-spaces: enable + truthy: + level: warning From 8e60ed8d28914792006f69bb28b09235da4bb5d4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Apr 2022 16:07:00 +0200 Subject: [PATCH 032/433] Bump actions/setup-python from 2 to 3 (#36) --- .github/workflows/helm-lint.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/helm-lint.yaml b/.github/workflows/helm-lint.yaml index 8d1dcbb3c..03af669af 100644 --- a/.github/workflows/helm-lint.yaml +++ b/.github/workflows/helm-lint.yaml @@ -31,7 +31,7 @@ jobs: version: v3.8.1 - name: python (setup) - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: 3.7 - From d12a999957ba86e414691d2ed12e96d9d3f84555 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Apr 2022 16:07:06 +0200 Subject: [PATCH 033/433] Bump azure/setup-helm from 1 to 2.1 (#37) --- .github/workflows/helm-lint.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/helm-lint.yaml b/.github/workflows/helm-lint.yaml index 03af669af..f11844fde 100644 --- a/.github/workflows/helm-lint.yaml +++ b/.github/workflows/helm-lint.yaml @@ -26,7 +26,7 @@ jobs: fetch-depth: 0 - name: helm (setup) - uses: azure/setup-helm@v1 + uses: azure/setup-helm@v2.1 with: version: v3.8.1 - From 69d1ee85771f091eb36d0d6641abdadb2b145b23 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Apr 2022 16:07:10 +0200 Subject: [PATCH 034/433] Bump actions/setup-java from 3.1.1 to 3.2.0 (#38) --- .github/workflows/build.yaml | 2 +- .github/workflows/draft-new-release.yaml | 2 +- .github/workflows/publish-new-release.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 912a5c4a5..7c6f495d0 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -42,7 +42,7 @@ jobs: git -C edc checkout 79b58b8a8fe7b9183f3e678bd880db3c6069f97d - name: Set up JDK 11 - uses: actions/setup-java@v3.1.1 + uses: actions/setup-java@v3.2.0 with: java-version: '11' distribution: 'adopt' diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index ce5bc7c51..c67a51228 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -29,7 +29,7 @@ jobs: git config user.email noreply@github.com - name: Set up JDK 11 - uses: actions/setup-java@v3.1.1 + uses: actions/setup-java@v3.2.0 with: java-version: '11' distribution: 'adopt' diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index ac5fc29ee..5a8352ed0 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -61,7 +61,7 @@ jobs: prerelease: false - name: Set up JDK 11 - uses: actions/setup-java@v3.1.1 + uses: actions/setup-java@v3.2.0 with: java-version: '11' distribution: 'adopt' From 65ead31b2ac2d0c458f0628ce998ee46d9745ebd Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Tue, 26 Apr 2022 16:57:39 +0200 Subject: [PATCH 035/433] Update dependabot configuration to cover edc git submodule (#39) --- .github/dependabot.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 062340eb8..fe7de904a 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,6 +1,15 @@ --- version: 2 updates: + - + package-ecosystem: "gitsubmodule" + target-branch: develop + directory: "/" + labels: + - "dependabot" + - "edc" + schedule: + interval: "daily" - package-ecosystem: "github-actions" target-branch: develop From e8945a01581f125a4c6b16c47cca4addad17ccd0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Apr 2022 17:06:02 +0200 Subject: [PATCH 036/433] Bump postgresql from 42.2.6 to 42.3.4 (#45) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 508ec35ad..98dcce340 100644 --- a/pom.xml +++ b/pom.xml @@ -38,7 +38,7 @@ 0.0.1-SNAPSHOT 1.2.0 3.0.0 - 42.2.6 + 42.3.4 8.5.5 5.8.2 1.8.2 From d1d56f0cd4f2e6a2f1bdecd8dd27c3fc6857dbde Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Apr 2022 17:06:33 +0200 Subject: [PATCH 037/433] Bump spotless-maven-plugin from 1.31.3 to 2.22.2 (#44) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 98dcce340..4faa27cef 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ 3.2.4 1.4.13 - 1.31.3 + 2.22.2 0.0.1-SNAPSHOT 1.2.0 From 105ab1727d6c0fce180065304570aec1004cc0aa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Apr 2022 17:06:59 +0200 Subject: [PATCH 038/433] Bump maven-surefire-plugin from 2.19.1 to 2.22.2 (#41) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4faa27cef..b749da878 100644 --- a/pom.xml +++ b/pom.xml @@ -117,7 +117,7 @@ maven-surefire-plugin - 2.19.1 + 2.22.2 org.junit.platform From 97cb9d1b521d7ba3c79b6bb2b83289cad85f081b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Apr 2022 17:12:32 +0200 Subject: [PATCH 039/433] Bump flyway-core from 8.5.5 to 8.5.9 (#42) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b749da878..560c0549f 100644 --- a/pom.xml +++ b/pom.xml @@ -39,7 +39,7 @@ 1.2.0 3.0.0 42.3.4 - 8.5.5 + 8.5.9 5.8.2 1.8.2 1.10.19 From f6a593b556736035fc170afa19ad8376675fb492 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 Apr 2022 09:53:13 +0200 Subject: [PATCH 040/433] Bump junit-platform-surefire-provider from 1.1.0 to 1.3.2 (#43) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 560c0549f..b1e9a6a3b 100644 --- a/pom.xml +++ b/pom.xml @@ -43,7 +43,7 @@ 5.8.2 1.8.2 1.10.19 - 1.1.0 + 1.3.2 From 204f7266585cf13fe8c319bf5b789ae76abe0650 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Wed, 27 Apr 2022 11:45:31 +0200 Subject: [PATCH 041/433] Remove edc checkout step in build workflow (#47) --- .github/workflows/build.yaml | 25 ++++++++--------------- .github/workflows/draft-new-release.yaml | 2 +- .github/workflows/publish-new-release.yml | 2 +- 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 7c6f495d0..8dcc6b50e 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -32,14 +32,7 @@ jobs: with: registry: ghcr.io username: ${{ github.actor }} - password: ${{ secrets.CXNG_GHCR_PAT }} - - - name: Use specific edc version via git submodule # due to lack of release versions in maven central - run: |- - [ ! -d "edc" ] && git submodule add https://github.com/eclipse-dataspaceconnector/DataSpaceConnector.git edc - git submodule update --init - git -C edc fetch --all - git -C edc checkout 79b58b8a8fe7b9183f3e678bd880db3c6069f97d + password: ${{ secrets.GITHUB_TOKEN }} - name: Set up JDK 11 uses: actions/setup-java@v3.2.0 @@ -61,7 +54,7 @@ jobs: ./mvnw -s settings.xml -B -pl .,edc-controlplane/edc-controlplane-memory -am verify env: GITHUB_PACKAGE_USERNAME: ${{ github.actor }} - GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} + GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - name: edc-controlplane-memory Docker Metadata id: edc_controlplane_memory_meta @@ -83,7 +76,7 @@ jobs: file: edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile build-args: | JAR=edc-controlplane/edc-controlplane-memory/target/edc-controlplane-memory.jar - push: ${{ github.event_name != 'pull_request' }} + push: ${{ github.event_name != 'pull_request' && github.repository_owner == 'product-edc' }} tags: ${{ steps.edc_controlplane_memory_meta.outputs.tags }} labels: ${{ steps.edc_controlplane_memory_meta.outputs.labels }} @@ -96,7 +89,7 @@ jobs: ./mvnw -s settings.xml -B -pl .,edc-controlplane/edc-controlplane-cosmosdb -am verify env: GITHUB_PACKAGE_USERNAME: ${{ github.actor }} - GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} + GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - name: edc-controlplane-cosmosdb Docker Metadata id: edc_controlplane_cosmosdb_meta @@ -118,7 +111,7 @@ jobs: file: edc-controlplane/edc-controlplane-cosmosdb/src/main/docker/Dockerfile build-args: | JAR=edc-controlplane/edc-controlplane-cosmosdb/target/edc-controlplane-cosmosdb.jar - push: ${{ github.event_name != 'pull_request' }} + push: ${{ github.event_name != 'pull_request' && github.repository_owner == 'product-edc' }} tags: ${{ steps.edc_controlplane_cosmosdb_meta.outputs.tags }} labels: ${{ steps.edc_controlplane_cosmosdb_meta.outputs.labels }} @@ -131,7 +124,7 @@ jobs: ./mvnw -s settings.xml -B -pl .,edc-controlplane/edc-controlplane-postgresql -am verify env: GITHUB_PACKAGE_USERNAME: ${{ github.actor }} - GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} + GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - name: edc-controlplane-postgresql Docker Metadata id: edc_controlplane_postgresql_meta @@ -153,7 +146,7 @@ jobs: file: edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile build-args: | JAR=edc-controlplane/edc-controlplane-postgresql/target/edc-controlplane-postgresql.jar - push: ${{ github.event_name != 'pull_request' }} + push: ${{ github.event_name != 'pull_request' && github.repository_owner == 'product-edc' }} tags: ${{ steps.edc_controlplane_postgresql_meta.outputs.tags }} labels: ${{ steps.edc_controlplane_postgresql_meta.outputs.labels }} @@ -166,7 +159,7 @@ jobs: ./mvnw -s settings.xml -B -pl .,edc-dataplane -am verify env: GITHUB_PACKAGE_USERNAME: ${{ github.actor }} - GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} + GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - name: edc-dataplane Docker Metadata id: edc_dataplane_meta @@ -188,6 +181,6 @@ jobs: file: edc-dataplane/src/main/docker/Dockerfile build-args: | JAR=edc-dataplane/target/edc-dataplane.jar - push: ${{ github.event_name != 'pull_request' }} + push: ${{ github.event_name != 'pull_request' && github.repository_owner == 'product-edc' }} tags: ${{ steps.edc_dataplane_meta.outputs.tags }} labels: ${{ steps.edc_dataplane_meta.outputs.labels }} diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index c67a51228..349652393 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -40,7 +40,7 @@ jobs: ./mvnw -s settings.xml -B versions:set -DnewVersion=${{ github.event.inputs.version }} -DprocessAllModules env: GITHUB_PACKAGE_USERNAME: ${{ github.actor }} - GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} + GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - name: Bump version in deployment/helm uses: mikefarah/yq@v4.24.5 diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index 5a8352ed0..6ea00ace3 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -70,7 +70,7 @@ jobs: name: Merge main back into develop and set new snapshot version env: GITHUB_PACKAGE_USERNAME: ${{ github.actor }} - GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} + GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} run: | # Prepare git env git config user.name "GitHub actions" From b26dbb97e70444f3c7e96689d0f986c7aa43cc35 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Wed, 27 Apr 2022 13:39:05 +0200 Subject: [PATCH 042/433] Remove junit-platform-surefire-provider due to deprecation (#51) --- pom.xml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/pom.xml b/pom.xml index b1e9a6a3b..561da1c4c 100644 --- a/pom.xml +++ b/pom.xml @@ -43,7 +43,6 @@ 5.8.2 1.8.2 1.10.19 - 1.3.2 @@ -118,13 +117,6 @@ maven-surefire-plugin 2.22.2 - - - org.junit.platform - junit-platform-surefire-provider - ${org.junit.platform.surefire.provider.version} - - From 1f8ca1e1ed13f092be60d3a46e5e193bbe44d336 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 Apr 2022 16:37:54 +0200 Subject: [PATCH 043/433] Bump maven-shade-plugin from 3.2.4 to 3.3.0 (#52) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 561da1c4c..d16b1b70f 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,7 @@ 11 - 3.2.4 + 3.3.0 1.4.13 2.22.2 From e5f3b1eeb7d903a916f8ef21d7b61f8273989e79 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 28 Apr 2022 09:06:15 +0200 Subject: [PATCH 044/433] Bump jakarta.ws.rs-api from 3.0.0 to 3.1.0 (#53) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d16b1b70f..76ca500c2 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,7 @@ 0.0.1-SNAPSHOT 1.2.0 - 3.0.0 + 3.1.0 42.3.4 8.5.9 5.8.2 From f2784c3f60d5fadca388a231abc03a07374eaf90 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 28 Apr 2022 09:16:17 +0200 Subject: [PATCH 045/433] Bump spotless-maven-plugin from 2.22.2 to 2.22.3 (#57) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 76ca500c2..087159859 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ 3.3.0 1.4.13 - 2.22.2 + 2.22.3 0.0.1-SNAPSHOT 1.2.0 From 15b3a966ed6f7c5689101fd51b9f59e3c0b8543f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 28 Apr 2022 09:16:44 +0200 Subject: [PATCH 046/433] Bump azure-sdk-bom from 1.2.0 to 1.2.1 (#54) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 087159859..258a0ae4c 100644 --- a/pom.xml +++ b/pom.xml @@ -36,7 +36,7 @@ 2.22.3 0.0.1-SNAPSHOT - 1.2.0 + 1.2.1 3.1.0 42.3.4 8.5.9 From 4e98960ca46e80663672d14e52ea7c7fe4d562a9 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Thu, 28 Apr 2022 09:31:25 +0200 Subject: [PATCH 047/433] Activate edc data-management-api (#50) --- edc-controlplane/edc-controlplane-cosmosdb/pom.xml | 2 -- edc-controlplane/edc-controlplane-postgresql/pom.xml | 4 +--- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml index a374ea421..5e593ef59 100644 --- a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml +++ b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml @@ -114,12 +114,10 @@ org.eclipse.dataspaceconnector control-api - org.eclipse.dataspaceconnector observability-api diff --git a/edc-controlplane/edc-controlplane-postgresql/pom.xml b/edc-controlplane/edc-controlplane-postgresql/pom.xml index fa8440ee8..81cd99ab0 100644 --- a/edc-controlplane/edc-controlplane-postgresql/pom.xml +++ b/edc-controlplane/edc-controlplane-postgresql/pom.xml @@ -119,16 +119,14 @@ - + org.eclipse.dataspaceconnector control-api - org.eclipse.dataspaceconnector observability-api From 2d6e4891a9bb340f0b5eac00ce86e8aea2ba9437 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Thu, 28 Apr 2022 15:07:02 +0200 Subject: [PATCH 048/433] Fix due to renaming broken edc sql extension dependencies (#60) --- .../edc-controlplane-postgresql/pom.xml | 12 ++++++------ edc-extensions/postgresql-migration/pom.xml | 4 ++-- pom.xml | 17 ++++++++--------- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/edc-controlplane/edc-controlplane-postgresql/pom.xml b/edc-controlplane/edc-controlplane-postgresql/pom.xml index 81cd99ab0..998e8cd33 100644 --- a/edc-controlplane/edc-controlplane-postgresql/pom.xml +++ b/edc-controlplane/edc-controlplane-postgresql/pom.xml @@ -165,23 +165,23 @@ org.eclipse.dataspaceconnector - sql-asset-index + asset-index-sql org.eclipse.dataspaceconnector - sql-contractdefinition-store + contractdefinition-store-sql org.eclipse.dataspaceconnector - sql-contractnegotiation-store + contractnegotiation-store-sql org.eclipse.dataspaceconnector - sql-transferprocess-store + transfer-process-store-sql org.eclipse.dataspaceconnector - sql-policy-store + policy-store-sql @@ -246,7 +246,7 @@ org.eclipse.dataspaceconnector - sql-pool-apache-commons-pool + apache-commons-pool-sql org.eclipse.dataspaceconnector diff --git a/edc-extensions/postgresql-migration/pom.xml b/edc-extensions/postgresql-migration/pom.xml index 48338ba2c..624bdc3d6 100644 --- a/edc-extensions/postgresql-migration/pom.xml +++ b/edc-extensions/postgresql-migration/pom.xml @@ -38,14 +38,14 @@ org.eclipse.dataspaceconnector - sql-asset-index + asset-index-sql compile org.eclipse.dataspaceconnector - sql-common + common-sql diff --git a/pom.xml b/pom.xml index 258a0ae4c..b501e35b5 100644 --- a/pom.xml +++ b/pom.xml @@ -671,45 +671,44 @@ spi ${org.eclipse.dataspaceconnector.version} - org.eclipse.dataspaceconnector - sql-asset-index + asset-index-sql ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector - sql-common + common-sql ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector - sql-contractdefinition-store + contractdefinition-store-sql ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector - sql-lease + lease-sql ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector - sql-pool-apache-commons-pool + apache-commons-pool-sql ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector - sql-transferprocess-store + transfer-process-store-sql ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector - sql-contractnegotiation-store + contractnegotiation-store-sql ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector - sql-policy-store + policy-store-sql ${org.eclipse.dataspaceconnector.version} From 9f6c719142f67d04ed47e30b71f36837f144bf3e Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Thu, 28 Apr 2022 15:28:22 +0200 Subject: [PATCH 049/433] Fix gh action docker push (#61) --- .github/workflows/build.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 8dcc6b50e..3490de795 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -76,7 +76,7 @@ jobs: file: edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile build-args: | JAR=edc-controlplane/edc-controlplane-memory/target/edc-controlplane-memory.jar - push: ${{ github.event_name != 'pull_request' && github.repository_owner == 'product-edc' }} + push: ${{ github.event_name != 'pull_request' && github.repository == 'catenax-ng/catena-x-edc' }} tags: ${{ steps.edc_controlplane_memory_meta.outputs.tags }} labels: ${{ steps.edc_controlplane_memory_meta.outputs.labels }} @@ -111,7 +111,7 @@ jobs: file: edc-controlplane/edc-controlplane-cosmosdb/src/main/docker/Dockerfile build-args: | JAR=edc-controlplane/edc-controlplane-cosmosdb/target/edc-controlplane-cosmosdb.jar - push: ${{ github.event_name != 'pull_request' && github.repository_owner == 'product-edc' }} + push: ${{ github.event_name != 'pull_request' && github.repository == 'catenax-ng/catena-x-edc' }} tags: ${{ steps.edc_controlplane_cosmosdb_meta.outputs.tags }} labels: ${{ steps.edc_controlplane_cosmosdb_meta.outputs.labels }} @@ -146,7 +146,7 @@ jobs: file: edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile build-args: | JAR=edc-controlplane/edc-controlplane-postgresql/target/edc-controlplane-postgresql.jar - push: ${{ github.event_name != 'pull_request' && github.repository_owner == 'product-edc' }} + push: ${{ github.event_name != 'pull_request' && github.repository == 'catenax-ng/catena-x-edc' }} tags: ${{ steps.edc_controlplane_postgresql_meta.outputs.tags }} labels: ${{ steps.edc_controlplane_postgresql_meta.outputs.labels }} @@ -181,6 +181,6 @@ jobs: file: edc-dataplane/src/main/docker/Dockerfile build-args: | JAR=edc-dataplane/target/edc-dataplane.jar - push: ${{ github.event_name != 'pull_request' && github.repository_owner == 'product-edc' }} + push: ${{ github.event_name != 'pull_request' && github.repository == 'catenax-ng/catena-x-edc' }} tags: ${{ steps.edc_dataplane_meta.outputs.tags }} labels: ${{ steps.edc_dataplane_meta.outputs.labels }} From d56e2e106c3783e6da387d603b8cf21eccd73aa5 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Thu, 28 Apr 2022 15:44:55 +0200 Subject: [PATCH 050/433] Fix gh action docker push (#62) --- .github/workflows/build.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 3490de795..76d3fb579 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -76,7 +76,7 @@ jobs: file: edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile build-args: | JAR=edc-controlplane/edc-controlplane-memory/target/edc-controlplane-memory.jar - push: ${{ github.event_name != 'pull_request' && github.repository == 'catenax-ng/catena-x-edc' }} + push: ${{ github.event_name != 'pull_request' && github.repository == 'catenax-ng/product-edc' }} tags: ${{ steps.edc_controlplane_memory_meta.outputs.tags }} labels: ${{ steps.edc_controlplane_memory_meta.outputs.labels }} @@ -111,7 +111,7 @@ jobs: file: edc-controlplane/edc-controlplane-cosmosdb/src/main/docker/Dockerfile build-args: | JAR=edc-controlplane/edc-controlplane-cosmosdb/target/edc-controlplane-cosmosdb.jar - push: ${{ github.event_name != 'pull_request' && github.repository == 'catenax-ng/catena-x-edc' }} + push: ${{ github.event_name != 'pull_request' && github.repository == 'catenax-ng/product-edc' }} tags: ${{ steps.edc_controlplane_cosmosdb_meta.outputs.tags }} labels: ${{ steps.edc_controlplane_cosmosdb_meta.outputs.labels }} @@ -146,7 +146,7 @@ jobs: file: edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile build-args: | JAR=edc-controlplane/edc-controlplane-postgresql/target/edc-controlplane-postgresql.jar - push: ${{ github.event_name != 'pull_request' && github.repository == 'catenax-ng/catena-x-edc' }} + push: ${{ github.event_name != 'pull_request' && github.repository == 'catenax-ng/product-edc' }} tags: ${{ steps.edc_controlplane_postgresql_meta.outputs.tags }} labels: ${{ steps.edc_controlplane_postgresql_meta.outputs.labels }} @@ -181,6 +181,6 @@ jobs: file: edc-dataplane/src/main/docker/Dockerfile build-args: | JAR=edc-dataplane/target/edc-dataplane.jar - push: ${{ github.event_name != 'pull_request' && github.repository == 'catenax-ng/catena-x-edc' }} + push: ${{ github.event_name != 'pull_request' && github.repository == 'catenax-ng/product-edc' }} tags: ${{ steps.edc_dataplane_meta.outputs.tags }} labels: ${{ steps.edc_dataplane_meta.outputs.labels }} From 043a97ee0a91902ab4d6c245a65c044031a02c10 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Thu, 28 Apr 2022 16:08:34 +0200 Subject: [PATCH 051/433] Use CXNG_GHCR_PAT instead of GITHUB_TOKEN for authentication \w ghcr (#64) --- .github/workflows/build.yaml | 10 +++++----- .github/workflows/draft-new-release.yaml | 2 +- .github/workflows/publish-new-release.yml | 3 --- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 76d3fb579..4440032d3 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -32,7 +32,7 @@ jobs: with: registry: ghcr.io username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} + password: ${{ secrets.CXNG_GHCR_PAT }} - name: Set up JDK 11 uses: actions/setup-java@v3.2.0 @@ -54,7 +54,7 @@ jobs: ./mvnw -s settings.xml -B -pl .,edc-controlplane/edc-controlplane-memory -am verify env: GITHUB_PACKAGE_USERNAME: ${{ github.actor }} - GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} - name: edc-controlplane-memory Docker Metadata id: edc_controlplane_memory_meta @@ -89,7 +89,7 @@ jobs: ./mvnw -s settings.xml -B -pl .,edc-controlplane/edc-controlplane-cosmosdb -am verify env: GITHUB_PACKAGE_USERNAME: ${{ github.actor }} - GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} - name: edc-controlplane-cosmosdb Docker Metadata id: edc_controlplane_cosmosdb_meta @@ -124,7 +124,7 @@ jobs: ./mvnw -s settings.xml -B -pl .,edc-controlplane/edc-controlplane-postgresql -am verify env: GITHUB_PACKAGE_USERNAME: ${{ github.actor }} - GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} - name: edc-controlplane-postgresql Docker Metadata id: edc_controlplane_postgresql_meta @@ -159,7 +159,7 @@ jobs: ./mvnw -s settings.xml -B -pl .,edc-dataplane -am verify env: GITHUB_PACKAGE_USERNAME: ${{ github.actor }} - GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} - name: edc-dataplane Docker Metadata id: edc_dataplane_meta diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index 349652393..c67a51228 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -40,7 +40,7 @@ jobs: ./mvnw -s settings.xml -B versions:set -DnewVersion=${{ github.event.inputs.version }} -DprocessAllModules env: GITHUB_PACKAGE_USERNAME: ${{ github.actor }} - GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} - name: Bump version in deployment/helm uses: mikefarah/yq@v4.24.5 diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index 6ea00ace3..534ddd2ba 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -68,9 +68,6 @@ jobs: cache: 'maven' - name: Merge main back into develop and set new snapshot version - env: - GITHUB_PACKAGE_USERNAME: ${{ github.actor }} - GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} run: | # Prepare git env git config user.name "GitHub actions" From 368d3a676d4a753c262460ff58edde5ee9ce084c Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Fri, 29 Apr 2022 12:33:07 +0200 Subject: [PATCH 052/433] Add capability to define container environment variables (#65) --- .../helm/edc-controlplane/templates/configmap-env.yaml | 9 +++++++++ .../helm/edc-controlplane/templates/deployment.yaml | 3 +++ deployment/helm/edc-controlplane/values.yaml | 9 +++++++++ .../helm/edc-dataplane/templates/configmap-env.yaml | 9 +++++++++ deployment/helm/edc-dataplane/templates/deployment.yaml | 3 +++ deployment/helm/edc-dataplane/values.yaml | 9 +++++++++ 6 files changed, 42 insertions(+) create mode 100644 deployment/helm/edc-controlplane/templates/configmap-env.yaml create mode 100644 deployment/helm/edc-dataplane/templates/configmap-env.yaml diff --git a/deployment/helm/edc-controlplane/templates/configmap-env.yaml b/deployment/helm/edc-controlplane/templates/configmap-env.yaml new file mode 100644 index 000000000..f7f63f709 --- /dev/null +++ b/deployment/helm/edc-controlplane/templates/configmap-env.yaml @@ -0,0 +1,9 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "edc-controlplane.fullname" . }}-env + labels: + {{- include "edc-controlplane.labels" . | nindent 4 }} +data: + {{- toYaml .Values.env | nindent 2 }} diff --git a/deployment/helm/edc-controlplane/templates/deployment.yaml b/deployment/helm/edc-controlplane/templates/deployment.yaml index 99824c75e..1dd351201 100644 --- a/deployment/helm/edc-controlplane/templates/deployment.yaml +++ b/deployment/helm/edc-controlplane/templates/deployment.yaml @@ -78,6 +78,9 @@ spec: failureThreshold: 12 initialDelaySeconds: 10 {{- end }} + envFrom: + - configMapRef: + name: {{ include "edc-controlplane.fullname" . }}-env resources: {{- toYaml .Values.resources | nindent 12 }} volumeMounts: diff --git a/deployment/helm/edc-controlplane/values.yaml b/deployment/helm/edc-controlplane/values.yaml index 3ba6adc41..3a5ccfae8 100644 --- a/deployment/helm/edc-controlplane/values.yaml +++ b/deployment/helm/edc-controlplane/values.yaml @@ -188,6 +188,15 @@ tolerations: [] affinity: {} +## +## Container environment variables +## e.g. for configuring JAVA_TOOL_OPTIONS (see: https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/envvars002.html) +## +## JAVA_TOOL_OPTIONS: > +## -Dhttp.proxyHost=proxy -Dhttp.proxyPort=80 -Dhttp.nonProxyHosts="localhost|127.*|[::1]" -Dhttps.proxyHost=proxy -Dhttps.proxyPort=443 +## +env: {} + ## ## EDC logging.properties configuring the java.util.logging subsystem ## diff --git a/deployment/helm/edc-dataplane/templates/configmap-env.yaml b/deployment/helm/edc-dataplane/templates/configmap-env.yaml new file mode 100644 index 000000000..e23aba1a8 --- /dev/null +++ b/deployment/helm/edc-dataplane/templates/configmap-env.yaml @@ -0,0 +1,9 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "edc-dataplane.fullname" . }}-env + labels: + {{- include "edc-dataplane.labels" . | nindent 4 }} +data: + {{- toYaml .Values.env | nindent 2 }} diff --git a/deployment/helm/edc-dataplane/templates/deployment.yaml b/deployment/helm/edc-dataplane/templates/deployment.yaml index 86a5eec9a..3bf283165 100644 --- a/deployment/helm/edc-dataplane/templates/deployment.yaml +++ b/deployment/helm/edc-dataplane/templates/deployment.yaml @@ -72,6 +72,9 @@ spec: failureThreshold: 12 initialDelaySeconds: 10 {{- end }} + envFrom: + - configMapRef: + name: {{ include "edc-dataplane.fullname" . }}-env resources: {{- toYaml .Values.resources | nindent 12 }} volumeMounts: diff --git a/deployment/helm/edc-dataplane/values.yaml b/deployment/helm/edc-dataplane/values.yaml index 08cb3c9de..494bbb114 100644 --- a/deployment/helm/edc-dataplane/values.yaml +++ b/deployment/helm/edc-dataplane/values.yaml @@ -174,6 +174,15 @@ tolerations: [] affinity: {} +## +## Container environment variables +## e.g. for configuring JAVA_TOOL_OPTIONS (see: https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/envvars002.html) +## +## JAVA_TOOL_OPTIONS: > +## -Dhttp.proxyHost=proxy -Dhttp.proxyPort=80 -Dhttp.nonProxyHosts="localhost|127.*|[::1]" -Dhttps.proxyHost=proxy -Dhttps.proxyPort=443 +## +env: {} + ## ## EDC logging.properties configuring the java.util.logging subsystem ## From 489628bc71c0b7bd1d71a11bd97b14427af7ec3a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 May 2022 10:36:57 +0000 Subject: [PATCH 053/433] Bump edc from `79b58b8` to `0da15e1` Bumps [edc](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) from `79b58b8` to `0da15e1`. - [Release notes](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/releases) - [Commits](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/compare/79b58b8a8fe7b9183f3e678bd880db3c6069f97d...0da15e194d553178676f4fa6845c876bc9d7a2ad) --- updated-dependencies: - dependency-name: edc dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- edc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc b/edc index 79b58b8a8..0da15e194 160000 --- a/edc +++ b/edc @@ -1 +1 @@ -Subproject commit 79b58b8a8fe7b9183f3e678bd880db3c6069f97d +Subproject commit 0da15e194d553178676f4fa6845c876bc9d7a2ad From 6778a2f3b6cea57c7f3bf61c4011873e7b5f0ce9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 May 2022 10:28:28 +0200 Subject: [PATCH 054/433] Bump mikefarah/yq from 4.24.5 to 4.25.1 (#67) --- .github/workflows/draft-new-release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index c67a51228..56f95736c 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -43,7 +43,7 @@ jobs: GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} - name: Bump version in deployment/helm - uses: mikefarah/yq@v4.24.5 + uses: mikefarah/yq@v4.25.1 with: cmd: |- find deployment/helm -name Chart.yaml | xargs -n1 yq -i '.appVersion = "${{ github.event.inputs.version }}" | .version = "${{ github.event.inputs.version }}"' From 2017ab592bc0eb5a185d0833dda84a10c1e9cd77 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 May 2022 10:40:27 +0200 Subject: [PATCH 055/433] Bump flyway-core from 8.5.9 to 8.5.10 (#69) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b501e35b5..29a700029 100644 --- a/pom.xml +++ b/pom.xml @@ -39,7 +39,7 @@ 1.2.1 3.1.0 42.3.4 - 8.5.9 + 8.5.10 5.8.2 1.8.2 1.10.19 From 776dcd365aaa1ef644ceec3d546da121692594d4 Mon Sep 17 00:00:00 2001 From: Dominik Pinsel Date: Tue, 3 May 2022 11:24:12 +0200 Subject: [PATCH 056/433] update control plane documentation --- deployment/helm/edc-controlplane/values.yaml | 14 +- deployment/helm/edc-dataplane/values.yaml | 11 +- edc-controlplane/README.md | 178 +++++++++--------- .../edc-controlplane-cosmosdb/README.md | 8 +- .../edc-controlplane-memory/README.md | 8 +- .../edc-controlplane-postgresql/README.md | 8 +- 6 files changed, 114 insertions(+), 113 deletions(-) diff --git a/deployment/helm/edc-controlplane/values.yaml b/deployment/helm/edc-controlplane/values.yaml index 3a5ccfae8..11d350297 100644 --- a/deployment/helm/edc-controlplane/values.yaml +++ b/deployment/helm/edc-controlplane/values.yaml @@ -52,10 +52,12 @@ serviceAccount: podAnnotations: {} -podSecurityContext: {} +podSecurityContext: + {} # fsGroup: 2000 -securityContext: {} +securityContext: + {} # capabilities: # drop: # - ALL @@ -163,7 +165,8 @@ ingress: ## hostname: "edc-controlplane.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 @@ -319,13 +322,12 @@ configuration: # edc.oauth.public.key.alias= # edc.oauth.token.url= # edc.oauth.validation.nbf.leeway= - # edc.public.key.alias= # edc.receiver.http.auth-code= # edc.receiver.http.auth-key= # edc.receiver.http.endpoint= - # edc.transfer.dataplane.sync.endpoint= + # edc.transfer.proxy.endpoint= # edc.transfer.dataplane.sync.token.validity= - # edc.transfer.dataplane.token.signer.privatekey.alias= + # edc.transfer.proxy.token.signer.privatekey.alias= # edc.transfer.functions.check.endpoint= # edc.transfer.functions.enabled.protocols= # edc.transfer.functions.transfer.endpoint= diff --git a/deployment/helm/edc-dataplane/values.yaml b/deployment/helm/edc-dataplane/values.yaml index 494bbb114..16ebba9a3 100644 --- a/deployment/helm/edc-dataplane/values.yaml +++ b/deployment/helm/edc-dataplane/values.yaml @@ -51,7 +51,8 @@ podAnnotations: {} podSecurityContext: {} # fsGroup: 2000 -securityContext: {} +securityContext: + {} # capabilities: # drop: # - ALL @@ -149,7 +150,8 @@ ingress: ## hostname: "edc-dataplane.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 @@ -305,13 +307,12 @@ configuration: # edc.oauth.public.key.alias= # edc.oauth.token.url= # edc.oauth.validation.nbf.leeway= - # edc.public.key.alias= # edc.receiver.http.auth-code= # edc.receiver.http.auth-key= # edc.receiver.http.endpoint= - # edc.transfer.dataplane.sync.endpoint= + # edc.transfer.proxy.endpoint= # edc.transfer.dataplane.sync.token.validity= - # edc.transfer.dataplane.token.signer.privatekey.alias= + # edc.transfer.proxy.token.signer.privatekey.alias= # edc.transfer.functions.check.endpoint= # edc.transfer.functions.enabled.protocols= # edc.transfer.functions.transfer.endpoint= diff --git a/edc-controlplane/README.md b/edc-controlplane/README.md index 01023daa6..76badee82 100644 --- a/edc-controlplane/README.md +++ b/edc-controlplane/README.md @@ -11,6 +11,17 @@ While the **Data Plane** handles the actual Data Transfer, the **Control Plane** This chapter is about integration the Control Plane with the Azure KeyVault and IDS DAPS. + +---- + +**Please note** +
+The documentation operates the Azure Key Vault using the Azure CLI. Please visit the Microsoft has documented to learn how the Azure CLI is installed. +
+https://docs.microsoft.com/en-us/cli/azure/install-azure-cli + +---- + ## Azure Key Vault Setup The Eclipse Dataspace Connector requires a key vault, where it can store and retrieve secrets and certificates.
@@ -79,130 +90,129 @@ edc.vault.name= Please note that the key vault could also be configured using the `edc.vault.certificate`, which is not covered by this documentation. -## Connector Certificate Setup - -The connector needs it's own certificate / private key in the key vault, so that it is able to encrypt and decrypt data. -Therefore, generate a PEM file that contains the private key and the certificate and put it into the key vault. - -```bash -# Generate PKCS8 Key -openssl genpkey -out con_key.pem -algorithm RSA -pkeyopt rsa_keygen_bits:2048 - -# Generate Certificate -openssl req -config cert.conf -new -x509 -key con_key.pem -nodes -days 365 -out con_cert.pem - -# Create Cert+Key PEM file -cat con_cert.pem >certkey.pem -cat con_key.pem >>certkey.pem -``` - -Then in the Azure Portal: - -1. Open the page of the newly created key vault -2. On the left side select _certificate_ and click _generate/import_ -3. Select certificate creation method _import_, choose a unique name and upload _certkey.pem_ file into the value -4. The certificate name must be configured in the `edc.public.key.alias` - ## IDS DAPS Setup The Eclipse Dataspace Connector is able to retrieve an identity token from the IDS DAPS. This token is part of all IDS messages. The DAPS application requires a certificate from the Eclipse Dataspace Connector. This certificate may then be used by -the EDC connector to prove its identity and retrieve its identity token. +the EDC connector to retrieve its identity token and prove its identity to other connectors. When writing this guidance these step were tested out using the open source omejdn DAPS of the Fraunhofer AISEC ([GitHub](https://github.com/International-Data-Spaces-Association/omejdn-daps)). -### 1. Key / Certificate Generation +### (optional) 1. Key / Certificate Generation -In the first step generate a PKSC8 Key and the corresponding certificate. +In the first step generate a PKSC8 Key and the corresponding certificate. This step is optional, because it might be possible that this key is provided by the DAPS maintainers. ```bash -# Private Key -openssl genpkey -out key.pem -algorithm RSA -pkeyopt rsa_keygen_bits:2048 +# Generate Private Key +openssl genpkey -out daps_key.pem -algorithm RSA -pkeyopt rsa_keygen_bits:2048 ``` -````bash -# Certificate -openssl req -new -x509 -key key.pem -nodes -days 365 -out cert.pem -```` +```bash +# Generate Certificate +openssl req -new -x509 -key daps_key.pem -nodes -days 365 -out daps_cert.pem +``` -### 2. Certificate Setup +In case the certificate was not generated by the DAPS maintainers themself, it needs to be send to them. After the DAPS maintainers have registered a new client, it gets a unique client ID. Configure the DAPS client ID in `edc.oauth.client.id`. -#### 2.1. Certificate Setup - DAPS +### 2. Azure Key Vault Upload -Each connector client must be registered in the DAPS. Send the certificate to the DAPS maintainers. After the DAPS has -created a new client, configure the corresponding client ID in `edc.oauth.client.id`. +```bash +# Upload Private Key +az keyvault secret set --name my-daps-key --vault-name tree512 --file daps_key.pem +``` -#### 2.2 Certificate Setup - Azure Key Vault +```bash +# Upload Certificate +az keyvault secret set --name my-daps-cert --vault-name tree512 --file daps_cert.pem +``` -##### 2.2.1 Format Certificate +### 3. EDC Configuration -The certificate must also be stored as _secret_ in the Azure Key Vault. But before that, all newline and space -characters must be removed. +Configure the private key alias in the `edc.oauth.private.key.alias`, and the certificate alias in `edc.oauth.public.key.alias`. -```bash -# Format Certificate -cat cert.pem | sed "s/[[:space:]]*//" | tr -d \\n >cert.txt +In this example it would be +```properties +edc.oauth.private.key.alias=my-daps-key +edc.oauth.public.key.alias=my-daps-cert ``` -#### 2.2.2 Create Key Vault Secret +Additionally these properties must be requested from the DAPS maintainers: -In the Azure Portal: +- DAPS Connector Client ID must be configured in `edc.oauth.client.id` +- DAPS Token URL must be configured in `edc.oauth.token.url` +- DAPS JWKS URL must be configured in `edc.oauth.provider.jwks.url` +- Token Audience must be configured in `edc.oauth.provider.audience` -1. Open the page of the newly created key vault -2. On the left side select _secret_ and click _generate/import_ to create a new secret -3. Select upload options _manual_, choose a unique name and copy the content of the _cert.txt_ file into the value -4. The secret name must be configured in the `edc.oauth.public.key.alias` +### 4. Summary -### 3. Private Key Setup +The complete EDC configuration could look like this: -#### 3.1 Format Key +```properties +# DAPS Properties +edc.oauth.token.url=http://localhost:4567/token +edc.oauth.client.id= +edc.oauth.provider.audience= +edc.oauth.provider.jwks.url=http://localhost:4567/.well-known/jwks.json +# OAUTH Properties +edc.oauth.private.key.alias= +edc.oauth.public.key.alias= +``` -Before storing the key as _secret_ in the Azure Key Vault format it. The PCKS8 identifying lines must be removed with -all spaces and newlines, so that only the key itself remains. -```bash -# Format Key -cat key.pem | sed "s/^-----BEGIN PRIVATE KEY-----//" | sed "s/-----END PRIVATE KEY-----$//" | sed "s/[[:space:]]*//" | tr -d \\n >key.txt -``` +## Dataplane Setup -#### 3.2 Create Key Vault Secret +Configure the control plane so that is able to communicate with an EDC data plane instance. -In the Azure Portal: +---- -1. Open the page of the newly created key vault -2. On the left side select _secret_ and click _generate/import_ to create a new secret -3. Select upload options _manual_, choose a unique name and copy the content of the _key.txt_ file into the value. -4. The secret name must be configured in the `edc.oauth.private.key.alias` +**Please note** +
+This chapter contains only the mandatory data plane configuration. -### 4. DAPS Setup/Configuration +---- -The these properties should be requested from the DAPS maintainer: +### Encryption -- DAPS Token URL must be configured in `edc.oauth.token.url` -- DAPS JWKS URL must be configured in `edc.oauth.provider.jwks.url` -- Token Audience must be configured in `edc.oauth.provider.audience` +The communication between dataplane and controlplane is encrypted and needs some keys in the key vault and configuration in the EDC. -### Summary +#### Private Key -The EDC DAPS configuration could look like this: +```bash +# Generate Private Key +openssl genpkey -out my-data-plane-private-key.pem -algorithm RSA -pkeyopt rsa_keygen_bits:2048 +``` + +```bash +# Upload Private Key +az keyvault secret set --name my-data-plane-private-key --vault-name tree512 --file my-data-plane-private-key.pem +``` ```properties -edc.oauth.token.url=http://localhost:4567/token -edc.oauth.client.id= -edc.oauth.provider.audience= -edc.oauth.provider.jwks.url=http://localhost:4567/.well-known/jwks.json +# Configuration +edc.transfer.proxy.token.signer.privatekey.alias=my-data-plane-private-key +``` + +#### Public Key + +```bash +# Generate Public Key +openssl rsa -in my-data-plane-private-key.pem -out my-data-plane-public-key.pem -pubout -outform PEM ``` -And for the OAUTH extensions there should the following properties set: +```bash +# Upload Public Key +az keyvault secret set --name my-data-plane-public-key --vault-name tree512 --file my-data-plane-public-key.pem +``` ```properties -edc.oauth.public.key.alias= -edc.oauth.private.key.alias= +# Configuration +edc.transfer.proxy.token.verifier.publickey.alias=my-data-plane-public-key ``` + # Short Overview of the EDC Domain This chapter gives a short overview of the EDC domain. The idea is to get a basic understanding of the domain objects and their roles.
@@ -512,15 +522,3 @@ curl -G -X GET $__connectorUrl/$__defaultApiPath/control/catalog --header "$__ap Please have look at all the open issues in the open source repository. The list below might not be maintained well and only contains the most important issues. EDC Github Repository https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues - -### All Contract Offers and their data is public available - -https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1137 - -### Contract Negotiation not working - -https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1140 - -### Missing Input validation in the Data Management API - -https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1111 diff --git a/edc-controlplane/edc-controlplane-cosmosdb/README.md b/edc-controlplane/edc-controlplane-cosmosdb/README.md index 645e9506d..8e835dbc9 100644 --- a/edc-controlplane/edc-controlplane-cosmosdb/README.md +++ b/edc-controlplane/edc-controlplane-cosmosdb/README.md @@ -60,8 +60,8 @@ Details regarding each configuration property can be found at the [documentary s | edc.transfer-process-store.database.name | X | transfer-process-store | | | edc.transfer-process-store.cosmos.preferred-region | X | westeurope | | | edc.transfer-process-store.cosmos.container-name | X | cosmosdb-transfer-process-store-container-name | | -| edc.transfer.dataplane.sync.endpoint | X | | | -| edc.transfer.dataplane.token.signer.privatekey.alias | X | | | +| edc.transfer.proxy.endpoint | X | | | +| edc.transfer.proxy.token.signer.privatekey.alias | X | | | #### Example configuration.properties @@ -113,8 +113,8 @@ edc.vault.name=my-vault-name edc.vault.clientsecret=34-chars-secret # Control- / Data- Plane configuration -edc.transfer.dataplane.sync.endpoint=http://dataplane-public-endpoint/public -edc.transfer.dataplane.token.signer.privatekey.alias=azure-vault-token-signer-private-key +edc.transfer.proxy.endpoint=http://dataplane-public-endpoint/public +edc.transfer.proxy.token.signer.privatekey.alias=azure-vault-token-signer-private-key # Azure CosmosDB related configuration edc.assetindex.cosmos.account-name=cosmosdb-assetindex-account-name diff --git a/edc-controlplane/edc-controlplane-memory/README.md b/edc-controlplane/edc-controlplane-memory/README.md index 71c60d167..e8349e646 100644 --- a/edc-controlplane/edc-controlplane-memory/README.md +++ b/edc-controlplane/edc-controlplane-memory/README.md @@ -36,8 +36,8 @@ Details regarding each configuration property can be found at the [documentary s | edc.api.control.auth.apikey.key | | X-Api-Key | | | edc.api.control.auth.apikey.value | | super-strong-api-key | | | edc.hostname | | localhost | | -| edc.transfer.dataplane.sync.endpoint | X | | | -| edc.transfer.dataplane.token.signer.privatekey.alias | X | | | +| edc.transfer.proxy.endpoint | X | | | +| edc.transfer.proxy.token.signer.privatekey.alias | X | | | #### Example configuration.properties @@ -84,8 +84,8 @@ edc.vault.name=my-vault-name edc.vault.clientsecret=34-chars-secret # Control- / Data- Plane configuration -edc.transfer.dataplane.sync.endpoint=http://dataplane-public-endpoint/public -edc.transfer.dataplane.token.signer.privatekey.alias=azure-vault-token-signer-private-key +edc.transfer.proxy.endpoint=http://dataplane-public-endpoint/public +edc.transfer.proxy.token.signer.privatekey.alias=azure-vault-token-signer-private-key EOF ``` diff --git a/edc-controlplane/edc-controlplane-postgresql/README.md b/edc-controlplane/edc-controlplane-postgresql/README.md index e0c648c0e..af44e6a12 100644 --- a/edc-controlplane/edc-controlplane-postgresql/README.md +++ b/edc-controlplane/edc-controlplane-postgresql/README.md @@ -64,8 +64,8 @@ Details regarding each configuration property can be found at the [documentary s | edc.datasource.transferprocess.url | X | jdbc:postgresql://postgres.svc.cluster.local:5432/edc_transferprocess_db | | | edc.datasource.transferprocess.user | X | username | | | edc.datasource.transferprocess.password | X | password | | -| edc.transfer.dataplane.sync.endpoint | X | | | -| edc.transfer.dataplane.token.signer.privatekey.alias | X | | | +| edc.transfer.proxy.endpoint | X | | | +| edc.transfer.proxy.token.signer.privatekey.alias | X | | | #### Example configuration.properties @@ -117,8 +117,8 @@ edc.vault.name=my-vault-name edc.vault.clientsecret=34-chars-secret # Control- / Data- Plane configuration -edc.transfer.dataplane.sync.endpoint=http://dataplane-public-endpoint/public -edc.transfer.dataplane.token.signer.privatekey.alias=azure-vault-token-signer-private-key +edc.transfer.proxy.endpoint=http://dataplane-public-endpoint/public +edc.transfer.proxy.token.signer.privatekey.alias=azure-vault-token-signer-private-key # Postgresql related configuration edc.datasource.asset.name=asset From 77854020db32dafe16590497bf33867d949cdee4 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Tue, 3 May 2022 11:40:30 +0200 Subject: [PATCH 057/433] Parallelize build steps (#59) --- .github/workflows/build.yaml | 182 +++++++++++++++++++++++++---------- 1 file changed, 133 insertions(+), 49 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 4440032d3..8e5da3bb5 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -13,13 +13,13 @@ on: - '*' jobs: - build: + ##################### + ### edc-dataplane ### + ##################### + build-edc-dataplane: runs-on: ubuntu-latest steps: - - ############## - ### Set-Up ### - ############## + # Set-Up - name: Checkout uses: actions/checkout@v3 @@ -44,10 +44,71 @@ jobs: name: Build edc with Gradle to get latest snapshots run: ./gradlew publishToMavenLocal working-directory: edc + # Build + - + name: Build edc-dataplane + run: |- + ./mvnw -s settings.xml -B -pl .,edc-dataplane -am verify + env: + GITHUB_PACKAGE_USERNAME: ${{ github.actor }} + GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + - + name: edc-dataplane Docker Metadata + id: edc_dataplane_meta + uses: docker/metadata-action@v3 + with: + images: | + ghcr.io/catenax-ng/product-edc/edc-dataplane + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{raw}} + type=match,pattern=\d.\d.\d + type=sha + - + name: Build edc-dataplane Docker Image + uses: docker/build-push-action@v2 + with: + context: . + file: edc-dataplane/src/main/docker/Dockerfile + build-args: | + JAR=edc-dataplane/target/edc-dataplane.jar + push: ${{ github.event_name != 'pull_request' && github.repository_owner == 'product-edc' }} + tags: ${{ steps.edc_dataplane_meta.outputs.tags }} + labels: ${{ steps.edc_dataplane_meta.outputs.labels }} - ################################# - ### edc-controlplane-memory ### - ################################# + ################################# + ### edc-controlplane-memory ### + ################################# + build-edc-controlplane-memory: + runs-on: ubuntu-latest + steps: + # Set-Up + - + name: Checkout + uses: actions/checkout@v3 + with: + submodules: recursive + - + name: Login to Github Packages + if: github.event_name != 'pull_request' + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - + name: Set up JDK 11 + uses: actions/setup-java@v3.2.0 + with: + java-version: '11' + distribution: 'adopt' + cache: 'maven' + - + name: Build edc with Gradle to get latest snapshots + run: ./gradlew publishToMavenLocal + working-directory: edc + # Build - name: Build edc-controlplane-memory run: |- @@ -80,9 +141,38 @@ jobs: tags: ${{ steps.edc_controlplane_memory_meta.outputs.tags }} labels: ${{ steps.edc_controlplane_memory_meta.outputs.labels }} - ################################# - ### edc-controlplane-cosmosdb ### - ################################# + ################################# + ### edc-controlplane-cosmosdb ### + ################################# + build-edc-controlplane-cosmosdb: + runs-on: ubuntu-latest + steps: + # Set-Up + - + name: Checkout + uses: actions/checkout@v3 + with: + submodules: recursive + - + name: Login to Github Packages + if: github.event_name != 'pull_request' + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - + name: Set up JDK 11 + uses: actions/setup-java@v3.2.0 + with: + java-version: '11' + distribution: 'adopt' + cache: 'maven' + - + name: Build edc with Gradle to get latest snapshots + run: ./gradlew publishToMavenLocal + working-directory: edc + # Build - name: Build edc-controlplane-cosmosdb run: |- @@ -115,9 +205,38 @@ jobs: tags: ${{ steps.edc_controlplane_cosmosdb_meta.outputs.tags }} labels: ${{ steps.edc_controlplane_cosmosdb_meta.outputs.labels }} - ################################# - ### edc-controlplane-postgresql ### - ################################# + ################################### + ### edc-controlplane-postgresql ### + ################################### + build-edc-controlplane-postgresql: + runs-on: ubuntu-latest + steps: + # Set-Up + - + name: Checkout + uses: actions/checkout@v3 + with: + submodules: recursive + - + name: Login to Github Packages + if: github.event_name != 'pull_request' + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - + name: Set up JDK 11 + uses: actions/setup-java@v3.2.0 + with: + java-version: '11' + distribution: 'adopt' + cache: 'maven' + - + name: Build edc with Gradle to get latest snapshots + run: ./gradlew publishToMavenLocal + working-directory: edc + # Build - name: Build edc-controlplane-postgresql run: |- @@ -149,38 +268,3 @@ jobs: push: ${{ github.event_name != 'pull_request' && github.repository == 'catenax-ng/product-edc' }} tags: ${{ steps.edc_controlplane_postgresql_meta.outputs.tags }} labels: ${{ steps.edc_controlplane_postgresql_meta.outputs.labels }} - - ##################### - ### edc-dataplane ### - ##################### - - - name: Build edc-dataplane - run: |- - ./mvnw -s settings.xml -B -pl .,edc-dataplane -am verify - env: - GITHUB_PACKAGE_USERNAME: ${{ github.actor }} - GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} - - - name: edc-dataplane Docker Metadata - id: edc_dataplane_meta - uses: docker/metadata-action@v3 - with: - images: | - ghcr.io/catenax-ng/product-edc/edc-dataplane - tags: | - type=ref,event=branch - type=ref,event=pr - type=semver,pattern={{raw}} - type=match,pattern=\d.\d.\d - type=sha - - - name: Build edc-dataplane Docker Image - uses: docker/build-push-action@v2 - with: - context: . - file: edc-dataplane/src/main/docker/Dockerfile - build-args: | - JAR=edc-dataplane/target/edc-dataplane.jar - push: ${{ github.event_name != 'pull_request' && github.repository == 'catenax-ng/product-edc' }} - tags: ${{ steps.edc_dataplane_meta.outputs.tags }} - labels: ${{ steps.edc_dataplane_meta.outputs.labels }} From 462513dd5c22ffeac1eb4253de1aee08cbc054a6 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Tue, 3 May 2022 11:40:47 +0200 Subject: [PATCH 058/433] Ignore non-helm related paths when running gh action helm-lint step (#58) --- .github/workflows/helm-lint.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/helm-lint.yaml b/.github/workflows/helm-lint.yaml index f11844fde..8b49c30b7 100644 --- a/.github/workflows/helm-lint.yaml +++ b/.github/workflows/helm-lint.yaml @@ -8,9 +8,15 @@ on: - develop tags: - '[0-9]+.[0-9]+.[0-9]+' + paths-ignore: + - '**' + - '!deployment/helm/**' pull_request: branches: - '*' + paths-ignore: + - '**' + - '!deployment/helm/**' jobs: helm-lint: From 3c4bfd50010c412983ec687c02b1b12ee27d2c30 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Tue, 3 May 2022 13:03:29 +0200 Subject: [PATCH 059/433] Fix gh build workflow: replace GITHUB_TOKEN by CXNG_GHCR_PAT (#77) --- .github/workflows/build.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 8e5da3bb5..089b92b08 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -51,7 +51,7 @@ jobs: ./mvnw -s settings.xml -B -pl .,edc-dataplane -am verify env: GITHUB_PACKAGE_USERNAME: ${{ github.actor }} - GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} - name: edc-dataplane Docker Metadata id: edc_dataplane_meta @@ -96,7 +96,7 @@ jobs: with: registry: ghcr.io username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} + password: ${{ secrets.CXNG_GHCR_PAT }} - name: Set up JDK 11 uses: actions/setup-java@v3.2.0 @@ -160,7 +160,7 @@ jobs: with: registry: ghcr.io username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} + password: ${{ secrets.CXNG_GHCR_PAT }} - name: Set up JDK 11 uses: actions/setup-java@v3.2.0 @@ -224,7 +224,7 @@ jobs: with: registry: ghcr.io username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} + password: ${{ secrets.CXNG_GHCR_PAT }} - name: Set up JDK 11 uses: actions/setup-java@v3.2.0 From c64de702dbcf4b938a842fc7d4605a9ddae9f055 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Tue, 3 May 2022 13:34:10 +0200 Subject: [PATCH 060/433] Apply suggestions from code review Co-authored-by: Florian Rusch (ZF Friedrichshafen AG) --- edc-controlplane/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/edc-controlplane/README.md b/edc-controlplane/README.md index 76badee82..1180c7400 100644 --- a/edc-controlplane/README.md +++ b/edc-controlplane/README.md @@ -176,18 +176,18 @@ This chapter contains only the mandatory data plane configuration. ### Encryption -The communication between dataplane and controlplane is encrypted and needs some keys in the key vault and configuration in the EDC. +The communication between dataplane and controlplane is encrypted and needs some keys in the key vault and configuration in the EDC. #### Private Key ```bash # Generate Private Key -openssl genpkey -out my-data-plane-private-key.pem -algorithm RSA -pkeyopt rsa_keygen_bits:2048 +openssl genpkey -out my-data-plane-private-key.pem -algorithm RSA -pkeyopt rsa_keygen_bits:2048 ``` ```bash # Upload Private Key -az keyvault secret set --name my-data-plane-private-key --vault-name tree512 --file my-data-plane-private-key.pem +az keyvault secret set --name my-data-plane-private-key --vault-name tree512 --file my-data-plane-private-key.pem ``` ```properties From c6d1d797b53880311ddc6b03832261d58c943d92 Mon Sep 17 00:00:00 2001 From: Dominik Pinsel Date: Tue, 3 May 2022 15:20:33 +0200 Subject: [PATCH 061/433] update known issues Signed-off-by: Dominik Pinsel --- edc-controlplane/README.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/edc-controlplane/README.md b/edc-controlplane/README.md index 01023daa6..1908ad03c 100644 --- a/edc-controlplane/README.md +++ b/edc-controlplane/README.md @@ -507,20 +507,24 @@ curl -G -X GET $__connectorUrl/$__defaultApiPath/control/catalog --header "$__ap | $__targetConnectorUrl | URL of the Connector of the target connector with the IDS API port configured in `web.http.ids.port`(in the configuration of the other connector) | | $__targetConnectorIdsPath | The IDS Path as configured in `web.http.ids.path` (in the configuration of the other connector) | + + # Known Control Plane Issues Please have look at all the open issues in the open source repository. The list below might not be maintained well and only contains the most important issues. EDC Github Repository https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues -### All Contract Offers and their data is public available +## Contract negotiation not working when `web.http.ids.path` is configured/changed -https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1137 +https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1249 -### Contract Negotiation not working +**Workaround:** +Don't configure `web.http.ids.path`, so that the default path is used. -https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1140 +## Contract negotiation not working when initiated with policy id -### Missing Input validation in the Data Management API +https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1251 -https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1111 +**Workaround:** +The DataManagement API can also initiate a contract negotiation using the actual policy object. From d6026aa7558ae55401b369281ee1991228e25a19 Mon Sep 17 00:00:00 2001 From: "Florian Rusch (ZF Friedrichshafen AG)" Date: Wed, 4 May 2022 09:28:28 +0200 Subject: [PATCH 062/433] Remove all in-memory stores deps which are now default (#80) --- .../edc-controlplane-cosmosdb/pom.xml | 6 ---- .../edc-controlplane-memory/pom.xml | 24 --------------- pom.xml | 30 ------------------- 3 files changed, 60 deletions(-) diff --git a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml index 5e593ef59..7a9a761df 100644 --- a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml +++ b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml @@ -195,12 +195,6 @@
- - - org.eclipse.dataspaceconnector - policy-store-memory - - org.eclipse.dataspaceconnector diff --git a/edc-controlplane/edc-controlplane-memory/pom.xml b/edc-controlplane/edc-controlplane-memory/pom.xml index 75604176e..1cc04a72a 100644 --- a/edc-controlplane/edc-controlplane-memory/pom.xml +++ b/edc-controlplane/edc-controlplane-memory/pom.xml @@ -153,30 +153,6 @@ ids-token-validation - - - org.eclipse.dataspaceconnector - assetindex-memory - - - org.eclipse.dataspaceconnector - contractdefinition-store-memory - - - org.eclipse.dataspaceconnector - contractnegotiation-store-memory - - - org.eclipse.dataspaceconnector - transfer-process-store-memory - - - - - org.eclipse.dataspaceconnector - policy-store-memory - - org.eclipse.dataspaceconnector diff --git a/pom.xml b/pom.xml index 29a700029..7b50b89e8 100644 --- a/pom.xml +++ b/pom.xml @@ -186,11 +186,6 @@ assetindex-cosmos ${org.eclipse.dataspaceconnector.version} - - org.eclipse.dataspaceconnector - assetindex-memory - ${org.eclipse.dataspaceconnector.version} - org.eclipse.dataspaceconnector auth-spi @@ -301,11 +296,6 @@ contract-definition-store-cosmos ${org.eclipse.dataspaceconnector.version} - - org.eclipse.dataspaceconnector - contractdefinition-store-memory - ${org.eclipse.dataspaceconnector.version} - org.eclipse.dataspaceconnector contractnegotiation-api @@ -316,11 +306,6 @@ contract-negotiation-store-cosmos ${org.eclipse.dataspaceconnector.version} - - org.eclipse.dataspaceconnector - contractnegotiation-store-memory - ${org.eclipse.dataspaceconnector.version} - org.eclipse.dataspaceconnector contract-spi @@ -571,11 +556,6 @@ ids-token-validation ${org.eclipse.dataspaceconnector.version} - - org.eclipse.dataspaceconnector - in-memory - ${org.eclipse.dataspaceconnector.version} - org.eclipse.dataspaceconnector jdk-logger-monitor @@ -636,11 +616,6 @@ policy-spi ${org.eclipse.dataspaceconnector.version} - - org.eclipse.dataspaceconnector - policy-store-memory - ${org.eclipse.dataspaceconnector.version} - org.eclipse.dataspaceconnector registration-service @@ -772,11 +747,6 @@ transfer-process-store-cosmos ${org.eclipse.dataspaceconnector.version} - - org.eclipse.dataspaceconnector - transfer-process-store-memory - ${org.eclipse.dataspaceconnector.version} - org.eclipse.dataspaceconnector transfer-spi From bac2ebeeaac0a06521e1bfc26606d7bc3a1bc01c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 May 2022 10:55:09 +0200 Subject: [PATCH 063/433] Bump spotless-maven-plugin from 2.22.3 to 2.22.4 (#81) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7b50b89e8..13887e1d6 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ 3.3.0 1.4.13 - 2.22.3 + 2.22.4 0.0.1-SNAPSHOT 1.2.1 From 2574078c1fe3aecb33d2ff4664a705f39c5acac6 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Wed, 4 May 2022 10:57:01 +0200 Subject: [PATCH 064/433] Update edc bom section and use policy-store-cosmos in edc-controlplane-cosmosdb (#82) --- .../edc-controlplane-cosmosdb/pom.xml | 10 ++ pom.xml | 127 +++++++++++------- 2 files changed, 88 insertions(+), 49 deletions(-) diff --git a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml index 7a9a761df..9aa9873b2 100644 --- a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml +++ b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml @@ -184,6 +184,16 @@ + + org.eclipse.dataspaceconnector + policy-store-cosmos + + + com.azure + azure-cosmos + + + org.eclipse.dataspaceconnector transfer-process-store-cosmos diff --git a/pom.xml b/pom.xml index 13887e1d6..bb412961a 100644 --- a/pom.xml +++ b/pom.xml @@ -166,6 +166,11 @@ + + org.eclipse.dataspaceconnector + apache-commons-pool-sql + ${org.eclipse.dataspaceconnector.version} + org.eclipse.dataspaceconnector api-configuration @@ -186,6 +191,16 @@ assetindex-cosmos ${org.eclipse.dataspaceconnector.version} + + org.eclipse.dataspaceconnector + asset-index-sql + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + auth-basic + ${org.eclipse.dataspaceconnector.version} + org.eclipse.dataspaceconnector auth-spi @@ -253,22 +268,22 @@ org.eclipse.dataspaceconnector - catalog-cache + catalog-api ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector - catalog-cache-store-memory + catalog-cache ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector - catalog-node-directory-memory + catalog-spi ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector - catalog-spi + common-sql ${org.eclipse.dataspaceconnector.version} @@ -296,6 +311,11 @@ contract-definition-store-cosmos ${org.eclipse.dataspaceconnector.version} + + org.eclipse.dataspaceconnector + contractdefinition-store-sql + ${org.eclipse.dataspaceconnector.version} + org.eclipse.dataspaceconnector contractnegotiation-api @@ -306,6 +326,11 @@ contract-negotiation-store-cosmos ${org.eclipse.dataspaceconnector.version} + + org.eclipse.dataspaceconnector + contractnegotiation-store-sql + ${org.eclipse.dataspaceconnector.version} + org.eclipse.dataspaceconnector contract-spi @@ -331,6 +356,11 @@ core-boot ${org.eclipse.dataspaceconnector.version} + + org.eclipse.dataspaceconnector + core-defaults + ${org.eclipse.dataspaceconnector.version} + org.eclipse.dataspaceconnector core-micrometer @@ -386,11 +416,26 @@ data-plane-http ${org.eclipse.dataspaceconnector.version} + + org.eclipse.dataspaceconnector + data-plane-s3 + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + data-plane-selector-api + ${org.eclipse.dataspaceconnector.version} + org.eclipse.dataspaceconnector data-plane-selector-client ${org.eclipse.dataspaceconnector.version} + + org.eclipse.dataspaceconnector + data-plane-selector-core + ${org.eclipse.dataspaceconnector.version} + org.eclipse.dataspaceconnector data-plane-selector-spi @@ -408,27 +453,27 @@ org.eclipse.dataspaceconnector - data-plane-transfer-core + data-plane-transfer ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector - data-plane-transfer-spi + data-plane-transfer-client ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector - data-plane-transfer-sync + data-plane-transfer-spi ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector - decentralized-identity + data-plane-transfer-sync ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector - did-document-store-memory + decentralized-identity ${org.eclipse.dataspaceconnector.version} @@ -461,6 +506,11 @@ http ${org.eclipse.dataspaceconnector.version} + + org.eclipse.dataspaceconnector + http-provisioner + ${org.eclipse.dataspaceconnector.version} + org.eclipse.dataspaceconnector http-receiver @@ -506,11 +556,6 @@ identity-did-web ${org.eclipse.dataspaceconnector.version} - - org.eclipse.dataspaceconnector - identity-hub-memory - ${org.eclipse.dataspaceconnector.version} - org.eclipse.dataspaceconnector ids @@ -581,6 +626,11 @@ jetty-micrometer ${org.eclipse.dataspaceconnector.version} + + org.eclipse.dataspaceconnector + lease-sql + ${org.eclipse.dataspaceconnector.version} + org.eclipse.dataspaceconnector oauth2-core @@ -618,75 +668,49 @@ org.eclipse.dataspaceconnector - registration-service - ${org.eclipse.dataspaceconnector.version} - - - org.eclipse.dataspaceconnector - registration-service-api - ${org.eclipse.dataspaceconnector.version} - - - org.eclipse.dataspaceconnector - s3-core - ${org.eclipse.dataspaceconnector.version} - - - org.eclipse.dataspaceconnector - s3-operator + policy-store-cosmos ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector - s3-provision - ${org.eclipse.dataspaceconnector.version} - - - org.eclipse.dataspaceconnector - spi - ${org.eclipse.dataspaceconnector.version} - - - org.eclipse.dataspaceconnector - asset-index-sql + policy-store-sql ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector - common-sql + registration-service ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector - contractdefinition-store-sql + registration-service-api ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector - lease-sql + s3-core ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector - apache-commons-pool-sql + s3-operator ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector - transfer-process-store-sql + s3-provision ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector - contractnegotiation-store-sql + spi ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector - policy-store-sql + sql ${org.eclipse.dataspaceconnector.version} - org.eclipse.dataspaceconnector state-machine-lib @@ -747,6 +771,11 @@ transfer-process-store-cosmos ${org.eclipse.dataspaceconnector.version} + + org.eclipse.dataspaceconnector + transfer-process-store-sql + ${org.eclipse.dataspaceconnector.version} + org.eclipse.dataspaceconnector transfer-spi From 7cda703d6eef33334d338fe5dc5d0e8d2c9567d8 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Wed, 4 May 2022 14:02:19 +0200 Subject: [PATCH 065/433] Update sql migration for edc_contract_definition due to introduction of access_policy_id and contract_policy_id (#83) --- ...ition_Access_Contract_Policy_Id_Schema.sql | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractdefinition/V0_0_2__Alter_ContractDefinition_Access_Contract_Policy_Id_Schema.sql diff --git a/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractdefinition/V0_0_2__Alter_ContractDefinition_Access_Contract_Policy_Id_Schema.sql b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractdefinition/V0_0_2__Alter_ContractDefinition_Access_Contract_Policy_Id_Schema.sql new file mode 100644 index 000000000..a6f0b53ba --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractdefinition/V0_0_2__Alter_ContractDefinition_Access_Contract_Policy_Id_Schema.sql @@ -0,0 +1,29 @@ +-- +-- Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +-- +-- This program and the accompanying materials are made available under the +-- terms of the Apache License, Version 2.0 which is available at +-- https://www.apache.org/licenses/LICENSE-2.0 +-- +-- SPDX-License-Identifier: Apache-2.0 +-- +-- Contributors: +-- Mercedes-Benz Tech Innovation GmbH - Migrate edc_contract_definitions to contain access_policy_id and contract_policy_id +-- + +-- Add new access_policy_id and contract_policy_id columns +ALTER TABLE edc_contract_definitions ADD access_policy_id VARCHAR(255) DEFAULT NULL; +ALTER TABLE edc_contract_definitions ADD contract_policy_id VARCHAR(255) DEFAULT NULL; + +-- Extract the id from access_policy and store its value into access_policy_id +UPDATE edc_contract_definitions SET access_policy_id=access_policy::json->>'uid'; +-- Extract the id from contract_policy and store its value into contract_policy_id +UPDATE edc_contract_definitions SET contract_policy_id=contract_policy::json->>'uid'; + +-- DROP obsolete access_policy columns +ALTER TABLE edc_contract_definitions DROP COLUMN access_policy; +ALTER TABLE edc_contract_definitions DROP COLUMN contract_policy; + +-- Add non-null constraints to the new columns +ALTER TABLE edc_contract_definitions ALTER COLUMN access_policy_id SET NOT NULL; +ALTER TABLE edc_contract_definitions ALTER COLUMN contract_policy_id SET NOT NULL; From 37606c9fe95c3aeecee770b69e53638485b65145 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 May 2022 09:09:37 +0200 Subject: [PATCH 066/433] Bump actions/setup-java from 3.2.0 to 3.3.0 (#85) --- .github/workflows/build.yaml | 8 ++++---- .github/workflows/draft-new-release.yaml | 2 +- .github/workflows/publish-new-release.yml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 089b92b08..d3524bcbf 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -35,7 +35,7 @@ jobs: password: ${{ secrets.CXNG_GHCR_PAT }} - name: Set up JDK 11 - uses: actions/setup-java@v3.2.0 + uses: actions/setup-java@v3.3.0 with: java-version: '11' distribution: 'adopt' @@ -99,7 +99,7 @@ jobs: password: ${{ secrets.CXNG_GHCR_PAT }} - name: Set up JDK 11 - uses: actions/setup-java@v3.2.0 + uses: actions/setup-java@v3.3.0 with: java-version: '11' distribution: 'adopt' @@ -163,7 +163,7 @@ jobs: password: ${{ secrets.CXNG_GHCR_PAT }} - name: Set up JDK 11 - uses: actions/setup-java@v3.2.0 + uses: actions/setup-java@v3.3.0 with: java-version: '11' distribution: 'adopt' @@ -227,7 +227,7 @@ jobs: password: ${{ secrets.CXNG_GHCR_PAT }} - name: Set up JDK 11 - uses: actions/setup-java@v3.2.0 + uses: actions/setup-java@v3.3.0 with: java-version: '11' distribution: 'adopt' diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index 56f95736c..d298ab174 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -29,7 +29,7 @@ jobs: git config user.email noreply@github.com - name: Set up JDK 11 - uses: actions/setup-java@v3.2.0 + uses: actions/setup-java@v3.3.0 with: java-version: '11' distribution: 'adopt' diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index 534ddd2ba..fad49a49c 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -61,7 +61,7 @@ jobs: prerelease: false - name: Set up JDK 11 - uses: actions/setup-java@v3.2.0 + uses: actions/setup-java@v3.3.0 with: java-version: '11' distribution: 'adopt' From 98b2d6f5f57b9dab16000406bbf930c179168c90 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Thu, 5 May 2022 10:02:26 +0200 Subject: [PATCH 067/433] Provide functionality to configure more than one ingress / hostname (#78) --- .../helm/edc-controlplane/templates/NOTES.txt | 18 ++- .../edc-controlplane/templates/ingress.yaml | 89 +++++++------- deployment/helm/edc-controlplane/values.yaml | 109 ++++++++++++------ .../helm/edc-dataplane/templates/NOTES.txt | 18 ++- .../helm/edc-dataplane/templates/ingress.yaml | 89 +++++++------- deployment/helm/edc-dataplane/values.yaml | 68 +++++------ lintconf.yaml | 2 +- 7 files changed, 229 insertions(+), 164 deletions(-) diff --git a/deployment/helm/edc-controlplane/templates/NOTES.txt b/deployment/helm/edc-controlplane/templates/NOTES.txt index fd365cb24..6758c6bdf 100644 --- a/deployment/helm/edc-controlplane/templates/NOTES.txt +++ b/deployment/helm/edc-controlplane/templates/NOTES.txt @@ -6,15 +6,23 @@ APP VERSION: {{ .Chart.AppVersion }} Logs can be accessed by running this command: kubectl logs --tail 100 -f \ - --namespace {{ .Release.Namespace }} + --namespace {{ .Release.Namespace }} \ -l "app.kubernetes.io/name={{ include "edc-controlplane.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -{{- if .Values.ingress.enabled }} +{{- if .Values.ingresses }} Following ingress URLS are available: - {{- range $name, $mapping := .Values.edc.endpoints }} - {{- if $mapping.ingress }} - Visit http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $.Values.ingress.hostname }}{{ $mapping.path }} to access the {{ $name }} api + {{- $edcEndpoints := .Values.edc.endpoints }} + {{- range .Values.ingresses }} + {{- if .enabled }} + {{- $ingressEdcEndpoints := .endpoints }} + {{- $hostname := .hostname }} + {{- $tls := .tls }} + {{- range $name, $mapping := $edcEndpoints }} + {{- if (has $name $ingressEdcEndpoints) }} + Visit http{{ if $tls }}s{{ end }}://{{ $hostname }}{{ $mapping.path }} to access the {{ $name }} api + {{- end }} + {{- end }} {{- end }} {{- end }} diff --git a/deployment/helm/edc-controlplane/templates/ingress.yaml b/deployment/helm/edc-controlplane/templates/ingress.yaml index b62ee2481..f395970ff 100644 --- a/deployment/helm/edc-controlplane/templates/ingress.yaml +++ b/deployment/helm/edc-controlplane/templates/ingress.yaml @@ -1,65 +1,72 @@ -{{- if .Values.ingress.enabled }} +{{- $fullName := include "edc-controlplane.fullname" . }} +{{- $labels := include "edc-controlplane.labels" . | nindent 4 }} +{{- $gitVersion := .Capabilities.KubeVersion.GitVersion }} +{{- $edcEndpoints := .Values.edc.endpoints }} +{{- range .Values.ingresses }} +{{- if and .enabled .endpoints }} +{{- $ingressName := printf "%s-%s" $fullName .hostname }} --- -{{- $fullName := include "edc-controlplane.fullname" . -}} -{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} - {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} - {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} - {{- end }} -{{- end }} -{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion }} +{{- if semverCompare ">=1.19-0" $gitVersion }} apiVersion: networking.k8s.io/v1 -{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion }} +{{- else if semverCompare ">=1.14-0" $gitVersion }} apiVersion: networking.k8s.io/v1beta1 {{- else }} apiVersion: extensions/v1beta1 {{- end }} kind: Ingress metadata: - name: {{ $fullName }} + name: {{ $ingressName }} labels: - {{- include "edc-controlplane.labels" . | nindent 4 }} + {{- $labels | nindent 2 }} annotations: - {{- with .Values.ingress.annotations }} - {{- toYaml . | nindent 4 }} + {{- if and .className (not (semverCompare ">=1.18-0" $gitVersion)) }} + {{- if not (hasKey .annotations "kubernetes.io/ingress.class") }} + {{- $_ := set .annotations "kubernetes.io/ingress.class" .className}} {{- end }} - {{- if .Values.ingress.certManager }} - {{- if .Values.ingress.certManager.issuer }} - cert-manager.io/issuer: {{ .Values.ingress.certManager.issuer }} {{- end }} - {{- if .Values.ingress.certManager.clusterIssuer }} - cert-manager.io/cluster-issuer: {{ .Values.ingress.certManager.clusterIssuer }} + {{- if .certManager }} + {{- if .certManager.issuer }} + {{- $_ := set .annotations "cert-manager.io/issuer" .certManager.issuer}} {{- end }} + {{- if .certManager.clusterIssuer }} + {{- $_ := set .annotations "cert-manager.io/cluster-issuer" .certManager.clusterIssuer}} + {{- end }} + {{- end }} + {{- with .annotations }} + {{- toYaml . | nindent 4 }} {{- end }} spec: - {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} - ingressClassName: {{ .Values.ingress.className }} + {{- if and .className (semverCompare ">=1.18-0" $gitVersion) }} + ingressClassName: {{ .className }} {{- end }} - {{- if .Values.ingress.hostname }} - {{- if .Values.ingress.tls }} + {{- if .hostname }} + {{- if .tls }} tls: - hosts: - - {{ .Values.ingress.hostname }} - secretName: {{ $fullName }}-tls + - {{ .hostname }} + secretName: {{ $ingressName }}-tls {{- end }} rules: - - host: {{ .Values.ingress.hostname }} + - host: {{ .hostname }} http: paths: - {{- range $name, $mapping := .Values.edc.endpoints }} - {{- if $mapping.ingress }} - - path: {{ $mapping.path }} - pathType: Prefix - backend: - {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} - service: - name: {{ $fullName }} - port: - number: {{ $mapping.port }} - {{- else }} - serviceName: {{ $fullName }} - servicePort: {{ $mapping.port }} - {{- end }} - {{- end }} + {{- $ingressEdcEndpoints := .endpoints }} + {{- range $name, $mapping := $edcEndpoints }} + {{- if (has $name $ingressEdcEndpoints) }} + - path: {{ $mapping.path }} + pathType: Prefix + backend: + {{- if semverCompare ">=1.19-0" $gitVersion }} + service: + name: {{ $fullName }} + port: + number: {{ $mapping.port }} + {{- else }} + serviceName: {{ $fullName }} + servicePort: {{ $mapping.port }} + {{- end }} {{- end }} {{- end }} -{{- end }} + {{- end }} +{{- end }}{{- /* end: if .enabled */}} +{{- end }}{{- /* end: range .Values.ingresses */}} diff --git a/deployment/helm/edc-controlplane/values.yaml b/deployment/helm/edc-controlplane/values.yaml index 11d350297..b0a6e21e4 100644 --- a/deployment/helm/edc-controlplane/values.yaml +++ b/deployment/helm/edc-controlplane/values.yaml @@ -52,18 +52,16 @@ serviceAccount: podAnnotations: {} -podSecurityContext: - {} - # fsGroup: 2000 +podSecurityContext: {} +# fsGroup: 2000 securityContext: - {} # capabilities: # drop: # - ALL # readOnlyRootFilesystem: true # runAsNonRoot: true - # runAsUser: 1000 + runAsUser: 1001 ## ## Whether to enable kubernetes liveness-probes @@ -126,44 +124,81 @@ service: type: ClusterIP ## -## Ingress declaration to publicly expose the network service. +## Ingress declaration to expose the network service. ## -ingress: - ## - ## Set to true to enable ingress record generation - ## - enabled: false - ## - ## Defines the ingress class to use https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class - ## - className: "" - ## - ## Enables TLS on the ingress resource - ## - tls: false - - ## - ## Adds cert-manager annotations to the ingress - ## - certManager: +ingresses: + ## Public / Internet facing Ingress + - enabled: true ## - ## If preset enables certificate generation via cert-manager namespace scoped issuer + ## The hostname to be used to precisely map incoming traffic onto the underlying network service ## - issuer: "" + hostname: "edc-controlplane.local" ## - ## If preset enables certificate generation via cert-manager cluster-wide issuer + ## Additional ingress annotations to add ## - clusterIssuer: "" - - ## - ## Additional ingress annotations to add - ## - annotations: {} + annotations: {} + ## + ## EDC endpoints exposed by this ingress resource + ## + endpoints: + - ids + ## + ## Defines the ingress class to use https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class + ## + className: "" + ## + ## Enables TLS on the ingress resource + ## + tls: false + ## + ## Adds cert-manager annotations to the ingress + ## + certManager: + ## + ## If preset enables certificate generation via cert-manager namespace scoped issuer + ## + issuer: "" + ## + ## If preset enables certificate generation via cert-manager cluster-wide issuer + ## + clusterIssuer: "" - ## - ## The hostname to be used to precisely map incoming traffic onto the underlying network service - ## - hostname: "edc-controlplane.local" + ## Private / Intranet facing Ingress + - enabled: false + ## + ## The hostname to be used to precisely map incoming traffic onto the underlying network service + ## + hostname: "edc-controlplane.intranet" + ## + ## Additional ingress annotations to add + ## + annotations: {} + ## + ## EDC endpoints exposed by this ingress resource + ## + endpoints: + - data + - control + ## + ## Defines the ingress class to use https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class + ## + className: "" + ## + ## Enables TLS on the ingress resource + ## + tls: false + ## + ## Adds cert-manager annotations to the ingress + ## + certManager: + ## + ## If preset enables certificate generation via cert-manager namespace scoped issuer + ## + issuer: "" + ## + ## If preset enables certificate generation via cert-manager cluster-wide issuer + ## + clusterIssuer: "" resources: {} diff --git a/deployment/helm/edc-dataplane/templates/NOTES.txt b/deployment/helm/edc-dataplane/templates/NOTES.txt index 5197611e0..454b250eb 100644 --- a/deployment/helm/edc-dataplane/templates/NOTES.txt +++ b/deployment/helm/edc-dataplane/templates/NOTES.txt @@ -6,15 +6,23 @@ APP VERSION: {{ .Chart.AppVersion }} Logs can be accessed by running this command: kubectl logs --tail 100 -f \ - --namespace {{ .Release.Namespace }} + --namespace {{ .Release.Namespace }} \ -l "app.kubernetes.io/name={{ include "edc-dataplane.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -{{- if .Values.ingress.enabled }} +{{- if .Values.ingresses }} Following ingress URLS are available: - {{- range $name, $mapping := .Values.edc.endpoints }} - {{- if $mapping.ingress }} - Visit http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $.Values.ingress.hostname }}{{ $mapping.path }} to access the {{ $name }} api + {{- $edcEndpoints := .Values.edc.endpoints }} + {{- range .Values.ingresses }} + {{- if .enabled }} + {{- $ingressEdcEndpoints := .endpoints }} + {{- $hostname := .hostname }} + {{- $tls := .tls }} + {{- range $name, $mapping := $edcEndpoints }} + {{- if (has $name $ingressEdcEndpoints) }} + Visit http{{ if $tls }}s{{ end }}://{{ $hostname }}{{ $mapping.path }} to access the {{ $name }} api + {{- end }} + {{- end }} {{- end }} {{- end }} diff --git a/deployment/helm/edc-dataplane/templates/ingress.yaml b/deployment/helm/edc-dataplane/templates/ingress.yaml index cdcafb45a..5558391ea 100644 --- a/deployment/helm/edc-dataplane/templates/ingress.yaml +++ b/deployment/helm/edc-dataplane/templates/ingress.yaml @@ -1,65 +1,72 @@ -{{- if .Values.ingress.enabled }} +{{- $fullName := include "edc-dataplane.fullname" . }} +{{- $labels := include "edc-dataplane.labels" . | nindent 4 }} +{{- $gitVersion := .Capabilities.KubeVersion.GitVersion }} +{{- $edcEndpoints := .Values.edc.endpoints }} +{{- range .Values.ingresses }} +{{- if and .enabled .endpoints }} +{{- $ingressName := printf "%s-%s" $fullName .hostname }} --- -{{- $fullName := include "edc-dataplane.fullname" . -}} -{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} - {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} - {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} - {{- end }} -{{- end }} -{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion }} +{{- if semverCompare ">=1.19-0" $gitVersion }} apiVersion: networking.k8s.io/v1 -{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion }} +{{- else if semverCompare ">=1.14-0" $gitVersion }} apiVersion: networking.k8s.io/v1beta1 {{- else }} apiVersion: extensions/v1beta1 {{- end }} kind: Ingress metadata: - name: {{ $fullName }} + name: {{ $ingressName }} labels: - {{- include "edc-dataplane.labels" . | nindent 4 }} + {{- $labels | nindent 2 }} annotations: - {{- with .Values.ingress.annotations }} - {{- toYaml . | nindent 4 }} + {{- if and .className (not (semverCompare ">=1.18-0" $gitVersion)) }} + {{- if not (hasKey .annotations "kubernetes.io/ingress.class") }} + {{- $_ := set .annotations "kubernetes.io/ingress.class" .className}} {{- end }} - {{- if .Values.ingress.certManager }} - {{- if .Values.ingress.certManager.issuer }} - cert-manager.io/issuer: {{ .Values.ingress.certManager.issuer }} {{- end }} - {{- if .Values.ingress.certManager.clusterIssuer }} - cert-manager.io/cluster-issuer: {{ .Values.ingress.certManager.clusterIssuer }} + {{- if .certManager }} + {{- if .certManager.issuer }} + {{- $_ := set .annotations "cert-manager.io/issuer" .certManager.issuer}} {{- end }} + {{- if .certManager.clusterIssuer }} + {{- $_ := set .annotations "cert-manager.io/cluster-issuer" .certManager.clusterIssuer}} + {{- end }} + {{- end }} + {{- with .annotations }} + {{- toYaml . | nindent 4 }} {{- end }} spec: - {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} - ingressClassName: {{ .Values.ingress.className }} + {{- if and .className (semverCompare ">=1.18-0" $gitVersion) }} + ingressClassName: {{ .className }} {{- end }} - {{- if .Values.ingress.hostname }} - {{- if .Values.ingress.tls }} + {{- if .hostname }} + {{- if .tls }} tls: - hosts: - - {{ .Values.ingress.hostname }} - secretName: {{ $fullName }}-tls + - {{ .hostname }} + secretName: {{ $ingressName }}-tls {{- end }} rules: - - host: {{ .Values.ingress.hostname }} + - host: {{ .hostname }} http: paths: - {{- range $name, $mapping := .Values.edc.endpoints }} - {{- if $mapping.ingress }} - - path: {{ $mapping.path }} - pathType: Prefix - backend: - {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} - service: - name: {{ $fullName }} - port: - number: {{ $mapping.port }} - {{- else }} - serviceName: {{ $fullName }} - servicePort: {{ $mapping.port }} - {{- end }} - {{- end }} + {{- $ingressEdcEndpoints := .endpoints }} + {{- range $name, $mapping := $edcEndpoints }} + {{- if (has $name $ingressEdcEndpoints) }} + - path: {{ $mapping.path }} + pathType: Prefix + backend: + {{- if semverCompare ">=1.19-0" $gitVersion }} + service: + name: {{ $fullName }} + port: + number: {{ $mapping.port }} + {{- else }} + serviceName: {{ $fullName }} + servicePort: {{ $mapping.port }} + {{- end }} {{- end }} {{- end }} -{{- end }} + {{- end }} +{{- end }}{{- /* end: if .enabled */}} +{{- end }}{{- /* end: range .Values.ingresses */}} diff --git a/deployment/helm/edc-dataplane/values.yaml b/deployment/helm/edc-dataplane/values.yaml index 16ebba9a3..782973e92 100644 --- a/deployment/helm/edc-dataplane/values.yaml +++ b/deployment/helm/edc-dataplane/values.yaml @@ -111,44 +111,44 @@ service: type: ClusterIP ## -## Ingress declaration to publicly expose the network service. +## Ingress declaration to expose the network service. ## -ingress: - ## - ## Set to true to enable ingress record generation - ## - enabled: false - ## - ## Defines the ingress class to use https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class - ## - className: "" - ## - ## Enables TLS on the ingress resource - ## - tls: false - - ## - ## Adds cert-manager annotations to the ingress - ## - certManager: +ingresses: + ## Public / Internet facing Ingress + - enabled: true ## - ## If preset enables certificate generation via cert-manager namespace scoped issuer + ## The hostname to be used to precisely map incoming traffic onto the underlying network service ## - issuer: "" + hostname: "edc-dataplane.local" ## - ## If preset enables certificate generation via cert-manager cluster-wide issuer + ## Additional ingress annotations to add ## - clusterIssuer: "" - - ## - ## Additional ingress annotations to add - ## - annotations: {} - - ## - ## The hostname to be used to precisely map incoming traffic onto the underlying network service - ## - hostname: "edc-dataplane.local" + annotations: {} + ## + ## EDC endpoints exposed by this ingress resource + ## + endpoints: + - public + ## + ## Defines the ingress class to use https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class + ## + className: "" + ## + ## Enables TLS on the ingress resource + ## + tls: false + ## + ## Adds cert-manager annotations to the ingress + ## + certManager: + ## + ## If preset enables certificate generation via cert-manager namespace scoped issuer + ## + issuer: "" + ## + ## If preset enables certificate generation via cert-manager cluster-wide issuer + ## + clusterIssuer: "" resources: {} @@ -161,7 +161,7 @@ resources: # memory: 128Mi # requests: # cpu: 100m -# memory: 128Mi + # memory: 128Mi autoscaling: enabled: false diff --git a/lintconf.yaml b/lintconf.yaml index 8fd25b1f8..71d5b1db7 100644 --- a/lintconf.yaml +++ b/lintconf.yaml @@ -19,7 +19,7 @@ rules: max-spaces-after: 1 comments: require-starting-space: true - min-spaces-from-content: 2 + min-spaces-from-content: 1 document-end: disable document-start: true # No --- to start a file empty-lines: From 492e834c4119d7fdd015d0b5c0430b4e636320bd Mon Sep 17 00:00:00 2001 From: Dominik Pinsel Date: Thu, 5 May 2022 10:05:19 +0200 Subject: [PATCH 068/433] update README.md Signed-off-by: Dominik Pinsel --- README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.md b/README.md index f16a5c5aa..4411747d7 100644 --- a/README.md +++ b/README.md @@ -27,13 +27,7 @@ not yet publish artifacts to a maven repository, which this project relies on, i to be built upfront to be used: ```shell -# Init / update git-submodule -[ ! -d "edc" ] && git submodule add https://github.com/eclipse-dataspaceconnector/DataSpaceConnector.git edc git submodule update --init -git -C edc fetch --all -git -C edc checkout milestone-3 - -# build edc 0.0.1-SNAPSHOT artifacts cd edc && ./gradlew publishToMavenLocal ``` From a482d87309aad60c64b875f6dd1525fd5c86eccd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 May 2022 15:50:03 +0200 Subject: [PATCH 069/433] Bump postgresql from 42.3.4 to 42.3.5 (#96) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bb412961a..bedcd3ff6 100644 --- a/pom.xml +++ b/pom.xml @@ -38,7 +38,7 @@ 0.0.1-SNAPSHOT 1.2.1 3.1.0 - 42.3.4 + 42.3.5 8.5.10 5.8.2 1.8.2 From fe089f1c0f549ab23987ee8990f271344c77ecba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 May 2022 15:52:24 +0200 Subject: [PATCH 070/433] Bump docker/metadata-action from 3 to 4 (#94) --- .github/workflows/build.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index d3524bcbf..048eba246 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -55,7 +55,7 @@ jobs: - name: edc-dataplane Docker Metadata id: edc_dataplane_meta - uses: docker/metadata-action@v3 + uses: docker/metadata-action@v4 with: images: | ghcr.io/catenax-ng/product-edc/edc-dataplane @@ -119,7 +119,7 @@ jobs: - name: edc-controlplane-memory Docker Metadata id: edc_controlplane_memory_meta - uses: docker/metadata-action@v3 + uses: docker/metadata-action@v4 with: images: | ghcr.io/catenax-ng/product-edc/edc-controlplane-memory @@ -183,7 +183,7 @@ jobs: - name: edc-controlplane-cosmosdb Docker Metadata id: edc_controlplane_cosmosdb_meta - uses: docker/metadata-action@v3 + uses: docker/metadata-action@v4 with: images: | ghcr.io/catenax-ng/product-edc/edc-controlplane-cosmosdb @@ -247,7 +247,7 @@ jobs: - name: edc-controlplane-postgresql Docker Metadata id: edc_controlplane_postgresql_meta - uses: docker/metadata-action@v3 + uses: docker/metadata-action@v4 with: images: | ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql From 70f569da340fc1a6e1a9151d91eb387c01acd396 Mon Sep 17 00:00:00 2001 From: "Florian Rusch (ZF Friedrichshafen AG)" Date: Thu, 5 May 2022 16:34:19 +0200 Subject: [PATCH 071/433] Expose validation endpoint through k8s service (#99) --- deployment/helm/edc-controlplane/templates/service.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/deployment/helm/edc-controlplane/templates/service.yaml b/deployment/helm/edc-controlplane/templates/service.yaml index 020a48026..7f6dafe1c 100644 --- a/deployment/helm/edc-controlplane/templates/service.yaml +++ b/deployment/helm/edc-controlplane/templates/service.yaml @@ -20,6 +20,10 @@ spec: targetPort: data protocol: TCP name: data + - port: {{ .Values.edc.endpoints.validation.port }} + targetPort: validation + protocol: TCP + name: validation - port: {{ .Values.edc.endpoints.ids.port }} targetPort: ids protocol: TCP From 3d05b951b9d0a409b5ae49e964d3f584b5f9fd9a Mon Sep 17 00:00:00 2001 From: "Florian Rusch (ZF Friedrichshafen AG)" Date: Thu, 5 May 2022 19:40:22 +0200 Subject: [PATCH 072/433] Remove keystore configurations These are not needed, because they are only used in the vault-fs extension of the edc. --- deployment/helm/edc-controlplane/values.yaml | 2 -- deployment/helm/edc-dataplane/values.yaml | 2 -- 2 files changed, 4 deletions(-) diff --git a/deployment/helm/edc-controlplane/values.yaml b/deployment/helm/edc-controlplane/values.yaml index b0a6e21e4..48d41adf9 100644 --- a/deployment/helm/edc-controlplane/values.yaml +++ b/deployment/helm/edc-controlplane/values.yaml @@ -335,8 +335,6 @@ configuration: # edc.ion.crawler.did-type= # edc.ion.crawler.interval-minutes= # edc.ion.crawler.ion.url= - # edc.keystore= - # edc.keystore.password= # edc.metrics.enabled= # edc.metrics.executor.enabled= # edc.metrics.jersey.enabled= diff --git a/deployment/helm/edc-dataplane/values.yaml b/deployment/helm/edc-dataplane/values.yaml index 782973e92..e63ce75d8 100644 --- a/deployment/helm/edc-dataplane/values.yaml +++ b/deployment/helm/edc-dataplane/values.yaml @@ -285,8 +285,6 @@ configuration: # edc.ion.crawler.did-type= # edc.ion.crawler.interval-minutes= # edc.ion.crawler.ion.url= - # edc.keystore= - # edc.keystore.password= # edc.metrics.enabled= # edc.metrics.executor.enabled= # edc.metrics.jersey.enabled= From 4fa798ec212ed98b3233c22ef3e3fbed24ffadfb Mon Sep 17 00:00:00 2001 From: Dominik Pinsel Date: Fri, 6 May 2022 10:36:40 +0200 Subject: [PATCH 073/433] bpn - register rules and update docu --- edc-controlplane/README.md | 7 ++ .../business-partner-validation/README.md | 78 ++++++++++++++++++- .../BusinessPartnerValidationExtension.java | 11 ++- ...usinessPartnerValidationExtensionTest.java | 13 ++-- 4 files changed, 101 insertions(+), 8 deletions(-) diff --git a/edc-controlplane/README.md b/edc-controlplane/README.md index d5aed8266..f715f983d 100644 --- a/edc-controlplane/README.md +++ b/edc-controlplane/README.md @@ -538,3 +538,10 @@ https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1251 **Workaround:** The DataManagement API can also initiate a contract negotiation using the actual policy object. + +## Non-IDS-Transformable-ContractDefinition causes connector to be unable to send out self-descriptions/catalogs + +https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1265 + +**Solution** +Delete non-transformable ContractDefinition or Policy. \ No newline at end of file diff --git a/edc-extensions/business-partner-validation/README.md b/edc-extensions/business-partner-validation/README.md index 420c25033..174ec30a9 100644 --- a/edc-extensions/business-partner-validation/README.md +++ b/edc-extensions/business-partner-validation/README.md @@ -69,4 +69,80 @@ Management API. ``` The business partner number of another connector is part of the DAPS token. Once a BPN constraint is used in an access -policy the connector checks the token before sending out contract offers. \ No newline at end of file +policy the connector checks the token before sending out contract offers. + +# Important: EDC Policies are input sensitive + +Please be aware that the EDC ignores all Rules and Constraint it does not understand. This could cause your constrained policies to be public. + +--- + +Example 1 for accidentially public: + +```json +{ + "uid": "1", + "prohibitions": [], + "obligations": [], + "permissions": [ + { + "edctype": "dataspaceconnector:permission", + "action": { + "type": "MY-USE" + }, + "constraints": [ + { + "edctype": "AtomicConstraint", + "leftExpression": { + "edctype": "dataspaceconnector:literalexpression", + "value": "BusinessPartnerNumber" + }, + "rightExpression": { + "edctype": "dataspaceconnector:literalexpression", + "value": "BPNLCDQ90000X42KU" + }, + "operator": "EQ" + } + ] + } + ] +} +``` + +This policy is public available, even though the constraint is described correct. The reason is, that this extension only registeres the Policy.Action `USE` within the EDC. Any other Action Type will have the EDC ignore the corresponding permission, hence interpret the polics as public policy. + +--- + +Example 2 for accidentially public: + +```json +{ + "uid": "1", + "prohibitions": [], + "obligations": [], + "permissions": [ + { + "edctype": "dataspaceconnector:permission", + "action": { + "type": "USE" + }, + "constraints": [ + { + "edctype": "AtomicConstraint", + "leftExpression": { + "edctype": "dataspaceconnector:literalexpression", + "value": "BusinesPartnerNumber" + }, + "rightExpression": { + "edctype": "dataspaceconnector:literalexpression", + "value": "BPNLCDQ90000X42KU" + }, + "operator": "EQ" + } + ] + } + ] +} +``` + +This policy is public available, too. The cause is a typo in the left-expression of the constraint. This extension only registeres the Constraint.LeftExpression `BusinessPartnerNumber` within the EDC. Any other term will have the EDC ignore the corresponding constraint, hence interpret the polics as public policy. diff --git a/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/BusinessPartnerValidationExtension.java b/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/BusinessPartnerValidationExtension.java index 48aca1b33..16343530d 100644 --- a/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/BusinessPartnerValidationExtension.java +++ b/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/BusinessPartnerValidationExtension.java @@ -24,9 +24,12 @@ import org.eclipse.dataspaceconnector.policy.model.Prohibition; import org.eclipse.dataspaceconnector.spi.monitor.Monitor; import org.eclipse.dataspaceconnector.spi.policy.PolicyEngine; +import org.eclipse.dataspaceconnector.spi.policy.RuleBindingRegistry; +import org.eclipse.dataspaceconnector.spi.system.Requires; import org.eclipse.dataspaceconnector.spi.system.ServiceExtension; import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; +@Requires({RuleBindingRegistry.class, PolicyEngine.class}) public class BusinessPartnerValidationExtension implements ServiceExtension { /** @@ -38,14 +41,14 @@ public class BusinessPartnerValidationExtension implements ServiceExtension { *

    * {
    *     "constraint": {
-   *         "leftOperand": "BusinessPartner",
+   *         "leftOperand": "BusinessPartnerNumber",
    *         "operator": "EQ",
    *         "rightOperand": "BPNLCDQ90000X42KU"
    *     }
    * }
    * 
*/ - private static final String BUSINESS_PARTNER_CONSTRAINT_KEY = "BusinessPartner"; + public static final String BUSINESS_PARTNER_CONSTRAINT_KEY = "BusinessPartnerNumber"; @Override public String name() { @@ -57,6 +60,7 @@ public void initialize(ServiceExtensionContext context) { final Monitor monitor = context.getMonitor(); final PolicyEngine policyEngine = context.getService(PolicyEngine.class); + final RuleBindingRegistry ruleBindingRegistry = context.getService(RuleBindingRegistry.class); final BusinessPartnerDutyFunction dutyFunction = new BusinessPartnerDutyFunction(monitor); final BusinessPartnerPermissionFunction permissionFunction = @@ -64,6 +68,9 @@ public void initialize(ServiceExtensionContext context) { final BusinessPartnerProhibitionFunction prohibitionFunction = new BusinessPartnerProhibitionFunction(monitor); + ruleBindingRegistry.bind("USE", ALL_SCOPES); + ruleBindingRegistry.bind("BusinessPartnerNumber", ALL_SCOPES); + policyEngine.registerFunction( ALL_SCOPES, Duty.class, BUSINESS_PARTNER_CONSTRAINT_KEY, dutyFunction); policyEngine.registerFunction( diff --git a/edc-extensions/business-partner-validation/src/test/java/net/catenax/edc/validation/businesspartner/BusinessPartnerValidationExtensionTest.java b/edc-extensions/business-partner-validation/src/test/java/net/catenax/edc/validation/businesspartner/BusinessPartnerValidationExtensionTest.java index 6cfe5c0f0..44086fc39 100644 --- a/edc-extensions/business-partner-validation/src/test/java/net/catenax/edc/validation/businesspartner/BusinessPartnerValidationExtensionTest.java +++ b/edc-extensions/business-partner-validation/src/test/java/net/catenax/edc/validation/businesspartner/BusinessPartnerValidationExtensionTest.java @@ -19,6 +19,7 @@ import org.eclipse.dataspaceconnector.policy.model.Prohibition; import org.eclipse.dataspaceconnector.spi.monitor.Monitor; import org.eclipse.dataspaceconnector.spi.policy.PolicyEngine; +import org.eclipse.dataspaceconnector.spi.policy.RuleBindingRegistry; import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -26,24 +27,26 @@ public class BusinessPartnerValidationExtensionTest { - private static final String BUSINESS_PARTNER_CONSTRAINT_KEY = "BusinessPartner"; - private BusinessPartnerValidationExtension extension; // mocks private ServiceExtensionContext serviceExtensionContext; private PolicyEngine policyEngine; + private RuleBindingRegistry ruleBindingRegistry; @BeforeEach public void setup() { policyEngine = Mockito.mock(PolicyEngine.class); + ruleBindingRegistry = Mockito.mock(RuleBindingRegistry.class); final Monitor monitor = Mockito.mock(Monitor.class); serviceExtensionContext = Mockito.mock(ServiceExtensionContext.class); Mockito.when(serviceExtensionContext.getMonitor()).thenReturn(monitor); Mockito.when(serviceExtensionContext.getService(PolicyEngine.class)).thenReturn(policyEngine); + Mockito.when(serviceExtensionContext.getService(RuleBindingRegistry.class)) + .thenReturn(ruleBindingRegistry); extension = new BusinessPartnerValidationExtension(); } @@ -59,7 +62,7 @@ public void testRegisterDutyFunction() { .registerFunction( Mockito.anyString(), Mockito.eq(Duty.class), - Mockito.eq(BUSINESS_PARTNER_CONSTRAINT_KEY), + Mockito.eq(BusinessPartnerValidationExtension.BUSINESS_PARTNER_CONSTRAINT_KEY), Mockito.any()); } @@ -74,7 +77,7 @@ public void testRegisterPermissionFunction() { .registerFunction( Mockito.anyString(), Mockito.eq(Permission.class), - Mockito.eq(BUSINESS_PARTNER_CONSTRAINT_KEY), + Mockito.eq(BusinessPartnerValidationExtension.BUSINESS_PARTNER_CONSTRAINT_KEY), Mockito.any()); } @@ -89,7 +92,7 @@ public void testRegisterProhibitionFunction() { .registerFunction( Mockito.anyString(), Mockito.eq(Prohibition.class), - Mockito.eq(BUSINESS_PARTNER_CONSTRAINT_KEY), + Mockito.eq(BusinessPartnerValidationExtension.BUSINESS_PARTNER_CONSTRAINT_KEY), Mockito.any()); } } From be038a78a4b2bb3c518b995ec6a794ab406638a7 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Fri, 6 May 2022 13:00:34 +0200 Subject: [PATCH 074/433] Replace dockerfile-maven-plugin by exec-maven-plugin for building docker images (#93) --- .../edc-controlplane-cosmosdb/pom.xml | 46 ++++++++++++------- .../edc-controlplane-memory/pom.xml | 46 ++++++++++++------- .../edc-controlplane-postgresql/pom.xml | 46 ++++++++++++------- edc-dataplane/pom.xml | 46 ++++++++++++------- edc-extensions/aas-controller/pom.xml | 4 -- pom.xml | 18 +++++--- 6 files changed, 127 insertions(+), 79 deletions(-) diff --git a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml index 9aa9873b2..caaba9e1f 100644 --- a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml +++ b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml @@ -293,39 +293,51 @@ - com.spotify - dockerfile-maven-plugin + org.codehaus.mojo + exec-maven-plugin + - ${project.artifactId}:${project.version} + docker-build-${project.artifactId}:${project.version} package - build - tag + exec - ${project.version} + docker + ${project.basedir} + + build + -f + src/main/docker/Dockerfile + --build-arg + JAR=target/${project.artifactId}.jar + -t + ${project.artifactId}:${project.version} + . + - ${project.artifactId}:latest + docker-tag-${project.artifactId}:latest package - tag + exec - latest + docker + ${project.basedir} + + tag + ${project.artifactId}:${project.version} + ${project.artifactId}:latest + - - . - src/main/docker/Dockerfile - ${project.artifactId} - - target/${project.artifactId}.jar - - diff --git a/edc-controlplane/edc-controlplane-memory/pom.xml b/edc-controlplane/edc-controlplane-memory/pom.xml index 1cc04a72a..894d339a1 100644 --- a/edc-controlplane/edc-controlplane-memory/pom.xml +++ b/edc-controlplane/edc-controlplane-memory/pom.xml @@ -235,39 +235,51 @@ - com.spotify - dockerfile-maven-plugin + org.codehaus.mojo + exec-maven-plugin + - ${project.artifactId}:${project.version} + docker-build-${project.artifactId}:${project.version} package - build - tag + exec - ${project.version} + docker + ${project.basedir} + + build + -f + src/main/docker/Dockerfile + --build-arg + JAR=target/${project.artifactId}.jar + -t + ${project.artifactId}:${project.version} + . + - ${project.artifactId}:latest + docker-tag-${project.artifactId}:latest package - tag + exec - latest + docker + ${project.basedir} + + tag + ${project.artifactId}:${project.version} + ${project.artifactId}:latest + - - . - src/main/docker/Dockerfile - ${project.artifactId} - - target/${project.artifactId}.jar - - diff --git a/edc-controlplane/edc-controlplane-postgresql/pom.xml b/edc-controlplane/edc-controlplane-postgresql/pom.xml index 998e8cd33..9d5f87e96 100644 --- a/edc-controlplane/edc-controlplane-postgresql/pom.xml +++ b/edc-controlplane/edc-controlplane-postgresql/pom.xml @@ -280,39 +280,51 @@ - com.spotify - dockerfile-maven-plugin + org.codehaus.mojo + exec-maven-plugin + - ${project.artifactId}:${project.version} + docker-build-${project.artifactId}:${project.version} package - build - tag + exec - ${project.version} + docker + ${project.basedir} + + build + -f + src/main/docker/Dockerfile + --build-arg + JAR=target/${project.artifactId}.jar + -t + ${project.artifactId}:${project.version} + . + - ${project.artifactId}:latest + docker-tag-${project.artifactId}:latest package - tag + exec - latest + docker + ${project.basedir} + + tag + ${project.artifactId}:${project.version} + ${project.artifactId}:latest + - - . - src/main/docker/Dockerfile - ${project.artifactId} - - target/${project.artifactId}.jar - - diff --git a/edc-dataplane/pom.xml b/edc-dataplane/pom.xml index 25dfe6672..6537909ea 100644 --- a/edc-dataplane/pom.xml +++ b/edc-dataplane/pom.xml @@ -177,39 +177,51 @@ - com.spotify - dockerfile-maven-plugin + org.codehaus.mojo + exec-maven-plugin + - ${project.artifactId}:${project.version} + docker-build-${project.artifactId}:${project.version} package - build - tag + exec - ${project.version} + docker + ${project.basedir} + + build + -f + src/main/docker/Dockerfile + --build-arg + JAR=target/${project.artifactId}.jar + -t + ${project.artifactId}:${project.version} + . + - ${project.artifactId}:latest + docker-tag-${project.artifactId}:latest package - tag + exec - latest + docker + ${project.basedir} + + tag + ${project.artifactId}:${project.version} + ${project.artifactId}:latest + - - . - src/main/docker/Dockerfile - ${project.artifactId} - - target/${project.artifactId}.jar - - diff --git a/edc-extensions/aas-controller/pom.xml b/edc-extensions/aas-controller/pom.xml index eda62f3d6..da2a685d2 100644 --- a/edc-extensions/aas-controller/pom.xml +++ b/edc-extensions/aas-controller/pom.xml @@ -38,10 +38,6 @@ org.eclipse.dataspaceconnector contract-spi
- - org.eclipse.dataspaceconnector - contract-spi - org.eclipse.dataspaceconnector dataloading diff --git a/pom.xml b/pom.xml index bedcd3ff6..a62efa4f5 100644 --- a/pom.xml +++ b/pom.xml @@ -29,17 +29,21 @@ 11 11 + UTF-8 - 3.3.0 - 1.4.13 2.22.4 + 3.3.0 + 3.0.0 + 0.0.1-SNAPSHOT 1.2.1 3.1.0 42.3.5 8.5.10 + + 5.8.2 1.8.2 1.10.19 @@ -82,16 +86,16 @@ + + org.codehaus.mojo + exec-maven-plugin + ${org.codehaus.mojo.exec.maven.plugin.version} + org.apache.maven.plugins maven-shade-plugin ${org.apache.maven.plugins.shade.version} - - com.spotify - dockerfile-maven-plugin - ${com.spotify.dockerfile.maven.plugin.version} - com.diffplug.spotless spotless-maven-plugin From b679a7bd151e19708fbafa1d41b15c33ea321d30 Mon Sep 17 00:00:00 2001 From: "Florian Rusch (ZF Friedrichshafen AG)" Date: Fri, 6 May 2022 13:01:31 +0200 Subject: [PATCH 075/433] Restart pods if configmap changed (#100) --- deployment/helm/edc-controlplane/templates/deployment.yaml | 6 ++++-- deployment/helm/edc-dataplane/templates/deployment.yaml | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/deployment/helm/edc-controlplane/templates/deployment.yaml b/deployment/helm/edc-controlplane/templates/deployment.yaml index 1dd351201..7eb27a307 100644 --- a/deployment/helm/edc-controlplane/templates/deployment.yaml +++ b/deployment/helm/edc-controlplane/templates/deployment.yaml @@ -14,10 +14,12 @@ spec: {{- include "edc-controlplane.selectorLabels" . | nindent 6 }} template: metadata: - {{- with .Values.podAnnotations }} annotations: + checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + checksum/env-config: {{ include (print $.Template.BasePath "/configmap-env.yaml") . | sha256sum }} + {{- with .Values.podAnnotations }} {{- toYaml . | nindent 8 }} - {{- end }} + {{- end }} labels: {{- include "edc-controlplane.selectorLabels" . | nindent 8 }} spec: diff --git a/deployment/helm/edc-dataplane/templates/deployment.yaml b/deployment/helm/edc-dataplane/templates/deployment.yaml index 3bf283165..3f1b284d8 100644 --- a/deployment/helm/edc-dataplane/templates/deployment.yaml +++ b/deployment/helm/edc-dataplane/templates/deployment.yaml @@ -14,10 +14,12 @@ spec: {{- include "edc-dataplane.selectorLabels" . | nindent 6 }} template: metadata: - {{- with .Values.podAnnotations }} annotations: + checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + checksum/env-config: {{ include (print $.Template.BasePath "/configmap-env.yaml") . | sha256sum }} + {{- with .Values.podAnnotations }} {{- toYaml . | nindent 8 }} - {{- end }} + {{- end }} labels: {{- include "edc-dataplane.selectorLabels" . | nindent 8 }} spec: From 59c7cd5e78ba8e3b3f32cc34faf7a485232b226d Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Mon, 9 May 2022 14:20:30 +0200 Subject: [PATCH 076/433] Integrate veracode into gh build workflow (#102) --- .github/workflows/build.yaml | 115 +++++++++++++++++++++++++++++++---- 1 file changed, 103 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 048eba246..84be6a965 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -13,10 +13,26 @@ on: - '*' jobs: + secret-presence: + runs-on: ubuntu-latest + outputs: + CXNG_GHCR_PAT: ${{ steps.secret-present.outputs.CXNG_GHCR_PAT }} + VERACODE_ID: ${{ steps.secret-present.outputs.VERACODE_ID }} + VERACODE_SECRET_KEY: ${{ steps.secret-present.outputs.VERACODE_SECRET_KEY }} + steps: + - name: Check whether secrets exist + id: secret-present + run: | + [ ! -z "${{ secrets.CXNG_GHCR_PAT }}" ] && echo "::set-output name=CXNG_GHCR_PAT::true" + [ ! -z "${{ secrets.VERACODE_ID }}" ] && echo "::set-output name=VERACODE_ID::true" + [ ! -z "${{ secrets.VERACODE_SECRET_KEY }}" ] && echo "::set-output name=VERACODE_SECRET_KEY::true" + + exit 0 ##################### ### edc-dataplane ### ##################### build-edc-dataplane: + needs: [ secret-presence ] runs-on: ubuntu-latest steps: # Set-Up @@ -27,7 +43,8 @@ jobs: submodules: recursive - name: Login to Github Packages - if: github.event_name != 'pull_request' + if: | + needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' uses: docker/login-action@v1 with: registry: ghcr.io @@ -58,7 +75,7 @@ jobs: uses: docker/metadata-action@v4 with: images: | - ghcr.io/catenax-ng/product-edc/edc-dataplane + ghcr.io/${{ github.repository }}/edc-dataplane tags: | type=ref,event=branch type=ref,event=pr @@ -73,14 +90,32 @@ jobs: file: edc-dataplane/src/main/docker/Dockerfile build-args: | JAR=edc-dataplane/target/edc-dataplane.jar - push: ${{ github.event_name != 'pull_request' && github.repository_owner == 'product-edc' }} + push: | + ${{ secrets.CXNG_GHCR_PAT && github.event_name != 'pull_request' }} tags: ${{ steps.edc_dataplane_meta.outputs.tags }} labels: ${{ steps.edc_dataplane_meta.outputs.labels }} + - + name: Veracode Upload And Scan + uses: veracode/veracode-uploadandscan-action@0.2.1 + if: | + needs.secret-presence.outputs.VERACODE_ID && needs.secret-presence.outputs.VERACODE_SECRET_KEY && contains(' + refs/heads/develop + refs/heads/release/ + refs/tags/ + refs/heads/main', github.ref) + with: + appname: 'product-edc/edc-dataplane' + createprofile: true + version: ${{ github.ref }} + filepath: edc-dataplane/target/edc-dataplane.jar + vid: ${{ secrets.VERACODE_ID }} + vkey: ${{ secrets.VERACODE_SECRET_KEY }} ################################# ### edc-controlplane-memory ### ################################# build-edc-controlplane-memory: + needs: [ secret-presence ] runs-on: ubuntu-latest steps: # Set-Up @@ -91,7 +126,8 @@ jobs: submodules: recursive - name: Login to Github Packages - if: github.event_name != 'pull_request' + if: | + needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' uses: docker/login-action@v1 with: registry: ghcr.io @@ -122,7 +158,7 @@ jobs: uses: docker/metadata-action@v4 with: images: | - ghcr.io/catenax-ng/product-edc/edc-controlplane-memory + ghcr.io/${{ github.repository }}/edc-controlplane-memory tags: | type=ref,event=branch type=ref,event=pr @@ -137,14 +173,32 @@ jobs: file: edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile build-args: | JAR=edc-controlplane/edc-controlplane-memory/target/edc-controlplane-memory.jar - push: ${{ github.event_name != 'pull_request' && github.repository == 'catenax-ng/product-edc' }} + push: | + ${{ secrets.CXNG_GHCR_PAT && github.event_name != 'pull_request' }} tags: ${{ steps.edc_controlplane_memory_meta.outputs.tags }} labels: ${{ steps.edc_controlplane_memory_meta.outputs.labels }} + - + name: Veracode Upload And Scan + uses: veracode/veracode-uploadandscan-action@0.2.1 + if: | + needs.secret-presence.outputs.VERACODE_ID && needs.secret-presence.outputs.VERACODE_SECRET_KEY && contains(' + refs/heads/develop + refs/heads/release/ + refs/tags/ + refs/heads/main', github.ref) + with: + appname: 'product-edc/edc-controlplane-memory' + createprofile: true + version: ${{ github.ref }} + filepath: edc-controlplane/edc-controlplane-memory/target/edc-controlplane-memory.jar + vid: ${{ secrets.VERACODE_ID }} + vkey: ${{ secrets.VERACODE_SECRET_KEY }} ################################# ### edc-controlplane-cosmosdb ### ################################# build-edc-controlplane-cosmosdb: + needs: [ secret-presence ] runs-on: ubuntu-latest steps: # Set-Up @@ -155,7 +209,8 @@ jobs: submodules: recursive - name: Login to Github Packages - if: github.event_name != 'pull_request' + if: | + needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' uses: docker/login-action@v1 with: registry: ghcr.io @@ -186,7 +241,7 @@ jobs: uses: docker/metadata-action@v4 with: images: | - ghcr.io/catenax-ng/product-edc/edc-controlplane-cosmosdb + ghcr.io/${{ github.repository }}/edc-controlplane-cosmosdb tags: | type=ref,event=branch type=ref,event=pr @@ -201,14 +256,32 @@ jobs: file: edc-controlplane/edc-controlplane-cosmosdb/src/main/docker/Dockerfile build-args: | JAR=edc-controlplane/edc-controlplane-cosmosdb/target/edc-controlplane-cosmosdb.jar - push: ${{ github.event_name != 'pull_request' && github.repository == 'catenax-ng/product-edc' }} + push: | + ${{ secrets.CXNG_GHCR_PAT && github.event_name != 'pull_request' }} tags: ${{ steps.edc_controlplane_cosmosdb_meta.outputs.tags }} labels: ${{ steps.edc_controlplane_cosmosdb_meta.outputs.labels }} + - + name: Veracode Upload And Scan + uses: veracode/veracode-uploadandscan-action@0.2.1 + if: | + needs.secret-presence.outputs.VERACODE_ID && needs.secret-presence.outputs.VERACODE_SECRET_KEY && contains(' + refs/heads/develop + refs/heads/release/ + refs/tags/ + refs/heads/main', github.ref) + with: + appname: 'product-edc/edc-controlplane-cosmosdb' + createprofile: true + filepath: edc-controlplane/edc-controlplane-cosmosdb/target/edc-controlplane-cosmosdb.jar + version: ${{ github.ref }} + vid: ${{ secrets.VERACODE_ID }} + vkey: ${{ secrets.VERACODE_SECRET_KEY }} ################################### ### edc-controlplane-postgresql ### ################################### build-edc-controlplane-postgresql: + needs: [ secret-presence ] runs-on: ubuntu-latest steps: # Set-Up @@ -219,7 +292,8 @@ jobs: submodules: recursive - name: Login to Github Packages - if: github.event_name != 'pull_request' + if: | + needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' uses: docker/login-action@v1 with: registry: ghcr.io @@ -250,7 +324,7 @@ jobs: uses: docker/metadata-action@v4 with: images: | - ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql + ghcr.io/${{ github.repository }}/edc-controlplane-postgresql tags: | type=ref,event=branch type=ref,event=pr @@ -265,6 +339,23 @@ jobs: file: edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile build-args: | JAR=edc-controlplane/edc-controlplane-postgresql/target/edc-controlplane-postgresql.jar - push: ${{ github.event_name != 'pull_request' && github.repository == 'catenax-ng/product-edc' }} + push: | + ${{ secrets.CXNG_GHCR_PAT && github.event_name != 'pull_request' }} tags: ${{ steps.edc_controlplane_postgresql_meta.outputs.tags }} labels: ${{ steps.edc_controlplane_postgresql_meta.outputs.labels }} + - + name: Veracode Upload And Scan + uses: veracode/veracode-uploadandscan-action@0.2.1 + if: | + needs.secret-presence.outputs.VERACODE_ID && needs.secret-presence.outputs.VERACODE_SECRET_KEY && contains(' + refs/heads/develop + refs/heads/release/ + refs/tags/ + refs/heads/main', github.ref) + with: + appname: 'product-edc/edc-controlplane-postgresql' + createprofile: true + filepath: edc-controlplane/edc-controlplane-postgresql/target/edc-controlplane-postgresql.jar + version: ${{ github.ref }} + vid: ${{ secrets.VERACODE_ID }} + vkey: ${{ secrets.VERACODE_SECRET_KEY }} From 50712a0ffcce928bc0dc8589c65b93c02623d419 Mon Sep 17 00:00:00 2001 From: Dominik Pinsel Date: Mon, 9 May 2022 16:15:27 +0200 Subject: [PATCH 077/433] update documentation and schema for postgresql --- edc-controlplane/README.md | 9 ++++++++- ...ctNegotation_Contract_Agreement_Id_Schema.sql | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractnegotiation/V0_0_2__Alter_ContractNegotation_Contract_Agreement_Id_Schema.sql diff --git a/edc-controlplane/README.md b/edc-controlplane/README.md index f715f983d..63c727dc6 100644 --- a/edc-controlplane/README.md +++ b/edc-controlplane/README.md @@ -544,4 +544,11 @@ The DataManagement API can also initiate a contract negotiation using the actual https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1265 **Solution** -Delete non-transformable ContractDefinition or Policy. \ No newline at end of file +Delete non-transformable ContractDefinition or Policy. + +## Contract Negotatiation broken for SQL Control Plane + +https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1270 + +**Solution** +No solution. Please use another control plane. \ No newline at end of file diff --git a/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractnegotiation/V0_0_2__Alter_ContractNegotation_Contract_Agreement_Id_Schema.sql b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractnegotiation/V0_0_2__Alter_ContractNegotation_Contract_Agreement_Id_Schema.sql new file mode 100644 index 000000000..a4589d0a5 --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractnegotiation/V0_0_2__Alter_ContractNegotation_Contract_Agreement_Id_Schema.sql @@ -0,0 +1,16 @@ +-- +-- Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +-- +-- This program and the accompanying materials are made available under the +-- terms of the Apache License, Version 2.0 which is available at +-- https://www.apache.org/licenses/LICENSE-2.0 +-- +-- SPDX-License-Identifier: Apache-2.0 +-- +-- Contributors: +-- Mercedes-Benz Tech Innovation GmbH - Migrate edc_contract_negotiation to contain agreement_id +-- + +-- Add new agreement_id +ALTER TABLE edc_contract_agreement ADD agreement_id VARCHAR(255) DEFAULT NULL; +ALTER TABLE edc_contract_negotiation ALTER COLUMN correlation_id DROP NOT NULL; \ No newline at end of file From a2e1c36f18d9e8e196aed45d14740371e94ccc94 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Mon, 9 May 2022 16:44:17 +0200 Subject: [PATCH 078/433] Fix build workflow due to broken docker push action (#115) --- .github/workflows/build.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 84be6a965..ae79ff6a3 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -91,7 +91,7 @@ jobs: build-args: | JAR=edc-dataplane/target/edc-dataplane.jar push: | - ${{ secrets.CXNG_GHCR_PAT && github.event_name != 'pull_request' }} + ${{ needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' }} tags: ${{ steps.edc_dataplane_meta.outputs.tags }} labels: ${{ steps.edc_dataplane_meta.outputs.labels }} - @@ -174,7 +174,7 @@ jobs: build-args: | JAR=edc-controlplane/edc-controlplane-memory/target/edc-controlplane-memory.jar push: | - ${{ secrets.CXNG_GHCR_PAT && github.event_name != 'pull_request' }} + ${{ needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' }} tags: ${{ steps.edc_controlplane_memory_meta.outputs.tags }} labels: ${{ steps.edc_controlplane_memory_meta.outputs.labels }} - @@ -257,7 +257,7 @@ jobs: build-args: | JAR=edc-controlplane/edc-controlplane-cosmosdb/target/edc-controlplane-cosmosdb.jar push: | - ${{ secrets.CXNG_GHCR_PAT && github.event_name != 'pull_request' }} + ${{ needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' }} tags: ${{ steps.edc_controlplane_cosmosdb_meta.outputs.tags }} labels: ${{ steps.edc_controlplane_cosmosdb_meta.outputs.labels }} - @@ -340,7 +340,7 @@ jobs: build-args: | JAR=edc-controlplane/edc-controlplane-postgresql/target/edc-controlplane-postgresql.jar push: | - ${{ secrets.CXNG_GHCR_PAT && github.event_name != 'pull_request' }} + ${{ needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' }} tags: ${{ steps.edc_controlplane_postgresql_meta.outputs.tags }} labels: ${{ steps.edc_controlplane_postgresql_meta.outputs.labels }} - From 0bcbf4485b7a57ec43f11ec7492011625f33bb70 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Mon, 9 May 2022 17:06:48 +0200 Subject: [PATCH 079/433] Change gh build workflow: Allow Veracode scan to fail (#116) --- .github/workflows/build.yaml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index ae79ff6a3..aed425394 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -42,7 +42,7 @@ jobs: with: submodules: recursive - - name: Login to Github Packages + name: Login to GitHub Container Registry if: | needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' uses: docker/login-action@v1 @@ -103,6 +103,7 @@ jobs: refs/heads/release/ refs/tags/ refs/heads/main', github.ref) + continue-on-error: true with: appname: 'product-edc/edc-dataplane' createprofile: true @@ -125,7 +126,7 @@ jobs: with: submodules: recursive - - name: Login to Github Packages + name: Login to GitHub Container Registry if: | needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' uses: docker/login-action@v1 @@ -186,6 +187,7 @@ jobs: refs/heads/release/ refs/tags/ refs/heads/main', github.ref) + continue-on-error: true with: appname: 'product-edc/edc-controlplane-memory' createprofile: true @@ -208,7 +210,7 @@ jobs: with: submodules: recursive - - name: Login to Github Packages + name: Login to GitHub Container Registry if: | needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' uses: docker/login-action@v1 @@ -269,6 +271,7 @@ jobs: refs/heads/release/ refs/tags/ refs/heads/main', github.ref) + continue-on-error: true with: appname: 'product-edc/edc-controlplane-cosmosdb' createprofile: true @@ -352,6 +355,7 @@ jobs: refs/heads/release/ refs/tags/ refs/heads/main', github.ref) + continue-on-error: true with: appname: 'product-edc/edc-controlplane-postgresql' createprofile: true From 27e446f3eee298e02b80f9c65a450d6abeaec1ef Mon Sep 17 00:00:00 2001 From: Zied-Belkhiria-Mhp <95630225+Zied-Belkhiria-Mhp@users.noreply.github.com> Date: Mon, 9 May 2022 17:13:28 +0200 Subject: [PATCH 080/433] Create SECURITY.md (#113) Proposal of a security proposal. This is the template provided by the Catena-X Security Expert team. --- SECURITY.md | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..ebfd8b30b --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,63 @@ +# Security Policy + + + + +## Reporting a bug in Catena-X + + + + +Report security bugs in Catena-X to "dl_CoP_IT_Security@catena-x.net". + +Your report will be acknowledged within 5 days, and you’ll receive a more detailed response to your report within 10 days indicating the next steps in handling your submission. + +After the initial reply to your report, the security team will endeavor to keep you informed of the progress being made towards a fix and full announcement, and may ask for additional information or guidance surrounding the reported issue. + +Please do not report security bugs through public GitHub issues. + + + + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + +- Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + +- Full paths of source file(s) related to the manifestation of the issue + +- The location of the affected source code (tag/branch/commit or direct URL) + +- Any special configuration required to reproduce the issue + +- Step-by-step instructions to reproduce the issue + +- Proof-of-concept or exploit code (if possible) + +- Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + + + + +## Reporting a bug in a third party module + +Security bugs in third party modules should be reported to their respective maintainers. + + + + +## Disclosure policy + +Here is the security disclosure policy for Catena-X. + +- The security report is received and is assigned a primary handler. + +- This person will coordinate the fix and release process. + +- Fixes are prepared for all releases which are still under maintenance. + +- A suggested embargo date for this vulnerability is chosen. Typically the embargo date will be set to 72 hours. However, this may vary depending on the severity of the bug or difficulty in applying a fix. + +This process can take some time, especially when coordination is required with maintainers of other projects. +Every effort will be made to handle the bug in as timely a manner as possible; however, it’s important that we follow the release process above to ensure that the disclosure is handled in a consistent manner. From 25ddef4588d641c73ba9d14c832543d6e1e00e6e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 May 2022 15:14:33 +0000 Subject: [PATCH 081/433] Bump docker/build-push-action from 2 to 3 Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 2 to 3. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v2...v3) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index aed425394..730b12f7e 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -84,7 +84,7 @@ jobs: type=sha - name: Build edc-dataplane Docker Image - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v3 with: context: . file: edc-dataplane/src/main/docker/Dockerfile @@ -168,7 +168,7 @@ jobs: type=sha - name: Build edc-controlplane-memory Docker Image - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v3 with: context: . file: edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile @@ -252,7 +252,7 @@ jobs: type=sha - name: Build edc-controlplane-cosmosdb Docker Image - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v3 with: context: . file: edc-controlplane/edc-controlplane-cosmosdb/src/main/docker/Dockerfile @@ -336,7 +336,7 @@ jobs: type=sha - name: Build edc-controlplane-postgresql Docker Image - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v3 with: context: . file: edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile From 7ae9e4cdf81d5300b189a80b032b68c0b86cdff3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 May 2022 09:26:48 +0200 Subject: [PATCH 082/433] Bump veracode/veracode-uploadandscan-action from 0.2.1 to 1.0 (#111) --- .github/workflows/build.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index aed425394..db0c5baa9 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -96,7 +96,7 @@ jobs: labels: ${{ steps.edc_dataplane_meta.outputs.labels }} - name: Veracode Upload And Scan - uses: veracode/veracode-uploadandscan-action@0.2.1 + uses: veracode/veracode-uploadandscan-action@v1.0 if: | needs.secret-presence.outputs.VERACODE_ID && needs.secret-presence.outputs.VERACODE_SECRET_KEY && contains(' refs/heads/develop @@ -180,7 +180,7 @@ jobs: labels: ${{ steps.edc_controlplane_memory_meta.outputs.labels }} - name: Veracode Upload And Scan - uses: veracode/veracode-uploadandscan-action@0.2.1 + uses: veracode/veracode-uploadandscan-action@v1.0 if: | needs.secret-presence.outputs.VERACODE_ID && needs.secret-presence.outputs.VERACODE_SECRET_KEY && contains(' refs/heads/develop @@ -264,7 +264,7 @@ jobs: labels: ${{ steps.edc_controlplane_cosmosdb_meta.outputs.labels }} - name: Veracode Upload And Scan - uses: veracode/veracode-uploadandscan-action@0.2.1 + uses: veracode/veracode-uploadandscan-action@v1.0 if: | needs.secret-presence.outputs.VERACODE_ID && needs.secret-presence.outputs.VERACODE_SECRET_KEY && contains(' refs/heads/develop @@ -348,7 +348,7 @@ jobs: labels: ${{ steps.edc_controlplane_postgresql_meta.outputs.labels }} - name: Veracode Upload And Scan - uses: veracode/veracode-uploadandscan-action@0.2.1 + uses: veracode/veracode-uploadandscan-action@v1.0 if: | needs.secret-presence.outputs.VERACODE_ID && needs.secret-presence.outputs.VERACODE_SECRET_KEY && contains(' refs/heads/develop From df0932aaa27d2d9d2707f127a1e420a3ec8b17c0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 May 2022 07:28:49 +0000 Subject: [PATCH 083/433] Bump docker/login-action from 1 to 2 Bumps [docker/login-action](https://github.com/docker/login-action) from 1 to 2. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/v1...v2) --- updated-dependencies: - dependency-name: docker/login-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index db0c5baa9..21441a4bd 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -45,7 +45,7 @@ jobs: name: Login to GitHub Container Registry if: | needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' - uses: docker/login-action@v1 + uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.actor }} @@ -129,7 +129,7 @@ jobs: name: Login to GitHub Container Registry if: | needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' - uses: docker/login-action@v1 + uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.actor }} @@ -213,7 +213,7 @@ jobs: name: Login to GitHub Container Registry if: | needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' - uses: docker/login-action@v1 + uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.actor }} @@ -297,7 +297,7 @@ jobs: name: Login to Github Packages if: | needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' - uses: docker/login-action@v1 + uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.actor }} From f2bbe8ddb7c54fe7494f974860f0dfe1dbddb325 Mon Sep 17 00:00:00 2001 From: Dominik Pinsel Date: Tue, 10 May 2022 11:59:43 +0200 Subject: [PATCH 084/433] update workflows/build.yaml Signed-off-by: Dominik Pinsel --- .github/workflows/build.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index db0c5baa9..b778fad71 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -16,12 +16,12 @@ jobs: secret-presence: runs-on: ubuntu-latest outputs: - CXNG_GHCR_PAT: ${{ steps.secret-present.outputs.CXNG_GHCR_PAT }} - VERACODE_ID: ${{ steps.secret-present.outputs.VERACODE_ID }} - VERACODE_SECRET_KEY: ${{ steps.secret-present.outputs.VERACODE_SECRET_KEY }} + CXNG_GHCR_PAT: ${{ steps.secret-presence.outputs.CXNG_GHCR_PAT }} + VERACODE_ID: ${{ steps.secret-presence.outputs.VERACODE_ID }} + VERACODE_SECRET_KEY: ${{ steps.secret-presence.outputs.VERACODE_SECRET_KEY }} steps: - name: Check whether secrets exist - id: secret-present + id: secret-presence run: | [ ! -z "${{ secrets.CXNG_GHCR_PAT }}" ] && echo "::set-output name=CXNG_GHCR_PAT::true" [ ! -z "${{ secrets.VERACODE_ID }}" ] && echo "::set-output name=VERACODE_ID::true" From add6ec96fd23d3706690b0031c616dc1121b6d6c Mon Sep 17 00:00:00 2001 From: Dominik Pinsel Date: Wed, 11 May 2022 12:07:05 +0200 Subject: [PATCH 085/433] update cosmos db documentation --- edc-controlplane/edc-controlplane-cosmosdb/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/edc-controlplane/edc-controlplane-cosmosdb/README.md b/edc-controlplane/edc-controlplane-cosmosdb/README.md index 8e835dbc9..27cdbc16a 100644 --- a/edc-controlplane/edc-controlplane-cosmosdb/README.md +++ b/edc-controlplane/edc-controlplane-cosmosdb/README.md @@ -6,6 +6,11 @@ ./mvnw -pl .,edc-controlplane/edc-controlplane-cosmosdb -am package -Pwith-docker-image ``` +### Key Vault Setup + +The connector will lookup a secret in the key vault, that has the same alias as the `account-name` setting for CosmosDB (e.g. `edc.assetindex.cosmos.account-name`). +This secret must contain the the primary or secondard CosmosDB Read-write key. + ### Configuration Listed below are configuration keys needed to get the `edc-controlplane-cosmosdb` up and running. From 40b21faf0aabad5c97332f879c44e7da5b648f49 Mon Sep 17 00:00:00 2001 From: Dominik Pinsel Date: Wed, 11 May 2022 14:59:55 +0200 Subject: [PATCH 086/433] typo --- edc-controlplane/edc-controlplane-cosmosdb/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-controlplane/edc-controlplane-cosmosdb/README.md b/edc-controlplane/edc-controlplane-cosmosdb/README.md index 27cdbc16a..8c932d960 100644 --- a/edc-controlplane/edc-controlplane-cosmosdb/README.md +++ b/edc-controlplane/edc-controlplane-cosmosdb/README.md @@ -9,7 +9,7 @@ ### Key Vault Setup The connector will lookup a secret in the key vault, that has the same alias as the `account-name` setting for CosmosDB (e.g. `edc.assetindex.cosmos.account-name`). -This secret must contain the the primary or secondard CosmosDB Read-write key. +This secret must contain the primary or the secondard CosmosDB Read-write key. ### Configuration From e364d043e386788c0c056999bb9f2b9c484a7615 Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Wed, 11 May 2022 16:29:03 +0200 Subject: [PATCH 087/433] secure data mgmt API with auth key (#123) * secure data mgmt API with auth key * Update edc-controlplane/README.md Co-authored-by: Florian Rusch (ZF Friedrichshafen AG) * add edc.api.auth.key to values.yaml Co-authored-by: Dominik Pinsel Co-authored-by: Florian Rusch (ZF Friedrichshafen AG) --- deployment/helm/edc-controlplane/values.yaml | 1 + edc-controlplane/README.md | 10 ++++++++++ edc-controlplane/edc-controlplane-cosmosdb/pom.xml | 6 ++++++ edc-controlplane/edc-controlplane-memory/pom.xml | 6 ++++++ edc-controlplane/edc-controlplane-postgresql/pom.xml | 6 ++++++ 5 files changed, 29 insertions(+) diff --git a/deployment/helm/edc-controlplane/values.yaml b/deployment/helm/edc-controlplane/values.yaml index 48d41adf9..f832b06a1 100644 --- a/deployment/helm/edc-controlplane/values.yaml +++ b/deployment/helm/edc-controlplane/values.yaml @@ -264,6 +264,7 @@ opentelemetry: ## configuration: properties: |- + # edc.api.auth.key= # edc.api.control.auth.apikey.key= # edc.api.control.auth.apikey.value= # edc.assetindex.cosmos.account-name= diff --git a/edc-controlplane/README.md b/edc-controlplane/README.md index ae4584ce5..aae98af4b 100644 --- a/edc-controlplane/README.md +++ b/edc-controlplane/README.md @@ -517,7 +517,17 @@ curl -G -X GET $__connectorUrl/$__defaultApiPath/control/catalog --header "$__ap | $__targetConnectorUrl | URL of the Connector of the target connector with the IDS API port configured in `web.http.ids.port`(in the configuration of the other connector) | | $__targetConnectorIdsPath | The IDS Path as configured in `web.http.ids.path` (in the configuration of the other connector) | +# Secure your connector +## API Security + +The only API that is protected by some kind of security mechanism is the Data Management API. At the time of writing this is done by a simple API key. +The key value must be configured in `edc.api.auth.key`. All requests to the Data Management API must have `X-Api-Key` header with the key value. + +Example: +```bash +curl -X GET --header "X-Api-Key: " +``` # Known Control Plane Issues diff --git a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml index caaba9e1f..2df4d270d 100644 --- a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml +++ b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml @@ -93,6 +93,12 @@ filesystem-configuration + + + org.eclipse.dataspaceconnector + auth-tokenbased + + org.eclipse.dataspaceconnector diff --git a/edc-controlplane/edc-controlplane-memory/pom.xml b/edc-controlplane/edc-controlplane-memory/pom.xml index 894d339a1..9c28f547b 100644 --- a/edc-controlplane/edc-controlplane-memory/pom.xml +++ b/edc-controlplane/edc-controlplane-memory/pom.xml @@ -93,6 +93,12 @@ filesystem-configuration + + + org.eclipse.dataspaceconnector + auth-tokenbased + + org.eclipse.dataspaceconnector diff --git a/edc-controlplane/edc-controlplane-postgresql/pom.xml b/edc-controlplane/edc-controlplane-postgresql/pom.xml index 9d5f87e96..2e6b709da 100644 --- a/edc-controlplane/edc-controlplane-postgresql/pom.xml +++ b/edc-controlplane/edc-controlplane-postgresql/pom.xml @@ -102,6 +102,12 @@ filesystem-configuration + + + org.eclipse.dataspaceconnector + auth-tokenbased + + org.eclipse.dataspaceconnector From e58a73495b76431e554b3bba56d3403e240ddd20 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Thu, 12 May 2022 10:23:56 +0200 Subject: [PATCH 088/433] Fix docker image step when running on PRs (#127) --- .github/workflows/build.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 668e78fbb..4c4a7d14f 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -91,7 +91,7 @@ jobs: build-args: | JAR=edc-dataplane/target/edc-dataplane.jar push: | - ${{ needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' }} + ${{ (needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' && 'true') || 'false' }} tags: ${{ steps.edc_dataplane_meta.outputs.tags }} labels: ${{ steps.edc_dataplane_meta.outputs.labels }} - @@ -175,7 +175,7 @@ jobs: build-args: | JAR=edc-controlplane/edc-controlplane-memory/target/edc-controlplane-memory.jar push: | - ${{ needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' }} + ${{ (needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' && 'true') || 'false' }} tags: ${{ steps.edc_controlplane_memory_meta.outputs.tags }} labels: ${{ steps.edc_controlplane_memory_meta.outputs.labels }} - @@ -259,7 +259,7 @@ jobs: build-args: | JAR=edc-controlplane/edc-controlplane-cosmosdb/target/edc-controlplane-cosmosdb.jar push: | - ${{ needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' }} + ${{ (needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' && 'true') || 'false' }} tags: ${{ steps.edc_controlplane_cosmosdb_meta.outputs.tags }} labels: ${{ steps.edc_controlplane_cosmosdb_meta.outputs.labels }} - @@ -343,7 +343,7 @@ jobs: build-args: | JAR=edc-controlplane/edc-controlplane-postgresql/target/edc-controlplane-postgresql.jar push: | - ${{ needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' }} + ${{ (needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' && 'true') || 'false' }} tags: ${{ steps.edc_controlplane_postgresql_meta.outputs.tags }} labels: ${{ steps.edc_controlplane_postgresql_meta.outputs.labels }} - From 6c8d97e1c80258b31a1520d7e43e6a8339424072 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 12 May 2022 10:45:24 +0200 Subject: [PATCH 089/433] Bump spotless-maven-plugin from 2.22.4 to 2.22.5 (#125) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a62efa4f5..847a824c7 100644 --- a/pom.xml +++ b/pom.xml @@ -32,7 +32,7 @@ UTF-8 - 2.22.4 + 2.22.5 3.3.0 3.0.0 From b866966c48c77baf64a4e45d0ffb505bc94308a0 Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Thu, 12 May 2022 16:22:09 +0200 Subject: [PATCH 090/433] sql fix: udate edc and schema (#129) --- edc | 2 +- ...2__Alter_TransferProcess_Add_DataAddress.sql | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/transferprocess/V0_0_2__Alter_TransferProcess_Add_DataAddress.sql diff --git a/edc b/edc index 0da15e194..79fe5d3ca 160000 --- a/edc +++ b/edc @@ -1 +1 @@ -Subproject commit 0da15e194d553178676f4fa6845c876bc9d7a2ad +Subproject commit 79fe5d3cacf7c323b02fc09110e81ec28b37d8e6 diff --git a/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/transferprocess/V0_0_2__Alter_TransferProcess_Add_DataAddress.sql b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/transferprocess/V0_0_2__Alter_TransferProcess_Add_DataAddress.sql new file mode 100644 index 000000000..a399b1c22 --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/transferprocess/V0_0_2__Alter_TransferProcess_Add_DataAddress.sql @@ -0,0 +1,17 @@ +-- +-- Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +-- +-- This program and the accompanying materials are made available under the +-- terms of the Apache License, Version 2.0 which is available at +-- https://www.apache.org/licenses/LICENSE-2.0 +-- +-- SPDX-License-Identifier: Apache-2.0 +-- +-- Contributors: +-- Mercedes-Benz Tech Innovation GmbH - Add DataAddress Column +-- + + +-- Add new content_data_address columns +ALTER TABLE edc_transfer_process ADD content_data_address TEXT; +COMMENT ON COLUMN edc_transfer_process.content_data_address IS 'DataAddress serialized as JSON'; From 348462a00e185c5ad0c4fb7f11d54c5a81a113d4 Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Fri, 13 May 2022 13:56:57 +0200 Subject: [PATCH 091/433] remove edc jar shading (#130) --- .github/workflows/build.yaml | 4 + .../edc-controlplane-cosmosdb/pom.xml | 63 ++++++--------- .../src/main/docker/Dockerfile | 4 +- .../edc-controlplane-memory/pom.xml | 60 ++++++--------- .../src/main/docker/Dockerfile | 4 +- .../edc-controlplane-postgresql/pom.xml | 76 ++++++++----------- .../src/main/docker/Dockerfile | 4 +- edc-dataplane/pom.xml | 73 +++++++----------- edc-dataplane/src/main/docker/Dockerfile | 6 +- pom.xml | 12 ++- 10 files changed, 131 insertions(+), 175 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 4c4a7d14f..a6f2ff925 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -90,6 +90,7 @@ jobs: file: edc-dataplane/src/main/docker/Dockerfile build-args: | JAR=edc-dataplane/target/edc-dataplane.jar + LIB=edc-dataplane/target/lib push: | ${{ (needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' && 'true') || 'false' }} tags: ${{ steps.edc_dataplane_meta.outputs.tags }} @@ -174,6 +175,7 @@ jobs: file: edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile build-args: | JAR=edc-controlplane/edc-controlplane-memory/target/edc-controlplane-memory.jar + LIB=edc-controlplane/edc-controlplane-memory/target/lib push: | ${{ (needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' && 'true') || 'false' }} tags: ${{ steps.edc_controlplane_memory_meta.outputs.tags }} @@ -258,6 +260,7 @@ jobs: file: edc-controlplane/edc-controlplane-cosmosdb/src/main/docker/Dockerfile build-args: | JAR=edc-controlplane/edc-controlplane-cosmosdb/target/edc-controlplane-cosmosdb.jar + LIB=edc-controlplane/edc-controlplane-cosmosdb/target/lib push: | ${{ (needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' && 'true') || 'false' }} tags: ${{ steps.edc_controlplane_cosmosdb_meta.outputs.tags }} @@ -342,6 +345,7 @@ jobs: file: edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile build-args: | JAR=edc-controlplane/edc-controlplane-postgresql/target/edc-controlplane-postgresql.jar + LIB=edc-controlplane/edc-controlplane-postgresql/target/lib push: | ${{ (needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' && 'true') || 'false' }} tags: ${{ steps.edc_controlplane_postgresql_meta.outputs.tags }} diff --git a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml index 2df4d270d..21b39e3dd 100644 --- a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml +++ b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml @@ -24,51 +24,35 @@ jar + ${project.artifactId} + org.apache.maven.plugins - maven-shade-plugin + maven-jar-plugin + + + + true + lib/ + + org.eclipse.dataspaceconnector.boot.system.runtime.BaseRuntime + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + package - shade + copy-dependencies - ${project.artifactId} - true - - - *:* - - module-info.class - META-INF/*.SF - META-INF/*.DSA - META-INF/*.RSA - - - - - - org.eclipse.dataspaceconnector.boot.system.runtime.BaseRuntime - - - - - META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension - - - - - META-INF/services/org.eclipse.dataspaceconnector.spi.system.VaultExtension - - - - - META-INF/services/org.eclipse.dataspaceconnector.spi.system.ConfigurationExtension - - - - + ${project.build.directory}/lib @@ -76,6 +60,7 @@ + @@ -98,7 +83,7 @@ org.eclipse.dataspaceconnector auth-tokenbased - + org.eclipse.dataspaceconnector @@ -321,6 +306,8 @@ src/main/docker/Dockerfile --build-arg JAR=target/${project.artifactId}.jar + --build-arg + LIB=target/lib -t ${project.artifactId}:${project.version} . diff --git a/edc-controlplane/edc-controlplane-cosmosdb/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-cosmosdb/src/main/docker/Dockerfile index 6236f2dbf..2bb41f209 100644 --- a/edc-controlplane/edc-controlplane-cosmosdb/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-cosmosdb/src/main/docker/Dockerfile @@ -18,11 +18,13 @@ RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar FROM gcr.io/distroless/java11-debian11 ARG JAR +ARG LIB WORKDIR /app COPY --from=otel /tmp/opentelemetry-javaagent.jar . -COPY $JAR edc-controlplane.jar +COPY ${JAR} edc-controlplane.jar +COPY ${LIB} ./lib/ CMD ["-javaagent:/app/opentelemetry-javaagent.jar", \ "-Dedc.fs.config=/app/configuration.properties", \ diff --git a/edc-controlplane/edc-controlplane-memory/pom.xml b/edc-controlplane/edc-controlplane-memory/pom.xml index 9c28f547b..809683cde 100644 --- a/edc-controlplane/edc-controlplane-memory/pom.xml +++ b/edc-controlplane/edc-controlplane-memory/pom.xml @@ -24,51 +24,35 @@ jar + ${project.artifactId} + org.apache.maven.plugins - maven-shade-plugin + maven-jar-plugin + + + + true + lib/ + + org.eclipse.dataspaceconnector.boot.system.runtime.BaseRuntime + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + package - shade + copy-dependencies - ${project.artifactId} - true - - - *:* - - module-info.class - META-INF/*.SF - META-INF/*.DSA - META-INF/*.RSA - - - - - - org.eclipse.dataspaceconnector.boot.system.runtime.BaseRuntime - - - - - META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension - - - - - META-INF/services/org.eclipse.dataspaceconnector.spi.system.VaultExtension - - - - - META-INF/services/org.eclipse.dataspaceconnector.spi.system.ConfigurationExtension - - - - + ${project.build.directory}/lib @@ -263,6 +247,8 @@ src/main/docker/Dockerfile --build-arg JAR=target/${project.artifactId}.jar + --build-arg + LIB=target/lib -t ${project.artifactId}:${project.version} . diff --git a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile index 6236f2dbf..2bb41f209 100644 --- a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile @@ -18,11 +18,13 @@ RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar FROM gcr.io/distroless/java11-debian11 ARG JAR +ARG LIB WORKDIR /app COPY --from=otel /tmp/opentelemetry-javaagent.jar . -COPY $JAR edc-controlplane.jar +COPY ${JAR} edc-controlplane.jar +COPY ${LIB} ./lib/ CMD ["-javaagent:/app/opentelemetry-javaagent.jar", \ "-Dedc.fs.config=/app/configuration.properties", \ diff --git a/edc-controlplane/edc-controlplane-postgresql/pom.xml b/edc-controlplane/edc-controlplane-postgresql/pom.xml index 2e6b709da..75b97addb 100644 --- a/edc-controlplane/edc-controlplane-postgresql/pom.xml +++ b/edc-controlplane/edc-controlplane-postgresql/pom.xml @@ -12,9 +12,7 @@ Mercedes-Benz Tech Innovation GmbH - Initial POM --> - + edc-controlplane net.catenax.edc @@ -26,54 +24,35 @@ jar + ${project.artifactId} + org.apache.maven.plugins - maven-shade-plugin + maven-jar-plugin + + + + true + lib/ + + org.eclipse.dataspaceconnector.boot.system.runtime.BaseRuntime + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + package - shade + copy-dependencies - ${project.artifactId} - true - - - *:* - - META-INF/*.SF - META-INF/*.DSA - META-INF/*.RSA - - - - - - org.eclipse.dataspaceconnector.boot.system.runtime.BaseRuntime - - - - - META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension - - - - - META-INF/services/org.eclipse.dataspaceconnector.spi.system.VaultExtension - - - - - META-INF/services/org.eclipse.dataspaceconnector.spi.system.ConfigurationExtension - - - - + ${project.build.directory}/lib @@ -81,6 +60,7 @@ + @@ -107,7 +87,7 @@ org.eclipse.dataspaceconnector auth-tokenbased - + org.eclipse.dataspaceconnector @@ -245,7 +225,8 @@ - + + org.eclipse.dataspaceconnector http @@ -259,7 +240,8 @@ transaction-local - + + org.eclipse.dataspaceconnector http-receiver @@ -308,6 +290,8 @@ src/main/docker/Dockerfile --build-arg JAR=target/${project.artifactId}.jar + --build-arg + LIB=target/lib -t ${project.artifactId}:${project.version} . diff --git a/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile index 6236f2dbf..2bb41f209 100644 --- a/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile @@ -18,11 +18,13 @@ RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar FROM gcr.io/distroless/java11-debian11 ARG JAR +ARG LIB WORKDIR /app COPY --from=otel /tmp/opentelemetry-javaagent.jar . -COPY $JAR edc-controlplane.jar +COPY ${JAR} edc-controlplane.jar +COPY ${LIB} ./lib/ CMD ["-javaagent:/app/opentelemetry-javaagent.jar", \ "-Dedc.fs.config=/app/configuration.properties", \ diff --git a/edc-dataplane/pom.xml b/edc-dataplane/pom.xml index 6537909ea..2c5a997dc 100644 --- a/edc-dataplane/pom.xml +++ b/edc-dataplane/pom.xml @@ -12,9 +12,7 @@ Mercedes-Benz Tech Innovation GmbH - Initial POM --> - + 4.0.0 @@ -26,55 +24,35 @@ jar + ${project.artifactId} + org.apache.maven.plugins - maven-shade-plugin + maven-jar-plugin + + + + true + lib/ + + org.eclipse.dataspaceconnector.boot.system.runtime.BaseRuntime + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + package - shade + copy-dependencies - ${project.artifactId} - true - - - *:* - - module-info.class - META-INF/*.SF - META-INF/*.DSA - META-INF/*.RSA - - - - - - org.eclipse.dataspaceconnector.boot.system.runtime.BaseRuntime - - - - - META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension - - - - - META-INF/services/org.eclipse.dataspaceconnector.spi.system.VaultExtension - - - - - META-INF/services/org.eclipse.dataspaceconnector.spi.system.ConfigurationExtension - - - - + ${project.build.directory}/lib @@ -154,7 +132,8 @@ - + + org.eclipse.dataspaceconnector http @@ -199,6 +178,8 @@ src/main/docker/Dockerfile --build-arg JAR=target/${project.artifactId}.jar + --build-arg + LIB=target/lib -t ${project.artifactId}:${project.version} . @@ -227,4 +208,4 @@ - \ No newline at end of file + diff --git a/edc-dataplane/src/main/docker/Dockerfile b/edc-dataplane/src/main/docker/Dockerfile index e18b6adc0..be19ac944 100644 --- a/edc-dataplane/src/main/docker/Dockerfile +++ b/edc-dataplane/src/main/docker/Dockerfile @@ -18,11 +18,13 @@ RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar FROM gcr.io/distroless/java11-debian11 ARG JAR +ARG LIB WORKDIR /app COPY --from=otel /tmp/opentelemetry-javaagent.jar . -COPY $JAR edc-dataplane.jar +COPY ${JAR} edc-dataplane.jar +COPY ${LIB} ./lib/ CMD ["-javaagent:/app/opentelemetry-javaagent.jar", \ "-Dedc.fs.config=/app/configuration.properties", \ @@ -32,4 +34,4 @@ CMD ["-javaagent:/app/opentelemetry-javaagent.jar", \ "-Dotel.exporter.prometheus.port=9090", \ "-Djava.security.edg=file:/dev/.urandom", \ "-jar", \ - "edc-dataplane.jar"] \ No newline at end of file + "edc-dataplane.jar"] diff --git a/pom.xml b/pom.xml index 847a824c7..e1c8479a8 100644 --- a/pom.xml +++ b/pom.xml @@ -32,8 +32,9 @@ UTF-8 + 3.3.0 + 3.2.2 2.22.5 - 3.3.0 3.0.0 @@ -93,8 +94,13 @@ org.apache.maven.plugins - maven-shade-plugin - ${org.apache.maven.plugins.shade.version} + maven-jar-plugin + ${org.apache.maven.plugins.maven.jar.plugin.version} + + + org.apache.maven.plugins + maven-dependency-plugin + ${org.apache.maven.plugins.maven.dependency.plugin.version} com.diffplug.spotless From 44d84f7c8b2186836fbab96b3d351f4380333999 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Fri, 13 May 2022 17:34:01 +0200 Subject: [PATCH 092/433] Add LICENSE and NOTICE.md (#133) --- LICENSE | 2 +- NOTICE.md | 254 ++++++ .../edc-controlplane-cosmosdb/pom.xml | 19 + .../edc-controlplane-memory/pom.xml | 19 + .../edc-controlplane-postgresql/pom.xml | 19 + edc-dataplane/pom.xml | 20 +- edc-extensions/aas-controller/pom.xml | 19 + .../business-partner-validation/pom.xml | 20 +- edc-extensions/postgresql-migration/pom.xml | 19 + misc/NOTICE.md.template | 21 + misc/license-mappings.xml | 755 ++++++++++++++++++ pom.xml | 145 +++- 12 files changed, 1295 insertions(+), 17 deletions(-) create mode 100644 NOTICE.md create mode 100644 misc/NOTICE.md.template create mode 100644 misc/license-mappings.xml diff --git a/LICENSE b/LICENSE index 337440099..c815b0d05 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2021 Catena-X + Copyright 2022 Catena-X 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/NOTICE.md b/NOTICE.md new file mode 100644 index 000000000..2886d7b03 --- /dev/null +++ b/NOTICE.md @@ -0,0 +1,254 @@ +# Notices for Catena-X NG Product EDC + +## Copyright + +All content is the property of the respective authors or their employers. For more information regarding authorship of content, please consult the listed source code repository logs. + +## Declared Project Licenses + +This program and the accompanying materials are made available under the terms of the Apache License, Version 2.0 which is available at https://www.apache.org/licenses/LICENSE-2.0. + +SPDX-License-Identifier: Apache-2.0 + +## Source Code + +The project maintains the following source code repositoriy: + +* https://github.com/catenax-ng/product-edc + +## Third-party Content (Overarching All Modules) + +* aas-controller under Apache License 2.0 +* aopalliance version 1.0 repackaged as a module under EPL 2.0 or GPL2 w/ CPE +* Apache Commons Codec under Apache License, Version 2.0 +* Apache Commons Lang under Apache License, Version 2.0 +* Apache Commons Pool under Apache License, Version 2.0 +* apache-commons-pool-sql under Apache License, Version 2.0 +* api-configuration under Apache License, Version 2.0 +* api-core under Apache License, Version 2.0 +* asm under BSD-3-Clause +* ASM based accessors helper used by json-smart under The Apache Software License, Version 2.0 +* asm-analysis under BSD-3-Clause +* asm-commons under BSD-3-Clause +* asm-tree under BSD-3-Clause +* asset-api under Apache License, Version 2.0 +* asset-index-sql under Apache License, Version 2.0 +* assetindex-cosmos under Apache License, Version 2.0 +* auth-spi under Apache License, Version 2.0 +* auth-tokenbased under Apache License, Version 2.0 +* azure-vault under Apache License, Version 2.0 +* Bean Validation API under Apache License 2.0 +* Bouncy Castle ASN.1 Extension and Utility APIs under Bouncy Castle Licence +* Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs under Bouncy Castle Licence +* Bouncy Castle Provider under Bouncy Castle Licence +* business-partner-validation under Apache License 2.0 +* catalog-api under Apache License, Version 2.0 +* catalog-spi under Apache License, Version 2.0 +* Checker Qual under The MIT License +* ClassGraph under The MIT License (MIT) +* common-sql under Apache License, Version 2.0 +* common-util under Apache License, Version 2.0 +* contract under Apache License, Version 2.0 +* contract-definition-store-cosmos under Apache License, Version 2.0 +* contract-negotiation-store-cosmos under Apache License, Version 2.0 +* contract-spi under Apache License, Version 2.0 +* contractagreement-api under Apache License, Version 2.0 +* contractdefinition-api under Apache License, Version 2.0 +* contractdefinition-store-sql under Apache License, Version 2.0 +* contractnegotiation-api under Apache License, Version 2.0 +* contractnegotiation-store-sql under Apache License, Version 2.0 +* control-api under Apache License, Version 2.0 +* Core functionality for the Reactor Netty library under The Apache Software License, Version 2.0 +* core-base under Apache License, Version 2.0 +* core-boot under Apache License, Version 2.0 +* core-defaults under Apache License, Version 2.0 +* core-micrometer under Apache License, Version 2.0 +* core-spi under Apache License, Version 2.0 +* cosmos-common under Apache License, Version 2.0 +* data-management-api under Apache License, Version 2.0 +* data-plane-api under Apache License, Version 2.0 +* data-plane-framework under Apache License, Version 2.0 +* data-plane-http under Apache License, Version 2.0 +* data-plane-spi under Apache License, Version 2.0 +* data-plane-transfer-spi under Apache License, Version 2.0 +* data-plane-transfer-sync under Apache License, Version 2.0 +* dataloading under Apache License, Version 2.0 +* edc-controlplane under Apache License 2.0 +* edc-controlplane-cosmosdb under Apache License 2.0 +* edc-controlplane-memory under Apache License 2.0 +* edc-controlplane-postgresql under Apache License 2.0 +* edc-dataplane under Apache License 2.0 +* edc-extensions under Apache License 2.0 +* Failsafe under Apache License, Version 2.0 +* filesystem-configuration under Apache License, Version 2.0 +* flyway-core under Apache License, Version 2.0 +* HdrHistogram under Public Domain, per Creative Commons CC0 or BSD-2-Clause +* HK2 API module under EPL 2.0 or GPL2 w/ CPE +* HK2 Implementation Utilities under EPL 2.0 or GPL2 w/ CPE +* http under Apache License, Version 2.0 +* HTTP functionality for the Reactor Netty library under The Apache Software License, Version 2.0 +* http-receiver under Apache License, Version 2.0 +* iam-daps under Apache License, Version 2.0 +* iam-mock under Apache License, Version 2.0 +* ids-api-configuration under Apache License, Version 2.0 +* ids-api-multipart-dispatcher-v1 under Apache License, Version 2.0 +* ids-api-multipart-endpoint-v1 under Apache License, Version 2.0 +* ids-api-transform-v1 under Apache License, Version 2.0 +* ids-core under Apache License, Version 2.0 +* ids-spi under Apache License, Version 2.0 +* ids-token-validation under Apache License, Version 2.0 +* IntelliJ IDEA Annotations under The Apache Software License, Version 2.0 +* Jackson datatype: JSR310 under The Apache Software License, Version 2.0 +* Jackson module: Afterburner under The Apache Software License, Version 2.0 +* Jackson module: Jakarta XML Bind Annotations (jakarta.xml.bind) under The Apache Software License, Version 2.0 +* Jackson-annotations under The Apache Software License, Version 2.0 +* Jackson-core under The Apache Software License, Version 2.0 +* jackson-databind under The Apache Software License, Version 2.0 +* Jackson-dataformat-XML under The Apache Software License, Version 2.0 +* Jackson-dataformat-YAML under The Apache Software License, Version 2.0 +* Jakarta Activation under EDL 1.0 +* Jakarta Annotations API under EPL 2.0 or GPL2 w/ CPE +* Jakarta Bean Validation API under Apache License 2.0 +* Jakarta Dependency Injection under The Apache Software License, Version 2.0 +* Jakarta RESTful WS API under EPL-2.0 or GPL-2.0-with-classpath-exception +* Jakarta XML Binding API under Eclipse Distribution License - v 1.0 +* jakarta.transaction API under EPL 2.0 or GPL2 w/ CPE +* java under Apache License, Version 2.0 +* java jwt under The MIT License (MIT) +* Java Native Access under LGPL, version 2.1 or Apache License v2.0 +* Java Native Access Platform under LGPL, version 2.1 or Apache License v2.0 +* Javassist under MPL 1.1 or LGPL 2.1 or Apache License 2.0 +* JCIP Annotations under Apache License under Apache License, Version 2.0 +* jdk-logger-monitor under Apache License, Version 2.0 +* jersey under Apache License, Version 2.0 +* jersey-container-servlet under EPL 2.0 or GPL2 w/ CPE or EDL 1.0 or BSD 2-Clause or Apache License, 2.0 or Public Domain or Modified BSD or jQuery license or MIT license or W3C license +* jersey-container-servlet-core under EPL 2.0 or GPL2 w/ CPE or EDL 1.0 or BSD 2-Clause or Apache License, 2.0 or Public Domain or Modified BSD or jQuery license or MIT license or W3C license +* jersey-core-client under EPL 2.0 or GPL2 w/ CPE or EDL 1.0 or BSD 2-Clause or Apache License, 2.0 or Public Domain or Modified BSD or jQuery license or MIT license or W3C license +* jersey-core-common under EPL 2.0 or The GNU General Public License (GPL), Version 2, With Classpath Exception or Apache License, 2.0 or Public Domain +* jersey-core-server under EPL 2.0 or The GNU General Public License (GPL), Version 2, With Classpath Exception or Apache License, 2.0 or Modified BSD +* jersey-ext-entity-filtering under EPL 2.0 or GPL2 w/ CPE or EDL 1.0 or BSD 2-Clause or Apache License, 2.0 or Public Domain or Modified BSD or jQuery license or MIT license or W3C license +* jersey-inject-hk2 under EPL 2.0 or GPL2 w/ CPE or EDL 1.0 or BSD 2-Clause or Apache License, 2.0 or Public Domain or Modified BSD or jQuery license or MIT license or W3C license +* jersey-media-json-jackson under EPL 2.0 or The GNU General Public License (GPL), Version 2, With Classpath Exception or Apache License, 2.0 +* jersey-media-multipart under EPL 2.0 or GPL2 w/ CPE or EDL 1.0 or BSD 2-Clause or Apache License, 2.0 or Public Domain or Modified BSD or jQuery license or MIT license or W3C license +* jersey-micrometer under Apache License, Version 2.0 +* jetty under Apache License, Version 2.0 +* Jetty :: ALPN :: Client under Eclipse Public License - Version 2.0 or Apache Software License - Version 2.0 +* Jetty :: Asynchronous HTTP Client under Eclipse Public License - Version 2.0 or Apache Software License - Version 2.0 +* Jetty :: Http Utility under Eclipse Public License - Version 2.0 or Apache Software License - Version 2.0 +* Jetty :: IO Utility under Eclipse Public License - Version 2.0 or Apache Software License - Version 2.0 +* Jetty :: Jakarta Servlet API and Schemas for JPMS and OSGi under Apache Software License - Version 2.0 or Eclipse Public License - Version 1.0 +* Jetty :: Jakarta WebSocket API for JPMS and OSGi under Apache Software License - Version 2.0 or Eclipse Public License - Version 1.0 +* Jetty :: JNDI Naming under Eclipse Public License - Version 2.0 or Apache Software License - Version 2.0 +* Jetty :: Plus under Eclipse Public License - Version 2.0 or Apache Software License - Version 2.0 +* Jetty :: Security under Eclipse Public License - Version 2.0 or Apache Software License - Version 2.0 +* Jetty :: Server Core under Eclipse Public License - Version 2.0 or Apache Software License - Version 2.0 +* Jetty :: Servlet Annotations under Eclipse Public License - Version 2.0 or Apache Software License - Version 2.0 +* Jetty :: Servlet Handling under Eclipse Public License - Version 2.0 or Apache Software License - Version 2.0 +* Jetty :: Utilities under Eclipse Public License - Version 2.0 or Apache Software License - Version 2.0 +* Jetty :: Webapp Application Support under Eclipse Public License - Version 2.0 or Apache Software License - Version 2.0 +* Jetty :: Websocket :: Core :: Client under Eclipse Public License - Version 2.0 or Apache Software License - Version 2.0 +* Jetty :: Websocket :: Core :: Common under Eclipse Public License - Version 2.0 or Apache Software License - Version 2.0 +* Jetty :: Websocket :: Core :: Server under Eclipse Public License - Version 2.0 or Apache Software License - Version 2.0 +* Jetty :: Websocket :: jakarta.websocket :: Client under Eclipse Public License - Version 2.0 or Apache Software License - Version 2.0 +* Jetty :: Websocket :: jakarta.websocket :: Common under Eclipse Public License - Version 2.0 or Apache Software License - Version 2.0 +* Jetty :: Websocket :: jakarta.websocket :: Server under Eclipse Public License - Version 2.0 or Apache Software License - Version 2.0 +* Jetty :: Websocket :: Servlet under Eclipse Public License - Version 2.0 or Apache Software License - Version 2.0 +* Jetty :: XML utilities under Eclipse Public License - Version 2.0 or Apache Software License - Version 2.0 +* jetty-micrometer under Apache License, Version 2.0 +* JSON Small and Fast Parser under The Apache Software License, Version 2.0 +* JUnit Jupiter API under Eclipse Public License v2.0 +* JUnit Jupiter Engine under Eclipse Public License v2.0 +* JUnit Jupiter Params under Eclipse Public License v2.0 +* JUnit Platform Commons under Eclipse Public License v2.0 +* JUnit Platform Engine API under Eclipse Public License v2.0 +* JUnit Platform Launcher under Eclipse Public License v2.0 +* JUnit Platform Suite (Aggregator) under Eclipse Public License v2.0 +* JUnit Platform Suite API under Eclipse Public License v2.0 +* JUnit Platform Suite Commons under Eclipse Public License v2.0 +* JUnit Platform Suite Engine under Eclipse Public License v2.0 +* LatencyUtils under Public Domain, per Creative Commons CC0 +* lease-sql under Apache License, Version 2.0 +* Metrics Core under Apache License 2.0 +* micrometer-core under The Apache Software License, Version 2.0 +* Microsoft Azure client library for Identity under The MIT License (MIT) +* Microsoft Azure client library for KeyVault Secrets under The MIT License (MIT) +* Microsoft Azure Java Core Library under The MIT License (MIT) +* Microsoft Azure Netty HTTP Client Library under The MIT License (MIT) +* Microsoft Azure SDK for SQL API of Azure Cosmos DB Service under The MIT License (MIT) +* MIME streaming extension under Eclipse Distribution License - v 1.0 +* Mockito under The MIT License +* msal4j under MIT License +* msal4j-persistence-extension under MIT License +* Netty/Buffer under Apache License, Version 2.0 +* Netty/Codec under Apache License, Version 2.0 +* Netty/Codec/DNS under Apache License, Version 2.0 +* Netty/Codec/HTTP under Apache License, Version 2.0 +* Netty/Codec/HTTP2 under Apache License, Version 2.0 +* Netty/Codec/Socks under Apache License, Version 2.0 +* Netty/Common under Apache License, Version 2.0 +* Netty/Handler under Apache License, Version 2.0 +* Netty/Handler/Proxy under Apache License, Version 2.0 +* Netty/Resolver under Apache License, Version 2.0 +* Netty/Resolver/DNS under Apache License, Version 2.0 +* Netty/Resolver/DNS/Classes/MacOS under Apache License, Version 2.0 +* Netty/Resolver/DNS/Native/MacOS under Apache License, Version 2.0 +* Netty/TomcatNative [BoringSSL - Static] under The Apache Software License, Version 2.0 +* Netty/TomcatNative [OpenSSL - Classes] under The Apache Software License, Version 2.0 +* Netty/Transport under Apache License, Version 2.0 +* Netty/Transport/Classes/Epoll under Apache License, Version 2.0 +* Netty/Transport/Classes/KQueue under Apache License, Version 2.0 +* Netty/Transport/Native/Epoll under Apache License, Version 2.0 +* Netty/Transport/Native/KQueue under Apache License, Version 2.0 +* Netty/Transport/Native/Unix/Common under Apache License, Version 2.0 +* Nimbus Content Type under The Apache Software License, Version 2.0 +* Nimbus JOSE+JWT under The Apache Software License, Version 2.0 +* Nimbus LangTag under The Apache Software License, Version 2.0 +* Non-Blocking Reactive Foundation for the JVM under Apache License, Version 2.0 +* OAuth 2.0 SDK with OpenID Connect extensions under Apache License, version 2.0 +* oauth2-core under Apache License, Version 2.0 +* oauth2-spi under Apache License, Version 2.0 +* observability-api under Apache License, Version 2.0 +* okhttp under The Apache Software License, Version 2.0 +* Okio under The Apache Software License, Version 2.0 +* OpenTelemetry Java under The Apache License, Version 2.0 +* org.apiguardian:apiguardian-api under The Apache License, Version 2.0 +* org.jetbrains.kotlin:kotlin-stdlib under The Apache License, Version 2.0 +* org.jetbrains.kotlin:kotlin-stdlib-common under The Apache License, Version 2.0 +* org.opentest4j:opentest4j under The Apache License, Version 2.0 +* OSGi resource locator under EPL 2.0 or GPL2 w/ CPE +* policy-engine under Apache License, Version 2.0 +* policy-evaluator under Apache License, Version 2.0 +* policy-spi under Apache License, Version 2.0 +* policy-store-cosmos under Apache License, Version 2.0 +* policy-store-sql under Apache License, Version 2.0 +* policydefinition-api under Apache License, Version 2.0 +* PostgreSQL JDBC Driver under BSD-2-Clause +* postgresql-migration under Apache License 2.0 +* product-edc-parent under Apache License 2.0 +* reactive-streams under CC0 +* ServiceLocator Default Implementation under EPL 2.0 or GPL2 w/ CPE +* SLF4J API Module under MIT License +* SnakeYAML under Apache License, Version 2.0 +* spi under Apache License, Version 2.0 +* state-machine-lib under Apache License, Version 2.0 +* Stax2 API under The BSD License +* swagger-annotations-jakarta under Apache License 2.0 +* swagger-core-jakarta under Apache License 2.0 +* swagger-integration-jakarta under Apache License 2.0 +* swagger-jaxrs2-jakarta under Apache License 2.0 +* swagger-models-jakarta under Apache License 2.0 +* token-generation-lib under Apache License, Version 2.0 +* token-validation-lib under Apache License, Version 2.0 +* transaction-datasource-spi under Apache License, Version 2.0 +* transaction-local under Apache License, Version 2.0 +* transaction-spi under Apache License, Version 2.0 +* transfer under Apache License, Version 2.0 +* transfer-process-store-cosmos under Apache License, Version 2.0 +* transfer-process-store-sql under Apache License, Version 2.0 +* transfer-spi under Apache License, Version 2.0 +* transferprocess-api under Apache License, Version 2.0 +* transport-spi under Apache License, Version 2.0 +* util under Apache License, Version 2.0 +* web-spi under Apache License, Version 2.0 +* Woodstox under The Apache License, Version 2.0 + diff --git a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml index 21b39e3dd..a79ce4053 100644 --- a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml +++ b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml @@ -25,6 +25,22 @@ ${project.artifactId} + + + src/main/resources + + **/* + + + + ../../ + META-INF + + NOTICE.md + LICENSE + + + @@ -280,6 +296,9 @@ + with-docker-image diff --git a/edc-controlplane/edc-controlplane-memory/pom.xml b/edc-controlplane/edc-controlplane-memory/pom.xml index 809683cde..fb526fb8c 100644 --- a/edc-controlplane/edc-controlplane-memory/pom.xml +++ b/edc-controlplane/edc-controlplane-memory/pom.xml @@ -25,6 +25,22 @@ ${project.artifactId} + + + src/main/resources + + **/* + + + + ../../ + META-INF + + NOTICE.md + LICENSE + + + @@ -221,6 +237,9 @@ + with-docker-image diff --git a/edc-controlplane/edc-controlplane-postgresql/pom.xml b/edc-controlplane/edc-controlplane-postgresql/pom.xml index 75b97addb..6501ba4a3 100644 --- a/edc-controlplane/edc-controlplane-postgresql/pom.xml +++ b/edc-controlplane/edc-controlplane-postgresql/pom.xml @@ -25,6 +25,22 @@ ${project.artifactId} + + + src/main/resources + + **/* + + + + ../../ + META-INF + + NOTICE.md + LICENSE + + + @@ -264,6 +280,9 @@ + with-docker-image diff --git a/edc-dataplane/pom.xml b/edc-dataplane/pom.xml index 2c5a997dc..98eac70b5 100644 --- a/edc-dataplane/pom.xml +++ b/edc-dataplane/pom.xml @@ -25,6 +25,22 @@ ${project.artifactId} + + + src/main/resources + + **/* + + + + ../ + META-INF + + NOTICE.md + LICENSE + + + @@ -149,9 +165,11 @@ - + with-docker-image diff --git a/edc-extensions/aas-controller/pom.xml b/edc-extensions/aas-controller/pom.xml index da2a685d2..4d72541ef 100644 --- a/edc-extensions/aas-controller/pom.xml +++ b/edc-extensions/aas-controller/pom.xml @@ -24,6 +24,25 @@ aas-controller jar + + + + src/main/resources + + **/* + + + + ../../ + META-INF + + NOTICE.md + LICENSE + + + + + diff --git a/edc-extensions/business-partner-validation/pom.xml b/edc-extensions/business-partner-validation/pom.xml index 90aa72ff7..8f3050ac2 100644 --- a/edc-extensions/business-partner-validation/pom.xml +++ b/edc-extensions/business-partner-validation/pom.xml @@ -23,6 +23,25 @@ business-partner-validation jar + + + + src/main/resources + + **/* + + + + ../../ + META-INF + + NOTICE.md + LICENSE + + + + + @@ -52,5 +71,4 @@ mockito-all - \ No newline at end of file diff --git a/edc-extensions/postgresql-migration/pom.xml b/edc-extensions/postgresql-migration/pom.xml index 624bdc3d6..0f0d3d490 100644 --- a/edc-extensions/postgresql-migration/pom.xml +++ b/edc-extensions/postgresql-migration/pom.xml @@ -25,6 +25,25 @@ postgresql-migration jar + + + + src/main/resources + + **/* + + + + ../../ + META-INF + + NOTICE.md + LICENSE + + + + + org.eclipse.dataspaceconnector diff --git a/misc/NOTICE.md.template b/misc/NOTICE.md.template new file mode 100644 index 000000000..ca8fb8acc --- /dev/null +++ b/misc/NOTICE.md.template @@ -0,0 +1,21 @@ +# Notices for Catena-X NG Product EDC + +## Copyright + +All content is the property of the respective authors or their employers. For more information regarding authorship of content, please consult the listed source code repository logs. + +## Declared Project Licenses + +This program and the accompanying materials are made available under the terms of the Apache License, Version 2.0 which is available at https://www.apache.org/licenses/LICENSE-2.0. + +SPDX-License-Identifier: Apache-2.0 + +## Source Code + +The project maintains the following source code repositoriy: + +* https://github.com/catenax-ng/product-edc + +## Third-party Content (Overarching All Modules) + +@{GENERATED_NOTICES} \ No newline at end of file diff --git a/misc/license-mappings.xml b/misc/license-mappings.xml new file mode 100644 index 000000000..f26375c3e --- /dev/null +++ b/misc/license-mappings.xml @@ -0,0 +1,755 @@ + + + + org.eclipse.dataspaceconnector + apache-commons-pool-sql + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + api-configuration + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + api-core + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + asset-api + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + assetindex-cosmos + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + asset-index-sql + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + auth-basic + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + auth-spi + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + auth-tokenbased + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + aws-s3 + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + aws-test + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + azure-eventgrid + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + azure-eventgrid-config + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + azure-test + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + azure-vault + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + blob-core + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + blob-operator + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + blob-provision + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + blobstorage + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + catalog + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + catalog-api + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + catalog-cache + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + catalog-spi + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + common-sql + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + common-util + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + contract + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + contractagreement-api + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + contractdefinition-api + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + contract-definition-store-cosmos + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + contractdefinition-store-sql + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + contractnegotiation-api + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + contract-negotiation-store-cosmos + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + contractnegotiation-store-sql + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + contract-spi + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + control-api + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + core + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + core-base + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + core-boot + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + core-defaults + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + core-micrometer + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + core-policy + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + core-spi + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + cosmos + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + cosmos-common + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + dataloading + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + data-management-api + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + data-plane-api + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + data-plane-azure-storage + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + data-plane-framework + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + data-plane-http + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + data-plane-s3 + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + data-plane-selector-api + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + data-plane-selector-client + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + data-plane-selector-core + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + data-plane-selector-spi + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + data-plane-selector-store + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + data-plane-spi + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + data-plane-transfer + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + data-plane-transfer-client + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + data-plane-transfer-spi + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + data-plane-transfer-sync + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + decentralized-identity + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + dummy-credentials-verifier + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + fcc-node-directory-cosmos + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + federated-catalog-spi + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + filesystem-configuration + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + filesystem-vault + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + http + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + http-provisioner + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + http-receiver + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + iam-daps + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + iam-mock + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + identity-common-test + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + identity-did-core + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + identity-did-crypto + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + identity-did-service + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + identity-did-spi + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + identity-did-web + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + ids + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + ids-api-configuration + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + ids-api-multipart-dispatcher-v1 + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + ids-api-multipart-endpoint-v1 + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + ids-api-transform-v1 + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + ids-core + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + ids-policy + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + ids-spi + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + ids-token-validation + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + jdk-logger-monitor + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + jersey + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + jersey-micrometer + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + jetty + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + jetty-micrometer + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + lease-sql + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + oauth2-core + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + oauth2-spi + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + observability-api + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + policydefinition-api + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + policy-engine + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + policy-evaluator + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + policy-spi + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + policy-store-cosmos + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + policy-store-sql + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + registration-service + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + registration-service-api + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + s3-core + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + s3-operator + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + s3-provision + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + spi + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + sql + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + state-machine-lib + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + token-generation-lib + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + token-validation-lib + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + transaction-atomikos + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + transaction-datasource-spi + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + transaction-local + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + transaction-spi + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + transfer + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + transfer-functions-core + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + transfer-functions-spi + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + transferprocess-api + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + transfer-process-store-cosmos + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + transfer-process-store-sql + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + transfer-spi + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + transport-spi + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + + org.eclipse.dataspaceconnector + web-spi + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + + diff --git a/pom.xml b/pom.xml index e1c8479a8..ec0c17ada 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,8 @@ Mercedes-Benz Tech Innovation GmbH - Initial POM --> - + 4.0.0 net.catenax.edc @@ -26,6 +27,15 @@ edc-dataplane
+ 2022 + + + Apache License 2.0 + https://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + 11 11 @@ -36,6 +46,7 @@ 3.2.2 2.22.5 3.0.0 + 1.5 0.0.1-SNAPSHOT @@ -69,19 +80,6 @@ Fraunhofer IAIS https://maven.iais.fraunhofer.de/artifactory/eis-ids-public - - github-edc - EDC: Github Packages - https://maven.pkg.github.com/eclipse-dataspaceconnector/DataSpaceConnector - - true - - - - github-catenax-ng - Catena-X NG: Github Packages - https://maven.pkg.github.com/catenax-ng/product-edc - @@ -92,6 +90,16 @@ exec-maven-plugin ${org.codehaus.mojo.exec.maven.plugin.version} + + org.apache.commons + commons-build-plugin + 1.12 + + + org.codehaus.mojo + license-maven-plugin + 2.0.0 + org.apache.maven.plugins maven-jar-plugin @@ -128,6 +136,75 @@ maven-surefire-plugin 2.22.2 + + org.codehaus.mojo + license-maven-plugin + ${org.codehaus.mojo.license.maven.plugin} + + ${project.basedir}/LICENSE + true + + + Apache License 2.0|Apache License v2.0| + Apache License, 2.0| + Apache License, Version 2.0| + Apache License, version 2.0| + Apache Software License - Version 2.0| + The Apache License, Version 2.0| + The Apache Software License, Version 2.0 + + + BSD 2-Clause| + BSD-2-Clause + + + Eclipse Distribution License - v 1.0| + EDL 1.0 + + + Eclipse Public License - Version 1.0| + EPL 1.0| + EPL-1.0 + + + Eclipse Public License - Version 2.0| + EPL 2.0| + EPL-2.0 + + + GPL-2.0-with-classpath-exception| + GPL2 w/ CPE| + GPL-2.0-with-classpath-exception + + + LGPL 2.1| + LGPL, version 2.1 + + + MIT License| + MIT license| + The MIT License (MIT) + + + BSD-3-Clause| + Modified BSD + + + GNU Lesser General Public License, version 2.1| + LGPL 2.1 + + + + + + create-license-list + + aggregate-add-third-party + + generate-resources + + + @@ -844,4 +921,44 @@ + + + + generate-notice + + + + + org.jasig.maven + maven-notice-plugin + 1.1.0 + + + jakarta.xml.bind + jakarta.xml.bind-api + 4.0.0 + + + org.glassfish.jaxb + jaxb-runtime + 2.3.6 + + + + ${project.basedir}/misc/NOTICE.md.template + false + * {0} under {1} + NOTICE.md + @{GENERATED_NOTICES} + + ${project.basedir}/misc/license-mappings.xml + + + + + + + \ No newline at end of file From a02601306fed39a88a3b3b18fae98b80791157b9 Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Fri, 13 May 2022 17:34:23 +0200 Subject: [PATCH 093/433] Milestone 3 Docu (#132) --- CHANGELOG.md | 6 ------ README.md | 10 ++++++++++ deployment/helm/edc-controlplane/Chart.yaml | 4 ++-- deployment/helm/edc-dataplane/Chart.yaml | 4 ++-- edc-controlplane/README.md | 12 +++++++++++- edc-controlplane/edc-controlplane-cosmosdb/pom.xml | 2 +- edc-controlplane/edc-controlplane-memory/pom.xml | 2 +- edc-controlplane/edc-controlplane-postgresql/pom.xml | 2 +- edc-controlplane/pom.xml | 6 ++---- edc-dataplane/pom.xml | 4 ++-- edc-extensions/aas-controller/pom.xml | 2 +- edc-extensions/business-partner-validation/README.md | 4 ++++ edc-extensions/business-partner-validation/pom.xml | 2 +- edc-extensions/pom.xml | 2 +- edc-extensions/postgresql-migration/pom.xml | 6 ++---- pom.xml | 2 +- 16 files changed, 42 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee13d7841..11bddf32c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,9 +6,3 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] - -## [0.0.1] - 2022-03-29 - -[Unreleased]: https://github.com/catenax-ng/product-edc/compare/0.0.1...HEAD - -[0.0.1]: https://github.com/catenax-ng/product-edc/compare/ea573b6816bcbdbf2b1a4416652e551517f893d2...0.0.1 diff --git a/README.md b/README.md index 4411747d7..9cb72a662 100644 --- a/README.md +++ b/README.md @@ -36,3 +36,13 @@ cd edc && ./gradlew publishToMavenLocal ```shell ./mvnw package -Pwith-docker-image ``` + +## Releases + +### Milestone 3 + +The Catena-X milestone 3 release can be found in the `release/0.0.1` branch. +You can download the container image from our [repository](https://github.com/catenax-ng/product-edc/pkgs/container/product-edc%2Fedc-controlplane-postgresql). +```bash +docker pull ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql:0.0.1 +``` diff --git a/deployment/helm/edc-controlplane/Chart.yaml b/deployment/helm/edc-controlplane/Chart.yaml index 0661b813a..930da839a 100644 --- a/deployment/helm/edc-controlplane/Chart.yaml +++ b/deployment/helm/edc-controlplane/Chart.yaml @@ -4,6 +4,6 @@ name: edc-controlplane description: EDC Control-Plane home: https://github.com/catenax-ng/product-edc/deployment/helm/edc-controlplane type: application -appVersion: "0.0.1" -version: 0.0.1 +appVersion: "0.0.0" +version: 0.0.0 maintainers: [] diff --git a/deployment/helm/edc-dataplane/Chart.yaml b/deployment/helm/edc-dataplane/Chart.yaml index 82559407b..1d79db51c 100644 --- a/deployment/helm/edc-dataplane/Chart.yaml +++ b/deployment/helm/edc-dataplane/Chart.yaml @@ -4,6 +4,6 @@ name: edc-dataplane description: EDC Data-Plane home: https://github.com/catenax-ng/product-edc/deployment/helm/edc-dataplane type: application -appVersion: "0.0.1" -version: 0.0.1 +appVersion: "0.0.0" +version: 0.0.0 maintainers: [] diff --git a/edc-controlplane/README.md b/edc-controlplane/README.md index aae98af4b..bda80caac 100644 --- a/edc-controlplane/README.md +++ b/edc-controlplane/README.md @@ -101,6 +101,17 @@ the EDC connector to retrieve its identity token and prove its identity to other When writing this guidance these step were tested out using the open source omejdn DAPS of the Fraunhofer AISEC ([GitHub](https://github.com/International-Data-Spaces-Association/omejdn-daps)). +--- +**Client Unknown Issue:** +
+Pleaste know that, in the past there were some DAPS issues with the client certificate. If you see this error, please contact the DAPS Team so that they can support you. + +```json +{ "error":"invalid_client","error_description":"Client unknown"} +``` +--- + + ### (optional) 1. Key / Certificate Generation In the first step generate a PKSC8 Key and the corresponding certificate. This step is optional, because it might be possible that this key is provided by the DAPS maintainers. @@ -161,7 +172,6 @@ edc.oauth.private.key.alias= edc.oauth.public.key.alias= ``` - ## Dataplane Setup Configure the control plane so that is able to communicate with an EDC data plane instance. diff --git a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml index a79ce4053..45382d334 100644 --- a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml +++ b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml @@ -16,7 +16,7 @@ net.catenax.edc edc-controlplane - 0.0.2-SNAPSHOT + 0.0.1-SNAPSHOT 4.0.0 diff --git a/edc-controlplane/edc-controlplane-memory/pom.xml b/edc-controlplane/edc-controlplane-memory/pom.xml index fb526fb8c..67754b96a 100644 --- a/edc-controlplane/edc-controlplane-memory/pom.xml +++ b/edc-controlplane/edc-controlplane-memory/pom.xml @@ -16,7 +16,7 @@ edc-controlplane net.catenax.edc - 0.0.2-SNAPSHOT + 0.0.1-SNAPSHOT 4.0.0 diff --git a/edc-controlplane/edc-controlplane-postgresql/pom.xml b/edc-controlplane/edc-controlplane-postgresql/pom.xml index 6501ba4a3..b1ea43cbd 100644 --- a/edc-controlplane/edc-controlplane-postgresql/pom.xml +++ b/edc-controlplane/edc-controlplane-postgresql/pom.xml @@ -16,7 +16,7 @@ edc-controlplane net.catenax.edc - 0.0.2-SNAPSHOT + 0.0.1-SNAPSHOT 4.0.0 diff --git a/edc-controlplane/pom.xml b/edc-controlplane/pom.xml index f348fe98b..a3b210d03 100644 --- a/edc-controlplane/pom.xml +++ b/edc-controlplane/pom.xml @@ -12,15 +12,13 @@ Mercedes-Benz Tech Innovation GmbH - Initial POM --> - + 4.0.0 net.catenax.edc product-edc-parent - 0.0.2-SNAPSHOT + 0.0.1-SNAPSHOT edc-controlplane diff --git a/edc-dataplane/pom.xml b/edc-dataplane/pom.xml index 98eac70b5..45670a3e2 100644 --- a/edc-dataplane/pom.xml +++ b/edc-dataplane/pom.xml @@ -18,7 +18,7 @@ net.catenax.edc product-edc-parent - 0.0.2-SNAPSHOT + 0.0.1-SNAPSHOT edc-dataplane jar @@ -226,4 +226,4 @@
- + \ No newline at end of file diff --git a/edc-extensions/aas-controller/pom.xml b/edc-extensions/aas-controller/pom.xml index 4d72541ef..fe14053f1 100644 --- a/edc-extensions/aas-controller/pom.xml +++ b/edc-extensions/aas-controller/pom.xml @@ -18,7 +18,7 @@ net.catenax.edc.extensions edc-extensions - 0.0.2-SNAPSHOT + 0.0.1-SNAPSHOT aas-controller diff --git a/edc-extensions/business-partner-validation/README.md b/edc-extensions/business-partner-validation/README.md index 174ec30a9..fb7d53ce6 100644 --- a/edc-extensions/business-partner-validation/README.md +++ b/edc-extensions/business-partner-validation/README.md @@ -1,3 +1,7 @@ +# Important for Milestone 3! + +Please note, that with the start of the **Milestone 3** release (v0.0.1) there exists an issue, where the BPN number cannot be retrieved from the DAPS token. The missing token BPN makes offers, protected by a BPN constraint, unavailable to all connectors. + # Business Partner Validation Extension Using the Business Partner Validation Extension it's possible to add configurable validation against diff --git a/edc-extensions/business-partner-validation/pom.xml b/edc-extensions/business-partner-validation/pom.xml index 8f3050ac2..11b058f7f 100644 --- a/edc-extensions/business-partner-validation/pom.xml +++ b/edc-extensions/business-partner-validation/pom.xml @@ -18,7 +18,7 @@ net.catenax.edc.extensions edc-extensions - 0.0.2-SNAPSHOT + 0.0.1-SNAPSHOT business-partner-validation jar diff --git a/edc-extensions/pom.xml b/edc-extensions/pom.xml index a607c5bb1..60c975eb2 100644 --- a/edc-extensions/pom.xml +++ b/edc-extensions/pom.xml @@ -18,7 +18,7 @@ net.catenax.edc product-edc-parent - 0.0.2-SNAPSHOT + 0.0.1-SNAPSHOT net.catenax.edc.extensions diff --git a/edc-extensions/postgresql-migration/pom.xml b/edc-extensions/postgresql-migration/pom.xml index 0f0d3d490..2544b7dde 100644 --- a/edc-extensions/postgresql-migration/pom.xml +++ b/edc-extensions/postgresql-migration/pom.xml @@ -12,13 +12,11 @@ Mercedes-Benz Tech Innovation GmbH - Initial POM --> - + edc-extensions net.catenax.edc.extensions - 0.0.2-SNAPSHOT + 0.0.1-SNAPSHOT 4.0.0 diff --git a/pom.xml b/pom.xml index ec0c17ada..49d7e571e 100644 --- a/pom.xml +++ b/pom.xml @@ -18,7 +18,7 @@ net.catenax.edc product-edc-parent - 0.0.2-SNAPSHOT + 0.0.1-SNAPSHOT pom From 09842c20338b0442c7ae5075d5f91c384633543c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 13 May 2022 18:49:43 +0200 Subject: [PATCH 094/433] Bump jaxb-runtime from 2.3.6 to 3.0.2 (#134) Bumps jaxb-runtime from 2.3.6 to 3.0.2. --- updated-dependencies: - dependency-name: org.glassfish.jaxb:jaxb-runtime dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ec0c17ada..2e9cc1ebd 100644 --- a/pom.xml +++ b/pom.xml @@ -943,7 +943,7 @@ org.glassfish.jaxb jaxb-runtime - 2.3.6 + 3.0.2 From d4537d8cc3745edc6e95b6bbdd89a9af318b2ea4 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Mon, 16 May 2022 10:59:20 +0200 Subject: [PATCH 095/433] Replace iam-mock by iam-daps at controlplane-memory Signed-off-by: Denis Neuling --- .../edc-controlplane-memory/README.md | 15 ++++++++++++++- edc-controlplane/edc-controlplane-memory/pom.xml | 6 +++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/edc-controlplane/edc-controlplane-memory/README.md b/edc-controlplane/edc-controlplane-memory/README.md index e8349e646..34d17ebf7 100644 --- a/edc-controlplane/edc-controlplane-memory/README.md +++ b/edc-controlplane/edc-controlplane-memory/README.md @@ -36,6 +36,14 @@ Details regarding each configuration property can be found at the [documentary s | edc.api.control.auth.apikey.key | | X-Api-Key | | | edc.api.control.auth.apikey.value | | super-strong-api-key | | | edc.hostname | | localhost | | +| edc.oauth.token.url | X | https://daps.catena-x.net | | +| edc.oauth.public.key.alias | X | key-to-daps-certificate-in-keyvault | | +| edc.oauth.private.key.alias | X | key-to-private-key-in-keyvault | | +| edc.oauth.client.id | X | daps-oauth-client-id | | +| edc.vault.clientid | X | 00000000-1111-2222-3333-444444444444 | | +| edc.vault.tenantid | X | 55555555-6666-7777-8888-999999999999 | | +| edc.vault.name | X | my-vault-name | | +| edc.vault.clientsecret | X | 34-chars-secret | | | edc.transfer.proxy.endpoint | X | | | | edc.transfer.proxy.token.signer.privatekey.alias | X | | | @@ -76,12 +84,17 @@ edc.api.control.auth.apikey.value=pass edc.hostname=localhost +# OAuth / DAPS related configuration +edc.oauth.token.url=https://daps.catena-x.net +edc.oauth.public.key.alias=key-to-daps-certificate-in-keyvault +edc.oauth.private.key.alias=key-to-private-key-in-keyvault +edc.oauth.client.id=daps-oauth-client-id # Azure vault related configuration edc.vault.clientid=00000000-1111-2222-3333-444444444444 edc.vault.tenantid=55555555-6666-7777-8888-999999999999 edc.vault.name=my-vault-name -edc.vault.clientsecret=34-chars-secret +edc.vault.clientsecret=34-chars-secret=34-chars-secret # Control- / Data- Plane configuration edc.transfer.proxy.endpoint=http://dataplane-public-endpoint/public diff --git a/edc-controlplane/edc-controlplane-memory/pom.xml b/edc-controlplane/edc-controlplane-memory/pom.xml index fb526fb8c..cbaf54031 100644 --- a/edc-controlplane/edc-controlplane-memory/pom.xml +++ b/edc-controlplane/edc-controlplane-memory/pom.xml @@ -186,7 +186,11 @@ org.eclipse.dataspaceconnector - iam-mock + oauth2-core + + + org.eclipse.dataspaceconnector + iam-daps From 5b90f0dd9e1c6307bff0e638c584bac704118325 Mon Sep 17 00:00:00 2001 From: "Florian Rusch (ZF Friedrichshafen AG)" Date: Mon, 16 May 2022 15:38:35 +0200 Subject: [PATCH 096/433] Fix typo in ContractDefinitions URL (#141) --- edc-controlplane/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/edc-controlplane/README.md b/edc-controlplane/README.md index bda80caac..f1cf32338 100644 --- a/edc-controlplane/README.md +++ b/edc-controlplane/README.md @@ -448,7 +448,7 @@ __publicContractDefinition=" }" # Call Data Mgmt API -curl -X POST "$__connectorUrl/$__dataMgmtPath/policies" --header "$__apiKey: $__apiKeyValue" --header "Content-Type: application/json" --data "$__publicContractDefinition" +curl -X POST "$__connectorUrl/$__dataMgmtPath/contractdefinitions" --header "$__apiKey: $__apiKeyValue" --header "Content-Type: application/json" --data "$__publicContractDefinition" ``` #### Bash Parameters @@ -468,7 +468,7 @@ curl -X POST "$__connectorUrl/$__dataMgmtPath/policies" --header "$__apiKey: $__ Get Contract Definition ```bash -curl -X GET "$__connectorUrl/$__dataMgmtPath/policies/$__policyId" --header "$__apiKey: $__apiKeyValue" --header "Content-Type: application/json" | jq +curl -X GET "$__connectorUrl/$__dataMgmtPath/contractdefinitions/$__contractDefinitionId" --header "$__apiKey: $__apiKeyValue" --header "Content-Type: application/json" | jq ``` ### 4. Get Contract Offer Catalog From 25a0f630a21bb0c840cbb3096e2759ed8bc9b5ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 May 2022 15:50:40 +0200 Subject: [PATCH 097/433] Bump flyway-core from 8.5.10 to 8.5.11 (#142) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2e9cc1ebd..21012938e 100644 --- a/pom.xml +++ b/pom.xml @@ -53,7 +53,7 @@ 1.2.1 3.1.0 42.3.5 - 8.5.10 + 8.5.11 5.8.2 From 0cc911138049a28b4af4cefab28ab93f3cc0dc12 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Tue, 17 May 2022 09:41:36 +0200 Subject: [PATCH 098/433] Enable gh workflow for building published releases/tags as well (#137) --- .github/workflows/build.yaml | 3 +++ .github/workflows/publish-new-release.yml | 18 ++++++++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index a6f2ff925..57802e751 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -8,6 +8,9 @@ on: - develop tags: - '[0-9]+.[0-9]+.[0-9]+' + release: + types: + - released pull_request: branches: - '*' diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index fad49a49c..9639d2c3d 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -9,10 +9,22 @@ on: - closed jobs: + secret-presence: + runs-on: ubuntu-latest + outputs: + CXNG_RELEASE_PAT: ${{ steps.secret-presence.outputs.CXNG_RELEASE_PAT }} + steps: + - name: Check whether secrets exist + id: secret-presence + run: | + [ ! -z "${{ secrets.CXNG_RELEASE_PAT }}" ] && echo "::set-output name=CXNG_RELEASE_PAT::true" + + exit 0 release: name: Publish new release + needs: [ secret-presence ] runs-on: ubuntu-latest - if: github.event.pull_request.merged == true + if: github.event.pull_request.merged == true && needs.secret-presence.outputs.CXNG_RELEASE_PAT steps: - uses: actions/checkout@v3 with: @@ -36,6 +48,7 @@ jobs: echo "RELEASE_VERSION=$VERSION" >> $GITHUB_ENV - name: Create Release Tag + id: create_release_tag run: | # Prepare git env git config user.name "GitHub actions" @@ -50,9 +63,10 @@ jobs: git push --force origin ${{ env.RELEASE_VERSION }} - name: Create Github Release + id: create_release uses: thomaseizinger/create-release@1.0.0 env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.CXNG_RELEASE_PAT }} with: target_commitish: ${{ github.event.pull_request.merge_commit_sha }} tag_name: ${{ env.RELEASE_VERSION }} From a332dc7478b3a663a814e16c6d058c20b7b8c747 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Tue, 17 May 2022 09:55:20 +0200 Subject: [PATCH 099/433] Use different secrets for performing veracode scans (#144) --- .github/workflows/build.yaml | 40 ++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 57802e751..f16603612 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -20,15 +20,15 @@ jobs: runs-on: ubuntu-latest outputs: CXNG_GHCR_PAT: ${{ steps.secret-presence.outputs.CXNG_GHCR_PAT }} - VERACODE_ID: ${{ steps.secret-presence.outputs.VERACODE_ID }} - VERACODE_SECRET_KEY: ${{ steps.secret-presence.outputs.VERACODE_SECRET_KEY }} + ORG_VERACODE_API_ID: ${{ steps.secret-presence.outputs.ORG_VERACODE_API_ID }} + ORG_VERACODE_API_KEY: ${{ steps.secret-presence.outputs.ORG_VERACODE_API_KEY }} steps: - name: Check whether secrets exist id: secret-presence run: | [ ! -z "${{ secrets.CXNG_GHCR_PAT }}" ] && echo "::set-output name=CXNG_GHCR_PAT::true" - [ ! -z "${{ secrets.VERACODE_ID }}" ] && echo "::set-output name=VERACODE_ID::true" - [ ! -z "${{ secrets.VERACODE_SECRET_KEY }}" ] && echo "::set-output name=VERACODE_SECRET_KEY::true" + [ ! -z "${{ secrets.ORG_VERACODE_API_ID }}" ] && echo "::set-output name=ORG_VERACODE_API_ID::true" + [ ! -z "${{ secrets.ORG_VERACODE_API_KEY }}" ] && echo "::set-output name=ORG_VERACODE_API_KEY::true" exit 0 ##################### @@ -102,7 +102,7 @@ jobs: name: Veracode Upload And Scan uses: veracode/veracode-uploadandscan-action@v1.0 if: | - needs.secret-presence.outputs.VERACODE_ID && needs.secret-presence.outputs.VERACODE_SECRET_KEY && contains(' + needs.secret-presence.outputs.ORG_VERACODE_API_ID && needs.secret-presence.outputs.ORG_VERACODE_API_KEY && contains(' refs/heads/develop refs/heads/release/ refs/tags/ @@ -111,10 +111,10 @@ jobs: with: appname: 'product-edc/edc-dataplane' createprofile: true - version: ${{ github.ref }} + version: ${{ github.ref }}-${{ github.sha }} filepath: edc-dataplane/target/edc-dataplane.jar - vid: ${{ secrets.VERACODE_ID }} - vkey: ${{ secrets.VERACODE_SECRET_KEY }} + vid: ${{ secrets.ORG_VERACODE_API_ID }} + vkey: ${{ secrets.ORG_VERACODE_API_KEY }} ################################# ### edc-controlplane-memory ### @@ -187,7 +187,7 @@ jobs: name: Veracode Upload And Scan uses: veracode/veracode-uploadandscan-action@v1.0 if: | - needs.secret-presence.outputs.VERACODE_ID && needs.secret-presence.outputs.VERACODE_SECRET_KEY && contains(' + needs.secret-presence.outputs.ORG_VERACODE_API_ID && needs.secret-presence.outputs.ORG_VERACODE_API_KEY && contains(' refs/heads/develop refs/heads/release/ refs/tags/ @@ -196,10 +196,10 @@ jobs: with: appname: 'product-edc/edc-controlplane-memory' createprofile: true - version: ${{ github.ref }} + version: ${{ github.ref }}-${{ github.sha }} filepath: edc-controlplane/edc-controlplane-memory/target/edc-controlplane-memory.jar - vid: ${{ secrets.VERACODE_ID }} - vkey: ${{ secrets.VERACODE_SECRET_KEY }} + vid: ${{ secrets.ORG_VERACODE_API_ID }} + vkey: ${{ secrets.ORG_VERACODE_API_KEY }} ################################# ### edc-controlplane-cosmosdb ### @@ -272,7 +272,7 @@ jobs: name: Veracode Upload And Scan uses: veracode/veracode-uploadandscan-action@v1.0 if: | - needs.secret-presence.outputs.VERACODE_ID && needs.secret-presence.outputs.VERACODE_SECRET_KEY && contains(' + needs.secret-presence.outputs.ORG_VERACODE_API_ID && needs.secret-presence.outputs.ORG_VERACODE_API_KEY && contains(' refs/heads/develop refs/heads/release/ refs/tags/ @@ -282,9 +282,9 @@ jobs: appname: 'product-edc/edc-controlplane-cosmosdb' createprofile: true filepath: edc-controlplane/edc-controlplane-cosmosdb/target/edc-controlplane-cosmosdb.jar - version: ${{ github.ref }} - vid: ${{ secrets.VERACODE_ID }} - vkey: ${{ secrets.VERACODE_SECRET_KEY }} + version: ${{ github.ref }}-${{ github.sha }} + vid: ${{ secrets.ORG_VERACODE_API_ID }} + vkey: ${{ secrets.ORG_VERACODE_API_KEY }} ################################### ### edc-controlplane-postgresql ### @@ -357,7 +357,7 @@ jobs: name: Veracode Upload And Scan uses: veracode/veracode-uploadandscan-action@v1.0 if: | - needs.secret-presence.outputs.VERACODE_ID && needs.secret-presence.outputs.VERACODE_SECRET_KEY && contains(' + needs.secret-presence.outputs.ORG_VERACODE_API_ID && needs.secret-presence.outputs.ORG_VERACODE_API_KEY && contains(' refs/heads/develop refs/heads/release/ refs/tags/ @@ -367,6 +367,6 @@ jobs: appname: 'product-edc/edc-controlplane-postgresql' createprofile: true filepath: edc-controlplane/edc-controlplane-postgresql/target/edc-controlplane-postgresql.jar - version: ${{ github.ref }} - vid: ${{ secrets.VERACODE_ID }} - vkey: ${{ secrets.VERACODE_SECRET_KEY }} + version: ${{ github.ref_name }}-${{ github.sha }} + vid: ${{ secrets.ORG_VERACODE_API_ID }} + vkey: ${{ secrets.ORG_VERACODE_API_KEY }} From 7fda0bf19672fed01cd0565c6af0667108c5ed20 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Tue, 17 May 2022 10:30:21 +0200 Subject: [PATCH 100/433] Update edc-controlplane/edc-controlplane-memory/README.md Co-authored-by: Florian Rusch (ZF Friedrichshafen AG) --- edc-controlplane/edc-controlplane-memory/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-controlplane/edc-controlplane-memory/README.md b/edc-controlplane/edc-controlplane-memory/README.md index 34d17ebf7..bf617c21f 100644 --- a/edc-controlplane/edc-controlplane-memory/README.md +++ b/edc-controlplane/edc-controlplane-memory/README.md @@ -94,7 +94,7 @@ edc.oauth.client.id=daps-oauth-client-id edc.vault.clientid=00000000-1111-2222-3333-444444444444 edc.vault.tenantid=55555555-6666-7777-8888-999999999999 edc.vault.name=my-vault-name -edc.vault.clientsecret=34-chars-secret=34-chars-secret +edc.vault.clientsecret=34-chars-secret # Control- / Data- Plane configuration edc.transfer.proxy.endpoint=http://dataplane-public-endpoint/public From df1b1984b35f427fe6ba86beb1741e7024feef15 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Fri, 20 May 2022 09:43:49 +0200 Subject: [PATCH 101/433] Upload released helm charts to gh-pages (#148) --- .github/workflows/publish-new-release.yml | 82 ++++++++++++++++++++--- 1 file changed, 72 insertions(+), 10 deletions(-) diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index 9639d2c3d..2a8b33e72 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -10,6 +10,7 @@ on: jobs: secret-presence: + name: Determine secret presence runs-on: ubuntu-latest outputs: CXNG_RELEASE_PAT: ${{ steps.secret-presence.outputs.CXNG_RELEASE_PAT }} @@ -20,16 +21,13 @@ jobs: [ ! -z "${{ secrets.CXNG_RELEASE_PAT }}" ] && echo "::set-output name=CXNG_RELEASE_PAT::true" exit 0 - release: - name: Publish new release - needs: [ secret-presence ] + + release-version: + name: Determine release version runs-on: ubuntu-latest - if: github.event.pull_request.merged == true && needs.secret-presence.outputs.CXNG_RELEASE_PAT + outputs: + RELEASE_VERSION: ${{ steps.release-version.outputs.RELEASE_VERSION }} steps: - - uses: actions/checkout@v3 - with: - # 0 to fetch the full history due to upcoming merge of main into develop branch - fetch-depth: 0 - name: Extract version from branch name (for release branches) if: startsWith(github.event.pull_request.head.ref, 'release/') @@ -46,13 +44,35 @@ jobs: VERSION=${BRANCH_NAME#hotfix/} echo "RELEASE_VERSION=$VERSION" >> $GITHUB_ENV + - + name: Output release version + id: release-version + run: | + echo "::set-output name=RELEASE_VERSION::${{ env.RELEASE_VERSION }}" + + github-release: + name: Publish new github release + needs: [ secret-presence, release-version ] + runs-on: ubuntu-latest + if: github.event.pull_request.merged == true && needs.secret-presence.outputs.CXNG_RELEASE_PAT && needs.release-version.outputs.RELEASE_VERSION + steps: + - + name: Export RELEASE_VERSION env + run: | + echo "RELEASE_VERSION=${{ needs.release-version.outputs.RELEASE_VERSION }}" >> $GITHUB_ENV + - + name: Checkout + uses: actions/checkout@v3 + with: + # 0 to fetch the full history due to upcoming merge of main into develop branch + fetch-depth: 0 - name: Create Release Tag id: create_release_tag run: | # Prepare git env - git config user.name "GitHub actions" - git config user.email noreply@github.com + git config user.name "$GITHUB_ACTOR" + git config user.email "$GITHUB_ACTOR@users.noreply.github.com" # informative git branch -a @@ -104,3 +124,45 @@ jobs: git commit --message "Introduce new snapshot version $SNAPSHOT_VERSION" git push origin develop + + helm-release: + name: Publish new helm release + needs: [ release-version ] + runs-on: ubuntu-latest + if: github.event.pull_request.merged == true && needs.release-version.outputs.RELEASE_VERSION + steps: + - + name: Export RELEASE_VERSION env + run: | + echo "RELEASE_VERSION=${{ needs.release-version.outputs.RELEASE_VERSION }}" >> $GITHUB_ENV + - + name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - + name: Install Helm + uses: azure/setup-helm@v1 + with: + version: v3.8.1 + - + name: Package helm, update index.yaml and push to gh-pages + run: | + # Prepare git env + git config user.name "GitHub actions" + git config user.email noreply@github.com + + # Package all charts + find -name Chart.yaml | xargs -n1 dirname | xargs -n1 helm package -u -d helm-charts + + git checkout gh-pages || git checkout -b gh-pages + git pull --rebase origin gh-pages + + # Generate helm repo index.yaml + helm repo index . --merge index.yaml --url https://${GITHUB_REPOSITORY_OWNER}.github.io/${GITHUB_REPOSITORY#*/}/ + + # Commit and push to gh-pages + git add index.yaml helm-charts + git commit -s -m "Release ${{ env.RELEASE_VERSION }}" + + git push origin gh-pages From 9979201fd517dea3426d74419038b54b358f45c2 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Fri, 20 May 2022 09:44:47 +0200 Subject: [PATCH 102/433] Update chart readme's (#145) --- deployment/helm/README.md | 8 + deployment/helm/edc-controlplane/Chart.yaml | 4 +- deployment/helm/edc-controlplane/README.md | 82 ++++++- .../helm/edc-controlplane/README.md.gotmpl | 25 +++ deployment/helm/edc-controlplane/values.yaml | 210 +++++++----------- deployment/helm/edc-dataplane/Chart.yaml | 4 +- deployment/helm/edc-dataplane/README.md | 70 +++++- .../helm/edc-dataplane/README.md.gotmpl | 25 +++ deployment/helm/edc-dataplane/values.yaml | 160 ++++++------- 9 files changed, 361 insertions(+), 227 deletions(-) create mode 100644 deployment/helm/edc-controlplane/README.md.gotmpl create mode 100644 deployment/helm/edc-dataplane/README.md.gotmpl diff --git a/deployment/helm/README.md b/deployment/helm/README.md index c5f541187..895b9eb2e 100644 --- a/deployment/helm/README.md +++ b/deployment/helm/README.md @@ -3,3 +3,11 @@ Chart linting is performed using [helm's CT tool](https://github.com/helm/chart-testing). Configuration files for [CT](../../ct.yaml), [Yamale](../../chart_schema.yaml) and [Yamllint](../../lintconf.yaml) have been provided. + +# Generate Chart Readme's + +To generate chart README.md files from its respective values.yaml file we use the [helm-docs tool](https://github.com/norwoodj/helm-docs): + +```shell +docker run --rm --volume "$(pwd):/helm-docs" -u $(id -u) jnorwood/helm-docs:latest +``` diff --git a/deployment/helm/edc-controlplane/Chart.yaml b/deployment/helm/edc-controlplane/Chart.yaml index 0661b813a..aff1fe759 100644 --- a/deployment/helm/edc-controlplane/Chart.yaml +++ b/deployment/helm/edc-controlplane/Chart.yaml @@ -1,7 +1,9 @@ --- apiVersion: v2 name: edc-controlplane -description: EDC Control-Plane +description: >- + EDC Control-Plane - The Eclipse DataSpaceConnector administration layer with + responsibility of resource management and govern contracts and data transfers home: https://github.com/catenax-ng/product-edc/deployment/helm/edc-controlplane type: application appVersion: "0.0.1" diff --git a/deployment/helm/edc-controlplane/README.md b/deployment/helm/edc-controlplane/README.md index 489210dba..b4003b326 100644 --- a/deployment/helm/edc-controlplane/README.md +++ b/deployment/helm/edc-controlplane/README.md @@ -1 +1,81 @@ -# edc-controlplane Helm Chart \ No newline at end of file +# edc-controlplane + +![Version: 0.0.1](https://img.shields.io/badge/Version-0.0.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.0.1](https://img.shields.io/badge/AppVersion-0.0.1-informational?style=flat-square) + +EDC Control-Plane - The Eclipse DataSpaceConnector administration layer with responsibility of resource management and govern contracts and data transfers + +**Homepage:** + +## TL;DR +```shell +$ helm repo add catenax-ng-product-edc https://catenax-ng.github.io/product-edc +$ helm install my-release catenax-ng-product-edc/edc-controlplane +``` + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| affinity | object | `{}` | [Affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) constrains which nodes the Pod can be scheduled on based on node labels. | +| autoscaling.enabled | bool | `false` | Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) | +| autoscaling.maxReplicas | int | `100` | Maximum replicas if resource consumption exceeds resource threshholds | +| autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | +| autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | +| autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | +| configuration.properties | string | `"# edc.api.auth.key=\n# edc.api.control.auth.apikey.key=\n# edc.api.control.auth.apikey.value=\n# edc.assetindex.cosmos.account-name=\n# edc.assetindex.cosmos.container-name=\n# edc.assetindex.cosmos.database-name=\n# edc.assetindex.cosmos.preferred-region=\n# edc.atomikos.checkpoint.interval=\n# edc.atomikos.directory=\n# edc.atomikos.logging=\n# edc.atomikos.threaded2pc=\n# edc.atomikos.timeout=\n# edc.aws.access.key=\n# edc.aws.provision.retry.retries.max=\n# edc.aws.provision.role.duration.session.max=\n# edc.aws.secret.access.key=\n# edc.blobstore.endpoint=\n# edc.contractdefinitionstore.cosmos.account-name=\n# edc.contractdefinitionstore.cosmos.container-name=\n# edc.contractdefinitionstore.cosmos.database-name=\n# edc.contractdefinitionstore.cosmos.preferred-region=\n# edc.contractnegotiationstore.cosmos.account-name=\n# edc.contractnegotiationstore.cosmos.container-name=\n# edc.contractnegotiationstore.cosmos.database-name=\n# edc.contractnegotiationstore.cosmos.preferred-region=\n# edc.controlplane.validation-endpoint=\n# edc.core.retry.backoff.max=\n# edc.core.retry.backoff.min=\n# edc.core.retry.retries.max=\n# edc.core.system.health.check.liveness-period=\n# edc.core.system.health.check.readiness-period=\n# edc.core.system.health.check.startup-period=\n# edc.core.system.health.check.threadpool-size=\n# edc.cosmos.partition-key=\n# edc.cosmos.query-metrics-enabled=\n# edc.dataplane.queue.capacity=\n# edc.dataplane.wait=\n# edc.dataplane.workers=\n# edc.datasource.asset.name=\"default\"\n# edc.datasource.contractdefinition.name=\"default\"\n# edc.datasource.contractnegotiation.name=\"default\"\n# edc.datasource.policy.name=\"default\"\n# edc.datasource.transferprocess.name=\"default\"\n# edc.datasource.default.pool.maxIdleConnections=\n# edc.datasource.default.pool.maxTotalConnections=\n# edc.datasource.default.pool.minIdleConnections=\n# edc.datasource.default.pool.testConnectionOnBorrow=\n# edc.datasource.default.pool.testConnectionOnCreate=\n# edc.datasource.default.pool.testConnectionOnReturn=\n# edc.datasource.default.pool.testConnectionWhileIdle=\n# edc.datasource.default.pool.testQuery=\n# edc.datasource.default.url=\n# edc.datasource.default.user=\n# edc.datasource.default.password=\n# edc.dpf.selector.url=\n# edc.events.topic.endpoint=\n# edc.events.topic.name=\n# edc.fs.config=\n# edc.hostname=\n# edc.identity.did.url=\n# edc.ids.catalog.id=\n# edc.ids.curator=\n# edc.ids.description=\n# edc.ids.endpoint=\n# edc.ids.id=\n# edc.ids.maintainer=\n# edc.ids.security.profile=\n# edc.ids.title=\n# edc.ids.validation.referringconnector=\n# edc.ion.crawler.did-type=\n# edc.ion.crawler.interval-minutes=\n# edc.ion.crawler.ion.url=\n# edc.metrics.enabled=\n# edc.metrics.executor.enabled=\n# edc.metrics.jersey.enabled=\n# edc.metrics.jetty.enabled=\n# edc.metrics.okhttp.enabled=\n# edc.metrics.system.enabled=\n# edc.negotiation.consumer.state-machine.batch-size=\n# edc.negotiation.provider.state-machine.batch-size=\n# edc.node.directory.cosmos.account.name=\n# edc.node.directory.cosmos.container.name=\n# edc.node.directory.cosmos.database.name=\n# edc.node.directory.cosmos.preferred.region=\n# edc.oauth.client.id=\n# edc.oauth.private.key.alias=\n# edc.oauth.provider.audience=\n# edc.oauth.provider.jwks.refresh=\n# edc.oauth.provider.jwks.url=\n# edc.oauth.public.key.alias=\n# edc.oauth.token.url=\n# edc.oauth.validation.nbf.leeway=\n# edc.receiver.http.auth-code=\n# edc.receiver.http.auth-key=\n# edc.receiver.http.endpoint=\n# edc.transfer.proxy.endpoint=\n# edc.transfer.dataplane.sync.token.validity=\n# edc.transfer.proxy.token.signer.privatekey.alias=\n# edc.transfer.functions.check.endpoint=\n# edc.transfer.functions.enabled.protocols=\n# edc.transfer.functions.transfer.endpoint=\n# edc.transfer-process-store.cosmos.account.name=\n# edc.transfer-process-store.cosmos.container-name=\n# edc.transfer-process-store.cosmos.preferred-region=\n# edc.transfer-process-store.database.name=\n# edc.transfer.state-machine.batch-size=\n# edc.vault=\n# edc.vault.certificate=\n# edc.vault.clientid=\n# edc.vault.clientsecret=\n# edc.vault.name=\n# edc.vault.tenantid=\n# edc.webdid.doh.url=\n# edc.web.rest.cors.enabled=\n# edc.web.rest.cors.headers=\n# edc.web.rest.cors.methods=\n# edc.web.rest.cors.origins="` | EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) | +| edc.endpoints.control.path | string | `"/api/controlplane/control"` | The path mapping the "control" api is going to be exposed at | +| edc.endpoints.control.port | string | `"9999"` | The network port, which the "control" api is going to be exposed by the container, pod and service | +| edc.endpoints.data.path | string | `"/data"` | The path mapping the "data" management api is going to be exposed at | +| edc.endpoints.data.port | string | `"8181"` | The network port, which the "data" management api is going to be exposed by the container, pod and service | +| edc.endpoints.default.path | string | `"/api"` | The path mapping the "default" api is going to be exposed at | +| edc.endpoints.default.port | string | `"8080"` | The network port, which the "default" api is going to be exposed by the container, pod and service | +| edc.endpoints.ids.path | string | `"/api/v1/ids"` | The path mapping the "ids" multipart api is going to be exposed at | +| edc.endpoints.ids.port | string | `"8282"` | The network port, which the "ids" multipart api is going to be exposed by the container, pod and service | +| edc.endpoints.metrics.path | string | `"/metrics"` | The path mapping the prometheus metrics are going to be exposed at | +| edc.endpoints.metrics.port | string | `"9090"` | The network port, which the prometheus metrics are going to be exposed by the container, pod and service | +| edc.endpoints.validation.path | string | `"/validation"` | The path mapping the "validation" api is going to be exposed at | +| edc.endpoints.validation.port | string | `"8182"` | The network port, which the "validation" api is going to be exposed by the container, pod and service | +| env | object | `{}` | Container environment variables e.g. for configuring [JAVA_TOOL_OPTIONS](https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/envvars002.html) Ex.: JAVA_TOOL_OPTIONS: > -Dhttp.proxyHost=proxy -Dhttp.proxyPort=80 -Dhttp.nonProxyHosts="localhost|127.*|[::1]" -Dhttps.proxyHost=proxy -Dhttps.proxyPort=443 | +| fullnameOverride | string | `""` | Overrides the releases full name | +| image.pullPolicy | string | `"IfNotPresent"` | [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use | +| image.repository | string | `"ghcr.io/catenax-ng/product-edc/edc-controlplane-memory"` | Which derivate of the edc controlplane to use. One of: [ghcr.io/catenax-ng/product-edc/edc-controlplane-memory, , ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql, ghcr.io/catenax-ng/product-edc/edc-controlplane-cosmosdb] | +| image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion. | +| imagePullSecret.dockerconfigjson | string | `""` | Image pull secret to create to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) Note: This value needs to adhere to the [(base64 encoded) .dockerconfigjson format](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#registry-secret-existing-credentials). Furthermore, if 'imagePullSecret.dockerconfigjson' is defined, it takes precedence over 'imagePullSecrets'. | +| imagePullSecrets | list | `[]` | Existing image pull secret to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) | +| ingresses[0].annotations | object | `{}` | Additional ingress annotations to add | +| ingresses[0].certManager.clusterIssuer | string | `""` | If preset enables certificate generation via cert-manager cluster-wide issuer | +| ingresses[0].certManager.issuer | string | `""` | If preset enables certificate generation via cert-manager namespace scoped issuer | +| ingresses[0].className | string | `""` | Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use | +| ingresses[0].enabled | bool | `true` | | +| ingresses[0].endpoints | list | `["ids"]` | EDC endpoints exposed by this ingress resource | +| ingresses[0].hostname | string | `"edc-controlplane.local"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | +| ingresses[0].tls | bool | `false` | Enables TLS on the ingress resource | +| ingresses[1].annotations | object | `{}` | Additional ingress annotations to add | +| ingresses[1].certManager.clusterIssuer | string | `""` | If preset enables certificate generation via cert-manager cluster-wide issuer | +| ingresses[1].certManager.issuer | string | `""` | If preset enables certificate generation via cert-manager namespace scoped issuer | +| ingresses[1].className | string | `""` | Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use | +| ingresses[1].enabled | bool | `false` | | +| ingresses[1].endpoints | list | `["data","control"]` | EDC endpoints exposed by this ingress resource | +| ingresses[1].hostname | string | `"edc-controlplane.intranet"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | +| ingresses[1].tls | bool | `false` | Enables TLS on the ingress resource | +| livenessProbe.enabled | bool | `true` | Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) | +| logging.properties | string | `".level=INFO\norg.eclipse.dataspaceconnector.level=ALL\nhandlers=java.util.logging.ConsoleHandler\njava.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter\njava.util.logging.ConsoleHandler.level=ALL\njava.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n"` | EDC logging.properties configuring the [java.util.logging subsystem](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html#a1.8) | +| nameOverride | string | `""` | Overrides the charts name | +| nodeSelector | object | `{}` | [Node-Selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) to constrain the Pod to nodes with specific labels. | +| opentelemetry.properties | string | `"otel.javaagent.enabled=true\notel.javaagent.debug=false"` | opentelemetry.properties configuring the [opentelemetry agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) | +| podAnnotations | object | `{}` | [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) added to deployed [pods](https://kubernetes.io/docs/concepts/workloads/pods/) | +| podSecurityContext | object | `{}` | The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment | +| readinessProbe.enabled | bool | `true` | Whether to enable kubernetes readiness-probes | +| replicaCount | int | `1` | Specifies how many replicas of a deployed pod shall be created during the deployment Note: If horizontal pod autoscaling is enabled this setting has no effect | +| resources | object | `{}` | [Resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) applied to the deployed pod | +| securityContext.runAsNonRoot | bool | `true` | Requires the container to run without root privileges | +| securityContext.runAsUser | int | `1000` | The container's process will run with the specified uid | +| service.type | string | `"ClusterIP"` | [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. | +| serviceAccount.annotations | object | `{}` | [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) to add to the service account | +| serviceAccount.create | bool | `true` | Specifies whether a [service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) should be created per release | +| serviceAccount.name | string | `""` | The name of the service account to use. If not set and create is true, a name is generated using the release's fullname template | +| startupProbe.enabled | bool | `true` | Whether to enable kubernetes startup-probes | +| tolerations | list | `[]` | [Tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) are applied to Pods to schedule onto nodes with matching taints. | + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.10.0](https://github.com/norwoodj/helm-docs/releases/v1.10.0) diff --git a/deployment/helm/edc-controlplane/README.md.gotmpl b/deployment/helm/edc-controlplane/README.md.gotmpl new file mode 100644 index 000000000..95c28338e --- /dev/null +++ b/deployment/helm/edc-controlplane/README.md.gotmpl @@ -0,0 +1,25 @@ +{{ template "chart.header" . }} + +{{ template "chart.deprecationWarning" . }} + +{{ template "chart.badgesSection" . }} + +{{ template "chart.description" . }} + +{{ template "chart.homepageLine" . }} + +## TL;DR +```shell +$ helm repo add catenax-ng-product-edc https://catenax-ng.github.io/product-edc +$ helm install my-release catenax-ng-product-edc/edc-controlplane +``` + +{{ template "chart.maintainersSection" . }} + +{{ template "chart.sourcesSection" . }} + +{{ template "chart.requirementsSection" . }} + +{{ template "chart.valuesSection" . }} + +{{ template "helm-docs.versionFooter" . }} diff --git a/deployment/helm/edc-controlplane/values.yaml b/deployment/helm/edc-controlplane/values.yaml index f832b06a1..25f40018c 100644 --- a/deployment/helm/edc-controlplane/values.yaml +++ b/deployment/helm/edc-controlplane/values.yaml @@ -3,203 +3,162 @@ # This is a YAML-formatted file. # Declare variables to be passed into your templates. +# -- Specifies how many replicas of a deployed pod shall be created during the deployment +# Note: If horizontal pod autoscaling is enabled this setting has no effect replicaCount: 1 image: - ## - ## Which derivate of the edc controlplane to use. - ## One of: - ## * ghcr.io/catenax-ng/product-edc/edc-controlplane-memory - ## * ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql - ## * ghcr.io/catenax-ng/product-edc/edc-controlplane-cosmosdb - ## + # -- Which derivate of the edc controlplane to use. + # One of: [ghcr.io/catenax-ng/product-edc/edc-controlplane-memory, , ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql, ghcr.io/catenax-ng/product-edc/edc-controlplane-cosmosdb] repository: ghcr.io/catenax-ng/product-edc/edc-controlplane-memory + # -- [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use pullPolicy: IfNotPresent - ## - ## Overrides the image tag whose default is the chart appVersion. - ## + # -- Overrides the image tag whose default is the chart appVersion. tag: "" -## -## Image pull secret to create to obtain the container image -## Note: 'imagePullSecret.dockerconfigjson' takes precedence if configured together with 'imagePullSecrets' -## imagePullSecret: + # -- Image pull secret to create to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) + # Note: This value needs to adhere to the [(base64 encoded) .dockerconfigjson format](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#registry-secret-existing-credentials). + # Furthermore, if 'imagePullSecret.dockerconfigjson' is defined, it takes precedence over 'imagePullSecrets'. dockerconfigjson: "" -## -## Existing image pull secret to use to obtain the container image -## +# -- Existing image pull secret to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) imagePullSecrets: [] +# -- Overrides the charts name nameOverride: "" + +# -- Overrides the releases full name fullnameOverride: "" serviceAccount: - ## - ## Specifies whether a service account should be created per release - ## + # -- Specifies whether a [service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) should be created per release create: true - ## - ## Annotations to add to the service account - ## + # -- [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/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 - ## + # -- The name of the service account to use. If not set and create is true, a name is generated using the release's fullname template name: "" +# -- [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) added to deployed [pods](https://kubernetes.io/docs/concepts/workloads/pods/) podAnnotations: {} +# -- The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment podSecurityContext: {} -# fsGroup: 2000 + # runAsUser: 1000 + # runAsGroup: 3000 + # fsGroup: 2000 +# The [container security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) defines privilege and access control settings for a Container within a pod securityContext: # capabilities: # drop: # - ALL # readOnlyRootFilesystem: true - # runAsNonRoot: true - runAsUser: 1001 + # -- Requires the container to run without root privileges + runAsNonRoot: true + # -- The container's process will run with the specified uid + runAsUser: 1000 -## -## Whether to enable kubernetes liveness-probes -## livenessProbe: + # -- Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) enabled: true -## -## Whether to enable kubernetes readiness-probes -## readinessProbe: + # -- Whether to enable kubernetes readiness-probes enabled: true -## -## Whether to enable kubernetes startup-probes -## startupProbe: + # -- Whether to enable kubernetes startup-probes enabled: true -## -## EDC endpoints exposed by the conrol-plane -## +## EDC endpoints exposed by the control-plane edc: endpoints: - # Default api exposing health checks etc + ## Default api exposing health checks etc default: + # -- The network port, which the "default" api is going to be exposed by the container, pod and service port: "8080" + # -- The path mapping the "default" api is going to be exposed at path: /api - ingress: false - # Data management API + ## Data management API data: + # -- The network port, which the "data" management api is going to be exposed by the container, pod and service port: "8181" + # -- The path mapping the "data" management api is going to be exposed at path: /data - ingress: true - # Validation API + ## Validation API validation: + # -- The network port, which the "validation" api is going to be exposed by the container, pod and service port: "8182" + # -- The path mapping the "validation" api is going to be exposed at path: /validation - ingress: false - # Control API + ## Control API control: + # -- The network port, which the "control" api is going to be exposed by the container, pod and service port: "9999" + # -- The path mapping the "control" api is going to be exposed at path: /api/controlplane/control - ingress: true - # IDS endpoints + ## IDS endpoints ids: + # -- The network port, which the "ids" multipart api is going to be exposed by the container, pod and service port: "8282" + # -- The path mapping the "ids" multipart api is going to be exposed at path: /api/v1/ids - ingress: true - # Prometheus endpoint + ## Prometheus endpoint metrics: + # -- The network port, which the prometheus metrics are going to be exposed by the container, pod and service port: "9090" + # -- The path mapping the prometheus metrics are going to be exposed at path: /metrics - ingress: false -## -## Service to expose the running application on a set of Pods as a network service. -## service: + # -- [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. type: ClusterIP -## ## Ingress declaration to expose the network service. -## ingresses: ## Public / Internet facing Ingress - enabled: true - ## - ## The hostname to be used to precisely map incoming traffic onto the underlying network service - ## + # -- The hostname to be used to precisely map incoming traffic onto the underlying network service hostname: "edc-controlplane.local" - ## - ## Additional ingress annotations to add - ## + # -- Additional ingress annotations to add annotations: {} - ## - ## EDC endpoints exposed by this ingress resource - ## + # -- EDC endpoints exposed by this ingress resource endpoints: - ids - ## - ## Defines the ingress class to use https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class - ## + # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use className: "" - ## - ## Enables TLS on the ingress resource - ## + # -- Enables TLS on the ingress resource tls: false - ## - ## Adds cert-manager annotations to the ingress - ## + ## Adds [cert-manager](https://cert-manager.io/docs/) annotations to the ingress resource certManager: - ## - ## If preset enables certificate generation via cert-manager namespace scoped issuer - ## + # -- If preset enables certificate generation via cert-manager namespace scoped issuer issuer: "" - ## - ## If preset enables certificate generation via cert-manager cluster-wide issuer - ## + # -- If preset enables certificate generation via cert-manager cluster-wide issuer clusterIssuer: "" ## Private / Intranet facing Ingress - enabled: false - ## - ## The hostname to be used to precisely map incoming traffic onto the underlying network service - ## + # -- The hostname to be used to precisely map incoming traffic onto the underlying network service hostname: "edc-controlplane.intranet" - ## - ## Additional ingress annotations to add - ## + # -- Additional ingress annotations to add annotations: {} - ## - ## EDC endpoints exposed by this ingress resource - ## + # -- EDC endpoints exposed by this ingress resource endpoints: - data - control - ## - ## Defines the ingress class to use https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class - ## + # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use className: "" - ## - ## Enables TLS on the ingress resource - ## + # -- Enables TLS on the ingress resource tls: false - ## - ## Adds cert-manager annotations to the ingress - ## + ## Adds [cert-manager](https://cert-manager.io/docs/) annotations to the ingress resource certManager: - ## - ## If preset enables certificate generation via cert-manager namespace scoped issuer - ## + # -- If preset enables certificate generation via cert-manager namespace scoped issuer issuer: "" - ## - ## If preset enables certificate generation via cert-manager cluster-wide issuer - ## + # -- If preset enables certificate generation via cert-manager cluster-wide issuer clusterIssuer: "" +# -- [Resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) applied to the deployed pod resources: {} # We usually recommend not to specify default resources and to leave this as a conscious @@ -214,31 +173,34 @@ resources: # memory: 128Mi autoscaling: + # -- Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) enabled: false + # -- Minimal replicas if resource consumption falls below resource threshholds minReplicas: 1 + # -- Maximum replicas if resource consumption exceeds resource threshholds maxReplicas: 100 + # -- targetAverageUtilization of cpu provided to a pod targetCPUUtilizationPercentage: 80 - # targetMemoryUtilizationPercentage: 80 + # -- targetAverageUtilization of memory provided to a pod + targetMemoryUtilizationPercentage: 80 +# -- [Node-Selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) to constrain the Pod to nodes with specific labels. nodeSelector: {} +# -- [Tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) are applied to Pods to schedule onto nodes with matching taints. tolerations: [] +# -- [Affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) constrains which nodes the Pod can be scheduled on based on node labels. affinity: {} -## -## Container environment variables -## e.g. for configuring JAVA_TOOL_OPTIONS (see: https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/envvars002.html) -## -## JAVA_TOOL_OPTIONS: > -## -Dhttp.proxyHost=proxy -Dhttp.proxyPort=80 -Dhttp.nonProxyHosts="localhost|127.*|[::1]" -Dhttps.proxyHost=proxy -Dhttps.proxyPort=443 -## +# -- Container environment variables e.g. for configuring [JAVA_TOOL_OPTIONS](https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/envvars002.html) +# Ex.: +# JAVA_TOOL_OPTIONS: > +# -Dhttp.proxyHost=proxy -Dhttp.proxyPort=80 -Dhttp.nonProxyHosts="localhost|127.*|[::1]" -Dhttps.proxyHost=proxy -Dhttps.proxyPort=443 env: {} -## -## EDC logging.properties configuring the java.util.logging subsystem -## logging: + # -- EDC logging.properties configuring the [java.util.logging subsystem](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html#a1.8) properties: |- .level=INFO org.eclipse.dataspaceconnector.level=ALL @@ -247,22 +209,14 @@ logging: java.util.logging.ConsoleHandler.level=ALL java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n -## -## opentelemetry.properties configuring the opentelemetry agent -## -## See https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/ for further configuration options -## opentelemetry: + # -- opentelemetry.properties configuring the [opentelemetry agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) properties: |- otel.javaagent.enabled=true otel.javaagent.debug=false -## -## EDC configuration.properties -## -## Consult the eclipse-dataspaceconnector documentation for further details: https://github.com/eclipse-dataspaceconnector/DataSpaceConnector -## configuration: + # -- EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) properties: |- # edc.api.auth.key= # edc.api.control.auth.apikey.key= diff --git a/deployment/helm/edc-dataplane/Chart.yaml b/deployment/helm/edc-dataplane/Chart.yaml index 82559407b..cd2386e6a 100644 --- a/deployment/helm/edc-dataplane/Chart.yaml +++ b/deployment/helm/edc-dataplane/Chart.yaml @@ -1,7 +1,9 @@ --- apiVersion: v2 name: edc-dataplane -description: EDC Data-Plane +description: >- + EDC Data-Plane - The Eclipse DataSpaceConnector data layer with + responsibility of transferring and receiving data streams home: https://github.com/catenax-ng/product-edc/deployment/helm/edc-dataplane type: application appVersion: "0.0.1" diff --git a/deployment/helm/edc-dataplane/README.md b/deployment/helm/edc-dataplane/README.md index acc4aa7c8..1b634fa17 100644 --- a/deployment/helm/edc-dataplane/README.md +++ b/deployment/helm/edc-dataplane/README.md @@ -1 +1,69 @@ -# edc-dataplane Helm Chart \ No newline at end of file +# edc-dataplane + +![Version: 0.0.1](https://img.shields.io/badge/Version-0.0.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.0.1](https://img.shields.io/badge/AppVersion-0.0.1-informational?style=flat-square) + +EDC Data-Plane - The Eclipse DataSpaceConnector data layer with responsibility of transferring and receiving data streams + +**Homepage:** + +## TL;DR +```shell +$ helm repo add catenax-ng-product-edc https://catenax-ng.github.io/product-edc +$ helm install my-release catenax-ng-product-edc/edc-dataplane +``` + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| affinity | object | `{}` | [Affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) constrains which nodes the Pod can be scheduled on based on node labels. | +| autoscaling.enabled | bool | `false` | Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) | +| autoscaling.maxReplicas | int | `100` | Maximum replicas if resource consumption exceeds resource threshholds | +| autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | +| autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | +| autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | +| configuration.properties | string | `"# edc.api.control.auth.apikey.key=\n# edc.api.control.auth.apikey.value=\n# edc.assetindex.cosmos.account-name=\n# edc.assetindex.cosmos.container-name=\n# edc.assetindex.cosmos.database-name=\n# edc.assetindex.cosmos.preferred-region=\n# edc.atomikos.checkpoint.interval=\n# edc.atomikos.directory=\n# edc.atomikos.logging=\n# edc.atomikos.threaded2pc=\n# edc.atomikos.timeout=\n# edc.aws.access.key=\n# edc.aws.provision.retry.retries.max=\n# edc.aws.provision.role.duration.session.max=\n# edc.aws.secret.access.key=\n# edc.blobstore.endpoint=\n# edc.contractdefinitionstore.cosmos.account-name=\n# edc.contractdefinitionstore.cosmos.container-name=\n# edc.contractdefinitionstore.cosmos.database-name=\n# edc.contractdefinitionstore.cosmos.preferred-region=\n# edc.contractnegotiationstore.cosmos.account-name=\n# edc.contractnegotiationstore.cosmos.container-name=\n# edc.contractnegotiationstore.cosmos.database-name=\n# edc.contractnegotiationstore.cosmos.preferred-region=\n# edc.controlplane.validation-endpoint=\n# edc.core.retry.backoff.max=\n# edc.core.retry.backoff.min=\n# edc.core.retry.retries.max=\n# edc.core.system.health.check.liveness-period=\n# edc.core.system.health.check.readiness-period=\n# edc.core.system.health.check.startup-period=\n# edc.core.system.health.check.threadpool-size=\n# edc.cosmos.partition-key=\n# edc.cosmos.query-metrics-enabled=\n# edc.dataplane.queue.capacity=\n# edc.dataplane.wait=\n# edc.dataplane.workers=\n# edc.datasource.asset.name=\"default\"\n# edc.datasource.contractdefinition.name=\"default\"\n# edc.datasource.contractnegotiation.name=\"default\"\n# edc.datasource.policy.name=\"default\"\n# edc.datasource.transferprocess.name=\"default\"\n# edc.datasource.default.pool.maxIdleConnections=\n# edc.datasource.default.pool.maxTotalConnections=\n# edc.datasource.default.pool.minIdleConnections=\n# edc.datasource.default.pool.testConnectionOnBorrow=\n# edc.datasource.default.pool.testConnectionOnCreate=\n# edc.datasource.default.pool.testConnectionOnReturn=\n# edc.datasource.default.pool.testConnectionWhileIdle=\n# edc.datasource.default.pool.testQuery=\n# edc.datasource.default.url=\n# edc.datasource.default.user=\n# edc.datasource.default.password=\n# edc.dpf.selector.url=\n# edc.events.topic.endpoint=\n# edc.events.topic.name=\n# edc.fs.config=\n# edc.hostname=\n# edc.identity.did.url=\n# edc.ids.catalog.id=\n# edc.ids.curator=\n# edc.ids.description=\n# edc.ids.endpoint=\n# edc.ids.id=\n# edc.ids.maintainer=\n# edc.ids.security.profile=\n# edc.ids.title=\n# edc.ids.validation.referringconnector=\n# edc.ion.crawler.did-type=\n# edc.ion.crawler.interval-minutes=\n# edc.ion.crawler.ion.url=\n# edc.metrics.enabled=\n# edc.metrics.executor.enabled=\n# edc.metrics.jersey.enabled=\n# edc.metrics.jetty.enabled=\n# edc.metrics.okhttp.enabled=\n# edc.metrics.system.enabled=\n# edc.negotiation.consumer.state-machine.batch-size=\n# edc.negotiation.provider.state-machine.batch-size=\n# edc.node.directory.cosmos.account.name=\n# edc.node.directory.cosmos.container.name=\n# edc.node.directory.cosmos.database.name=\n# edc.node.directory.cosmos.preferred.region=\n# edc.oauth.client.id=\n# edc.oauth.private.key.alias=\n# edc.oauth.provider.audience=\n# edc.oauth.provider.jwks.refresh=\n# edc.oauth.provider.jwks.url=\n# edc.oauth.public.key.alias=\n# edc.oauth.token.url=\n# edc.oauth.validation.nbf.leeway=\n# edc.receiver.http.auth-code=\n# edc.receiver.http.auth-key=\n# edc.receiver.http.endpoint=\n# edc.transfer.proxy.endpoint=\n# edc.transfer.dataplane.sync.token.validity=\n# edc.transfer.proxy.token.signer.privatekey.alias=\n# edc.transfer.functions.check.endpoint=\n# edc.transfer.functions.enabled.protocols=\n# edc.transfer.functions.transfer.endpoint=\n# edc.transfer-process-store.cosmos.account.name=\n# edc.transfer-process-store.cosmos.container-name=\n# edc.transfer-process-store.cosmos.preferred-region=\n# edc.transfer-process-store.database.name=\n# edc.transfer.state-machine.batch-size=\n# edc.vault=\n# edc.vault.certificate=\n# edc.vault.clientid=\n# edc.vault.clientsecret=\n# edc.vault.name=\n# edc.vault.tenantid=\n# edc.webdid.doh.url=\n# edc.web.rest.cors.enabled=\n# edc.web.rest.cors.headers=\n# edc.web.rest.cors.methods=\n# edc.web.rest.cors.origins="` | EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) | +| edc.endpoints.control.path | string | `"/api/dataplane/control"` | The path mapping the "control" api is going to be exposed by | +| edc.endpoints.control.port | string | `"9999"` | The network port, which the "control" api is going to be exposed by the container, pod and service | +| edc.endpoints.default.path | string | `"/api"` | The path mapping the "default" api is going to be exposed by | +| edc.endpoints.default.port | string | `"8080"` | The network port, which the "default" api is going to be exposed by the container, pod and service | +| edc.endpoints.metrics.path | string | `"/metrics"` | The path mapping the prometheus metrics are going to be exposed at | +| edc.endpoints.metrics.port | string | `"9090"` | The network port, which the prometheus metrics are going to be exposed by the container, pod and service | +| edc.endpoints.public.path | string | `"/api/public"` | The path mapping the "public" api is going to be exposed by | +| edc.endpoints.public.port | string | `"8185"` | The network port, which the "public" api is going to be exposed by the container, pod and service | +| env | object | `{}` | Container environment variables e.g. for configuring [JAVA_TOOL_OPTIONS](https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/envvars002.html) Ex.: JAVA_TOOL_OPTIONS: > -Dhttp.proxyHost=proxy -Dhttp.proxyPort=80 -Dhttp.nonProxyHosts="localhost|127.*|[::1]" -Dhttps.proxyHost=proxy -Dhttps.proxyPort=443 | +| fullnameOverride | string | `""` | Overrides the releases full name | +| image.pullPolicy | string | `"IfNotPresent"` | [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use | +| image.repository | string | `"ghcr.io/catenax-ng/product-edc/edc-dataplane"` | Which edc-dataplane container image to use | +| image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion | +| imagePullSecret.dockerconfigjson | string | `""` | Image pull secret to create to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) Note: This value needs to adhere to the [(base64 encoded) .dockerconfigjson format](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#registry-secret-existing-credentials). Furthermore, if 'imagePullSecret.dockerconfigjson' is defined, it takes precedence over 'imagePullSecrets'. | +| imagePullSecrets | list | `[]` | Existing image pull secret to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) | +| ingresses[0].annotations | object | `{}` | Additional ingress annotations to add | +| ingresses[0].certManager.clusterIssuer | string | `""` | If preset enables certificate generation via cert-manager cluster-wide issuer | +| ingresses[0].certManager.issuer | string | `""` | If preset enables certificate generation via cert-manager namespace scoped issuer | +| ingresses[0].className | string | `""` | Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use | +| ingresses[0].enabled | bool | `true` | | +| ingresses[0].endpoints | list | `["public"]` | EDC endpoints exposed by this ingress resource | +| ingresses[0].hostname | string | `"edc-dataplane.local"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | +| ingresses[0].tls | bool | `false` | Enables TLS on the ingress resource | +| livenessProbe.enabled | bool | `true` | Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) | +| logging.properties | string | `".level=INFO\norg.eclipse.dataspaceconnector.level=ALL\nhandlers=java.util.logging.ConsoleHandler\njava.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter\njava.util.logging.ConsoleHandler.level=ALL\njava.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n"` | EDC logging.properties configuring the [java.util.logging subsystem](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html#a1.8) | +| nameOverride | string | `""` | Overrides the charts name | +| nodeSelector | object | `{}` | [Node-Selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) to constrain the Pod to nodes with specific labels. | +| opentelemetry.properties | string | `"otel.javaagent.enabled=true\notel.javaagent.debug=false"` | opentelemetry.properties configuring the [opentelemetry agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) | +| podAnnotations | object | `{}` | [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) added to deployed [pods](https://kubernetes.io/docs/concepts/workloads/pods/) | +| podSecurityContext | object | `{}` | The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment | +| readinessProbe.enabled | bool | `true` | Whether to enable kubernetes readiness-probes | +| replicaCount | int | `1` | Specifies how many replicas of a deployed pod shall be created during the deployment Note: If horizontal pod autoscaling is enabled this setting has no effect | +| resources | object | `{}` | [Resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) applied to the deployed pod | +| securityContext.runAsNonRoot | bool | `true` | Requires the container to run without root privileges | +| securityContext.runAsUser | int | `1000` | The container's process will run with the specified uid | +| service.type | string | `"ClusterIP"` | [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. | +| serviceAccount.annotations | object | `{}` | [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) to add to the service account | +| serviceAccount.create | bool | `true` | Specifies whether a [service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) should be created per release | +| serviceAccount.name | string | `""` | The name of the service account to use. If not set and create is true, a name is generated using the release's fullname template | +| startupProbe.enabled | bool | `true` | Whether to enable kubernetes startup-probes | +| tolerations | list | `[]` | [Tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) are applied to Pods to schedule onto nodes with matching taints. | + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.10.0](https://github.com/norwoodj/helm-docs/releases/v1.10.0) diff --git a/deployment/helm/edc-dataplane/README.md.gotmpl b/deployment/helm/edc-dataplane/README.md.gotmpl new file mode 100644 index 000000000..0e9ca3d5d --- /dev/null +++ b/deployment/helm/edc-dataplane/README.md.gotmpl @@ -0,0 +1,25 @@ +{{ template "chart.header" . }} + +{{ template "chart.deprecationWarning" . }} + +{{ template "chart.badgesSection" . }} + +{{ template "chart.description" . }} + +{{ template "chart.homepageLine" . }} + +## TL;DR +```shell +$ helm repo add catenax-ng-product-edc https://catenax-ng.github.io/product-edc +$ helm install my-release catenax-ng-product-edc/edc-dataplane +``` + +{{ template "chart.maintainersSection" . }} + +{{ template "chart.sourcesSection" . }} + +{{ template "chart.requirementsSection" . }} + +{{ template "chart.valuesSection" . }} + +{{ template "helm-docs.versionFooter" . }} diff --git a/deployment/helm/edc-dataplane/values.yaml b/deployment/helm/edc-dataplane/values.yaml index e63ce75d8..a652f9530 100644 --- a/deployment/helm/edc-dataplane/values.yaml +++ b/deployment/helm/edc-dataplane/values.yaml @@ -3,153 +3,128 @@ # This is a YAML-formatted file. # Declare variables to be passed into your templates. +# -- Specifies how many replicas of a deployed pod shall be created during the deployment +# Note: If horizontal pod autoscaling is enabled this setting has no effect replicaCount: 1 image: - ## - ## Which edc-dataplane container image to use. - ## + # -- Which edc-dataplane container image to use repository: ghcr.io/catenax-ng/product-edc/edc-dataplane + # -- [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use pullPolicy: IfNotPresent - ## - ## Overrides the image tag whose default is the chart appVersion. - ## + # -- Overrides the image tag whose default is the chart appVersion tag: "" -## -## Image pull secret to create to obtain the container image -## Note: 'imagePullSecret.dockerconfigjson' takes precedence if configured together with 'imagePullSecrets' -## imagePullSecret: + # -- Image pull secret to create to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) + # Note: This value needs to adhere to the [(base64 encoded) .dockerconfigjson format](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#registry-secret-existing-credentials). + # Furthermore, if 'imagePullSecret.dockerconfigjson' is defined, it takes precedence over 'imagePullSecrets'. dockerconfigjson: "" -## -## Existing image pull secret to use to obtain the container image -## +# -- Existing image pull secret to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) imagePullSecrets: [] +# -- Overrides the charts name nameOverride: "" + +# -- Overrides the releases full name fullnameOverride: "" serviceAccount: - ## - ## Specifies whether a service account should be created per release - ## + # -- Specifies whether a [service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) should be created per release create: true - ## - ## Annotations to add to the service account - ## + # -- [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/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 - ## + # -- The name of the service account to use. If not set and create is true, a name is generated using the release's fullname template name: "" +# -- [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) added to deployed [pods](https://kubernetes.io/docs/concepts/workloads/pods/) podAnnotations: {} +# -- The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment podSecurityContext: {} + # runAsUser: 1000 + # runAsGroup: 3000 # fsGroup: 2000 +# The [container security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) defines privilege and access control settings for a Container within a pod securityContext: - {} # capabilities: # drop: # - ALL # readOnlyRootFilesystem: true - # runAsNonRoot: true -# runAsUser: 1000 + # -- Requires the container to run without root privileges + runAsNonRoot: true + # -- The container's process will run with the specified uid + runAsUser: 1000 -## -## Whether to enable kubernetes liveness-probes -## livenessProbe: + # -- Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) enabled: true -## -## Whether to enable kubernetes readiness-probes -## readinessProbe: + # -- Whether to enable kubernetes readiness-probes enabled: true -## -## Whether to enable kubernetes startup-probes -## startupProbe: + # -- Whether to enable kubernetes startup-probes enabled: true -## ## EDC endpoints exposed by the data-plane -## edc: endpoints: - # Default api exposing health checks etc + ## Default api exposing health checks etc default: + # -- The network port, which the "default" api is going to be exposed by the container, pod and service port: "8080" + # -- The path mapping the "default" api is going to be exposed by path: /api - ingress: false - # Public endpoint for data transfer + ## Public endpoint for data transfer public: + # -- The network port, which the "public" api is going to be exposed by the container, pod and service port: "8185" + # -- The path mapping the "public" api is going to be exposed by path: /api/public - ingress: true - # Control API + ## Control API control: + # -- The network port, which the "control" api is going to be exposed by the container, pod and service port: "9999" + # -- The path mapping the "control" api is going to be exposed by path: /api/dataplane/control - ingress: true - # Prometheus endpoint + ## Prometheus endpoint metrics: + # -- The network port, which the prometheus metrics are going to be exposed by the container, pod and service port: "9090" + # -- The path mapping the prometheus metrics are going to be exposed at path: /metrics - ingress: false -## -## Service to expose the running application on a set of Pods as a network service. -## service: + # -- [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. type: ClusterIP -## ## Ingress declaration to expose the network service. -## ingresses: ## Public / Internet facing Ingress - enabled: true - ## - ## The hostname to be used to precisely map incoming traffic onto the underlying network service - ## + # -- The hostname to be used to precisely map incoming traffic onto the underlying network service hostname: "edc-dataplane.local" - ## - ## Additional ingress annotations to add - ## + # -- Additional ingress annotations to add annotations: {} - ## - ## EDC endpoints exposed by this ingress resource - ## + # -- EDC endpoints exposed by this ingress resource endpoints: - public - ## - ## Defines the ingress class to use https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class - ## + # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use className: "" - ## - ## Enables TLS on the ingress resource - ## + # -- Enables TLS on the ingress resource tls: false - ## - ## Adds cert-manager annotations to the ingress - ## + ## Adds [cert-manager](https://cert-manager.io/docs/) annotations to the ingress resource certManager: - ## - ## If preset enables certificate generation via cert-manager namespace scoped issuer - ## + # -- If preset enables certificate generation via cert-manager namespace scoped issuer issuer: "" - ## - ## If preset enables certificate generation via cert-manager cluster-wide issuer - ## + # -- If preset enables certificate generation via cert-manager cluster-wide issuer clusterIssuer: "" +# -- [Resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) applied to the deployed pod resources: {} # We usually recommend not to specify default resources and to leave this as a conscious @@ -164,31 +139,34 @@ resources: # memory: 128Mi autoscaling: + # -- Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) enabled: false + # -- Minimal replicas if resource consumption falls below resource threshholds minReplicas: 1 + # -- Maximum replicas if resource consumption exceeds resource threshholds maxReplicas: 100 + # -- targetAverageUtilization of cpu provided to a pod targetCPUUtilizationPercentage: 80 - # targetMemoryUtilizationPercentage: 80 + # -- targetAverageUtilization of memory provided to a pod + targetMemoryUtilizationPercentage: 80 +# -- [Node-Selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) to constrain the Pod to nodes with specific labels. nodeSelector: {} +# -- [Tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) are applied to Pods to schedule onto nodes with matching taints. tolerations: [] +# -- [Affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) constrains which nodes the Pod can be scheduled on based on node labels. affinity: {} -## -## Container environment variables -## e.g. for configuring JAVA_TOOL_OPTIONS (see: https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/envvars002.html) -## -## JAVA_TOOL_OPTIONS: > -## -Dhttp.proxyHost=proxy -Dhttp.proxyPort=80 -Dhttp.nonProxyHosts="localhost|127.*|[::1]" -Dhttps.proxyHost=proxy -Dhttps.proxyPort=443 -## +# -- Container environment variables e.g. for configuring [JAVA_TOOL_OPTIONS](https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/envvars002.html) +# Ex.: +# JAVA_TOOL_OPTIONS: > +# -Dhttp.proxyHost=proxy -Dhttp.proxyPort=80 -Dhttp.nonProxyHosts="localhost|127.*|[::1]" -Dhttps.proxyHost=proxy -Dhttps.proxyPort=443 env: {} -## -## EDC logging.properties configuring the java.util.logging subsystem -## logging: + # -- EDC logging.properties configuring the [java.util.logging subsystem](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html#a1.8) properties: |- .level=INFO org.eclipse.dataspaceconnector.level=ALL @@ -197,22 +175,14 @@ logging: java.util.logging.ConsoleHandler.level=ALL java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n -## -## opentelemetry.properties configuring the opentelemetry agent -## -## See https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/ for further configuration options -## opentelemetry: + # -- opentelemetry.properties configuring the [opentelemetry agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) properties: |- otel.javaagent.enabled=true otel.javaagent.debug=false -## -## EDC configuration.properties -## -## Consult the eclipse-dataspaceconnector documentation for further details: https://github.com/eclipse-dataspaceconnector/DataSpaceConnector -## configuration: + # -- EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) properties: |- # edc.api.control.auth.apikey.key= # edc.api.control.auth.apikey.value= From ee3ceeb50705b04a79556f92d4e0b86ef83af088 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Fri, 20 May 2022 09:51:03 +0200 Subject: [PATCH 103/433] Update edc git submodule to be at dc8ff6d (#149) --- edc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc b/edc index 79fe5d3ca..dc8ff6d56 160000 --- a/edc +++ b/edc @@ -1 +1 @@ -Subproject commit 79fe5d3cacf7c323b02fc09110e81ec28b37d8e6 +Subproject commit dc8ff6d5681e80db284b6fb5d418b131eae6af57 From 340e7999b79828efb74c89236cfeb73b958330b3 Mon Sep 17 00:00:00 2001 From: "Florian Rusch (ZF Friedrichshafen AG)" Date: Mon, 23 May 2022 09:34:04 +0200 Subject: [PATCH 104/433] Remove unneeded variables from get contract offer catalog documentation (#150) --- edc-controlplane/README.md | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/edc-controlplane/README.md b/edc-controlplane/README.md index f1cf32338..9b5d4715f 100644 --- a/edc-controlplane/README.md +++ b/edc-controlplane/README.md @@ -494,23 +494,6 @@ __targetConnectorIdsPath=api/v1/ids __defaultApiPath=api __apiKey=X-Api-Key __apiKeyValue=pwd -__contractDefinitionId=1 -__policyId=1 -__assetId=1 - -__publicContractDefinition=" - { - \"id\": \"$__contractDefinitionId\", - \"accessPolicyId\": \"$__policyId\", - \"contractPolicyId\": \"$__policyId\", - \"criteria\": [ - { - \"left\": \"asset:prop:id\", - \"op\": \"=\", - \"right\": \"$__assetId\" - } - ] - }" # Call Control API curl -G -X GET $__connectorUrl/$__defaultApiPath/control/catalog --header "$__apiKey: $__apiKeyValue" --data-urlencode "provider=$__targetConnectorUrl/$__targetConnectorIdsPath/data" --header "Content-Type: application/json" -s | jq From b9c0bfa9c5b1e2132cfe0a8b2bb22ddf50263952 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Mon, 23 May 2022 09:35:03 +0200 Subject: [PATCH 105/433] Update edc git submodule to be at acd0233 (#154) Signed-off-by: Denis Neuling --- deployment/helm/edc-controlplane/values.yaml | 2 + edc | 2 +- .../edc-controlplane-cosmosdb/README.md | 104 +++++++++--------- 3 files changed, 56 insertions(+), 52 deletions(-) diff --git a/deployment/helm/edc-controlplane/values.yaml b/deployment/helm/edc-controlplane/values.yaml index 25f40018c..3be6dd27e 100644 --- a/deployment/helm/edc-controlplane/values.yaml +++ b/deployment/helm/edc-controlplane/values.yaml @@ -243,6 +243,7 @@ configuration: # edc.contractnegotiationstore.cosmos.container-name= # edc.contractnegotiationstore.cosmos.database-name= # edc.contractnegotiationstore.cosmos.preferred-region= + # edc.contractnegotiationstore.cosmos.allow.sproc.autoupload= # edc.controlplane.validation-endpoint= # edc.core.retry.backoff.max= # edc.core.retry.backoff.min= @@ -322,6 +323,7 @@ configuration: # edc.transfer-process-store.cosmos.account.name= # edc.transfer-process-store.cosmos.container-name= # edc.transfer-process-store.cosmos.preferred-region= + # edc.transfer-process-store.cosmos.allow.sproc.autoupload= # edc.transfer-process-store.database.name= # edc.transfer.state-machine.batch-size= # edc.vault= diff --git a/edc b/edc index dc8ff6d56..acd02337a 160000 --- a/edc +++ b/edc @@ -1 +1 @@ -Subproject commit dc8ff6d5681e80db284b6fb5d418b131eae6af57 +Subproject commit acd02337a16d9498727bee12e49e715e9be960e3 diff --git a/edc-controlplane/edc-controlplane-cosmosdb/README.md b/edc-controlplane/edc-controlplane-cosmosdb/README.md index 8c932d960..09c5dbef3 100644 --- a/edc-controlplane/edc-controlplane-cosmosdb/README.md +++ b/edc-controlplane/edc-controlplane-cosmosdb/README.md @@ -16,57 +16,59 @@ This secret must contain the primary or the secondard CosmosDB Read-write key. Listed below are configuration keys needed to get the `edc-controlplane-cosmosdb` up and running. Details regarding each configuration property can be found at the [documentary section of the EDC](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/docs). -| Key | Required | Example | Description | -|--- |--- |--- |--- | -| web.http.default.port | X | 8080 | | -| web.http.default.path | X | /api | | -| web.http.data.port | X | 8181 | | -| web.http.data.path | X | | | -| web.http.validation.port | X | 8182 | | -| web.http.validation.path | X | /validation | | -| web.http.control.port | X | 9999 | | -| web.http.control.path | X | /api/controlplane/control | | -| web.http.ids.port | X | 8282 | | -| web.http.ids.path | X | /api/v1/ids | | -| edc.receiver.http.endpoint | X | http://backend-service | | -| edc.ids.title | | Eclipse Dataspace Connector | | -| edc.ids.description | | Eclipse Dataspace Connector | | -| edc.ids.id | | urn:connector:edc | | -| edc.ids.security.profile | | base | | -| edc.ids.endpoint | | http://localhost:8282/api/v1/ids | | -| edc.ids.maintainer | | http://localhost | | -| edc.ids.curator | | http://localhost | | -| edc.ids.catalog.id | | urn:catalog:default | | -| ids.webhook.address | | http://localhost:8282/api/v1/ids | | -| edc.api.control.auth.apikey.key | | X-Api-Key | | -| edc.api.control.auth.apikey.value | | super-strong-api-key | | -| edc.hostname | | localhost | | -| edc.oauth.token.url | X | https://daps.catena-x.net | | -| edc.oauth.public.key.alias | X | key-to-daps-certificate-in-keyvault | | -| edc.oauth.private.key.alias | X | key-to-private-key-in-keyvault | | -| edc.oauth.client.id | X | daps-oauth-client-id | | -| edc.vault.clientid | X | 00000000-1111-2222-3333-444444444444 | | -| edc.vault.tenantid | X | 55555555-6666-7777-8888-999999999999 | | -| edc.vault.name | X | my-vault-name | | -| edc.vault.clientsecret | X | 34-chars-secret | | -| edc.assetindex.cosmos.account-name | X | cosmosdb-assetindex-account-name | | -| edc.assetindex.cosmos.database-name | X | asset-index | | -| edc.assetindex.cosmos.preferred-region | X | westeurope | | -| edc.assetindex.cosmos.container-name | X | cosmosdb-assetindex-container-name | | -| edc.contractdefinitionstore.cosmos.account-name | X | cosmosdb-contractdefinitionstore-account-name | | -| edc.contractdefinitionstore.cosmos.database-name | X | contract-definition-store | | -| edc.contractdefinitionstore.cosmos.preferred-region | X | westeurope | | -| edc.contractdefinitionstore.cosmos.container-name | X | cosmosdb-contractdefinitionstore-container-name | | -| edc.contractnegotiationstore.cosmos.account-name | X | cosmosdb-contractnegotiationstore-account-name | | -| edc.contractnegotiationstore.cosmos.database-name | X | contract-negotiation-store | | -| edc.contractnegotiationstore.cosmos.preferred-region | X | westeurope | | -| edc.contractnegotiationstore.cosmos.container-name | X | cosmosdb-contractnegotiationstore-container-name | | -| edc.transfer-process-store.cosmos.account.name | X | cosmosdb-contractnegotiationstore-account-name | | -| edc.transfer-process-store.database.name | X | transfer-process-store | | -| edc.transfer-process-store.cosmos.preferred-region | X | westeurope | | -| edc.transfer-process-store.cosmos.container-name | X | cosmosdb-transfer-process-store-container-name | | -| edc.transfer.proxy.endpoint | X | | | -| edc.transfer.proxy.token.signer.privatekey.alias | X | | | +| Key | Required | Example | Description | +|--- |--- |--- |--- | +| web.http.default.port | X | 8080 | | +| web.http.default.path | X | /api | | +| web.http.data.port | X | 8181 | | +| web.http.data.path | X | /data | | +| web.http.validation.port | X | 8182 | | +| web.http.validation.path | X | /validation | | +| web.http.control.port | X | 9999 | | +| web.http.control.path | X | /api/controlplane/control | | +| web.http.ids.port | X | 8282 | | +| web.http.ids.path | X | /api/v1/ids | | +| edc.receiver.http.endpoint | X | http://backend-service | | +| edc.ids.title | | Eclipse Dataspace Connector | | +| edc.ids.description | | Eclipse Dataspace Connector | | +| edc.ids.id | | urn:connector:edc | | +| edc.ids.security.profile | | base | | +| edc.ids.endpoint | | http://localhost:8282/api/v1/ids | | +| edc.ids.maintainer | | http://localhost | | +| edc.ids.curator | | http://localhost | | +| edc.ids.catalog.id | | urn:catalog:default | | +| ids.webhook.address | | http://localhost:8282/api/v1/ids | | +| edc.api.control.auth.apikey.key | | X-Api-Key | | +| edc.api.control.auth.apikey.value | | super-strong-api-key | | +| edc.hostname | | localhost | | +| edc.oauth.token.url | X | https://daps.catena-x.net | | +| edc.oauth.public.key.alias | X | key-to-daps-certificate-in-keyvault | | +| edc.oauth.private.key.alias | X | key-to-private-key-in-keyvault | | +| edc.oauth.client.id | X | daps-oauth-client-id | | +| edc.vault.clientid | X | 00000000-1111-2222-3333-444444444444 | | +| edc.vault.tenantid | X | 55555555-6666-7777-8888-999999999999 | | +| edc.vault.name | X | my-vault-name | | +| edc.vault.clientsecret | X | 34-chars-secret | | +| edc.assetindex.cosmos.account-name | X | cosmosdb-assetindex-account-name | | +| edc.assetindex.cosmos.database-name | X | asset-index | | +| edc.assetindex.cosmos.preferred-region | X | westeurope | | +| edc.assetindex.cosmos.container-name | X | cosmosdb-assetindex-container-name | | +| edc.contractdefinitionstore.cosmos.account-name | X | cosmosdb-contractdefinitionstore-account-name | | +| edc.contractdefinitionstore.cosmos.database-name | X | contract-definition-store | | +| edc.contractdefinitionstore.cosmos.preferred-region | X | westeurope | | +| edc.contractdefinitionstore.cosmos.container-name | X | cosmosdb-contractdefinitionstore-container-name | | +| edc.contractnegotiationstore.cosmos.account-name | X | cosmosdb-contractnegotiationstore-account-name | | +| edc.contractnegotiationstore.cosmos.database-name | X | contract-negotiation-store | | +| edc.contractnegotiationstore.cosmos.preferred-region | X | westeurope | | +| edc.contractnegotiationstore.cosmos.container-name | X | cosmosdb-contractnegotiationstore-container-name | | +| edc.contractnegotiationstore.cosmos.allow.sproc.autoupload | | true | | +| edc.transfer-process-store.cosmos.account.name | X | cosmosdb-contractnegotiationstore-account-name | | +| edc.transfer-process-store.database.name | X | transfer-process-store | | +| edc.transfer-process-store.cosmos.preferred-region | X | westeurope | | +| edc.transfer-process-store.cosmos.container-name | X | cosmosdb-transfer-process-store-container-name | | +| edc.transfer-process-store.cosmos.allow.sproc.autoupload | | true | | +| edc.transfer.proxy.endpoint | X | http://dataplane-public-endpoint/public | | +| edc.transfer.proxy.token.signer.privatekey.alias | X | key-of-private-key-in-keyvault-to-sign-transfer-token | | #### Example configuration.properties From f409c19f2e2109642b5b9d154e07416b8c149827 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Mon, 23 May 2022 15:41:52 +0200 Subject: [PATCH 106/433] Extends release workflow to publish extension maven artifacts (#157) --- .github/workflows/publish-new-release.yml | 129 ++++++++++++------ edc-controlplane/pom.xml | 2 +- edc-extensions/aas-controller/pom.xml | 15 ++ .../business-partner-validation/pom.xml | 15 ++ edc-extensions/pom.xml | 2 +- edc-extensions/postgresql-migration/pom.xml | 15 ++ pom.xml | 41 ++++-- settings.xml | 4 +- 8 files changed, 162 insertions(+), 61 deletions(-) diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index 2a8b33e72..ca3a46f52 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -9,19 +9,22 @@ on: - closed jobs: + # Gate: Check secret presence secret-presence: name: Determine secret presence runs-on: ubuntu-latest outputs: + CXNG_GHCR_PAT: ${{ steps.secret-presence.outputs.CXNG_GHCR_PAT }} CXNG_RELEASE_PAT: ${{ steps.secret-presence.outputs.CXNG_RELEASE_PAT }} steps: - name: Check whether secrets exist id: secret-presence run: | + [ ! -z "${{ secrets.CXNG_GHCR_PAT }}" ] && echo "::set-output name=CXNG_GHCR_PAT::true" [ ! -z "${{ secrets.CXNG_RELEASE_PAT }}" ] && echo "::set-output name=CXNG_RELEASE_PAT::true" exit 0 - + # Gate: Check release version presence release-version: name: Determine release version runs-on: ubuntu-latest @@ -50,6 +53,88 @@ jobs: run: | echo "::set-output name=RELEASE_VERSION::${{ env.RELEASE_VERSION }}" + # Release: Maven Artifacts + maven-release: + name: Publish extension's release version to maven repository + needs: [ secret-presence, release-version ] + runs-on: ubuntu-latest + if: github.event.pull_request.merged == true && needs.secret-presence.outputs.CXNG_GHCR_PAT && needs.release-version.outputs.RELEASE_VERSION + steps: + - + name: Export RELEASE_VERSION env + run: | + echo "RELEASE_VERSION=${{ needs.release-version.outputs.RELEASE_VERSION }}" >> $GITHUB_ENV + - + name: Checkout + uses: actions/checkout@v3 + with: + submodules: recursive + - + name: Set up JDK 11 + uses: actions/setup-java@v3.3.0 + with: + java-version: '11' + distribution: 'adopt' + cache: 'maven' + - + name: Build edc with Gradle to get latest snapshots + run: ./gradlew publishToMavenLocal + working-directory: edc + - + name: Deploy + run: |- + ./mvnw -s settings.xml \ + -pl '!edc-controlplane,!edc-controlplane/edc-controlplane-cosmosdb,!edc-controlplane/edc-controlplane-memory,!edc-controlplane/edc-controlplane-postgresql,!edc-dataplane' \ + -DaltReleaseDeploymentRepository=github::https://maven.pkg.github.com/${{ github.repository }} \ + -Dmaven.test.skip=true -B package deploy:deploy + env: + GITHUB_PACKAGE_USERNAME: ${{ github.actor }} + GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} + + # Release: Helm Charts + helm-release: + name: Publish new helm release + needs: [ release-version ] + runs-on: ubuntu-latest + if: github.event.pull_request.merged == true && needs.release-version.outputs.RELEASE_VERSION + steps: + - + name: Export RELEASE_VERSION env + run: | + echo "RELEASE_VERSION=${{ needs.release-version.outputs.RELEASE_VERSION }}" >> $GITHUB_ENV + - + name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - + name: Install Helm + uses: azure/setup-helm@v1 + with: + version: v3.8.1 + - + name: Package helm, update index.yaml and push to gh-pages + run: | + # Prepare git env + git config user.name "GitHub actions" + git config user.email noreply@github.com + + # Package all charts + find -name Chart.yaml | xargs -n1 dirname | xargs -n1 helm package -u -d helm-charts + + git checkout gh-pages || git checkout -b gh-pages + git pull --rebase origin gh-pages + + # Generate helm repo index.yaml + helm repo index . --merge index.yaml --url https://${GITHUB_REPOSITORY_OWNER}.github.io/${GITHUB_REPOSITORY#*/}/ + + # Commit and push to gh-pages + git add index.yaml helm-charts + git commit -s -m "Release ${{ env.RELEASE_VERSION }}" + + git push origin gh-pages + + # Release: GitHub tag & release; Merges back main into develop; Starts a new development cycle; github-release: name: Publish new github release needs: [ secret-presence, release-version ] @@ -124,45 +209,3 @@ jobs: git commit --message "Introduce new snapshot version $SNAPSHOT_VERSION" git push origin develop - - helm-release: - name: Publish new helm release - needs: [ release-version ] - runs-on: ubuntu-latest - if: github.event.pull_request.merged == true && needs.release-version.outputs.RELEASE_VERSION - steps: - - - name: Export RELEASE_VERSION env - run: | - echo "RELEASE_VERSION=${{ needs.release-version.outputs.RELEASE_VERSION }}" >> $GITHUB_ENV - - - name: Checkout - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Install Helm - uses: azure/setup-helm@v1 - with: - version: v3.8.1 - - - name: Package helm, update index.yaml and push to gh-pages - run: | - # Prepare git env - git config user.name "GitHub actions" - git config user.email noreply@github.com - - # Package all charts - find -name Chart.yaml | xargs -n1 dirname | xargs -n1 helm package -u -d helm-charts - - git checkout gh-pages || git checkout -b gh-pages - git pull --rebase origin gh-pages - - # Generate helm repo index.yaml - helm repo index . --merge index.yaml --url https://${GITHUB_REPOSITORY_OWNER}.github.io/${GITHUB_REPOSITORY#*/}/ - - # Commit and push to gh-pages - git add index.yaml helm-charts - git commit -s -m "Release ${{ env.RELEASE_VERSION }}" - - git push origin gh-pages diff --git a/edc-controlplane/pom.xml b/edc-controlplane/pom.xml index e5a953fdf..27624b1ec 100644 --- a/edc-controlplane/pom.xml +++ b/edc-controlplane/pom.xml @@ -29,4 +29,4 @@ edc-controlplane-memory edc-controlplane-postgresql - \ No newline at end of file + diff --git a/edc-extensions/aas-controller/pom.xml b/edc-extensions/aas-controller/pom.xml index 4983f14b4..737784f03 100644 --- a/edc-extensions/aas-controller/pom.xml +++ b/edc-extensions/aas-controller/pom.xml @@ -41,6 +41,21 @@ + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar + + + + + diff --git a/edc-extensions/business-partner-validation/pom.xml b/edc-extensions/business-partner-validation/pom.xml index e16991b8c..5141bc47e 100644 --- a/edc-extensions/business-partner-validation/pom.xml +++ b/edc-extensions/business-partner-validation/pom.xml @@ -40,6 +40,21 @@ + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar + + + + + diff --git a/edc-extensions/pom.xml b/edc-extensions/pom.xml index 6c1267229..e75f667ad 100644 --- a/edc-extensions/pom.xml +++ b/edc-extensions/pom.xml @@ -31,4 +31,4 @@ postgresql-migration - \ No newline at end of file + diff --git a/edc-extensions/postgresql-migration/pom.xml b/edc-extensions/postgresql-migration/pom.xml index 4a05474d1..1b1f5a06c 100644 --- a/edc-extensions/postgresql-migration/pom.xml +++ b/edc-extensions/postgresql-migration/pom.xml @@ -40,6 +40,21 @@ + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar + + + + + diff --git a/pom.xml b/pom.xml index f3e17247b..1db8c319e 100644 --- a/pom.xml +++ b/pom.xml @@ -46,7 +46,10 @@ 3.2.2 2.22.5 3.0.0 - 1.5 + 3.4.0 + 2.0.0 + 3.0.0-M2 + 3.2.1 0.0.1-SNAPSHOT @@ -61,14 +64,6 @@ 1.10.19
- - - github-catenax-ng - Catena-X NG: Github Packages - https://maven.pkg.github.com/catenax-ng/product-edc - - - central @@ -85,6 +80,19 @@ + + org.apache.maven.plugins + maven-javadoc-plugin + ${org.apache.maven.plugins.javadoc.version} + + + attach-javadocs + + jar + + + + org.codehaus.mojo exec-maven-plugin @@ -95,11 +103,6 @@ commons-build-plugin 1.12 - - org.codehaus.mojo - license-maven-plugin - 2.0.0 - org.apache.maven.plugins maven-jar-plugin @@ -110,6 +113,11 @@ maven-dependency-plugin ${org.apache.maven.plugins.maven.dependency.plugin.version} + + org.apache.maven.plugins + maven-deploy-plugin + ${org.apache.maven.plugins.deploy.version} + com.diffplug.spotless spotless-maven-plugin @@ -132,6 +140,11 @@ + + org.apache.maven.plugins + maven-source-plugin + ${org.apache.maven.plugins.source.version} + maven-surefire-plugin 2.22.2 diff --git a/settings.xml b/settings.xml index 34ab44077..805b7a95c 100644 --- a/settings.xml +++ b/settings.xml @@ -11,9 +11,9 @@ - github-catenax-ng + github ${env.GITHUB_PACKAGE_USERNAME} ${env.GITHUB_PACKAGE_PASSWORD} - \ No newline at end of file + From 0780f40b959fab141c26d3f1bbfb4369b6490587 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Mon, 23 May 2022 17:12:14 +0200 Subject: [PATCH 107/433] Integrate trivy vulnerability scanner (#159) --- .github/workflows/build.yaml | 4 +- .github/workflows/trivy.yaml | 168 +++++++++++++++++++++++++++++++++++ 2 files changed, 170 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/trivy.yaml diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index f16603612..20b8553ba 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -116,9 +116,9 @@ jobs: vid: ${{ secrets.ORG_VERACODE_API_ID }} vkey: ${{ secrets.ORG_VERACODE_API_KEY }} - ################################# + ############################### ### edc-controlplane-memory ### - ################################# + ############################### build-edc-controlplane-memory: needs: [ secret-presence ] runs-on: ubuntu-latest diff --git a/.github/workflows/trivy.yaml b/.github/workflows/trivy.yaml new file mode 100644 index 000000000..9a9995c8f --- /dev/null +++ b/.github/workflows/trivy.yaml @@ -0,0 +1,168 @@ +--- +# Depending on the location of your Docker container +# you need to change the path to the specific Docker registry. +# +name: "Trivy" + +on: + workflow_run: + workflows: ["Build"] + branches: + - main + - develop + - release/* + - hotfix/* + tags: + - '[0-9]+.[0-9]+.[0-9]+' + types: + - completed + +jobs: + git-sha7: + name: Determine short git sha + runs-on: ubuntu-latest + outputs: + value: ${{ steps.git-sha7.outputs.SHA7 }} + steps: + - + name: Resolve git 7-chars sha + id: git-sha7 + run: | + echo "::set-output name=SHA7::$(git rev-parse --short HEAD)" + + analyze-config: + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + steps: + - + name: Run Trivy vulnerability scanner in repo mode + uses: aquasecurity/trivy-action@master + with: + scan-type: "config" + # ignore-unfixed: true + exit-code: "1" + hide-progress: false + format: "sarif" + output: "trivy-results-config.sarif" + severity: "CRITICAL,HIGH" + - + name: Upload Trivy scan results to GitHub Security tab + uses: github/codeql-action/upload-sarif@v1 + if: always() + with: + sarif_file: "trivy-results-config.sarif" + + ################################# + ### edc-controlplane-cosmosdb ### + ################################# + analyze-edc-controlplane-cosmosdb: + needs: [ git-sha7 ] + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + steps: + - + name: Run Trivy vulnerability scanner + if: always() + uses: aquasecurity/trivy-action@master + with: + image-ref: "ghcr.io/${{ github.repository }}/edc-controlplane-cosmosdb:sha-${{ needs.git-sha7.outputs.value }}" + format: "sarif" + output: "trivy-results-edc-controlplane-cosmosdb.sarif" + exit-code: "1" + severity: "CRITICAL,HIGH" + - + name: Upload Trivy scan results to GitHub Security tab + if: always() + uses: github/codeql-action/upload-sarif@v1 + with: + sarif_file: "trivy-results-edc-controlplane-cosmosdb.sarif" + + ############################### + ### edc-controlplane-memory ### + ############################### + analyze-edc-controlplane-memory: + needs: [ git-sha7 ] + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + steps: + - + name: Run Trivy vulnerability scanner + if: always() + uses: aquasecurity/trivy-action@master + with: + image-ref: "ghcr.io/${{ github.repository }}/edc-controlplane-memory:sha-${{ needs.git-sha7.outputs.value }}" + format: "sarif" + output: "trivy-results-edc-controlplane-memory.sarif" + exit-code: "1" + severity: "CRITICAL,HIGH" + - + name: Upload Trivy scan results to GitHub Security tab + if: always() + uses: github/codeql-action/upload-sarif@v1 + with: + sarif_file: "trivy-results-edc-controlplane-memory.sarif" + + ################################### + ### edc-controlplane-postgresql ### + ################################### + analyze-edc-controlplane-postgresql: + needs: [ git-sha7 ] + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + steps: + - + name: Run Trivy vulnerability scanner + if: always() + uses: aquasecurity/trivy-action@master + with: + image-ref: "ghcr.io/${{ github.repository }}/edc-controlplane-postgresql:sha-${{ needs.git-sha7.outputs.value }}" + format: "sarif" + output: "trivy-results-edc-controlplane-postgresql.sarif" + exit-code: "1" + severity: "CRITICAL,HIGH" + - + name: Upload Trivy scan results to GitHub Security tab + if: always() + uses: github/codeql-action/upload-sarif@v1 + with: + sarif_file: "trivy-results-edc-controlplane-postgresql.sarif" + + ##################### + ### edc-dataplane ### + ##################### + analyze-edc-dataplane: + needs: [ git-sha7 ] + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + steps: + - + name: Run Trivy vulnerability scanner + if: always() + uses: aquasecurity/trivy-action@master + with: + image-ref: "ghcr.io/${{ github.repository }}/edc-dataplane:sha-${{ needs.git-sha7.outputs.value }}" + format: "sarif" + output: "trivy-results-edc-dataplane.sarif" + exit-code: "1" + severity: "CRITICAL,HIGH" + - + name: Upload Trivy scan results to GitHub Security tab + if: always() + uses: github/codeql-action/upload-sarif@v1 + with: + sarif_file: "trivy-results-edc-dataplane.sarif" From 08d49624ebfd6379f4657100749dab053ba6cfb0 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Mon, 23 May 2022 17:40:56 +0200 Subject: [PATCH 108/433] Fix trivy scan (#160) --- .github/workflows/trivy.yaml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/.github/workflows/trivy.yaml b/.github/workflows/trivy.yaml index 9a9995c8f..47e295143 100644 --- a/.github/workflows/trivy.yaml +++ b/.github/workflows/trivy.yaml @@ -28,7 +28,7 @@ jobs: name: Resolve git 7-chars sha id: git-sha7 run: | - echo "::set-output name=SHA7::$(git rev-parse --short HEAD)" + echo "::set-output name=SHA7::${GITHUB_SHA::7}" analyze-config: runs-on: ubuntu-latest @@ -37,6 +37,9 @@ jobs: contents: read security-events: write steps: + - + name: Checkout repository + uses: actions/checkout@v2 - name: Run Trivy vulnerability scanner in repo mode uses: aquasecurity/trivy-action@master @@ -66,6 +69,9 @@ jobs: contents: read security-events: write steps: + - + name: Checkout repository + uses: actions/checkout@v2 - name: Run Trivy vulnerability scanner if: always() @@ -94,6 +100,9 @@ jobs: contents: read security-events: write steps: + - + name: Checkout repository + uses: actions/checkout@v2 - name: Run Trivy vulnerability scanner if: always() @@ -122,6 +131,9 @@ jobs: contents: read security-events: write steps: + - + name: Checkout repository + uses: actions/checkout@v2 - name: Run Trivy vulnerability scanner if: always() @@ -150,6 +162,9 @@ jobs: contents: read security-events: write steps: + - + name: Checkout repository + uses: actions/checkout@v2 - name: Run Trivy vulnerability scanner if: always() From cbb5817ef5c3438f412c0b2ac2a0fe148f37aa12 Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Tue, 24 May 2022 11:03:21 +0200 Subject: [PATCH 109/433] update control plane link for json date source (#162) --- edc-controlplane/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-controlplane/README.md b/edc-controlplane/README.md index 9b5d4715f..32fe2099b 100644 --- a/edc-controlplane/README.md +++ b/edc-controlplane/README.md @@ -323,7 +323,7 @@ __apiKey=X-Api-Key __apiKeyValue=pwd __assetId=1 __assetDescription="Demo Asset" -__assetDataEndpoint=https://github.com/eclipse-dataspaceconnector +__assetDataEndpoint="https://jsonplaceholder.typicode.com/todos/3" __asset="{ \"asset\": { From 3eecca04e0080aaf79459b9622cd3bf9c064bd11 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Tue, 24 May 2022 12:04:07 +0200 Subject: [PATCH 110/433] Re-integrate checkov (#163) --- .github/workflows/build.yaml | 2 ++ .github/workflows/checkov.yaml | 52 ++++++++++++++++++++++++++++++++++ .github/workflows/trivy.yaml | 3 -- 3 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/checkov.yaml diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 20b8553ba..a33061364 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -12,6 +12,8 @@ on: types: - released pull_request: + paths-ignore: + - 'deployment/helm/**' branches: - '*' diff --git a/.github/workflows/checkov.yaml b/.github/workflows/checkov.yaml new file mode 100644 index 000000000..5bb375e4f --- /dev/null +++ b/.github/workflows/checkov.yaml @@ -0,0 +1,52 @@ +--- +name: "Checkov" + +on: + push: + branches: + - main + - develop + tags: + - '[0-9]+.[0-9]+.[0-9]+' + paths-ignore: + - '**' + - '!deployment/helm/**' + pull_request: + branches: + - '*' + paths-ignore: + - '**' + - '!deployment/helm/**' + +jobs: + analyze: + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + name: checkov-action + steps: + - + name: Checkout repo + uses: actions/checkout@master + - + name: Run Checkov action + id: checkov + uses: bridgecrewio/checkov-action@master + with: + directory: deployment/helm + quiet: true # optional: display only failed checks + soft_fail: false # optional: do not return an error code if there are failed checks + framework: helm # optional: run only on a specific infrastructure {cloudformation,terraform,kubernetes,all} + output_format: sarif # optional: the output format, one of: cli, json, junitxml, github_failed_only, or sarif. Default: sarif + download_external_modules: true # optional: download external terraform modules from public git repositories and terraform registry + log_level: DEBUG # optional: set log level. Default WARNING + # config_file: path/this_file + # baseline: cloudformation/.checkov.baseline # optional: Path to a generated baseline file. Will only report results not in the baseline. + - + name: Upload Checkov scan results to GitHub Security tab + uses: github/codeql-action/upload-sarif@v1 + if: always() + with: + sarif_file: "results.sarif" diff --git a/.github/workflows/trivy.yaml b/.github/workflows/trivy.yaml index 47e295143..a603c7516 100644 --- a/.github/workflows/trivy.yaml +++ b/.github/workflows/trivy.yaml @@ -1,7 +1,4 @@ --- -# Depending on the location of your Docker container -# you need to change the path to the specific Docker registry. -# name: "Trivy" on: From 431b611cb4dc7cbdfa48611e372c0385a019affc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 May 2022 15:55:36 +0200 Subject: [PATCH 111/433] Bump alpine (#165) --- .../edc-controlplane-cosmosdb/src/main/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-controlplane/edc-controlplane-cosmosdb/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-cosmosdb/src/main/docker/Dockerfile index 2bb41f209..d4aa94d6b 100644 --- a/edc-controlplane/edc-controlplane-cosmosdb/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-cosmosdb/src/main/docker/Dockerfile @@ -10,7 +10,7 @@ # Contributors: # Mercedes-Benz Tech Innovation GmbH - Initial Dockerfile # -FROM alpine:3.15.4 as otel +FROM alpine:3.16.0 as otel ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" From 76a22a01173aace86c663f9c1ce8d022aa368b78 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 May 2022 15:55:43 +0200 Subject: [PATCH 112/433] Bump alpine from 3.15.4 to 3.16.0 in /edc-dataplane/src/main/docker (#166) --- edc-dataplane/src/main/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-dataplane/src/main/docker/Dockerfile b/edc-dataplane/src/main/docker/Dockerfile index be19ac944..0af3e0b55 100644 --- a/edc-dataplane/src/main/docker/Dockerfile +++ b/edc-dataplane/src/main/docker/Dockerfile @@ -10,7 +10,7 @@ # Contributors: # Mercedes-Benz Tech Innovation GmbH - Initial Dockerfile # -FROM alpine:3.15.4 as otel +FROM alpine:3.16.0 as otel ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" From 4b5f604540c9b1e711d7f2c6d8879020c0981e88 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 May 2022 15:56:08 +0200 Subject: [PATCH 113/433] Bump github/codeql-action from 1 to 2 (#167) --- .github/workflows/checkov.yaml | 2 +- .github/workflows/trivy.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/checkov.yaml b/.github/workflows/checkov.yaml index 5bb375e4f..32c9b9919 100644 --- a/.github/workflows/checkov.yaml +++ b/.github/workflows/checkov.yaml @@ -46,7 +46,7 @@ jobs: # baseline: cloudformation/.checkov.baseline # optional: Path to a generated baseline file. Will only report results not in the baseline. - name: Upload Checkov scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@v1 + uses: github/codeql-action/upload-sarif@v2 if: always() with: sarif_file: "results.sarif" diff --git a/.github/workflows/trivy.yaml b/.github/workflows/trivy.yaml index a603c7516..34eefd170 100644 --- a/.github/workflows/trivy.yaml +++ b/.github/workflows/trivy.yaml @@ -50,7 +50,7 @@ jobs: severity: "CRITICAL,HIGH" - name: Upload Trivy scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@v1 + uses: github/codeql-action/upload-sarif@v2 if: always() with: sarif_file: "trivy-results-config.sarif" @@ -82,7 +82,7 @@ jobs: - name: Upload Trivy scan results to GitHub Security tab if: always() - uses: github/codeql-action/upload-sarif@v1 + uses: github/codeql-action/upload-sarif@v2 with: sarif_file: "trivy-results-edc-controlplane-cosmosdb.sarif" @@ -113,7 +113,7 @@ jobs: - name: Upload Trivy scan results to GitHub Security tab if: always() - uses: github/codeql-action/upload-sarif@v1 + uses: github/codeql-action/upload-sarif@v2 with: sarif_file: "trivy-results-edc-controlplane-memory.sarif" @@ -144,7 +144,7 @@ jobs: - name: Upload Trivy scan results to GitHub Security tab if: always() - uses: github/codeql-action/upload-sarif@v1 + uses: github/codeql-action/upload-sarif@v2 with: sarif_file: "trivy-results-edc-controlplane-postgresql.sarif" @@ -175,6 +175,6 @@ jobs: - name: Upload Trivy scan results to GitHub Security tab if: always() - uses: github/codeql-action/upload-sarif@v1 + uses: github/codeql-action/upload-sarif@v2 with: sarif_file: "trivy-results-edc-dataplane.sarif" From 7441fdd2e87e6a37622d514982c644189503f43d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 May 2022 15:58:22 +0200 Subject: [PATCH 114/433] Bump postgresql from 42.3.5 to 42.3.6 (#172) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1db8c319e..8d61831d6 100644 --- a/pom.xml +++ b/pom.xml @@ -55,7 +55,7 @@ 0.0.1-SNAPSHOT 1.2.1 3.1.0 - 42.3.5 + 42.3.6 8.5.11 From 6b3f12866ca42145801931972c1c9ef2421c7057 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 May 2022 15:58:34 +0200 Subject: [PATCH 115/433] Bump azure-sdk-bom from 1.2.1 to 1.2.2 (#171) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8d61831d6..efcde28de 100644 --- a/pom.xml +++ b/pom.xml @@ -53,7 +53,7 @@ 0.0.1-SNAPSHOT - 1.2.1 + 1.2.2 3.1.0 42.3.6 8.5.11 From 53c2e7ae6d1301df79f1f6781a8424fffdc92c65 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 May 2022 16:03:12 +0200 Subject: [PATCH 116/433] Bump alpine (#169) --- .../edc-controlplane-postgresql/src/main/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile index 2bb41f209..d4aa94d6b 100644 --- a/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile @@ -10,7 +10,7 @@ # Contributors: # Mercedes-Benz Tech Innovation GmbH - Initial Dockerfile # -FROM alpine:3.15.4 as otel +FROM alpine:3.16.0 as otel ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" From 90de20494fd1754f20f3b399afc23d9e52037028 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 May 2022 16:16:21 +0200 Subject: [PATCH 117/433] Bump alpine in /edc-controlplane/edc-controlplane-memory/src/main/docker (#170) --- .../edc-controlplane-memory/src/main/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile index 2bb41f209..d4aa94d6b 100644 --- a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile @@ -10,7 +10,7 @@ # Contributors: # Mercedes-Benz Tech Innovation GmbH - Initial Dockerfile # -FROM alpine:3.15.4 as otel +FROM alpine:3.16.0 as otel ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" From 97322c46a6927914c46640171e3ed1b2ce1b66d2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 May 2022 09:01:19 +0200 Subject: [PATCH 118/433] Bump mikefarah/yq from 4.25.1 to 4.25.2 (#173) --- .github/workflows/draft-new-release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index d298ab174..25c4b4e0e 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -43,7 +43,7 @@ jobs: GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} - name: Bump version in deployment/helm - uses: mikefarah/yq@v4.25.1 + uses: mikefarah/yq@v4.25.2 with: cmd: |- find deployment/helm -name Chart.yaml | xargs -n1 yq -i '.appVersion = "${{ github.event.inputs.version }}" | .version = "${{ github.event.inputs.version }}"' From 557a6ec56a6bbc382df6917e9c2d070af923217f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 May 2022 16:59:58 +0200 Subject: [PATCH 119/433] Bump flyway-core from 8.5.11 to 8.5.12 (#177) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index efcde28de..726692d84 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 1.2.2 3.1.0 42.3.6 - 8.5.11 + 8.5.12 5.8.2 From af065f3af646387c612ff40f88679169c958a14f Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Tue, 31 May 2022 17:02:43 +0200 Subject: [PATCH 120/433] Helm Chart Hardening (#164) --- .github/workflows/checkov.yaml | 3 +- .github/workflows/draft-new-release.yaml | 10 +++++- checkov.yaml | 9 +++++ deployment/helm/README.md | 2 +- deployment/helm/edc-controlplane/.helmignore | 2 ++ deployment/helm/edc-controlplane/README.md | 18 +++++++--- .../helm/edc-controlplane/README.md.gotmpl | 2 +- .../templates/configmap-env.yaml | 1 + .../edc-controlplane/templates/configmap.yaml | 1 + .../templates/deployment.yaml | 2 ++ .../helm/edc-controlplane/templates/hpa.yaml | 1 + .../templates/imagepullsecret.yaml | 1 + .../edc-controlplane/templates/ingress.yaml | 2 ++ .../edc-controlplane/templates/service.yaml | 1 + .../templates/serviceaccount.yaml | 1 + deployment/helm/edc-controlplane/values.yaml | 35 +++++++++++++------ deployment/helm/edc-dataplane/.helmignore | 2 ++ deployment/helm/edc-dataplane/README.md | 16 ++++++--- .../helm/edc-dataplane/README.md.gotmpl | 2 +- .../templates/configmap-env.yaml | 1 + .../edc-dataplane/templates/configmap.yaml | 1 + .../edc-dataplane/templates/deployment.yaml | 2 ++ .../helm/edc-dataplane/templates/hpa.yaml | 1 + .../templates/imagepullsecret.yaml | 1 + .../helm/edc-dataplane/templates/ingress.yaml | 2 ++ .../helm/edc-dataplane/templates/service.yaml | 1 + .../templates/serviceaccount.yaml | 1 + deployment/helm/edc-dataplane/values.yaml | 35 +++++++++++++------ 28 files changed, 121 insertions(+), 35 deletions(-) create mode 100644 checkov.yaml diff --git a/.github/workflows/checkov.yaml b/.github/workflows/checkov.yaml index 32c9b9919..5bdc9aeb0 100644 --- a/.github/workflows/checkov.yaml +++ b/.github/workflows/checkov.yaml @@ -42,8 +42,7 @@ jobs: output_format: sarif # optional: the output format, one of: cli, json, junitxml, github_failed_only, or sarif. Default: sarif download_external_modules: true # optional: download external terraform modules from public git repositories and terraform registry log_level: DEBUG # optional: set log level. Default WARNING - # config_file: path/this_file - # baseline: cloudformation/.checkov.baseline # optional: Path to a generated baseline file. Will only report results not in the baseline. + config_file: checkov.yaml - name: Upload Checkov scan results to GitHub Security tab uses: github/codeql-action/upload-sarif@v2 diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index 25c4b4e0e..264694ce2 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -47,11 +47,19 @@ jobs: with: cmd: |- find deployment/helm -name Chart.yaml | xargs -n1 yq -i '.appVersion = "${{ github.event.inputs.version }}" | .version = "${{ github.event.inputs.version }}"' + - + name: Update Chart READMEs + uses: addnab/docker-run-action@v3 + with: + image: jnorwood/helm-docs:v1.10.0 + options: -v ${{ github.workspace }}/deployment/helm:/helm-docs + run: | + helm-docs --log-level debug - name: Commit changelog and manifest files id: make-commit run: | - git add CHANGELOG.md $(find -name pom.xml) $(find deployment/helm -name Chart.yaml) + git add CHANGELOG.md $(find -name pom.xml) $(find deployment/helm -name Chart.yaml) $(find deployment/helm -name README.md) git commit --message "Prepare release ${{ github.event.inputs.version }}" echo "::set-output name=commit::$(git rev-parse HEAD)" diff --git a/checkov.yaml b/checkov.yaml new file mode 100644 index 000000000..5f5d0d107 --- /dev/null +++ b/checkov.yaml @@ -0,0 +1,9 @@ +--- +skip-check: + - CKV_K8S_10 # CPU requests should be set, https://docs.bridgecrew.io/docs/bc_k8s_9 + - CKV_K8S_11 # CPU limits should be set, https://docs.bridgecrew.io/docs/bc_k8s_10 + - CKV_K8S_12 # Memory requests should be set, https://docs.bridgecrew.io/docs/bc_k8s_11 + - CKV_K8S_13 # Memory limits should be set, https://docs.bridgecrew.io/docs/bc_k8s_12 + - CKV_K8S_15 # Image Pull Policy should be Always, https://docs.bridgecrew.io/docs/bc_k8s_14 + - CKV_K8S_21 # The default namespace should not be used, https://docs.bridgecrew.io/docs/bc_k8s_20 + - CKV_K8S_43 # Image should use digest, https://docs.bridgecrew.io/docs/bc_k8s_39 diff --git a/deployment/helm/README.md b/deployment/helm/README.md index 895b9eb2e..111df59d1 100644 --- a/deployment/helm/README.md +++ b/deployment/helm/README.md @@ -9,5 +9,5 @@ Configuration files for [CT](../../ct.yaml), [Yamale](../../chart_schema.yaml) a To generate chart README.md files from its respective values.yaml file we use the [helm-docs tool](https://github.com/norwoodj/helm-docs): ```shell -docker run --rm --volume "$(pwd):/helm-docs" -u $(id -u) jnorwood/helm-docs:latest +docker run --rm --volume "$(pwd):/helm-docs" -u $(id -u) jnorwood/helm-docs:v1.10.0 ``` diff --git a/deployment/helm/edc-controlplane/.helmignore b/deployment/helm/edc-controlplane/.helmignore index 0e8a0eb36..00ca644b2 100644 --- a/deployment/helm/edc-controlplane/.helmignore +++ b/deployment/helm/edc-controlplane/.helmignore @@ -21,3 +21,5 @@ .idea/ *.tmproj .vscode/ + +README.md.gotmpl diff --git a/deployment/helm/edc-controlplane/README.md b/deployment/helm/edc-controlplane/README.md index b4003b326..b707b8428 100644 --- a/deployment/helm/edc-controlplane/README.md +++ b/deployment/helm/edc-controlplane/README.md @@ -1,6 +1,6 @@ # edc-controlplane -![Version: 0.0.1](https://img.shields.io/badge/Version-0.0.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.0.1](https://img.shields.io/badge/AppVersion-0.0.1-informational?style=flat-square) +![Version: 0.0.3](https://img.shields.io/badge/Version-0.0.3-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.0.3](https://img.shields.io/badge/AppVersion-0.0.3-informational?style=flat-square) EDC Control-Plane - The Eclipse DataSpaceConnector administration layer with responsibility of resource management and govern contracts and data transfers @@ -9,7 +9,7 @@ EDC Control-Plane - The Eclipse DataSpaceConnector administration layer with res ## TL;DR ```shell $ helm repo add catenax-ng-product-edc https://catenax-ng.github.io/product-edc -$ helm install my-release catenax-ng-product-edc/edc-controlplane +$ helm install my-release catenax-ng-product-edc/edc-controlplane --version 0.0.3 ``` ## Values @@ -17,12 +17,13 @@ $ helm install my-release catenax-ng-product-edc/edc-controlplane | Key | Type | Default | Description | |-----|------|---------|-------------| | affinity | object | `{}` | [Affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) constrains which nodes the Pod can be scheduled on based on node labels. | +| automountServiceAccountToken | bool | `false` | Whether to [automount kubernetes API credentials](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server) into the pod | | autoscaling.enabled | bool | `false` | Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) | | autoscaling.maxReplicas | int | `100` | Maximum replicas if resource consumption exceeds resource threshholds | | autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | | autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | | autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | -| configuration.properties | string | `"# edc.api.auth.key=\n# edc.api.control.auth.apikey.key=\n# edc.api.control.auth.apikey.value=\n# edc.assetindex.cosmos.account-name=\n# edc.assetindex.cosmos.container-name=\n# edc.assetindex.cosmos.database-name=\n# edc.assetindex.cosmos.preferred-region=\n# edc.atomikos.checkpoint.interval=\n# edc.atomikos.directory=\n# edc.atomikos.logging=\n# edc.atomikos.threaded2pc=\n# edc.atomikos.timeout=\n# edc.aws.access.key=\n# edc.aws.provision.retry.retries.max=\n# edc.aws.provision.role.duration.session.max=\n# edc.aws.secret.access.key=\n# edc.blobstore.endpoint=\n# edc.contractdefinitionstore.cosmos.account-name=\n# edc.contractdefinitionstore.cosmos.container-name=\n# edc.contractdefinitionstore.cosmos.database-name=\n# edc.contractdefinitionstore.cosmos.preferred-region=\n# edc.contractnegotiationstore.cosmos.account-name=\n# edc.contractnegotiationstore.cosmos.container-name=\n# edc.contractnegotiationstore.cosmos.database-name=\n# edc.contractnegotiationstore.cosmos.preferred-region=\n# edc.controlplane.validation-endpoint=\n# edc.core.retry.backoff.max=\n# edc.core.retry.backoff.min=\n# edc.core.retry.retries.max=\n# edc.core.system.health.check.liveness-period=\n# edc.core.system.health.check.readiness-period=\n# edc.core.system.health.check.startup-period=\n# edc.core.system.health.check.threadpool-size=\n# edc.cosmos.partition-key=\n# edc.cosmos.query-metrics-enabled=\n# edc.dataplane.queue.capacity=\n# edc.dataplane.wait=\n# edc.dataplane.workers=\n# edc.datasource.asset.name=\"default\"\n# edc.datasource.contractdefinition.name=\"default\"\n# edc.datasource.contractnegotiation.name=\"default\"\n# edc.datasource.policy.name=\"default\"\n# edc.datasource.transferprocess.name=\"default\"\n# edc.datasource.default.pool.maxIdleConnections=\n# edc.datasource.default.pool.maxTotalConnections=\n# edc.datasource.default.pool.minIdleConnections=\n# edc.datasource.default.pool.testConnectionOnBorrow=\n# edc.datasource.default.pool.testConnectionOnCreate=\n# edc.datasource.default.pool.testConnectionOnReturn=\n# edc.datasource.default.pool.testConnectionWhileIdle=\n# edc.datasource.default.pool.testQuery=\n# edc.datasource.default.url=\n# edc.datasource.default.user=\n# edc.datasource.default.password=\n# edc.dpf.selector.url=\n# edc.events.topic.endpoint=\n# edc.events.topic.name=\n# edc.fs.config=\n# edc.hostname=\n# edc.identity.did.url=\n# edc.ids.catalog.id=\n# edc.ids.curator=\n# edc.ids.description=\n# edc.ids.endpoint=\n# edc.ids.id=\n# edc.ids.maintainer=\n# edc.ids.security.profile=\n# edc.ids.title=\n# edc.ids.validation.referringconnector=\n# edc.ion.crawler.did-type=\n# edc.ion.crawler.interval-minutes=\n# edc.ion.crawler.ion.url=\n# edc.metrics.enabled=\n# edc.metrics.executor.enabled=\n# edc.metrics.jersey.enabled=\n# edc.metrics.jetty.enabled=\n# edc.metrics.okhttp.enabled=\n# edc.metrics.system.enabled=\n# edc.negotiation.consumer.state-machine.batch-size=\n# edc.negotiation.provider.state-machine.batch-size=\n# edc.node.directory.cosmos.account.name=\n# edc.node.directory.cosmos.container.name=\n# edc.node.directory.cosmos.database.name=\n# edc.node.directory.cosmos.preferred.region=\n# edc.oauth.client.id=\n# edc.oauth.private.key.alias=\n# edc.oauth.provider.audience=\n# edc.oauth.provider.jwks.refresh=\n# edc.oauth.provider.jwks.url=\n# edc.oauth.public.key.alias=\n# edc.oauth.token.url=\n# edc.oauth.validation.nbf.leeway=\n# edc.receiver.http.auth-code=\n# edc.receiver.http.auth-key=\n# edc.receiver.http.endpoint=\n# edc.transfer.proxy.endpoint=\n# edc.transfer.dataplane.sync.token.validity=\n# edc.transfer.proxy.token.signer.privatekey.alias=\n# edc.transfer.functions.check.endpoint=\n# edc.transfer.functions.enabled.protocols=\n# edc.transfer.functions.transfer.endpoint=\n# edc.transfer-process-store.cosmos.account.name=\n# edc.transfer-process-store.cosmos.container-name=\n# edc.transfer-process-store.cosmos.preferred-region=\n# edc.transfer-process-store.database.name=\n# edc.transfer.state-machine.batch-size=\n# edc.vault=\n# edc.vault.certificate=\n# edc.vault.clientid=\n# edc.vault.clientsecret=\n# edc.vault.name=\n# edc.vault.tenantid=\n# edc.webdid.doh.url=\n# edc.web.rest.cors.enabled=\n# edc.web.rest.cors.headers=\n# edc.web.rest.cors.methods=\n# edc.web.rest.cors.origins="` | EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) | +| configuration.properties | string | `"# edc.api.auth.key=\n# edc.api.control.auth.apikey.key=\n# edc.api.control.auth.apikey.value=\n# edc.assetindex.cosmos.account-name=\n# edc.assetindex.cosmos.container-name=\n# edc.assetindex.cosmos.database-name=\n# edc.assetindex.cosmos.preferred-region=\n# edc.atomikos.checkpoint.interval=\n# edc.atomikos.directory=\n# edc.atomikos.logging=\n# edc.atomikos.threaded2pc=\n# edc.atomikos.timeout=\n# edc.aws.access.key=\n# edc.aws.provision.retry.retries.max=\n# edc.aws.provision.role.duration.session.max=\n# edc.aws.secret.access.key=\n# edc.blobstore.endpoint=\n# edc.contractdefinitionstore.cosmos.account-name=\n# edc.contractdefinitionstore.cosmos.container-name=\n# edc.contractdefinitionstore.cosmos.database-name=\n# edc.contractdefinitionstore.cosmos.preferred-region=\n# edc.contractnegotiationstore.cosmos.account-name=\n# edc.contractnegotiationstore.cosmos.container-name=\n# edc.contractnegotiationstore.cosmos.database-name=\n# edc.contractnegotiationstore.cosmos.preferred-region=\n# edc.contractnegotiationstore.cosmos.allow.sproc.autoupload=\n# edc.controlplane.validation-endpoint=\n# edc.core.retry.backoff.max=\n# edc.core.retry.backoff.min=\n# edc.core.retry.retries.max=\n# edc.core.system.health.check.liveness-period=\n# edc.core.system.health.check.readiness-period=\n# edc.core.system.health.check.startup-period=\n# edc.core.system.health.check.threadpool-size=\n# edc.cosmos.partition-key=\n# edc.cosmos.query-metrics-enabled=\n# edc.dataplane.queue.capacity=\n# edc.dataplane.wait=\n# edc.dataplane.workers=\n# edc.datasource.asset.name=\"default\"\n# edc.datasource.contractdefinition.name=\"default\"\n# edc.datasource.contractnegotiation.name=\"default\"\n# edc.datasource.policy.name=\"default\"\n# edc.datasource.transferprocess.name=\"default\"\n# edc.datasource.default.pool.maxIdleConnections=\n# edc.datasource.default.pool.maxTotalConnections=\n# edc.datasource.default.pool.minIdleConnections=\n# edc.datasource.default.pool.testConnectionOnBorrow=\n# edc.datasource.default.pool.testConnectionOnCreate=\n# edc.datasource.default.pool.testConnectionOnReturn=\n# edc.datasource.default.pool.testConnectionWhileIdle=\n# edc.datasource.default.pool.testQuery=\n# edc.datasource.default.url=\n# edc.datasource.default.user=\n# edc.datasource.default.password=\n# edc.dpf.selector.url=\n# edc.events.topic.endpoint=\n# edc.events.topic.name=\n# edc.fs.config=\n# edc.hostname=\n# edc.identity.did.url=\n# edc.ids.catalog.id=\n# edc.ids.curator=\n# edc.ids.description=\n# edc.ids.endpoint=\n# edc.ids.id=\n# edc.ids.maintainer=\n# edc.ids.security.profile=\n# edc.ids.title=\n# edc.ids.validation.referringconnector=\n# edc.ion.crawler.did-type=\n# edc.ion.crawler.interval-minutes=\n# edc.ion.crawler.ion.url=\n# edc.metrics.enabled=\n# edc.metrics.executor.enabled=\n# edc.metrics.jersey.enabled=\n# edc.metrics.jetty.enabled=\n# edc.metrics.okhttp.enabled=\n# edc.metrics.system.enabled=\n# edc.negotiation.consumer.state-machine.batch-size=\n# edc.negotiation.provider.state-machine.batch-size=\n# edc.node.directory.cosmos.account.name=\n# edc.node.directory.cosmos.container.name=\n# edc.node.directory.cosmos.database.name=\n# edc.node.directory.cosmos.preferred.region=\n# edc.oauth.client.id=\n# edc.oauth.private.key.alias=\n# edc.oauth.provider.audience=\n# edc.oauth.provider.jwks.refresh=\n# edc.oauth.provider.jwks.url=\n# edc.oauth.public.key.alias=\n# edc.oauth.token.url=\n# edc.oauth.validation.nbf.leeway=\n# edc.receiver.http.auth-code=\n# edc.receiver.http.auth-key=\n# edc.receiver.http.endpoint=\n# edc.transfer.proxy.endpoint=\n# edc.transfer.dataplane.sync.token.validity=\n# edc.transfer.proxy.token.signer.privatekey.alias=\n# edc.transfer.functions.check.endpoint=\n# edc.transfer.functions.enabled.protocols=\n# edc.transfer.functions.transfer.endpoint=\n# edc.transfer-process-store.cosmos.account.name=\n# edc.transfer-process-store.cosmos.container-name=\n# edc.transfer-process-store.cosmos.preferred-region=\n# edc.transfer-process-store.cosmos.allow.sproc.autoupload=\n# edc.transfer-process-store.database.name=\n# edc.transfer.state-machine.batch-size=\n# edc.vault=\n# edc.vault.certificate=\n# edc.vault.clientid=\n# edc.vault.clientsecret=\n# edc.vault.name=\n# edc.vault.tenantid=\n# edc.webdid.doh.url=\n# edc.web.rest.cors.enabled=\n# edc.web.rest.cors.headers=\n# edc.web.rest.cors.methods=\n# edc.web.rest.cors.origins="` | EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) | | edc.endpoints.control.path | string | `"/api/controlplane/control"` | The path mapping the "control" api is going to be exposed at | | edc.endpoints.control.port | string | `"9999"` | The network port, which the "control" api is going to be exposed by the container, pod and service | | edc.endpoints.data.path | string | `"/data"` | The path mapping the "data" management api is going to be exposed at | @@ -64,12 +65,19 @@ $ helm install my-release catenax-ng-product-edc/edc-controlplane | nodeSelector | object | `{}` | [Node-Selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) to constrain the Pod to nodes with specific labels. | | opentelemetry.properties | string | `"otel.javaagent.enabled=true\notel.javaagent.debug=false"` | opentelemetry.properties configuring the [opentelemetry agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) | | podAnnotations | object | `{}` | [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) added to deployed [pods](https://kubernetes.io/docs/concepts/workloads/pods/) | -| podSecurityContext | object | `{}` | The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment | +| podSecurityContext.fsGroup | int | `10001` | The owner for volumes and any files created within volumes will belong to this guid | +| podSecurityContext.runAsGroup | int | `10001` | Processes within a pod will belong to this guid | +| podSecurityContext.runAsUser | int | `10001` | Runs all processes within a pod with a special uid | +| podSecurityContext.seccompProfile.type | string | `"RuntimeDefault"` | Restrict a Container's Syscalls with seccomp | | readinessProbe.enabled | bool | `true` | Whether to enable kubernetes readiness-probes | | replicaCount | int | `1` | Specifies how many replicas of a deployed pod shall be created during the deployment Note: If horizontal pod autoscaling is enabled this setting has no effect | | resources | object | `{}` | [Resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) applied to the deployed pod | +| securityContext.allowPrivilegeEscalation | bool | `false` | Controls [Privilege Escalation](https://kubernetes.io/docs/concepts/security/pod-security-policy/#privilege-escalation) enabling setuid binaries changing the effective user ID | +| securityContext.capabilities.add | list | `[]` | Specifies which capabilities to add to issue specialized syscalls | +| securityContext.capabilities.drop | list | `["ALL"]` | Specifies which capabilities to drop to reduce syscall attack surface | +| securityContext.readOnlyRootFilesystem | bool | `true` | Whether the root filesystem is mounted in read-only mode | | securityContext.runAsNonRoot | bool | `true` | Requires the container to run without root privileges | -| securityContext.runAsUser | int | `1000` | The container's process will run with the specified uid | +| securityContext.runAsUser | int | `10001` | The container's process will run with the specified uid | | service.type | string | `"ClusterIP"` | [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. | | serviceAccount.annotations | object | `{}` | [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) to add to the service account | | serviceAccount.create | bool | `true` | Specifies whether a [service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) should be created per release | diff --git a/deployment/helm/edc-controlplane/README.md.gotmpl b/deployment/helm/edc-controlplane/README.md.gotmpl index 95c28338e..429dd5bf2 100644 --- a/deployment/helm/edc-controlplane/README.md.gotmpl +++ b/deployment/helm/edc-controlplane/README.md.gotmpl @@ -11,7 +11,7 @@ ## TL;DR ```shell $ helm repo add catenax-ng-product-edc https://catenax-ng.github.io/product-edc -$ helm install my-release catenax-ng-product-edc/edc-controlplane +$ helm install my-release catenax-ng-product-edc/edc-controlplane --version {{ .Version }} ``` {{ template "chart.maintainersSection" . }} diff --git a/deployment/helm/edc-controlplane/templates/configmap-env.yaml b/deployment/helm/edc-controlplane/templates/configmap-env.yaml index f7f63f709..b457973b8 100644 --- a/deployment/helm/edc-controlplane/templates/configmap-env.yaml +++ b/deployment/helm/edc-controlplane/templates/configmap-env.yaml @@ -3,6 +3,7 @@ apiVersion: v1 kind: ConfigMap metadata: name: {{ include "edc-controlplane.fullname" . }}-env + namespace: {{ .Release.Namespace | default "default" | quote }} labels: {{- include "edc-controlplane.labels" . | nindent 4 }} data: diff --git a/deployment/helm/edc-controlplane/templates/configmap.yaml b/deployment/helm/edc-controlplane/templates/configmap.yaml index 34a745bfa..a2a200919 100644 --- a/deployment/helm/edc-controlplane/templates/configmap.yaml +++ b/deployment/helm/edc-controlplane/templates/configmap.yaml @@ -3,6 +3,7 @@ apiVersion: v1 kind: ConfigMap metadata: name: {{ include "edc-controlplane.fullname" . }}-configmap + namespace: {{ .Release.Namespace | default "default" | quote }} labels: {{- include "edc-controlplane.labels" . | nindent 4 }} data: diff --git a/deployment/helm/edc-controlplane/templates/deployment.yaml b/deployment/helm/edc-controlplane/templates/deployment.yaml index 7eb27a307..353e23e26 100644 --- a/deployment/helm/edc-controlplane/templates/deployment.yaml +++ b/deployment/helm/edc-controlplane/templates/deployment.yaml @@ -3,6 +3,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "edc-controlplane.fullname" . }} + namespace: {{ .Release.Namespace | default "default" | quote }} labels: {{- include "edc-controlplane.labels" . | nindent 4 }} spec: @@ -33,6 +34,7 @@ spec: {{- end }} {{- end }} serviceAccountName: {{ include "edc-controlplane.serviceAccountName" . }} + automountServiceAccountToken: {{ if .Values.automountServiceAccountToken }}true{{ else }}false{{ end }} securityContext: {{- toYaml .Values.podSecurityContext | nindent 8 }} containers: diff --git a/deployment/helm/edc-controlplane/templates/hpa.yaml b/deployment/helm/edc-controlplane/templates/hpa.yaml index 0881a3ce2..ae80f5dc9 100644 --- a/deployment/helm/edc-controlplane/templates/hpa.yaml +++ b/deployment/helm/edc-controlplane/templates/hpa.yaml @@ -4,6 +4,7 @@ apiVersion: autoscaling/v2beta1 kind: HorizontalPodAutoscaler metadata: name: {{ include "edc-controlplane.fullname" . }} + namespace: {{ .Release.Namespace | default "default" | quote }} labels: {{- include "edc-controlplane.labels" . | nindent 4 }} spec: diff --git a/deployment/helm/edc-controlplane/templates/imagepullsecret.yaml b/deployment/helm/edc-controlplane/templates/imagepullsecret.yaml index 663efd28a..caedb0710 100644 --- a/deployment/helm/edc-controlplane/templates/imagepullsecret.yaml +++ b/deployment/helm/edc-controlplane/templates/imagepullsecret.yaml @@ -4,6 +4,7 @@ apiVersion: v1 kind: Secret metadata: name: {{ include "edc-controlplane.fullname" . }}-imagepullsecret + namespace: {{ .Release.Namespace | default "default" | quote }} labels: {{- include "edc-controlplane.labels" . | nindent 4 }} data: diff --git a/deployment/helm/edc-controlplane/templates/ingress.yaml b/deployment/helm/edc-controlplane/templates/ingress.yaml index f395970ff..e2586a7fc 100644 --- a/deployment/helm/edc-controlplane/templates/ingress.yaml +++ b/deployment/helm/edc-controlplane/templates/ingress.yaml @@ -2,6 +2,7 @@ {{- $labels := include "edc-controlplane.labels" . | nindent 4 }} {{- $gitVersion := .Capabilities.KubeVersion.GitVersion }} {{- $edcEndpoints := .Values.edc.endpoints }} +{{- $namespace := .Release.Namespace }} {{- range .Values.ingresses }} {{- if and .enabled .endpoints }} {{- $ingressName := printf "%s-%s" $fullName .hostname }} @@ -16,6 +17,7 @@ apiVersion: extensions/v1beta1 kind: Ingress metadata: name: {{ $ingressName }} + namespace: {{ $namespace | default "default" | quote }} labels: {{- $labels | nindent 2 }} annotations: diff --git a/deployment/helm/edc-controlplane/templates/service.yaml b/deployment/helm/edc-controlplane/templates/service.yaml index 7f6dafe1c..2992c7245 100644 --- a/deployment/helm/edc-controlplane/templates/service.yaml +++ b/deployment/helm/edc-controlplane/templates/service.yaml @@ -3,6 +3,7 @@ apiVersion: v1 kind: Service metadata: name: {{ include "edc-controlplane.fullname" . }} + namespace: {{ .Release.Namespace | default "default" | quote }} labels: {{- include "edc-controlplane.labels" . | nindent 4 }} spec: diff --git a/deployment/helm/edc-controlplane/templates/serviceaccount.yaml b/deployment/helm/edc-controlplane/templates/serviceaccount.yaml index 66568118b..0e15335ae 100644 --- a/deployment/helm/edc-controlplane/templates/serviceaccount.yaml +++ b/deployment/helm/edc-controlplane/templates/serviceaccount.yaml @@ -4,6 +4,7 @@ apiVersion: v1 kind: ServiceAccount metadata: name: {{ include "edc-controlplane.serviceAccountName" . }} + namespace: {{ .Release.Namespace | default "default" | quote }} labels: {{- include "edc-controlplane.labels" . | nindent 4 }} {{- with .Values.serviceAccount.annotations }} diff --git a/deployment/helm/edc-controlplane/values.yaml b/deployment/helm/edc-controlplane/values.yaml index 3be6dd27e..af9f9ba58 100644 --- a/deployment/helm/edc-controlplane/values.yaml +++ b/deployment/helm/edc-controlplane/values.yaml @@ -39,25 +39,40 @@ serviceAccount: # -- The name of the service account to use. If not set and create is true, a name is generated using the release's fullname template name: "" +# -- Whether to [automount kubernetes API credentials](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server) into the pod +automountServiceAccountToken: false + # -- [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) added to deployed [pods](https://kubernetes.io/docs/concepts/workloads/pods/) podAnnotations: {} -# -- The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment -podSecurityContext: {} - # runAsUser: 1000 - # runAsGroup: 3000 - # fsGroup: 2000 +# The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment +podSecurityContext: + seccompProfile: + # -- Restrict a Container's Syscalls with seccomp + type: RuntimeDefault + # -- Runs all processes within a pod with a special uid + runAsUser: 10001 + # -- Processes within a pod will belong to this guid + runAsGroup: 10001 + # -- The owner for volumes and any files created within volumes will belong to this guid + fsGroup: 10001 # The [container security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) defines privilege and access control settings for a Container within a pod securityContext: - # capabilities: - # drop: - # - ALL - # readOnlyRootFilesystem: true + capabilities: + # -- Specifies which capabilities to drop to reduce syscall attack surface + drop: + - ALL + # -- Specifies which capabilities to add to issue specialized syscalls + add: [] + # -- Whether the root filesystem is mounted in read-only mode + readOnlyRootFilesystem: true + # -- Controls [Privilege Escalation](https://kubernetes.io/docs/concepts/security/pod-security-policy/#privilege-escalation) enabling setuid binaries changing the effective user ID + allowPrivilegeEscalation: false # -- Requires the container to run without root privileges runAsNonRoot: true # -- The container's process will run with the specified uid - runAsUser: 1000 + runAsUser: 10001 livenessProbe: # -- Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) diff --git a/deployment/helm/edc-dataplane/.helmignore b/deployment/helm/edc-dataplane/.helmignore index 0e8a0eb36..00ca644b2 100644 --- a/deployment/helm/edc-dataplane/.helmignore +++ b/deployment/helm/edc-dataplane/.helmignore @@ -21,3 +21,5 @@ .idea/ *.tmproj .vscode/ + +README.md.gotmpl diff --git a/deployment/helm/edc-dataplane/README.md b/deployment/helm/edc-dataplane/README.md index 1b634fa17..f91788145 100644 --- a/deployment/helm/edc-dataplane/README.md +++ b/deployment/helm/edc-dataplane/README.md @@ -1,6 +1,6 @@ # edc-dataplane -![Version: 0.0.1](https://img.shields.io/badge/Version-0.0.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.0.1](https://img.shields.io/badge/AppVersion-0.0.1-informational?style=flat-square) +![Version: 0.0.3](https://img.shields.io/badge/Version-0.0.3-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.0.3](https://img.shields.io/badge/AppVersion-0.0.3-informational?style=flat-square) EDC Data-Plane - The Eclipse DataSpaceConnector data layer with responsibility of transferring and receiving data streams @@ -9,7 +9,7 @@ EDC Data-Plane - The Eclipse DataSpaceConnector data layer with responsibility o ## TL;DR ```shell $ helm repo add catenax-ng-product-edc https://catenax-ng.github.io/product-edc -$ helm install my-release catenax-ng-product-edc/edc-dataplane +$ helm install my-release catenax-ng-product-edc/edc-dataplane --version 0.0.3 ``` ## Values @@ -17,6 +17,7 @@ $ helm install my-release catenax-ng-product-edc/edc-dataplane | Key | Type | Default | Description | |-----|------|---------|-------------| | affinity | object | `{}` | [Affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) constrains which nodes the Pod can be scheduled on based on node labels. | +| automountServiceAccountToken | bool | `false` | Whether to [automount kubernetes API credentials](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server) into the pod | | autoscaling.enabled | bool | `false` | Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) | | autoscaling.maxReplicas | int | `100` | Maximum replicas if resource consumption exceeds resource threshholds | | autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | @@ -52,12 +53,19 @@ $ helm install my-release catenax-ng-product-edc/edc-dataplane | nodeSelector | object | `{}` | [Node-Selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) to constrain the Pod to nodes with specific labels. | | opentelemetry.properties | string | `"otel.javaagent.enabled=true\notel.javaagent.debug=false"` | opentelemetry.properties configuring the [opentelemetry agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) | | podAnnotations | object | `{}` | [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) added to deployed [pods](https://kubernetes.io/docs/concepts/workloads/pods/) | -| podSecurityContext | object | `{}` | The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment | +| podSecurityContext.fsGroup | int | `10001` | The owner for volumes and any files created within volumes will belong to this guid | +| podSecurityContext.runAsGroup | int | `10001` | Processes within a pod will belong to this guid | +| podSecurityContext.runAsUser | int | `10001` | Runs all processes within a pod with a special uid | +| podSecurityContext.seccompProfile.type | string | `"RuntimeDefault"` | Restrict a Container's Syscalls with seccomp | | readinessProbe.enabled | bool | `true` | Whether to enable kubernetes readiness-probes | | replicaCount | int | `1` | Specifies how many replicas of a deployed pod shall be created during the deployment Note: If horizontal pod autoscaling is enabled this setting has no effect | | resources | object | `{}` | [Resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) applied to the deployed pod | +| securityContext.allowPrivilegeEscalation | bool | `false` | Controls [Privilege Escalation](https://kubernetes.io/docs/concepts/security/pod-security-policy/#privilege-escalation) enabling setuid binaries changing the effective user ID | +| securityContext.capabilities.add | list | `[]` | Specifies which capabilities to add to issue specialized syscalls | +| securityContext.capabilities.drop | list | `["ALL"]` | Specifies which capabilities to drop to reduce syscall attack surface | +| securityContext.readOnlyRootFilesystem | bool | `true` | Whether the root filesystem is mounted in read-only mode | | securityContext.runAsNonRoot | bool | `true` | Requires the container to run without root privileges | -| securityContext.runAsUser | int | `1000` | The container's process will run with the specified uid | +| securityContext.runAsUser | int | `10001` | The container's process will run with the specified uid | | service.type | string | `"ClusterIP"` | [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. | | serviceAccount.annotations | object | `{}` | [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) to add to the service account | | serviceAccount.create | bool | `true` | Specifies whether a [service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) should be created per release | diff --git a/deployment/helm/edc-dataplane/README.md.gotmpl b/deployment/helm/edc-dataplane/README.md.gotmpl index 0e9ca3d5d..0b2e013b1 100644 --- a/deployment/helm/edc-dataplane/README.md.gotmpl +++ b/deployment/helm/edc-dataplane/README.md.gotmpl @@ -11,7 +11,7 @@ ## TL;DR ```shell $ helm repo add catenax-ng-product-edc https://catenax-ng.github.io/product-edc -$ helm install my-release catenax-ng-product-edc/edc-dataplane +$ helm install my-release catenax-ng-product-edc/edc-dataplane --version {{ .Version }} ``` {{ template "chart.maintainersSection" . }} diff --git a/deployment/helm/edc-dataplane/templates/configmap-env.yaml b/deployment/helm/edc-dataplane/templates/configmap-env.yaml index e23aba1a8..d8bf07df4 100644 --- a/deployment/helm/edc-dataplane/templates/configmap-env.yaml +++ b/deployment/helm/edc-dataplane/templates/configmap-env.yaml @@ -3,6 +3,7 @@ apiVersion: v1 kind: ConfigMap metadata: name: {{ include "edc-dataplane.fullname" . }}-env + namespace: {{ .Release.Namespace | default "default" | quote }} labels: {{- include "edc-dataplane.labels" . | nindent 4 }} data: diff --git a/deployment/helm/edc-dataplane/templates/configmap.yaml b/deployment/helm/edc-dataplane/templates/configmap.yaml index 5b32349ca..99a9ffa29 100644 --- a/deployment/helm/edc-dataplane/templates/configmap.yaml +++ b/deployment/helm/edc-dataplane/templates/configmap.yaml @@ -3,6 +3,7 @@ apiVersion: v1 kind: ConfigMap metadata: name: {{ include "edc-dataplane.fullname" . }}-configmap + namespace: {{ .Release.Namespace | default "default" | quote }} labels: {{- include "edc-dataplane.labels" . | nindent 4 }} data: diff --git a/deployment/helm/edc-dataplane/templates/deployment.yaml b/deployment/helm/edc-dataplane/templates/deployment.yaml index 3f1b284d8..1e4c428ba 100644 --- a/deployment/helm/edc-dataplane/templates/deployment.yaml +++ b/deployment/helm/edc-dataplane/templates/deployment.yaml @@ -3,6 +3,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "edc-dataplane.fullname" . }} + namespace: {{ .Release.Namespace | default "default" | quote }} labels: {{- include "edc-dataplane.labels" . | nindent 4 }} spec: @@ -33,6 +34,7 @@ spec: {{- end }} {{- end }} serviceAccountName: {{ include "edc-dataplane.serviceAccountName" . }} + automountServiceAccountToken: {{ if .Values.automountServiceAccountToken }}true{{ else }}false{{ end }} securityContext: {{- toYaml .Values.podSecurityContext | nindent 8 }} containers: diff --git a/deployment/helm/edc-dataplane/templates/hpa.yaml b/deployment/helm/edc-dataplane/templates/hpa.yaml index 3a481bccf..f996aa839 100644 --- a/deployment/helm/edc-dataplane/templates/hpa.yaml +++ b/deployment/helm/edc-dataplane/templates/hpa.yaml @@ -4,6 +4,7 @@ apiVersion: autoscaling/v2beta1 kind: HorizontalPodAutoscaler metadata: name: {{ include "edc-dataplane.fullname" . }} + namespace: {{ .Release.Namespace | default "default" | quote }} labels: {{- include "edc-dataplane.labels" . | nindent 4 }} spec: diff --git a/deployment/helm/edc-dataplane/templates/imagepullsecret.yaml b/deployment/helm/edc-dataplane/templates/imagepullsecret.yaml index 45b7332f1..d7c1d31d7 100644 --- a/deployment/helm/edc-dataplane/templates/imagepullsecret.yaml +++ b/deployment/helm/edc-dataplane/templates/imagepullsecret.yaml @@ -4,6 +4,7 @@ apiVersion: v1 kind: Secret metadata: name: {{ include "edc-dataplane.fullname" . }}-imagepullsecret + namespace: {{ .Release.Namespace | default "default" | quote }} labels: {{- include "edc-dataplane.labels" . | nindent 4 }} data: diff --git a/deployment/helm/edc-dataplane/templates/ingress.yaml b/deployment/helm/edc-dataplane/templates/ingress.yaml index 5558391ea..77a815083 100644 --- a/deployment/helm/edc-dataplane/templates/ingress.yaml +++ b/deployment/helm/edc-dataplane/templates/ingress.yaml @@ -2,6 +2,7 @@ {{- $labels := include "edc-dataplane.labels" . | nindent 4 }} {{- $gitVersion := .Capabilities.KubeVersion.GitVersion }} {{- $edcEndpoints := .Values.edc.endpoints }} +{{- $namespace := .Release.Namespace }} {{- range .Values.ingresses }} {{- if and .enabled .endpoints }} {{- $ingressName := printf "%s-%s" $fullName .hostname }} @@ -16,6 +17,7 @@ apiVersion: extensions/v1beta1 kind: Ingress metadata: name: {{ $ingressName }} + namespace: {{ $namespace | default "default" | quote }} labels: {{- $labels | nindent 2 }} annotations: diff --git a/deployment/helm/edc-dataplane/templates/service.yaml b/deployment/helm/edc-dataplane/templates/service.yaml index a29e2c125..7d2381634 100644 --- a/deployment/helm/edc-dataplane/templates/service.yaml +++ b/deployment/helm/edc-dataplane/templates/service.yaml @@ -3,6 +3,7 @@ apiVersion: v1 kind: Service metadata: name: {{ include "edc-dataplane.fullname" . }} + namespace: {{ .Release.Namespace | default "default" | quote }} labels: {{- include "edc-dataplane.labels" . | nindent 4 }} spec: diff --git a/deployment/helm/edc-dataplane/templates/serviceaccount.yaml b/deployment/helm/edc-dataplane/templates/serviceaccount.yaml index 7fbe537fb..6c997b64f 100644 --- a/deployment/helm/edc-dataplane/templates/serviceaccount.yaml +++ b/deployment/helm/edc-dataplane/templates/serviceaccount.yaml @@ -4,6 +4,7 @@ apiVersion: v1 kind: ServiceAccount metadata: name: {{ include "edc-dataplane.serviceAccountName" . }} + namespace: {{ .Release.Namespace | default "default" | quote }} labels: {{- include "edc-dataplane.labels" . | nindent 4 }} {{- with .Values.serviceAccount.annotations }} diff --git a/deployment/helm/edc-dataplane/values.yaml b/deployment/helm/edc-dataplane/values.yaml index a652f9530..92d4db32d 100644 --- a/deployment/helm/edc-dataplane/values.yaml +++ b/deployment/helm/edc-dataplane/values.yaml @@ -38,25 +38,40 @@ serviceAccount: # -- The name of the service account to use. If not set and create is true, a name is generated using the release's fullname template name: "" +# -- Whether to [automount kubernetes API credentials](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server) into the pod +automountServiceAccountToken: false + # -- [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) added to deployed [pods](https://kubernetes.io/docs/concepts/workloads/pods/) podAnnotations: {} -# -- The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment -podSecurityContext: {} - # runAsUser: 1000 - # runAsGroup: 3000 -# fsGroup: 2000 +# The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment +podSecurityContext: + seccompProfile: + # -- Restrict a Container's Syscalls with seccomp + type: RuntimeDefault + # -- Runs all processes within a pod with a special uid + runAsUser: 10001 + # -- Processes within a pod will belong to this guid + runAsGroup: 10001 + # -- The owner for volumes and any files created within volumes will belong to this guid + fsGroup: 10001 # The [container security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) defines privilege and access control settings for a Container within a pod securityContext: - # capabilities: - # drop: - # - ALL - # readOnlyRootFilesystem: true + capabilities: + # -- Specifies which capabilities to drop to reduce syscall attack surface + drop: + - ALL + # -- Specifies which capabilities to add to issue specialized syscalls + add: [] + # -- Whether the root filesystem is mounted in read-only mode + readOnlyRootFilesystem: true + # -- Controls [Privilege Escalation](https://kubernetes.io/docs/concepts/security/pod-security-policy/#privilege-escalation) enabling setuid binaries changing the effective user ID + allowPrivilegeEscalation: false # -- Requires the container to run without root privileges runAsNonRoot: true # -- The container's process will run with the specified uid - runAsUser: 1000 + runAsUser: 10001 livenessProbe: # -- Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) From 6250e8072b31e82af999807becc0987bb27fd76c Mon Sep 17 00:00:00 2001 From: "Florian Rusch (ZF Friedrichshafen AG)" Date: Fri, 3 Jun 2022 11:59:13 +0200 Subject: [PATCH 121/433] Update google java format dependency (#183) --- .mvn/jvm.config | 1 + .../business-partner-validation/pom.xml | 20 +++-------- ...usinessPartnerValidationExtensionTest.java | 13 ++++--- ...AbstractBusinessPartnerValidationTest.java | 16 ++++----- edc-extensions/pom.xml | 2 +- pom.xml | 36 +++++-------------- 6 files changed, 30 insertions(+), 58 deletions(-) create mode 100644 .mvn/jvm.config diff --git a/.mvn/jvm.config b/.mvn/jvm.config new file mode 100644 index 000000000..79ecf9292 --- /dev/null +++ b/.mvn/jvm.config @@ -0,0 +1 @@ +--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED diff --git a/edc-extensions/business-partner-validation/pom.xml b/edc-extensions/business-partner-validation/pom.xml index 5141bc47e..b106ac56c 100644 --- a/edc-extensions/business-partner-validation/pom.xml +++ b/edc-extensions/business-partner-validation/pom.xml @@ -67,23 +67,13 @@ org.junit.jupiter - junit-jupiter-engine - - - org.junit.jupiter - junit-jupiter-api - - - org.junit.jupiter - junit-jupiter-params - - - org.junit.platform - junit-platform-suite + junit-jupiter + test org.mockito - mockito-all + mockito-core + test - \ No newline at end of file + diff --git a/edc-extensions/business-partner-validation/src/test/java/net/catenax/edc/validation/businesspartner/BusinessPartnerValidationExtensionTest.java b/edc-extensions/business-partner-validation/src/test/java/net/catenax/edc/validation/businesspartner/BusinessPartnerValidationExtensionTest.java index 44086fc39..79dd839d8 100644 --- a/edc-extensions/business-partner-validation/src/test/java/net/catenax/edc/validation/businesspartner/BusinessPartnerValidationExtensionTest.java +++ b/edc-extensions/business-partner-validation/src/test/java/net/catenax/edc/validation/businesspartner/BusinessPartnerValidationExtensionTest.java @@ -25,20 +25,19 @@ import org.junit.jupiter.api.Test; import org.mockito.Mockito; -public class BusinessPartnerValidationExtensionTest { +class BusinessPartnerValidationExtensionTest { private BusinessPartnerValidationExtension extension; // mocks private ServiceExtensionContext serviceExtensionContext; private PolicyEngine policyEngine; - private RuleBindingRegistry ruleBindingRegistry; @BeforeEach - public void setup() { + void setup() { policyEngine = Mockito.mock(PolicyEngine.class); - ruleBindingRegistry = Mockito.mock(RuleBindingRegistry.class); + RuleBindingRegistry ruleBindingRegistry = Mockito.mock(RuleBindingRegistry.class); final Monitor monitor = Mockito.mock(Monitor.class); serviceExtensionContext = Mockito.mock(ServiceExtensionContext.class); @@ -52,7 +51,7 @@ public void setup() { } @Test - public void testRegisterDutyFunction() { + void testRegisterDutyFunction() { // invoke extension.initialize(serviceExtensionContext); @@ -67,7 +66,7 @@ public void testRegisterDutyFunction() { } @Test - public void testRegisterPermissionFunction() { + void testRegisterPermissionFunction() { // invoke extension.initialize(serviceExtensionContext); @@ -82,7 +81,7 @@ public void testRegisterPermissionFunction() { } @Test - public void testRegisterProhibitionFunction() { + void testRegisterProhibitionFunction() { // invoke extension.initialize(serviceExtensionContext); diff --git a/edc-extensions/business-partner-validation/src/test/java/net/catenax/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidationTest.java b/edc-extensions/business-partner-validation/src/test/java/net/catenax/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidationTest.java index 8bf70c247..451e79868 100644 --- a/edc-extensions/business-partner-validation/src/test/java/net/catenax/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidationTest.java +++ b/edc-extensions/business-partner-validation/src/test/java/net/catenax/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidationTest.java @@ -27,7 +27,7 @@ import org.junit.jupiter.params.provider.EnumSource; import org.mockito.Mockito; -public class AbstractBusinessPartnerValidationTest { +class AbstractBusinessPartnerValidationTest { private AbstractBusinessPartnerValidation validation; @@ -37,7 +37,7 @@ public class AbstractBusinessPartnerValidationTest { private ParticipantAgent participantAgent; @BeforeEach - public void BeforeEach() { + void BeforeEach() { this.monitor = Mockito.mock(Monitor.class); this.policyContext = Mockito.mock(PolicyContext.class); this.participantAgent = Mockito.mock(ParticipantAgent.class); @@ -49,7 +49,7 @@ public void BeforeEach() { @ParameterizedTest @EnumSource(Operator.class) - public void testThrowsOnUnsupportedOperations(Operator operator) { + void testThrowsOnUnsupportedOperations(Operator operator) { if (operator == Operator.EQ) { // only allowed operator return; @@ -66,7 +66,7 @@ public void testThrowsOnUnsupportedOperations(Operator operator) { } @Test - public void testThrowsOnUnsupportedRightValue() { + void testThrowsOnUnsupportedRightValue() { // prepare prepareContextProblems(null); @@ -82,7 +82,7 @@ public void testThrowsOnUnsupportedRightValue() { } @Test - public void testValidationFailsWhenClaimMissing() { + void testValidationFailsWhenClaimMissing() { // prepare prepareContextProblems(null); @@ -95,7 +95,7 @@ public void testValidationFailsWhenClaimMissing() { } @Test - public void testValidationSuccedesWhenClaimContainsNumber() { + void testValidationSuccedesWhenClaimContainsNumber() { // prepare prepareContextProblems(null); @@ -114,7 +114,7 @@ public void testValidationSuccedesWhenClaimContainsNumber() { } @Test - public void testValidationWhenParticipantHasProblems() { + void testValidationWhenParticipantHasProblems() { // prepare prepareContextProblems(Collections.singletonList("big problem")); @@ -128,7 +128,7 @@ public void testValidationWhenParticipantHasProblems() { } @Test - public void testValidationWhenParticipantIsValid() { + void testValidationWhenParticipantIsValid() { // prepare prepareContextProblems(null); diff --git a/edc-extensions/pom.xml b/edc-extensions/pom.xml index e75f667ad..55921c721 100644 --- a/edc-extensions/pom.xml +++ b/edc-extensions/pom.xml @@ -26,8 +26,8 @@ pom - business-partner-validation aas-controller + business-partner-validation postgresql-migration diff --git a/pom.xml b/pom.xml index 726692d84..3dd7716d6 100644 --- a/pom.xml +++ b/pom.xml @@ -61,7 +61,7 @@ 5.8.2 1.8.2 - 1.10.19 + 4.6.1 @@ -125,7 +125,7 @@ - 1.8 + 1.15.0 @@ -900,37 +900,19 @@ pom import - - - - org.junit.jupiter - junit-jupiter-engine - ${junit.jupiter.version} - test - - - org.junit.jupiter - junit-jupiter-api - ${junit.jupiter.version} - test - - org.junit.jupiter - junit-jupiter-params + org.junit + junit-bom ${junit.jupiter.version} - test - - - org.junit.platform - junit-platform-suite - ${junit.platform.version} - test + pom + import org.mockito - mockito-all + mockito-bom ${mockito.version} - test + pom + import From 26e691495c834a982a699c7c0c2da2ae5cbdf525 Mon Sep 17 00:00:00 2001 From: "Florian Rusch (ZF Friedrichshafen AG)" Date: Tue, 7 Jun 2022 11:21:49 +0200 Subject: [PATCH 122/433] Make startup probe more configurable in helm charts (#182) --- deployment/helm/edc-controlplane/README.md | 2 ++ deployment/helm/edc-controlplane/templates/deployment.yaml | 4 ++-- deployment/helm/edc-controlplane/values.yaml | 4 ++++ deployment/helm/edc-dataplane/README.md | 2 ++ deployment/helm/edc-dataplane/templates/deployment.yaml | 4 ++-- deployment/helm/edc-dataplane/values.yaml | 4 ++++ 6 files changed, 16 insertions(+), 4 deletions(-) diff --git a/deployment/helm/edc-controlplane/README.md b/deployment/helm/edc-controlplane/README.md index b707b8428..7cf4e6b40 100644 --- a/deployment/helm/edc-controlplane/README.md +++ b/deployment/helm/edc-controlplane/README.md @@ -83,6 +83,8 @@ $ helm install my-release catenax-ng-product-edc/edc-controlplane --version 0.0. | serviceAccount.create | bool | `true` | Specifies whether a [service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) should be created per release | | serviceAccount.name | string | `""` | The name of the service account to use. If not set and create is true, a name is generated using the release's fullname template | | startupProbe.enabled | bool | `true` | Whether to enable kubernetes startup-probes | +| startupProbe.failureThreshold | int | `12` | Minimum consecutive failures for the probe to be considered failed after having succeeded | +| startupProbe.initialDelaySeconds | int | `10` | Number of seconds after the container has started before liveness probes are initiated. | | tolerations | list | `[]` | [Tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) are applied to Pods to schedule onto nodes with matching taints. | ---------------------------------------------- diff --git a/deployment/helm/edc-controlplane/templates/deployment.yaml b/deployment/helm/edc-controlplane/templates/deployment.yaml index 353e23e26..8b69aac9f 100644 --- a/deployment/helm/edc-controlplane/templates/deployment.yaml +++ b/deployment/helm/edc-controlplane/templates/deployment.yaml @@ -79,8 +79,8 @@ spec: httpGet: path: {{ .Values.edc.endpoints.default.path }}/check/startup port: default - failureThreshold: 12 - initialDelaySeconds: 10 + failureThreshold: {{ .Values.startupProbe.failureThreshold }} + initialDelaySeconds: {{ .Values.startupProbe.initialDelaySeconds }} {{- end }} envFrom: - configMapRef: diff --git a/deployment/helm/edc-controlplane/values.yaml b/deployment/helm/edc-controlplane/values.yaml index af9f9ba58..2f6b225bd 100644 --- a/deployment/helm/edc-controlplane/values.yaml +++ b/deployment/helm/edc-controlplane/values.yaml @@ -85,6 +85,10 @@ readinessProbe: startupProbe: # -- Whether to enable kubernetes startup-probes enabled: true + # -- Minimum consecutive failures for the probe to be considered failed after having succeeded + failureThreshold: 12 + # -- Number of seconds after the container has started before liveness probes are initiated. + initialDelaySeconds: 10 ## EDC endpoints exposed by the control-plane edc: diff --git a/deployment/helm/edc-dataplane/README.md b/deployment/helm/edc-dataplane/README.md index f91788145..6e218afef 100644 --- a/deployment/helm/edc-dataplane/README.md +++ b/deployment/helm/edc-dataplane/README.md @@ -71,6 +71,8 @@ $ helm install my-release catenax-ng-product-edc/edc-dataplane --version 0.0.3 | serviceAccount.create | bool | `true` | Specifies whether a [service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) should be created per release | | serviceAccount.name | string | `""` | The name of the service account to use. If not set and create is true, a name is generated using the release's fullname template | | startupProbe.enabled | bool | `true` | Whether to enable kubernetes startup-probes | +| startupProbe.failureThreshold | int | `12` | Minimum consecutive failures for the probe to be considered failed after having succeeded | +| startupProbe.initialDelaySeconds | int | `10` | Number of seconds after the container has started before liveness probes are initiated. | | tolerations | list | `[]` | [Tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) are applied to Pods to schedule onto nodes with matching taints. | ---------------------------------------------- diff --git a/deployment/helm/edc-dataplane/templates/deployment.yaml b/deployment/helm/edc-dataplane/templates/deployment.yaml index 1e4c428ba..914c78866 100644 --- a/deployment/helm/edc-dataplane/templates/deployment.yaml +++ b/deployment/helm/edc-dataplane/templates/deployment.yaml @@ -73,8 +73,8 @@ spec: httpGet: path: {{ .Values.edc.endpoints.default.path }}/check/startup port: default - failureThreshold: 12 - initialDelaySeconds: 10 + failureThreshold: {{ .Values.startupProbe.failureThreshold }} + initialDelaySeconds: {{ .Values.startupProbe.initialDelaySeconds }} {{- end }} envFrom: - configMapRef: diff --git a/deployment/helm/edc-dataplane/values.yaml b/deployment/helm/edc-dataplane/values.yaml index 92d4db32d..cc813d8cf 100644 --- a/deployment/helm/edc-dataplane/values.yaml +++ b/deployment/helm/edc-dataplane/values.yaml @@ -84,6 +84,10 @@ readinessProbe: startupProbe: # -- Whether to enable kubernetes startup-probes enabled: true + # -- Minimum consecutive failures for the probe to be considered failed after having succeeded + failureThreshold: 12 + # -- Number of seconds after the container has started before liveness probes are initiated. + initialDelaySeconds: 10 ## EDC endpoints exposed by the data-plane edc: From 3485bd73082101fe660bb68c3de4381b41aa5d37 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Tue, 7 Jun 2022 11:25:56 +0200 Subject: [PATCH 123/433] Remove edc-controlplane-cosmosdb (#184) --- .github/dependabot.yml | 9 - .github/workflows/build.yaml | 85 ----- .github/workflows/publish-new-release.yml | 2 +- .github/workflows/trivy.yaml | 31 -- README.md | 1 - deployment/helm/edc-controlplane/README.md | 4 +- deployment/helm/edc-controlplane/values.yaml | 25 +- deployment/helm/edc-dataplane/README.md | 2 +- deployment/helm/edc-dataplane/values.yaml | 21 - .../edc-controlplane-cosmosdb/README.md | 180 --------- .../edc-controlplane-cosmosdb/pom.xml | 358 ------------------ .../src/main/docker/Dockerfile | 37 -- edc-controlplane/pom.xml | 1 - 13 files changed, 5 insertions(+), 751 deletions(-) delete mode 100644 edc-controlplane/edc-controlplane-cosmosdb/README.md delete mode 100644 edc-controlplane/edc-controlplane-cosmosdb/pom.xml delete mode 100644 edc-controlplane/edc-controlplane-cosmosdb/src/main/docker/Dockerfile diff --git a/.github/dependabot.yml b/.github/dependabot.yml index fe7de904a..ff4a7e7a1 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -28,15 +28,6 @@ updates: - "docker" schedule: interval: "daily" - - - package-ecosystem: "docker" - target-branch: develop - directory: "edc-controlplane/edc-controlplane-cosmosdb/src/main/docker" - labels: - - "dependabot" - - "docker" - schedule: - interval: "daily" - package-ecosystem: "docker" target-branch: develop diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index a33061364..c3925b7f8 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -203,91 +203,6 @@ jobs: vid: ${{ secrets.ORG_VERACODE_API_ID }} vkey: ${{ secrets.ORG_VERACODE_API_KEY }} - ################################# - ### edc-controlplane-cosmosdb ### - ################################# - build-edc-controlplane-cosmosdb: - needs: [ secret-presence ] - runs-on: ubuntu-latest - steps: - # Set-Up - - - name: Checkout - uses: actions/checkout@v3 - with: - submodules: recursive - - - name: Login to GitHub Container Registry - if: | - needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.CXNG_GHCR_PAT }} - - - name: Set up JDK 11 - uses: actions/setup-java@v3.3.0 - with: - java-version: '11' - distribution: 'adopt' - cache: 'maven' - - - name: Build edc with Gradle to get latest snapshots - run: ./gradlew publishToMavenLocal - working-directory: edc - # Build - - - name: Build edc-controlplane-cosmosdb - run: |- - ./mvnw -s settings.xml -B -pl .,edc-controlplane/edc-controlplane-cosmosdb -am verify - env: - GITHUB_PACKAGE_USERNAME: ${{ github.actor }} - GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} - - - name: edc-controlplane-cosmosdb Docker Metadata - id: edc_controlplane_cosmosdb_meta - uses: docker/metadata-action@v4 - with: - images: | - ghcr.io/${{ github.repository }}/edc-controlplane-cosmosdb - tags: | - type=ref,event=branch - type=ref,event=pr - type=semver,pattern={{raw}} - type=match,pattern=\d.\d.\d - type=sha - - - name: Build edc-controlplane-cosmosdb Docker Image - uses: docker/build-push-action@v3 - with: - context: . - file: edc-controlplane/edc-controlplane-cosmosdb/src/main/docker/Dockerfile - build-args: | - JAR=edc-controlplane/edc-controlplane-cosmosdb/target/edc-controlplane-cosmosdb.jar - LIB=edc-controlplane/edc-controlplane-cosmosdb/target/lib - push: | - ${{ (needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' && 'true') || 'false' }} - tags: ${{ steps.edc_controlplane_cosmosdb_meta.outputs.tags }} - labels: ${{ steps.edc_controlplane_cosmosdb_meta.outputs.labels }} - - - name: Veracode Upload And Scan - uses: veracode/veracode-uploadandscan-action@v1.0 - if: | - needs.secret-presence.outputs.ORG_VERACODE_API_ID && needs.secret-presence.outputs.ORG_VERACODE_API_KEY && contains(' - refs/heads/develop - refs/heads/release/ - refs/tags/ - refs/heads/main', github.ref) - continue-on-error: true - with: - appname: 'product-edc/edc-controlplane-cosmosdb' - createprofile: true - filepath: edc-controlplane/edc-controlplane-cosmosdb/target/edc-controlplane-cosmosdb.jar - version: ${{ github.ref }}-${{ github.sha }} - vid: ${{ secrets.ORG_VERACODE_API_ID }} - vkey: ${{ secrets.ORG_VERACODE_API_KEY }} - ################################### ### edc-controlplane-postgresql ### ################################### diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index ca3a46f52..974e14454 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -84,7 +84,7 @@ jobs: name: Deploy run: |- ./mvnw -s settings.xml \ - -pl '!edc-controlplane,!edc-controlplane/edc-controlplane-cosmosdb,!edc-controlplane/edc-controlplane-memory,!edc-controlplane/edc-controlplane-postgresql,!edc-dataplane' \ + -pl '!edc-controlplane,!edc-controlplane/edc-controlplane-memory,!edc-controlplane/edc-controlplane-postgresql,!edc-dataplane' \ -DaltReleaseDeploymentRepository=github::https://maven.pkg.github.com/${{ github.repository }} \ -Dmaven.test.skip=true -B package deploy:deploy env: diff --git a/.github/workflows/trivy.yaml b/.github/workflows/trivy.yaml index 34eefd170..7c722c4a8 100644 --- a/.github/workflows/trivy.yaml +++ b/.github/workflows/trivy.yaml @@ -55,37 +55,6 @@ jobs: with: sarif_file: "trivy-results-config.sarif" - ################################# - ### edc-controlplane-cosmosdb ### - ################################# - analyze-edc-controlplane-cosmosdb: - needs: [ git-sha7 ] - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - steps: - - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Run Trivy vulnerability scanner - if: always() - uses: aquasecurity/trivy-action@master - with: - image-ref: "ghcr.io/${{ github.repository }}/edc-controlplane-cosmosdb:sha-${{ needs.git-sha7.outputs.value }}" - format: "sarif" - output: "trivy-results-edc-controlplane-cosmosdb.sarif" - exit-code: "1" - severity: "CRITICAL,HIGH" - - - name: Upload Trivy scan results to GitHub Security tab - if: always() - uses: github/codeql-action/upload-sarif@v2 - with: - sarif_file: "trivy-results-edc-controlplane-cosmosdb.sarif" - ############################### ### edc-controlplane-memory ### ############################### diff --git a/README.md b/README.md index 9cb72a662..f7d2918d3 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,6 @@ The Data-Plane does the heavy lifting of transferring and receiving data streams Depending on your environment there are different derivatives of the control-plane prepared: -* [edc-controlplane-cosmosdb](edc-controlplane/edc-controlplane-cosmosdb) * [edc-controlplane-memory](edc-controlplane/edc-controlplane-memory) * [edc-controlplane-postgresql](edc-controlplane/edc-controlplane-postgresql) diff --git a/deployment/helm/edc-controlplane/README.md b/deployment/helm/edc-controlplane/README.md index 7cf4e6b40..a9f0bdc6d 100644 --- a/deployment/helm/edc-controlplane/README.md +++ b/deployment/helm/edc-controlplane/README.md @@ -23,7 +23,7 @@ $ helm install my-release catenax-ng-product-edc/edc-controlplane --version 0.0. | autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | | autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | | autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | -| configuration.properties | string | `"# edc.api.auth.key=\n# edc.api.control.auth.apikey.key=\n# edc.api.control.auth.apikey.value=\n# edc.assetindex.cosmos.account-name=\n# edc.assetindex.cosmos.container-name=\n# edc.assetindex.cosmos.database-name=\n# edc.assetindex.cosmos.preferred-region=\n# edc.atomikos.checkpoint.interval=\n# edc.atomikos.directory=\n# edc.atomikos.logging=\n# edc.atomikos.threaded2pc=\n# edc.atomikos.timeout=\n# edc.aws.access.key=\n# edc.aws.provision.retry.retries.max=\n# edc.aws.provision.role.duration.session.max=\n# edc.aws.secret.access.key=\n# edc.blobstore.endpoint=\n# edc.contractdefinitionstore.cosmos.account-name=\n# edc.contractdefinitionstore.cosmos.container-name=\n# edc.contractdefinitionstore.cosmos.database-name=\n# edc.contractdefinitionstore.cosmos.preferred-region=\n# edc.contractnegotiationstore.cosmos.account-name=\n# edc.contractnegotiationstore.cosmos.container-name=\n# edc.contractnegotiationstore.cosmos.database-name=\n# edc.contractnegotiationstore.cosmos.preferred-region=\n# edc.contractnegotiationstore.cosmos.allow.sproc.autoupload=\n# edc.controlplane.validation-endpoint=\n# edc.core.retry.backoff.max=\n# edc.core.retry.backoff.min=\n# edc.core.retry.retries.max=\n# edc.core.system.health.check.liveness-period=\n# edc.core.system.health.check.readiness-period=\n# edc.core.system.health.check.startup-period=\n# edc.core.system.health.check.threadpool-size=\n# edc.cosmos.partition-key=\n# edc.cosmos.query-metrics-enabled=\n# edc.dataplane.queue.capacity=\n# edc.dataplane.wait=\n# edc.dataplane.workers=\n# edc.datasource.asset.name=\"default\"\n# edc.datasource.contractdefinition.name=\"default\"\n# edc.datasource.contractnegotiation.name=\"default\"\n# edc.datasource.policy.name=\"default\"\n# edc.datasource.transferprocess.name=\"default\"\n# edc.datasource.default.pool.maxIdleConnections=\n# edc.datasource.default.pool.maxTotalConnections=\n# edc.datasource.default.pool.minIdleConnections=\n# edc.datasource.default.pool.testConnectionOnBorrow=\n# edc.datasource.default.pool.testConnectionOnCreate=\n# edc.datasource.default.pool.testConnectionOnReturn=\n# edc.datasource.default.pool.testConnectionWhileIdle=\n# edc.datasource.default.pool.testQuery=\n# edc.datasource.default.url=\n# edc.datasource.default.user=\n# edc.datasource.default.password=\n# edc.dpf.selector.url=\n# edc.events.topic.endpoint=\n# edc.events.topic.name=\n# edc.fs.config=\n# edc.hostname=\n# edc.identity.did.url=\n# edc.ids.catalog.id=\n# edc.ids.curator=\n# edc.ids.description=\n# edc.ids.endpoint=\n# edc.ids.id=\n# edc.ids.maintainer=\n# edc.ids.security.profile=\n# edc.ids.title=\n# edc.ids.validation.referringconnector=\n# edc.ion.crawler.did-type=\n# edc.ion.crawler.interval-minutes=\n# edc.ion.crawler.ion.url=\n# edc.metrics.enabled=\n# edc.metrics.executor.enabled=\n# edc.metrics.jersey.enabled=\n# edc.metrics.jetty.enabled=\n# edc.metrics.okhttp.enabled=\n# edc.metrics.system.enabled=\n# edc.negotiation.consumer.state-machine.batch-size=\n# edc.negotiation.provider.state-machine.batch-size=\n# edc.node.directory.cosmos.account.name=\n# edc.node.directory.cosmos.container.name=\n# edc.node.directory.cosmos.database.name=\n# edc.node.directory.cosmos.preferred.region=\n# edc.oauth.client.id=\n# edc.oauth.private.key.alias=\n# edc.oauth.provider.audience=\n# edc.oauth.provider.jwks.refresh=\n# edc.oauth.provider.jwks.url=\n# edc.oauth.public.key.alias=\n# edc.oauth.token.url=\n# edc.oauth.validation.nbf.leeway=\n# edc.receiver.http.auth-code=\n# edc.receiver.http.auth-key=\n# edc.receiver.http.endpoint=\n# edc.transfer.proxy.endpoint=\n# edc.transfer.dataplane.sync.token.validity=\n# edc.transfer.proxy.token.signer.privatekey.alias=\n# edc.transfer.functions.check.endpoint=\n# edc.transfer.functions.enabled.protocols=\n# edc.transfer.functions.transfer.endpoint=\n# edc.transfer-process-store.cosmos.account.name=\n# edc.transfer-process-store.cosmos.container-name=\n# edc.transfer-process-store.cosmos.preferred-region=\n# edc.transfer-process-store.cosmos.allow.sproc.autoupload=\n# edc.transfer-process-store.database.name=\n# edc.transfer.state-machine.batch-size=\n# edc.vault=\n# edc.vault.certificate=\n# edc.vault.clientid=\n# edc.vault.clientsecret=\n# edc.vault.name=\n# edc.vault.tenantid=\n# edc.webdid.doh.url=\n# edc.web.rest.cors.enabled=\n# edc.web.rest.cors.headers=\n# edc.web.rest.cors.methods=\n# edc.web.rest.cors.origins="` | EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) | +| configuration.properties | string | `"# edc.api.auth.key=\n# edc.api.control.auth.apikey.key=\n# edc.api.control.auth.apikey.value=\n# edc.atomikos.checkpoint.interval=\n# edc.atomikos.directory=\n# edc.atomikos.logging=\n# edc.atomikos.threaded2pc=\n# edc.atomikos.timeout=\n# edc.aws.access.key=\n# edc.aws.provision.retry.retries.max=\n# edc.aws.provision.role.duration.session.max=\n# edc.aws.secret.access.key=\n# edc.blobstore.endpoint=\n# edc.controlplane.validation-endpoint=\n# edc.core.retry.backoff.max=\n# edc.core.retry.backoff.min=\n# edc.core.retry.retries.max=\n# edc.core.system.health.check.liveness-period=\n# edc.core.system.health.check.readiness-period=\n# edc.core.system.health.check.startup-period=\n# edc.core.system.health.check.threadpool-size=\n# edc.dataplane.queue.capacity=\n# edc.dataplane.wait=\n# edc.dataplane.workers=\n# edc.datasource.asset.name=\"default\"\n# edc.datasource.contractdefinition.name=\"default\"\n# edc.datasource.contractnegotiation.name=\"default\"\n# edc.datasource.policy.name=\"default\"\n# edc.datasource.transferprocess.name=\"default\"\n# edc.datasource.default.pool.maxIdleConnections=\n# edc.datasource.default.pool.maxTotalConnections=\n# edc.datasource.default.pool.minIdleConnections=\n# edc.datasource.default.pool.testConnectionOnBorrow=\n# edc.datasource.default.pool.testConnectionOnCreate=\n# edc.datasource.default.pool.testConnectionOnReturn=\n# edc.datasource.default.pool.testConnectionWhileIdle=\n# edc.datasource.default.pool.testQuery=\n# edc.datasource.default.url=\n# edc.datasource.default.user=\n# edc.datasource.default.password=\n# edc.dpf.selector.url=\n# edc.events.topic.endpoint=\n# edc.events.topic.name=\n# edc.fs.config=\n# edc.hostname=\n# edc.identity.did.url=\n# edc.ids.catalog.id=\n# edc.ids.curator=\n# edc.ids.description=\n# edc.ids.endpoint=\n# edc.ids.id=\n# edc.ids.maintainer=\n# edc.ids.security.profile=\n# edc.ids.title=\n# edc.ids.validation.referringconnector=\n# edc.ion.crawler.did-type=\n# edc.ion.crawler.interval-minutes=\n# edc.ion.crawler.ion.url=\n# edc.metrics.enabled=\n# edc.metrics.executor.enabled=\n# edc.metrics.jersey.enabled=\n# edc.metrics.jetty.enabled=\n# edc.metrics.okhttp.enabled=\n# edc.metrics.system.enabled=\n# edc.negotiation.consumer.state-machine.batch-size=\n# edc.negotiation.provider.state-machine.batch-size=\n# edc.oauth.client.id=\n# edc.oauth.private.key.alias=\n# edc.oauth.provider.audience=\n# edc.oauth.provider.jwks.refresh=\n# edc.oauth.provider.jwks.url=\n# edc.oauth.public.key.alias=\n# edc.oauth.token.url=\n# edc.oauth.validation.nbf.leeway=\n# edc.receiver.http.auth-code=\n# edc.receiver.http.auth-key=\n# edc.receiver.http.endpoint=\n# edc.transfer.proxy.endpoint=\n# edc.transfer.dataplane.sync.token.validity=\n# edc.transfer.proxy.token.signer.privatekey.alias=\n# edc.transfer.functions.check.endpoint=\n# edc.transfer.functions.enabled.protocols=\n# edc.transfer.functions.transfer.endpoint=\n# edc.transfer-process-store.database.name=\n# edc.transfer.state-machine.batch-size=\n# edc.vault=\n# edc.vault.certificate=\n# edc.vault.clientid=\n# edc.vault.clientsecret=\n# edc.vault.name=\n# edc.vault.tenantid=\n# edc.webdid.doh.url=\n# edc.web.rest.cors.enabled=\n# edc.web.rest.cors.headers=\n# edc.web.rest.cors.methods=\n# edc.web.rest.cors.origins="` | EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) | | edc.endpoints.control.path | string | `"/api/controlplane/control"` | The path mapping the "control" api is going to be exposed at | | edc.endpoints.control.port | string | `"9999"` | The network port, which the "control" api is going to be exposed by the container, pod and service | | edc.endpoints.data.path | string | `"/data"` | The path mapping the "data" management api is going to be exposed at | @@ -39,7 +39,7 @@ $ helm install my-release catenax-ng-product-edc/edc-controlplane --version 0.0. | env | object | `{}` | Container environment variables e.g. for configuring [JAVA_TOOL_OPTIONS](https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/envvars002.html) Ex.: JAVA_TOOL_OPTIONS: > -Dhttp.proxyHost=proxy -Dhttp.proxyPort=80 -Dhttp.nonProxyHosts="localhost|127.*|[::1]" -Dhttps.proxyHost=proxy -Dhttps.proxyPort=443 | | fullnameOverride | string | `""` | Overrides the releases full name | | image.pullPolicy | string | `"IfNotPresent"` | [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use | -| image.repository | string | `"ghcr.io/catenax-ng/product-edc/edc-controlplane-memory"` | Which derivate of the edc controlplane to use. One of: [ghcr.io/catenax-ng/product-edc/edc-controlplane-memory, , ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql, ghcr.io/catenax-ng/product-edc/edc-controlplane-cosmosdb] | +| image.repository | string | `"ghcr.io/catenax-ng/product-edc/edc-controlplane-memory"` | Which derivate of the edc controlplane to use. One of: [ghcr.io/catenax-ng/product-edc/edc-controlplane-memory, ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql] | | image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion. | | imagePullSecret.dockerconfigjson | string | `""` | Image pull secret to create to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) Note: This value needs to adhere to the [(base64 encoded) .dockerconfigjson format](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#registry-secret-existing-credentials). Furthermore, if 'imagePullSecret.dockerconfigjson' is defined, it takes precedence over 'imagePullSecrets'. | | imagePullSecrets | list | `[]` | Existing image pull secret to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) | diff --git a/deployment/helm/edc-controlplane/values.yaml b/deployment/helm/edc-controlplane/values.yaml index 2f6b225bd..3b709fd69 100644 --- a/deployment/helm/edc-controlplane/values.yaml +++ b/deployment/helm/edc-controlplane/values.yaml @@ -9,7 +9,7 @@ replicaCount: 1 image: # -- Which derivate of the edc controlplane to use. - # One of: [ghcr.io/catenax-ng/product-edc/edc-controlplane-memory, , ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql, ghcr.io/catenax-ng/product-edc/edc-controlplane-cosmosdb] + # One of: [ghcr.io/catenax-ng/product-edc/edc-controlplane-memory, ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql] repository: ghcr.io/catenax-ng/product-edc/edc-controlplane-memory # -- [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use pullPolicy: IfNotPresent @@ -240,10 +240,6 @@ configuration: # edc.api.auth.key= # edc.api.control.auth.apikey.key= # edc.api.control.auth.apikey.value= - # edc.assetindex.cosmos.account-name= - # edc.assetindex.cosmos.container-name= - # edc.assetindex.cosmos.database-name= - # edc.assetindex.cosmos.preferred-region= # edc.atomikos.checkpoint.interval= # edc.atomikos.directory= # edc.atomikos.logging= @@ -254,15 +250,6 @@ configuration: # edc.aws.provision.role.duration.session.max= # edc.aws.secret.access.key= # edc.blobstore.endpoint= - # edc.contractdefinitionstore.cosmos.account-name= - # edc.contractdefinitionstore.cosmos.container-name= - # edc.contractdefinitionstore.cosmos.database-name= - # edc.contractdefinitionstore.cosmos.preferred-region= - # edc.contractnegotiationstore.cosmos.account-name= - # edc.contractnegotiationstore.cosmos.container-name= - # edc.contractnegotiationstore.cosmos.database-name= - # edc.contractnegotiationstore.cosmos.preferred-region= - # edc.contractnegotiationstore.cosmos.allow.sproc.autoupload= # edc.controlplane.validation-endpoint= # edc.core.retry.backoff.max= # edc.core.retry.backoff.min= @@ -271,8 +258,6 @@ configuration: # edc.core.system.health.check.readiness-period= # edc.core.system.health.check.startup-period= # edc.core.system.health.check.threadpool-size= - # edc.cosmos.partition-key= - # edc.cosmos.query-metrics-enabled= # edc.dataplane.queue.capacity= # edc.dataplane.wait= # edc.dataplane.workers= @@ -318,10 +303,6 @@ configuration: # edc.metrics.system.enabled= # edc.negotiation.consumer.state-machine.batch-size= # edc.negotiation.provider.state-machine.batch-size= - # edc.node.directory.cosmos.account.name= - # edc.node.directory.cosmos.container.name= - # edc.node.directory.cosmos.database.name= - # edc.node.directory.cosmos.preferred.region= # edc.oauth.client.id= # edc.oauth.private.key.alias= # edc.oauth.provider.audience= @@ -339,10 +320,6 @@ configuration: # edc.transfer.functions.check.endpoint= # edc.transfer.functions.enabled.protocols= # edc.transfer.functions.transfer.endpoint= - # edc.transfer-process-store.cosmos.account.name= - # edc.transfer-process-store.cosmos.container-name= - # edc.transfer-process-store.cosmos.preferred-region= - # edc.transfer-process-store.cosmos.allow.sproc.autoupload= # edc.transfer-process-store.database.name= # edc.transfer.state-machine.batch-size= # edc.vault= diff --git a/deployment/helm/edc-dataplane/README.md b/deployment/helm/edc-dataplane/README.md index 6e218afef..76f8c727e 100644 --- a/deployment/helm/edc-dataplane/README.md +++ b/deployment/helm/edc-dataplane/README.md @@ -23,7 +23,7 @@ $ helm install my-release catenax-ng-product-edc/edc-dataplane --version 0.0.3 | autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | | autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | | autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | -| configuration.properties | string | `"# edc.api.control.auth.apikey.key=\n# edc.api.control.auth.apikey.value=\n# edc.assetindex.cosmos.account-name=\n# edc.assetindex.cosmos.container-name=\n# edc.assetindex.cosmos.database-name=\n# edc.assetindex.cosmos.preferred-region=\n# edc.atomikos.checkpoint.interval=\n# edc.atomikos.directory=\n# edc.atomikos.logging=\n# edc.atomikos.threaded2pc=\n# edc.atomikos.timeout=\n# edc.aws.access.key=\n# edc.aws.provision.retry.retries.max=\n# edc.aws.provision.role.duration.session.max=\n# edc.aws.secret.access.key=\n# edc.blobstore.endpoint=\n# edc.contractdefinitionstore.cosmos.account-name=\n# edc.contractdefinitionstore.cosmos.container-name=\n# edc.contractdefinitionstore.cosmos.database-name=\n# edc.contractdefinitionstore.cosmos.preferred-region=\n# edc.contractnegotiationstore.cosmos.account-name=\n# edc.contractnegotiationstore.cosmos.container-name=\n# edc.contractnegotiationstore.cosmos.database-name=\n# edc.contractnegotiationstore.cosmos.preferred-region=\n# edc.controlplane.validation-endpoint=\n# edc.core.retry.backoff.max=\n# edc.core.retry.backoff.min=\n# edc.core.retry.retries.max=\n# edc.core.system.health.check.liveness-period=\n# edc.core.system.health.check.readiness-period=\n# edc.core.system.health.check.startup-period=\n# edc.core.system.health.check.threadpool-size=\n# edc.cosmos.partition-key=\n# edc.cosmos.query-metrics-enabled=\n# edc.dataplane.queue.capacity=\n# edc.dataplane.wait=\n# edc.dataplane.workers=\n# edc.datasource.asset.name=\"default\"\n# edc.datasource.contractdefinition.name=\"default\"\n# edc.datasource.contractnegotiation.name=\"default\"\n# edc.datasource.policy.name=\"default\"\n# edc.datasource.transferprocess.name=\"default\"\n# edc.datasource.default.pool.maxIdleConnections=\n# edc.datasource.default.pool.maxTotalConnections=\n# edc.datasource.default.pool.minIdleConnections=\n# edc.datasource.default.pool.testConnectionOnBorrow=\n# edc.datasource.default.pool.testConnectionOnCreate=\n# edc.datasource.default.pool.testConnectionOnReturn=\n# edc.datasource.default.pool.testConnectionWhileIdle=\n# edc.datasource.default.pool.testQuery=\n# edc.datasource.default.url=\n# edc.datasource.default.user=\n# edc.datasource.default.password=\n# edc.dpf.selector.url=\n# edc.events.topic.endpoint=\n# edc.events.topic.name=\n# edc.fs.config=\n# edc.hostname=\n# edc.identity.did.url=\n# edc.ids.catalog.id=\n# edc.ids.curator=\n# edc.ids.description=\n# edc.ids.endpoint=\n# edc.ids.id=\n# edc.ids.maintainer=\n# edc.ids.security.profile=\n# edc.ids.title=\n# edc.ids.validation.referringconnector=\n# edc.ion.crawler.did-type=\n# edc.ion.crawler.interval-minutes=\n# edc.ion.crawler.ion.url=\n# edc.metrics.enabled=\n# edc.metrics.executor.enabled=\n# edc.metrics.jersey.enabled=\n# edc.metrics.jetty.enabled=\n# edc.metrics.okhttp.enabled=\n# edc.metrics.system.enabled=\n# edc.negotiation.consumer.state-machine.batch-size=\n# edc.negotiation.provider.state-machine.batch-size=\n# edc.node.directory.cosmos.account.name=\n# edc.node.directory.cosmos.container.name=\n# edc.node.directory.cosmos.database.name=\n# edc.node.directory.cosmos.preferred.region=\n# edc.oauth.client.id=\n# edc.oauth.private.key.alias=\n# edc.oauth.provider.audience=\n# edc.oauth.provider.jwks.refresh=\n# edc.oauth.provider.jwks.url=\n# edc.oauth.public.key.alias=\n# edc.oauth.token.url=\n# edc.oauth.validation.nbf.leeway=\n# edc.receiver.http.auth-code=\n# edc.receiver.http.auth-key=\n# edc.receiver.http.endpoint=\n# edc.transfer.proxy.endpoint=\n# edc.transfer.dataplane.sync.token.validity=\n# edc.transfer.proxy.token.signer.privatekey.alias=\n# edc.transfer.functions.check.endpoint=\n# edc.transfer.functions.enabled.protocols=\n# edc.transfer.functions.transfer.endpoint=\n# edc.transfer-process-store.cosmos.account.name=\n# edc.transfer-process-store.cosmos.container-name=\n# edc.transfer-process-store.cosmos.preferred-region=\n# edc.transfer-process-store.database.name=\n# edc.transfer.state-machine.batch-size=\n# edc.vault=\n# edc.vault.certificate=\n# edc.vault.clientid=\n# edc.vault.clientsecret=\n# edc.vault.name=\n# edc.vault.tenantid=\n# edc.webdid.doh.url=\n# edc.web.rest.cors.enabled=\n# edc.web.rest.cors.headers=\n# edc.web.rest.cors.methods=\n# edc.web.rest.cors.origins="` | EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) | +| configuration.properties | string | `"# edc.api.control.auth.apikey.key=\n# edc.api.control.auth.apikey.value=\n# edc.atomikos.checkpoint.interval=\n# edc.atomikos.directory=\n# edc.atomikos.logging=\n# edc.atomikos.threaded2pc=\n# edc.atomikos.timeout=\n# edc.aws.access.key=\n# edc.aws.provision.retry.retries.max=\n# edc.aws.provision.role.duration.session.max=\n# edc.aws.secret.access.key=\n# edc.blobstore.endpoint=\n# edc.controlplane.validation-endpoint=\n# edc.core.retry.backoff.max=\n# edc.core.retry.backoff.min=\n# edc.core.retry.retries.max=\n# edc.core.system.health.check.liveness-period=\n# edc.core.system.health.check.readiness-period=\n# edc.core.system.health.check.startup-period=\n# edc.core.system.health.check.threadpool-size=\n# edc.dataplane.queue.capacity=\n# edc.dataplane.wait=\n# edc.dataplane.workers=\n# edc.datasource.asset.name=\"default\"\n# edc.datasource.contractdefinition.name=\"default\"\n# edc.datasource.contractnegotiation.name=\"default\"\n# edc.datasource.policy.name=\"default\"\n# edc.datasource.transferprocess.name=\"default\"\n# edc.datasource.default.pool.maxIdleConnections=\n# edc.datasource.default.pool.maxTotalConnections=\n# edc.datasource.default.pool.minIdleConnections=\n# edc.datasource.default.pool.testConnectionOnBorrow=\n# edc.datasource.default.pool.testConnectionOnCreate=\n# edc.datasource.default.pool.testConnectionOnReturn=\n# edc.datasource.default.pool.testConnectionWhileIdle=\n# edc.datasource.default.pool.testQuery=\n# edc.datasource.default.url=\n# edc.datasource.default.user=\n# edc.datasource.default.password=\n# edc.dpf.selector.url=\n# edc.events.topic.endpoint=\n# edc.events.topic.name=\n# edc.fs.config=\n# edc.hostname=\n# edc.identity.did.url=\n# edc.ids.catalog.id=\n# edc.ids.curator=\n# edc.ids.description=\n# edc.ids.endpoint=\n# edc.ids.id=\n# edc.ids.maintainer=\n# edc.ids.security.profile=\n# edc.ids.title=\n# edc.ids.validation.referringconnector=\n# edc.ion.crawler.did-type=\n# edc.ion.crawler.interval-minutes=\n# edc.ion.crawler.ion.url=\n# edc.metrics.enabled=\n# edc.metrics.executor.enabled=\n# edc.metrics.jersey.enabled=\n# edc.metrics.jetty.enabled=\n# edc.metrics.okhttp.enabled=\n# edc.metrics.system.enabled=\n# edc.negotiation.consumer.state-machine.batch-size=\n# edc.negotiation.provider.state-machine.batch-size=\n# edc.oauth.client.id=\n# edc.oauth.private.key.alias=\n# edc.oauth.provider.audience=\n# edc.oauth.provider.jwks.refresh=\n# edc.oauth.provider.jwks.url=\n# edc.oauth.public.key.alias=\n# edc.oauth.token.url=\n# edc.oauth.validation.nbf.leeway=\n# edc.receiver.http.auth-code=\n# edc.receiver.http.auth-key=\n# edc.receiver.http.endpoint=\n# edc.transfer.proxy.endpoint=\n# edc.transfer.dataplane.sync.token.validity=\n# edc.transfer.proxy.token.signer.privatekey.alias=\n# edc.transfer.functions.check.endpoint=\n# edc.transfer.functions.enabled.protocols=\n# edc.transfer.functions.transfer.endpoint=\n# edc.transfer-process-store.database.name=\n# edc.transfer.state-machine.batch-size=\n# edc.vault=\n# edc.vault.certificate=\n# edc.vault.clientid=\n# edc.vault.clientsecret=\n# edc.vault.name=\n# edc.vault.tenantid=\n# edc.webdid.doh.url=\n# edc.web.rest.cors.enabled=\n# edc.web.rest.cors.headers=\n# edc.web.rest.cors.methods=\n# edc.web.rest.cors.origins="` | EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) | | edc.endpoints.control.path | string | `"/api/dataplane/control"` | The path mapping the "control" api is going to be exposed by | | edc.endpoints.control.port | string | `"9999"` | The network port, which the "control" api is going to be exposed by the container, pod and service | | edc.endpoints.default.path | string | `"/api"` | The path mapping the "default" api is going to be exposed by | diff --git a/deployment/helm/edc-dataplane/values.yaml b/deployment/helm/edc-dataplane/values.yaml index cc813d8cf..80e3bb5c6 100644 --- a/deployment/helm/edc-dataplane/values.yaml +++ b/deployment/helm/edc-dataplane/values.yaml @@ -205,10 +205,6 @@ configuration: properties: |- # edc.api.control.auth.apikey.key= # edc.api.control.auth.apikey.value= - # edc.assetindex.cosmos.account-name= - # edc.assetindex.cosmos.container-name= - # edc.assetindex.cosmos.database-name= - # edc.assetindex.cosmos.preferred-region= # edc.atomikos.checkpoint.interval= # edc.atomikos.directory= # edc.atomikos.logging= @@ -219,14 +215,6 @@ configuration: # edc.aws.provision.role.duration.session.max= # edc.aws.secret.access.key= # edc.blobstore.endpoint= - # edc.contractdefinitionstore.cosmos.account-name= - # edc.contractdefinitionstore.cosmos.container-name= - # edc.contractdefinitionstore.cosmos.database-name= - # edc.contractdefinitionstore.cosmos.preferred-region= - # edc.contractnegotiationstore.cosmos.account-name= - # edc.contractnegotiationstore.cosmos.container-name= - # edc.contractnegotiationstore.cosmos.database-name= - # edc.contractnegotiationstore.cosmos.preferred-region= # edc.controlplane.validation-endpoint= # edc.core.retry.backoff.max= # edc.core.retry.backoff.min= @@ -235,8 +223,6 @@ configuration: # edc.core.system.health.check.readiness-period= # edc.core.system.health.check.startup-period= # edc.core.system.health.check.threadpool-size= - # edc.cosmos.partition-key= - # edc.cosmos.query-metrics-enabled= # edc.dataplane.queue.capacity= # edc.dataplane.wait= # edc.dataplane.workers= @@ -282,10 +268,6 @@ configuration: # edc.metrics.system.enabled= # edc.negotiation.consumer.state-machine.batch-size= # edc.negotiation.provider.state-machine.batch-size= - # edc.node.directory.cosmos.account.name= - # edc.node.directory.cosmos.container.name= - # edc.node.directory.cosmos.database.name= - # edc.node.directory.cosmos.preferred.region= # edc.oauth.client.id= # edc.oauth.private.key.alias= # edc.oauth.provider.audience= @@ -303,9 +285,6 @@ configuration: # edc.transfer.functions.check.endpoint= # edc.transfer.functions.enabled.protocols= # edc.transfer.functions.transfer.endpoint= - # edc.transfer-process-store.cosmos.account.name= - # edc.transfer-process-store.cosmos.container-name= - # edc.transfer-process-store.cosmos.preferred-region= # edc.transfer-process-store.database.name= # edc.transfer.state-machine.batch-size= # edc.vault= diff --git a/edc-controlplane/edc-controlplane-cosmosdb/README.md b/edc-controlplane/edc-controlplane-cosmosdb/README.md deleted file mode 100644 index 09c5dbef3..000000000 --- a/edc-controlplane/edc-controlplane-cosmosdb/README.md +++ /dev/null @@ -1,180 +0,0 @@ -# EDC Control-Plane backed by [Azure CosmosDB](https://docs.microsoft.com/en-us/azure/cosmos-db/introduction) - -### Building - -```shell -./mvnw -pl .,edc-controlplane/edc-controlplane-cosmosdb -am package -Pwith-docker-image -``` - -### Key Vault Setup - -The connector will lookup a secret in the key vault, that has the same alias as the `account-name` setting for CosmosDB (e.g. `edc.assetindex.cosmos.account-name`). -This secret must contain the primary or the secondard CosmosDB Read-write key. - -### Configuration - -Listed below are configuration keys needed to get the `edc-controlplane-cosmosdb` up and running. -Details regarding each configuration property can be found at the [documentary section of the EDC](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/docs). - -| Key | Required | Example | Description | -|--- |--- |--- |--- | -| web.http.default.port | X | 8080 | | -| web.http.default.path | X | /api | | -| web.http.data.port | X | 8181 | | -| web.http.data.path | X | /data | | -| web.http.validation.port | X | 8182 | | -| web.http.validation.path | X | /validation | | -| web.http.control.port | X | 9999 | | -| web.http.control.path | X | /api/controlplane/control | | -| web.http.ids.port | X | 8282 | | -| web.http.ids.path | X | /api/v1/ids | | -| edc.receiver.http.endpoint | X | http://backend-service | | -| edc.ids.title | | Eclipse Dataspace Connector | | -| edc.ids.description | | Eclipse Dataspace Connector | | -| edc.ids.id | | urn:connector:edc | | -| edc.ids.security.profile | | base | | -| edc.ids.endpoint | | http://localhost:8282/api/v1/ids | | -| edc.ids.maintainer | | http://localhost | | -| edc.ids.curator | | http://localhost | | -| edc.ids.catalog.id | | urn:catalog:default | | -| ids.webhook.address | | http://localhost:8282/api/v1/ids | | -| edc.api.control.auth.apikey.key | | X-Api-Key | | -| edc.api.control.auth.apikey.value | | super-strong-api-key | | -| edc.hostname | | localhost | | -| edc.oauth.token.url | X | https://daps.catena-x.net | | -| edc.oauth.public.key.alias | X | key-to-daps-certificate-in-keyvault | | -| edc.oauth.private.key.alias | X | key-to-private-key-in-keyvault | | -| edc.oauth.client.id | X | daps-oauth-client-id | | -| edc.vault.clientid | X | 00000000-1111-2222-3333-444444444444 | | -| edc.vault.tenantid | X | 55555555-6666-7777-8888-999999999999 | | -| edc.vault.name | X | my-vault-name | | -| edc.vault.clientsecret | X | 34-chars-secret | | -| edc.assetindex.cosmos.account-name | X | cosmosdb-assetindex-account-name | | -| edc.assetindex.cosmos.database-name | X | asset-index | | -| edc.assetindex.cosmos.preferred-region | X | westeurope | | -| edc.assetindex.cosmos.container-name | X | cosmosdb-assetindex-container-name | | -| edc.contractdefinitionstore.cosmos.account-name | X | cosmosdb-contractdefinitionstore-account-name | | -| edc.contractdefinitionstore.cosmos.database-name | X | contract-definition-store | | -| edc.contractdefinitionstore.cosmos.preferred-region | X | westeurope | | -| edc.contractdefinitionstore.cosmos.container-name | X | cosmosdb-contractdefinitionstore-container-name | | -| edc.contractnegotiationstore.cosmos.account-name | X | cosmosdb-contractnegotiationstore-account-name | | -| edc.contractnegotiationstore.cosmos.database-name | X | contract-negotiation-store | | -| edc.contractnegotiationstore.cosmos.preferred-region | X | westeurope | | -| edc.contractnegotiationstore.cosmos.container-name | X | cosmosdb-contractnegotiationstore-container-name | | -| edc.contractnegotiationstore.cosmos.allow.sproc.autoupload | | true | | -| edc.transfer-process-store.cosmos.account.name | X | cosmosdb-contractnegotiationstore-account-name | | -| edc.transfer-process-store.database.name | X | transfer-process-store | | -| edc.transfer-process-store.cosmos.preferred-region | X | westeurope | | -| edc.transfer-process-store.cosmos.container-name | X | cosmosdb-transfer-process-store-container-name | | -| edc.transfer-process-store.cosmos.allow.sproc.autoupload | | true | | -| edc.transfer.proxy.endpoint | X | http://dataplane-public-endpoint/public | | -| edc.transfer.proxy.token.signer.privatekey.alias | X | key-of-private-key-in-keyvault-to-sign-transfer-token | | - -#### Example configuration.properties - -JDK properties-style configuration of the EDC Control-Plane is expected to be mounted to `/app/configuration.properties` within the container. - -```shell -# Create configuration.properties -export CONFIGURATION_PROPERTIES_FILE=$(mktemp /tmp/configuration.properties.XXXXXX) -cat << 'EOF' > ${CONFIGURATION_PROPERTIES_FILE} - -web.http.default.port=8080 -web.http.default.path=/api -web.http.data.port=8181 -web.http.data.path=/data -web.http.validation.port=8182 -web.http.validation.path=/validation -web.http.control.port=9999 -web.http.control.path=/api/controlplane/control -web.http.ids.port=8282 -web.http.ids.path=/api/v1/ids - -edc.receiver.http.endpoint=http://backend-service - -edc.ids.title=Eclipse Dataspace Connector -edc.ids.description=Eclipse Dataspace Connector -edc.ids.id=urn:connector:edc -edc.ids.security.profile=base -edc.ids.endpoint=http://localhost:8282/api/v1/ids -edc.ids.maintainer=http://localhost -edc.ids.curator=http://localhost -edc.ids.catalog.id=urn:catalog:default -ids.webhook.address=http://localhost:8282/api/v1/ids - -edc.api.control.auth.apikey.key=X-Api-Key -edc.api.control.auth.apikey.value=pass - -edc.hostname=localhost - -# OAuth / DAPS related configuration -edc.oauth.token.url=https://daps.catena-x.net -edc.oauth.public.key.alias=key-to-daps-certificate-in-keyvault -edc.oauth.private.key.alias=key-to-private-key-in-keyvault -edc.oauth.client.id=daps-oauth-client-id - -# Azure vault related configuration -edc.vault.clientid=00000000-1111-2222-3333-444444444444 -edc.vault.tenantid=55555555-6666-7777-8888-999999999999 -edc.vault.name=my-vault-name -edc.vault.clientsecret=34-chars-secret - -# Control- / Data- Plane configuration -edc.transfer.proxy.endpoint=http://dataplane-public-endpoint/public -edc.transfer.proxy.token.signer.privatekey.alias=azure-vault-token-signer-private-key - -# Azure CosmosDB related configuration -edc.assetindex.cosmos.account-name=cosmosdb-assetindex-account-name -edc.assetindex.cosmos.database-name=asset-index -edc.assetindex.cosmos.preferred-region=westeurope -edc.assetindex.cosmos.container-name=cosmosdb-assetindex-container-name -edc.contractdefinitionstore.cosmos.account-name=cosmosdb-contractdefinitionstore-account-name -edc.contractdefinitionstore.cosmos.database-name=contract-definition-store -edc.contractdefinitionstore.cosmos.preferred-region=westeurope -edc.contractdefinitionstore.cosmos.container-name=cosmosdb-contractdefinitionstore-container-name -edc.contractnegotiationstore.cosmos.account-name=cosmosdb-contractnegotiationstore-account-name -edc.contractnegotiationstore.cosmos.database-name=contract-negotiation-store -edc.contractnegotiationstore.cosmos.preferred-region=westeurope -edc.contractnegotiationstore.cosmos.container-name=cosmosdb-contractnegotiationstore-container-name -edc.transfer-process-store.cosmos.account.name=cosmosdb-contractnegotiationstore-account-name -edc.transfer-process-store.database.name=transfer-process-store -edc.transfer-process-store.cosmos.preferred-region=westeurope -edc.transfer-process-store.cosmos.container-name=cosmosdb-transfer-process-store-container-name - -EOF -``` - -#### Example logging.properties -```shell -# Create logging.properties -export LOGGING_PROPERTIES_FILE=$(mktemp /tmp/logging.properties.XXXXXX) -cat << 'EOF' > ${LOGGING_PROPERTIES_FILE} -.level=INFO -org.eclipse.dataspaceconnector.level=ALL -handlers=java.util.logging.ConsoleHandler -java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter -java.util.logging.ConsoleHandler.level=ALL -java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n -EOF -``` - -#### Example opentelemetry.properties -```shell -# Create opentelemetry.properties -export OPENTELEMETRY_PROPERTIES_FILE=$(mktemp /tmp/opentelemetry.properties.XXXXXX) -cat << 'EOF' > ${OPENTELEMETRY_PROPERTIES_FILE} -otel.javaagent.enabled=true -otel.javaagent.debug=false -EOF -``` - -### Running - -```shell -docker run \ - -p 8080:8080 -p 8181:8181 -p 8182:8182 -p 8282:8282 -p 9090:9090 -p 9999:9999 \ - -v ${CONFIGURATION_PROPERTIES_FILE:-/dev/null}:/app/configuration.properties \ - -v ${LOGGING_PROPERTIES_FILE:-/dev/null}:/app/logging.properties \ - -v ${OPENTELEMETRY_PROPERTIES_FILE:-/dev/null}:/app/opentelemetry.properties \ - -i edc-controlplane-cosmosdb:latest -``` \ No newline at end of file diff --git a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml b/edc-controlplane/edc-controlplane-cosmosdb/pom.xml deleted file mode 100644 index 478f840e3..000000000 --- a/edc-controlplane/edc-controlplane-cosmosdb/pom.xml +++ /dev/null @@ -1,358 +0,0 @@ - - - - - net.catenax.edc - edc-controlplane - 0.0.4-SNAPSHOT - - 4.0.0 - - edc-controlplane-cosmosdb - jar - - - ${project.artifactId} - - - src/main/resources - - **/* - - - - ../../ - META-INF - - NOTICE.md - LICENSE - - - - - - - org.apache.maven.plugins - maven-jar-plugin - - - - true - lib/ - - org.eclipse.dataspaceconnector.boot.system.runtime.BaseRuntime - - - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - package - - copy-dependencies - - - ${project.build.directory}/lib - - - - - - - - - - - - net.catenax.edc.extensions - aas-controller - - - net.catenax.edc.extensions - business-partner-validation - - - - - org.eclipse.dataspaceconnector - filesystem-configuration - - - - - org.eclipse.dataspaceconnector - auth-tokenbased - - - - - org.eclipse.dataspaceconnector - azure-vault - - - com.azure - azure-security-keyvault-secrets - - - com.azure - azure-identity - - - - - - - org.eclipse.dataspaceconnector - control-api - - - org.eclipse.dataspaceconnector - data-management-api - - - org.eclipse.dataspaceconnector - observability-api - - - - - org.eclipse.dataspaceconnector - ids-api-configuration - - - org.eclipse.dataspaceconnector - ids-api-multipart-dispatcher-v1 - - - org.eclipse.dataspaceconnector - ids-api-multipart-endpoint-v1 - - - org.eclipse.dataspaceconnector - ids-api-transform-v1 - - - org.eclipse.dataspaceconnector - ids-core - - - org.eclipse.dataspaceconnector - ids-spi - - - org.eclipse.dataspaceconnector - ids-token-validation - - - - - org.eclipse.dataspaceconnector - assetindex-cosmos - - - com.azure - azure-cosmos - - - - - org.eclipse.dataspaceconnector - contract-definition-store-cosmos - - - com.azure - azure-cosmos - - - - - org.eclipse.dataspaceconnector - contract-negotiation-store-cosmos - - - com.azure - azure-cosmos - - - - - org.eclipse.dataspaceconnector - policy-store-cosmos - - - com.azure - azure-cosmos - - - - - org.eclipse.dataspaceconnector - transfer-process-store-cosmos - - - com.azure - azure-cosmos - - - - - - - org.eclipse.dataspaceconnector - core-base - - - org.eclipse.dataspaceconnector - core-boot - - - org.eclipse.dataspaceconnector - transfer - - - org.eclipse.dataspaceconnector - contract - - - - - org.eclipse.dataspaceconnector - data-plane-transfer-sync - - - - - org.eclipse.dataspaceconnector - oauth2-core - - - org.eclipse.dataspaceconnector - iam-daps - - - - - org.eclipse.dataspaceconnector - core-micrometer - - - org.eclipse.dataspaceconnector - jersey-micrometer - - - org.eclipse.dataspaceconnector - jetty-micrometer - - - - - org.eclipse.dataspaceconnector - jdk-logger-monitor - - - - - - org.eclipse.dataspaceconnector - http - - - - - org.eclipse.dataspaceconnector - http-receiver - - - - - com.azure - azure-cosmos - - - com.azure - azure-identity - - - com.azure - azure-security-keyvault-secrets - - - - - - - with-docker-image - - - - org.codehaus.mojo - exec-maven-plugin - - - - docker-build-${project.artifactId}:${project.version} - package - - exec - - - docker - ${project.basedir} - - build - -f - src/main/docker/Dockerfile - --build-arg - JAR=target/${project.artifactId}.jar - --build-arg - LIB=target/lib - -t - ${project.artifactId}:${project.version} - . - - - - - docker-tag-${project.artifactId}:latest - package - - exec - - - docker - ${project.basedir} - - tag - ${project.artifactId}:${project.version} - ${project.artifactId}:latest - - - - - - - - - - \ No newline at end of file diff --git a/edc-controlplane/edc-controlplane-cosmosdb/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-cosmosdb/src/main/docker/Dockerfile deleted file mode 100644 index d4aa94d6b..000000000 --- a/edc-controlplane/edc-controlplane-cosmosdb/src/main/docker/Dockerfile +++ /dev/null @@ -1,37 +0,0 @@ -# -# Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH -# -# This program and the accompanying materials are made available under the -# terms of the Apache License, Version 2.0 which is available at -# https://www.apache.org/licenses/LICENSE-2.0 -# -# SPDX-License-Identifier: Apache-2.0 -# -# Contributors: -# Mercedes-Benz Tech Innovation GmbH - Initial Dockerfile -# -FROM alpine:3.16.0 as otel - -ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" - -RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar - -FROM gcr.io/distroless/java11-debian11 -ARG JAR -ARG LIB - -WORKDIR /app - -COPY --from=otel /tmp/opentelemetry-javaagent.jar . -COPY ${JAR} edc-controlplane.jar -COPY ${LIB} ./lib/ - -CMD ["-javaagent:/app/opentelemetry-javaagent.jar", \ - "-Dedc.fs.config=/app/configuration.properties", \ - "-Djava.util.logging.config.file=/app/logging.properties", \ - "-Dotel.javaagent.configuration-file=/app/opentelemetry.properties", \ - "-Dotel.metrics.exporter=prometheus", \ - "-Dotel.exporter.prometheus.port=9090", \ - "-Djava.security.edg=file:/dev/.urandom", \ - "-jar", \ - "edc-controlplane.jar"] diff --git a/edc-controlplane/pom.xml b/edc-controlplane/pom.xml index 27624b1ec..9ca158d85 100644 --- a/edc-controlplane/pom.xml +++ b/edc-controlplane/pom.xml @@ -25,7 +25,6 @@ pom - edc-controlplane-cosmosdb edc-controlplane-memory edc-controlplane-postgresql From 436d4e2a42994c2d9a35d31288d890517f5a7b69 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Tue, 7 Jun 2022 11:37:19 +0200 Subject: [PATCH 124/433] Remove aas-controller-extension (#187) --- .../edc-controlplane-memory/pom.xml | 4 - .../edc-controlplane-postgresql/pom.xml | 4 - edc-extensions/aas-controller/README.md | 3 - edc-extensions/aas-controller/pom.xml | 86 ----------- .../controlplane/ControlPlaneController.java | 135 ------------------ .../controlplane/ControlPlaneExtension.java | 46 ------ ...spaceconnector.spi.system.ServiceExtension | 14 -- edc-extensions/pom.xml | 1 - pom.xml | 11 -- 9 files changed, 304 deletions(-) delete mode 100644 edc-extensions/aas-controller/README.md delete mode 100644 edc-extensions/aas-controller/pom.xml delete mode 100644 edc-extensions/aas-controller/src/main/java/net/catenax/edc/controlplane/ControlPlaneController.java delete mode 100644 edc-extensions/aas-controller/src/main/java/net/catenax/edc/controlplane/ControlPlaneExtension.java delete mode 100644 edc-extensions/aas-controller/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension diff --git a/edc-controlplane/edc-controlplane-memory/pom.xml b/edc-controlplane/edc-controlplane-memory/pom.xml index 512e218fc..0e84554eb 100644 --- a/edc-controlplane/edc-controlplane-memory/pom.xml +++ b/edc-controlplane/edc-controlplane-memory/pom.xml @@ -78,10 +78,6 @@ - - net.catenax.edc.extensions - aas-controller - net.catenax.edc.extensions business-partner-validation diff --git a/edc-controlplane/edc-controlplane-postgresql/pom.xml b/edc-controlplane/edc-controlplane-postgresql/pom.xml index 4272ceb7c..d8f0be46d 100644 --- a/edc-controlplane/edc-controlplane-postgresql/pom.xml +++ b/edc-controlplane/edc-controlplane-postgresql/pom.xml @@ -79,10 +79,6 @@ - - net.catenax.edc.extensions - aas-controller - net.catenax.edc.extensions business-partner-validation diff --git a/edc-extensions/aas-controller/README.md b/edc-extensions/aas-controller/README.md deleted file mode 100644 index f32ad27e5..000000000 --- a/edc-extensions/aas-controller/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Asset Administration Shell Controller - -This EDC extension implements a custom controller for the 'Asset Administration Shell'-Wrapper. \ No newline at end of file diff --git a/edc-extensions/aas-controller/pom.xml b/edc-extensions/aas-controller/pom.xml deleted file mode 100644 index 737784f03..000000000 --- a/edc-extensions/aas-controller/pom.xml +++ /dev/null @@ -1,86 +0,0 @@ - - - - 4.0.0 - - - net.catenax.edc.extensions - edc-extensions - 0.0.4-SNAPSHOT - - - aas-controller - jar - - - - - src/main/resources - - **/* - - - - ../../ - META-INF - - NOTICE.md - LICENSE - - - - - - - org.apache.maven.plugins - maven-source-plugin - - - attach-sources - - jar - - - - - - - - - - - org.eclipse.dataspaceconnector - web-spi - - - org.eclipse.dataspaceconnector - transfer-spi - - - org.eclipse.dataspaceconnector - contract-spi - - - org.eclipse.dataspaceconnector - dataloading - - - - - jakarta.ws.rs - jakarta.ws.rs-api - - - \ No newline at end of file diff --git a/edc-extensions/aas-controller/src/main/java/net/catenax/edc/controlplane/ControlPlaneController.java b/edc-extensions/aas-controller/src/main/java/net/catenax/edc/controlplane/ControlPlaneController.java deleted file mode 100644 index f8f510e74..000000000 --- a/edc-extensions/aas-controller/src/main/java/net/catenax/edc/controlplane/ControlPlaneController.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 - * - * SPDX-License-Identifier: Apache-2.0 - * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation - * - */ - -package net.catenax.edc.controlplane; - -import jakarta.ws.rs.Consumes; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.POST; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.core.MediaType; -import java.util.Map; -import org.eclipse.dataspaceconnector.dataloading.AssetLoader; -import org.eclipse.dataspaceconnector.spi.contract.offer.store.ContractDefinitionStore; -import org.eclipse.dataspaceconnector.spi.monitor.Monitor; -import org.eclipse.dataspaceconnector.spi.transfer.store.TransferProcessStore; -import org.eclipse.dataspaceconnector.spi.types.domain.DataAddress; -import org.eclipse.dataspaceconnector.spi.types.domain.asset.Asset; -import org.eclipse.dataspaceconnector.spi.types.domain.contract.offer.ContractDefinition; -import org.eclipse.dataspaceconnector.spi.types.domain.transfer.TransferProcess; - -@Consumes({MediaType.APPLICATION_JSON}) -@Produces({MediaType.APPLICATION_JSON}) -@Path("/v1") -public class ControlPlaneController { - private final Monitor monitor; - private final AssetLoader assetLoader; - private final ContractDefinitionStore contractDefinitionStore; - private final TransferProcessStore transferProcessStore; - - public ControlPlaneController( - Monitor monitor, - AssetLoader assetLoader, - ContractDefinitionStore contractDefinitionStore, - TransferProcessStore transferProcessStore) { - this.monitor = monitor; - this.assetLoader = assetLoader; - this.contractDefinitionStore = contractDefinitionStore; - this.transferProcessStore = transferProcessStore; - } - - // TODO: most of these api will be replaced by data management api - @Path("/assets") - @POST - public String createAsset(AssetEntryDto assetEntry) { - var assetProperties = assetEntry.asset.properties; - var asset = Asset.Builder.newInstance().properties(assetProperties).build(); - - var dataAddressProperties = assetEntry.dataAddress.properties; - var dataAddress = DataAddress.Builder.newInstance().properties(dataAddressProperties).build(); - monitor.debug("Create asset: " + asset.getId()); - assetLoader.accept(asset, dataAddress); - return asset.getId(); - } - - @Path("/contractdefinitions") - @POST - public void createContractDefinition(ContractDefinition definition) { - monitor.debug("Create contract definition: " + definition.getId()); - contractDefinitionStore.save(definition); - } - - @Path("/transfers/{id}") - @GET - public TransferProcess getTransferProcess(@PathParam("id") String id) { - return transferProcessStore.find(id); - } - - private static class AssetDto { - - public AssetDto() {} - - Map properties; - - public Map getProperties() { - return properties; - } - - public void setProperties(Map properties) { - this.properties = properties; - } - } - - private static class DataAddressDto { - - public DataAddressDto() {} - - Map properties; - - public Map getProperties() { - return properties; - } - - public void setProperties(Map properties) { - this.properties = properties; - } - } - - private static class AssetEntryDto { - - public AssetEntryDto() {} - - private AssetDto asset; - - private DataAddressDto dataAddress; - - public AssetDto getAsset() { - return asset; - } - - public void setAsset(AssetDto asset) { - this.asset = asset; - } - - public DataAddressDto getDataAddress() { - return dataAddress; - } - - public void setDataAddress(DataAddressDto dataAddress) { - this.dataAddress = dataAddress; - } - } -} diff --git a/edc-extensions/aas-controller/src/main/java/net/catenax/edc/controlplane/ControlPlaneExtension.java b/edc-extensions/aas-controller/src/main/java/net/catenax/edc/controlplane/ControlPlaneExtension.java deleted file mode 100644 index 4e5791c26..000000000 --- a/edc-extensions/aas-controller/src/main/java/net/catenax/edc/controlplane/ControlPlaneExtension.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 - * - * SPDX-License-Identifier: Apache-2.0 - * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation - * - */ - -package net.catenax.edc.controlplane; - -import org.eclipse.dataspaceconnector.dataloading.AssetLoader; -import org.eclipse.dataspaceconnector.spi.WebService; -import org.eclipse.dataspaceconnector.spi.contract.offer.store.ContractDefinitionStore; -import org.eclipse.dataspaceconnector.spi.system.Inject; -import org.eclipse.dataspaceconnector.spi.system.ServiceExtension; -import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; -import org.eclipse.dataspaceconnector.spi.transfer.store.TransferProcessStore; - -public class ControlPlaneExtension implements ServiceExtension { - - @Inject private WebService webService; - - @Inject private AssetLoader assetLoader; - - @Inject private ContractDefinitionStore contractDefinitionStore; - - @Inject private TransferProcessStore transferProcessStore; - - @Override - public String name() { - return "Control Plane"; - } - - @Override - public void initialize(ServiceExtensionContext context) { - webService.registerResource( - new ControlPlaneController( - context.getMonitor(), assetLoader, contractDefinitionStore, transferProcessStore)); - } -} diff --git a/edc-extensions/aas-controller/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension b/edc-extensions/aas-controller/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension deleted file mode 100644 index 80375f38d..000000000 --- a/edc-extensions/aas-controller/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension +++ /dev/null @@ -1,14 +0,0 @@ -# -# Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH -# -# This program and the accompanying materials are made available under the -# terms of the Apache License, Version 2.0 which is available at -# https://www.apache.org/licenses/LICENSE-2.0 -# -# SPDX-License-Identifier: Apache-2.0 -# -# Contributors: -# Mercedes-Benz Tech Innovation GmbH - Initial ServiceExtension file -# -# -net.catenax.edc.controlplane.ControlPlaneExtension \ No newline at end of file diff --git a/edc-extensions/pom.xml b/edc-extensions/pom.xml index 55921c721..daa03d35e 100644 --- a/edc-extensions/pom.xml +++ b/edc-extensions/pom.xml @@ -26,7 +26,6 @@ pom - aas-controller business-partner-validation postgresql-migration diff --git a/pom.xml b/pom.xml index 3dd7716d6..dc3a534f1 100644 --- a/pom.xml +++ b/pom.xml @@ -54,7 +54,6 @@ 0.0.1-SNAPSHOT 1.2.2 - 3.1.0 42.3.6 8.5.12 @@ -232,11 +231,6 @@ - - net.catenax.edc.extensions - aas-controller - ${project.version} - net.catenax.edc.extensions business-partner-validation @@ -249,11 +243,6 @@ - - jakarta.ws.rs - jakarta.ws.rs-api - ${jakarta.ws.rs.api.version} - org.postgresql postgresql From d0acfa954cdd8d803e2324affd9632486da20e56 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Jun 2022 11:57:12 +0200 Subject: [PATCH 125/433] Bump spotless-maven-plugin from 2.22.5 to 2.22.6 (#190) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index dc3a534f1..813f9905c 100644 --- a/pom.xml +++ b/pom.xml @@ -44,7 +44,7 @@ 3.3.0 3.2.2 - 2.22.5 + 2.22.6 3.0.0 3.4.0 2.0.0 From 970a09d52808082a94ca0bb714bda4b66f702ac7 Mon Sep 17 00:00:00 2001 From: "Florian Rusch (ZF Friedrichshafen AG)" Date: Thu, 9 Jun 2022 08:45:24 +0200 Subject: [PATCH 126/433] Update README.md (#193) --- edc-dataplane/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/edc-dataplane/README.md b/edc-dataplane/README.md index 76cb68245..b1bcd48a0 100644 --- a/edc-dataplane/README.md +++ b/edc-dataplane/README.md @@ -26,7 +26,7 @@ Details regarding each configuration property can be found at the [documentary s | edc.vault.tenantid | X | 55555555-6666-7777-8888-999999999999 | | | edc.vault.name | X | my-vault-name | | | edc.vault.clientsecret | X | 34-chars-secret | | -| edc.controlplane.validation-endpoint | X | http://controlplane:8182/validation | | +| edc.controlplane.validation-endpoint | X | http://controlplane:8182/validation/token | | #### Example configuration.properties @@ -45,7 +45,7 @@ web.http.control.port=9999 web.http.control.path=/api/dataplane/control # Validation endpoint of controlplane -edc.controlplane.validation-endpoint=http://controlplane:8182/validation +edc.controlplane.validation-endpoint=http://controlplane:8182/validation/token # EDC hostname edc.hostname=localhost @@ -91,4 +91,4 @@ docker run \ -v ${LOGGING_PROPERTIES_FILE:-/dev/null}:/app/logging.properties \ -v ${OPENTELEMETRY_PROPERTIES_FILE:-/dev/null}:/app/opentelemetry.properties \ -i edc-dataplane:latest -``` \ No newline at end of file +``` From e4c1b89383ec52cf19cf4182e3c1d1fe944ed4f8 Mon Sep 17 00:00:00 2001 From: bcronin90 <90203222+bcronin90@users.noreply.github.com> Date: Thu, 9 Jun 2022 16:49:45 +0200 Subject: [PATCH 127/433] Add hashicorp vault extension only with Token Auth (#175) --- .github/dependabot.yml | 20 +- .github/workflows/build.yaml | 233 +++++++++++-- .github/workflows/publish-new-release.yml | 2 +- .github/workflows/trivy.yaml | 76 +++- NOTICE.md | 48 +-- README.md | 15 +- deployment/helm/edc-controlplane/README.md | 4 +- deployment/helm/edc-controlplane/values.yaml | 9 +- deployment/helm/edc-dataplane/README.md | 4 +- deployment/helm/edc-dataplane/values.yaml | 8 +- .../edc-controlplane-memory/pom.xml | 3 +- .../README.md | 178 ++++++++++ .../pom.xml | 327 ++++++++++++++++++ .../src/main/docker/Dockerfile | 37 ++ .../edc-controlplane-postgresql/pom.xml | 5 +- edc-controlplane/pom.xml | 7 +- .../{ => edc-dataplane-azure-vault}/README.md | 8 +- .../edc-dataplane-azure-vault/pom.xml | 228 ++++++++++++ .../src/main/docker/Dockerfile | 0 .../edc-dataplane-hashicorp-vault/README.md | 92 +++++ .../edc-dataplane-hashicorp-vault/pom.xml | 210 +++++++++++ .../src/main/docker/Dockerfile | 37 ++ edc-dataplane/pom.xml | 212 +----------- .../business-partner-validation/pom.xml | 7 +- edc-extensions/hashicorp-vault/README.md | 9 + edc-extensions/hashicorp-vault/pom.xml | 223 ++++++++++++ .../HashicorpCertificateResolver.java | 53 +++ .../edc/hashicorpvault/HashicorpVault.java | 52 +++ .../hashicorpvault/HashicorpVaultClient.java | 162 +++++++++ .../HashicorpVaultClientConfig.java | 29 ++ ...shicorpVaultCreateEntryRequestPayload.java | 47 +++ ...hicorpVaultCreateEntryResponsePayload.java | 33 ++ .../HashicorpVaultEntryMetadata.java | 40 +++ .../HashicorpVaultException.java | 28 ++ .../HashicorpVaultExtension.java | 111 ++++++ ...HashicorpVaultGetEntryResponsePayload.java | 48 +++ .../catenax/edc/hashicorpvault/PemUtil.java | 57 +++ ...taspaceconnector.spi.system.VaultExtension | 13 + .../hashicorpvault/AbstractHashicorpIT.java | 89 +++++ .../HashicorpCertificateResolverIT.java | 54 +++ .../HashicorpCertificateResolverTest.java | 52 +++ .../HashicorpVaultClientTest.java | 146 ++++++++ .../edc/hashicorpvault/HashicorpVaultIT.java | 89 +++++ .../hashicorpvault/HashicorpVaultTest.java | 90 +++++ .../X509CertificateTestUtil.java | 132 +++++++ .../src/test/resources/logback.xml | 23 ++ edc-extensions/pom.xml | 7 +- edc-extensions/postgresql-migration/pom.xml | 3 +- misc/license-mappings.xml | 6 + pom.xml | 101 +++++- 50 files changed, 3176 insertions(+), 291 deletions(-) create mode 100644 edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/README.md create mode 100644 edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/pom.xml create mode 100644 edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile rename edc-dataplane/{ => edc-dataplane-azure-vault}/README.md (93%) create mode 100644 edc-dataplane/edc-dataplane-azure-vault/pom.xml rename edc-dataplane/{ => edc-dataplane-azure-vault}/src/main/docker/Dockerfile (100%) create mode 100644 edc-dataplane/edc-dataplane-hashicorp-vault/README.md create mode 100644 edc-dataplane/edc-dataplane-hashicorp-vault/pom.xml create mode 100644 edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile create mode 100644 edc-extensions/hashicorp-vault/README.md create mode 100644 edc-extensions/hashicorp-vault/pom.xml create mode 100644 edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpCertificateResolver.java create mode 100644 edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVault.java create mode 100644 edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultClient.java create mode 100644 edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultClientConfig.java create mode 100644 edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultCreateEntryRequestPayload.java create mode 100644 edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultCreateEntryResponsePayload.java create mode 100644 edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultEntryMetadata.java create mode 100644 edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultException.java create mode 100644 edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultExtension.java create mode 100644 edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultGetEntryResponsePayload.java create mode 100644 edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/PemUtil.java create mode 100644 edc-extensions/hashicorp-vault/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.VaultExtension create mode 100644 edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/AbstractHashicorpIT.java create mode 100644 edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpCertificateResolverIT.java create mode 100644 edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpCertificateResolverTest.java create mode 100644 edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultClientTest.java create mode 100644 edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultIT.java create mode 100644 edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultTest.java create mode 100644 edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/X509CertificateTestUtil.java create mode 100644 edc-extensions/hashicorp-vault/src/test/resources/logback.xml diff --git a/.github/dependabot.yml b/.github/dependabot.yml index ff4a7e7a1..74c476660 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -22,7 +22,16 @@ updates: - package-ecosystem: "docker" target-branch: develop - directory: "edc-dataplane/src/main/docker" + directory: "edc-dataplane/edc-dataplane-azure-vault/src/main/docker" + labels: + - "dependabot" + - "docker" + schedule: + interval: "daily" + - + package-ecosystem: "docker" + target-branch: develop + directory: "edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker" labels: - "dependabot" - "docker" @@ -46,6 +55,15 @@ updates: - "docker" schedule: interval: "daily" + - + package-ecosystem: "docker" + target-branch: develop + directory: "edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker" + labels: + - "dependabot" + - "docker" + schedule: + interval: "daily" - package-ecosystem: "maven" target-branch: develop diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index c3925b7f8..49da6a393 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -33,11 +33,30 @@ jobs: [ ! -z "${{ secrets.ORG_VERACODE_API_KEY }}" ] && echo "::set-output name=ORG_VERACODE_API_KEY::true" exit 0 - ##################### - ### edc-dataplane ### - ##################### - build-edc-dataplane: - needs: [ secret-presence ] + verify-formatting: + runs-on: ubuntu-latest + steps: + - + name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - + name: Set up JDK 11 + uses: actions/setup-java@v3.3.0 + with: + java-version: '11' + distribution: 'adopt' + cache: 'maven' + - + name: Verify proper formatting + run: ./mvnw spotless:check + + ################################# + ### edc-dataplane-azure-vault ### + ################################# + build-edc-dataplane-azure-vault: + needs: [ secret-presence, verify-formatting ] runs-on: ubuntu-latest steps: # Set-Up @@ -68,19 +87,19 @@ jobs: working-directory: edc # Build - - name: Build edc-dataplane + name: Build edc-dataplane-azure-vault run: |- - ./mvnw -s settings.xml -B -pl .,edc-dataplane -am verify + ./mvnw -Pfailsafe -s settings.xml -B -pl .,edc-dataplane/edc-dataplane-azure-vault -am verify env: GITHUB_PACKAGE_USERNAME: ${{ github.actor }} GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} - - name: edc-dataplane Docker Metadata - id: edc_dataplane_meta + name: edc-dataplane-azure-vault Docker Metadata + id: edc_dataplane_azure_vault_meta uses: docker/metadata-action@v4 with: images: | - ghcr.io/${{ github.repository }}/edc-dataplane + ghcr.io/${{ github.repository }}/edc-dataplane-azure-vault tags: | type=ref,event=branch type=ref,event=pr @@ -88,18 +107,18 @@ jobs: type=match,pattern=\d.\d.\d type=sha - - name: Build edc-dataplane Docker Image + name: Build edc-dataplane-azure-vault Docker Image uses: docker/build-push-action@v3 with: context: . - file: edc-dataplane/src/main/docker/Dockerfile + file: edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile build-args: | - JAR=edc-dataplane/target/edc-dataplane.jar - LIB=edc-dataplane/target/lib + JAR=edc-dataplane/edc-dataplane-azure-vault/target/edc-dataplane-azure-vault.jar + LIB=edc-dataplane/edc-dataplane-azure-vault/target/lib push: | ${{ (needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' && 'true') || 'false' }} - tags: ${{ steps.edc_dataplane_meta.outputs.tags }} - labels: ${{ steps.edc_dataplane_meta.outputs.labels }} + tags: ${{ steps.edc_dataplane_azure_vault_meta.outputs.tags }} + labels: ${{ steps.edc_dataplane_azure_vault_meta.outputs.labels }} - name: Veracode Upload And Scan uses: veracode/veracode-uploadandscan-action@v1.0 @@ -111,18 +130,103 @@ jobs: refs/heads/main', github.ref) continue-on-error: true with: - appname: 'product-edc/edc-dataplane' + appname: 'product-edc/edc-dataplane-azure-vault' createprofile: true version: ${{ github.ref }}-${{ github.sha }} - filepath: edc-dataplane/target/edc-dataplane.jar + filepath: edc-dataplane/edc-dataplane-azure-vault/target/edc-dataplane-azure-vault.jar vid: ${{ secrets.ORG_VERACODE_API_ID }} vkey: ${{ secrets.ORG_VERACODE_API_KEY }} + ##################################### + ### edc-dataplane-hashicorp-vault ### + ##################################### + build-edc-dataplane-hashicorp-vault: + needs: [ secret-presence, verify-formatting ] + runs-on: ubuntu-latest + steps: + # Set-Up + - + name: Checkout + uses: actions/checkout@v3 + with: + submodules: recursive + - + name: Login to GitHub Container Registry + if: | + needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.CXNG_GHCR_PAT }} + - + name: Set up JDK 11 + uses: actions/setup-java@v3.3.0 + with: + java-version: '11' + distribution: 'adopt' + cache: 'maven' + - + name: Build edc with Gradle to get latest snapshots + run: ./gradlew publishToMavenLocal + working-directory: edc + # Build + - + name: Build edc-dataplane-hashicorp-vault + run: |- + ./mvnw -Pfailsafe -s settings.xml -B -pl .,edc-dataplane/edc-dataplane-hashicorp-vault -am verify + env: + GITHUB_PACKAGE_USERNAME: ${{ github.actor }} + GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} + - + name: edc-dataplane-hashicorp-vault Docker Metadata + id: edc_dataplane_hashicorp_vault_metadata + uses: docker/metadata-action@v4 + with: + images: | + ghcr.io/${{ github.repository }}/edc-dataplane-hashicorp-vault + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{raw}} + type=match,pattern=\d.\d.\d + type=sha + - + name: Build edc-dataplane-hashicorp-vault Docker Image + uses: docker/build-push-action@v3 + with: + context: . + file: edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile + build-args: | + JAR=edc-dataplane/edc-dataplane-hashicorp-vault/target/edc-dataplane-hashicorp-vault.jar + LIB=edc-dataplane/edc-dataplane-hashicorp-vault/target/lib + push: | + ${{ (needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' && 'true') || 'false' }} + tags: ${{ steps.edc_dataplane_hashicorp_vault_metadata.outputs.tags }} + labels: ${{ steps.edc_dataplane_hashicorp_vault_metadata.outputs.labels }} + - + name: Veracode Upload And Scan + uses: veracode/veracode-uploadandscan-action@v1.0 + if: | + needs.secret-presence.outputs.ORG_VERACODE_API_ID && needs.secret-presence.outputs.ORG_VERACODE_API_KEY && contains(' + refs/heads/develop + refs/heads/release/ + refs/tags/ + refs/heads/main', github.ref) + continue-on-error: true + with: + appname: 'product-edc/edc-dataplane-hashicorp-vault' + createprofile: true + version: ${{ github.ref }}-${{ github.sha }} + filepath: edc-dataplane/edc-dataplane-hashicorp-vault/target/edc-dataplane-hashicorp-vault.jar + vid: ${{ secrets.ORG_VERACODE_API_ID }} + vkey: ${{ secrets.ORG_VERACODE_API_KEY }} + ############################### ### edc-controlplane-memory ### ############################### build-edc-controlplane-memory: - needs: [ secret-presence ] + needs: [ secret-presence, verify-formatting ] runs-on: ubuntu-latest steps: # Set-Up @@ -155,7 +259,7 @@ jobs: - name: Build edc-controlplane-memory run: |- - ./mvnw -s settings.xml -B -pl .,edc-controlplane/edc-controlplane-memory -am verify + ./mvnw -Pfailsafe -s settings.xml -B -pl .,edc-controlplane/edc-controlplane-memory -am verify env: GITHUB_PACKAGE_USERNAME: ${{ github.actor }} GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} @@ -207,7 +311,7 @@ jobs: ### edc-controlplane-postgresql ### ################################### build-edc-controlplane-postgresql: - needs: [ secret-presence ] + needs: [ secret-presence, verify-formatting ] runs-on: ubuntu-latest steps: # Set-Up @@ -240,7 +344,7 @@ jobs: - name: Build edc-controlplane-postgresql run: |- - ./mvnw -s settings.xml -B -pl .,edc-controlplane/edc-controlplane-postgresql -am verify + ./mvnw -Pfailsafe -s settings.xml -B -pl .,edc-controlplane/edc-controlplane-postgresql -am verify env: GITHUB_PACKAGE_USERNAME: ${{ github.actor }} GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} @@ -287,3 +391,88 @@ jobs: version: ${{ github.ref_name }}-${{ github.sha }} vid: ${{ secrets.ORG_VERACODE_API_ID }} vkey: ${{ secrets.ORG_VERACODE_API_KEY }} + + ################################################### + ### edc-controlplane-postgresql-hashicorp-vault ### + ################################################### + build-edc-controlplane-postgresql-hashicorp-vault: + needs: [ secret-presence, verify-formatting ] + runs-on: ubuntu-latest + steps: + # Set-Up + - + name: Checkout + uses: actions/checkout@v3 + with: + submodules: recursive + - + name: Login to Github Packages + if: | + needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.CXNG_GHCR_PAT }} + - + name: Set up JDK 11 + uses: actions/setup-java@v3.3.0 + with: + java-version: '11' + distribution: 'adopt' + cache: 'maven' + - + name: Build edc with Gradle to get latest snapshots + run: ./gradlew publishToMavenLocal + working-directory: edc + # Build + - + name: Build edc-controlplane-postgresql-hashicorp-vault + run: |- + ./mvnw -Pfailsafe -s settings.xml -B -pl .,edc-controlplane/edc-controlplane-postgresql-hashicorp-vault -am verify + env: + GITHUB_PACKAGE_USERNAME: ${{ github.actor }} + GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} + - + name: edc-controlplane-postgresql-hashicorp-vault Docker Metadata + id: edc_controlplane_postgresql_hashicorp_vault_meta + uses: docker/metadata-action@v4 + with: + images: | + ghcr.io/${{ github.repository }}/edc-controlplane-postgresql-hashicorp-vault + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{raw}} + type=match,pattern=\d.\d.\d + type=sha + - + name: Build edc-controlplane-postgresql-hashicorp-vault Docker Image + uses: docker/build-push-action@v3 + with: + context: . + file: edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile + build-args: | + JAR=edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/target/edc-controlplane-postgresql-hashicorp-vault.jar + LIB=edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/target/lib + push: | + ${{ (needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' && 'true') || 'false' }} + tags: ${{ steps.edc_controlplane_postgresql_hashicorp_vault_meta.outputs.tags }} + labels: ${{ steps.edc_controlplane_postgresql_hashicorp_vault_meta.outputs.labels }} + - + name: Veracode Upload And Scan + uses: veracode/veracode-uploadandscan-action@v1.0 + if: | + needs.secret-presence.outputs.ORG_VERACODE_API_ID && needs.secret-presence.outputs.ORG_VERACODE_API_KEY && contains(' + refs/heads/develop + refs/heads/release/ + refs/tags/ + refs/heads/main', github.ref) + continue-on-error: true + with: + appname: 'product-edc/edc-controlplane-postgresql-hashicorp-vault' + createprofile: true + filepath: edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/target/edc-controlplane-postgresql-hashicorp-vault.jar + version: ${{ github.ref_name }}-${{ github.sha }} + vid: ${{ secrets.ORG_VERACODE_API_ID }} + vkey: ${{ secrets.ORG_VERACODE_API_KEY }} diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index 974e14454..cb4997e3f 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -84,7 +84,7 @@ jobs: name: Deploy run: |- ./mvnw -s settings.xml \ - -pl '!edc-controlplane,!edc-controlplane/edc-controlplane-memory,!edc-controlplane/edc-controlplane-postgresql,!edc-dataplane' \ + -Pdelombok -pl '!edc-controlplane,!edc-controlplane/edc-controlplane-memory,!edc-controlplane/edc-controlplane-postgresql,!edc-controlplane/edc-controlplane-postgresql-hashicorp-vault,!edc-dataplane/edc-dataplane-azure-vault,!edc-dataplane/edc-dataplane-hashicorp-vault' \ -DaltReleaseDeploymentRepository=github::https://maven.pkg.github.com/${{ github.repository }} \ -Dmaven.test.skip=true -B package deploy:deploy env: diff --git a/.github/workflows/trivy.yaml b/.github/workflows/trivy.yaml index 7c722c4a8..d5fdb5cfa 100644 --- a/.github/workflows/trivy.yaml +++ b/.github/workflows/trivy.yaml @@ -117,10 +117,10 @@ jobs: with: sarif_file: "trivy-results-edc-controlplane-postgresql.sarif" - ##################### - ### edc-dataplane ### - ##################### - analyze-edc-dataplane: + ################################################### + ### edc-controlplane-postgresql-hashicorp-vault ### + ################################################### + analyze-edc-controlplane-postgresql-hashicorp-vault: needs: [ git-sha7 ] runs-on: ubuntu-latest permissions: @@ -136,9 +136,9 @@ jobs: if: always() uses: aquasecurity/trivy-action@master with: - image-ref: "ghcr.io/${{ github.repository }}/edc-dataplane:sha-${{ needs.git-sha7.outputs.value }}" + image-ref: "ghcr.io/${{ github.repository }}/edc-controlplane-postgresql-hashicorp-vault:sha-${{ needs.git-sha7.outputs.value }}" format: "sarif" - output: "trivy-results-edc-dataplane.sarif" + output: "trivy-results-edc-controlplane-postgresql-hashicorp-vault.sarif" exit-code: "1" severity: "CRITICAL,HIGH" - @@ -146,4 +146,66 @@ jobs: if: always() uses: github/codeql-action/upload-sarif@v2 with: - sarif_file: "trivy-results-edc-dataplane.sarif" + sarif_file: "trivy-results-edc-controlplane-postgresql-hashicorp-vault.sarif" + + ################################# + ### edc-dataplane-azure-vault ### + ################################# + analyze-edc-dataplane-azure-vault: + needs: [ git-sha7 ] + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + steps: + - + name: Checkout repository + uses: actions/checkout@v2 + - + name: Run Trivy vulnerability scanner + if: always() + uses: aquasecurity/trivy-action@master + with: + image-ref: "ghcr.io/${{ github.repository }}/edc-dataplane-azure-vault:sha-${{ needs.git-sha7.outputs.value }}" + format: "sarif" + output: "trivy-results-edc-dataplane-azure-vault.sarif" + exit-code: "1" + severity: "CRITICAL,HIGH" + - + name: Upload Trivy scan results to GitHub Security tab + if: always() + uses: github/codeql-action/upload-sarif@v2 + with: + sarif_file: "trivy-results-edc-dataplane-azure-vault.sarif" + + ##################################### + ### edc-dataplane-hashicorp-vault ### + ##################################### + analyze-edc-dataplane-hashicorp-vault: + needs: [ git-sha7 ] + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + steps: + - + name: Checkout repository + uses: actions/checkout@v2 + - + name: Run Trivy vulnerability scanner + if: always() + uses: aquasecurity/trivy-action@master + with: + image-ref: "ghcr.io/${{ github.repository }}/edc-dataplane-hashicorp-vault:sha-${{ needs.git-sha7.outputs.value }}" + format: "sarif" + output: "trivy-results-edc-dataplane-hashicorp-vault.sarif" + exit-code: "1" + severity: "CRITICAL,HIGH" + - + name: Upload Trivy scan results to GitHub Security tab + if: always() + uses: github/codeql-action/upload-sarif@v2 + with: + sarif_file: "trivy-results-edc-dataplane-hashicorp-vault.sarif" diff --git a/NOTICE.md b/NOTICE.md index 2886d7b03..25d95db3f 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -18,9 +18,8 @@ The project maintains the following source code repositoriy: ## Third-party Content (Overarching All Modules) -* aas-controller under Apache License 2.0 * aopalliance version 1.0 repackaged as a module under EPL 2.0 or GPL2 w/ CPE -* Apache Commons Codec under Apache License, Version 2.0 +* Apache Commons Compress under Apache License, Version 2.0 * Apache Commons Lang under Apache License, Version 2.0 * Apache Commons Pool under Apache License, Version 2.0 * apache-commons-pool-sql under Apache License, Version 2.0 @@ -33,7 +32,6 @@ The project maintains the following source code repositoriy: * asm-tree under BSD-3-Clause * asset-api under Apache License, Version 2.0 * asset-index-sql under Apache License, Version 2.0 -* assetindex-cosmos under Apache License, Version 2.0 * auth-spi under Apache License, Version 2.0 * auth-tokenbased under Apache License, Version 2.0 * azure-vault under Apache License, Version 2.0 @@ -42,6 +40,8 @@ The project maintains the following source code repositoriy: * Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs under Bouncy Castle Licence * Bouncy Castle Provider under Bouncy Castle Licence * business-partner-validation under Apache License 2.0 +* Byte Buddy (without dependencies) under Apache License, Version 2.0 +* Byte Buddy agent under Apache License, Version 2.0 * catalog-api under Apache License, Version 2.0 * catalog-spi under Apache License, Version 2.0 * Checker Qual under The MIT License @@ -49,8 +49,6 @@ The project maintains the following source code repositoriy: * common-sql under Apache License, Version 2.0 * common-util under Apache License, Version 2.0 * contract under Apache License, Version 2.0 -* contract-definition-store-cosmos under Apache License, Version 2.0 -* contract-negotiation-store-cosmos under Apache License, Version 2.0 * contract-spi under Apache License, Version 2.0 * contractagreement-api under Apache License, Version 2.0 * contractdefinition-api under Apache License, Version 2.0 @@ -64,7 +62,6 @@ The project maintains the following source code repositoriy: * core-defaults under Apache License, Version 2.0 * core-micrometer under Apache License, Version 2.0 * core-spi under Apache License, Version 2.0 -* cosmos-common under Apache License, Version 2.0 * data-management-api under Apache License, Version 2.0 * data-plane-api under Apache License, Version 2.0 * data-plane-framework under Apache License, Version 2.0 @@ -73,15 +70,24 @@ The project maintains the following source code repositoriy: * data-plane-transfer-spi under Apache License, Version 2.0 * data-plane-transfer-sync under Apache License, Version 2.0 * dataloading under Apache License, Version 2.0 +* docker-java-api under The Apache Software License, Version 2.0 +* docker-java-transport under The Apache Software License, Version 2.0 +* docker-java-transport-zerodep under The Apache Software License, Version 2.0 +* Duct Tape under MIT * edc-controlplane under Apache License 2.0 -* edc-controlplane-cosmosdb under Apache License 2.0 * edc-controlplane-memory under Apache License 2.0 * edc-controlplane-postgresql under Apache License 2.0 +* edc-controlplane-postgresql-hashicorp-vault under Apache License 2.0 * edc-dataplane under Apache License 2.0 +* edc-dataplane-azure-vault under Apache License 2.0 +* edc-dataplane-hashicorp-vault under Apache License 2.0 * edc-extensions under Apache License 2.0 * Failsafe under Apache License, Version 2.0 * filesystem-configuration under Apache License, Version 2.0 * flyway-core under Apache License, Version 2.0 +* Hamcrest under BSD License 3 +* Hamcrest Core under New BSD License +* hashicorp-vault under Apache License 2.0 * HdrHistogram under Public Domain, per Creative Commons CC0 or BSD-2-Clause * HK2 API module under EPL 2.0 or GPL2 w/ CPE * HK2 Implementation Utilities under EPL 2.0 or GPL2 w/ CPE @@ -89,7 +95,6 @@ The project maintains the following source code repositoriy: * HTTP functionality for the Reactor Netty library under The Apache Software License, Version 2.0 * http-receiver under Apache License, Version 2.0 * iam-daps under Apache License, Version 2.0 -* iam-mock under Apache License, Version 2.0 * ids-api-configuration under Apache License, Version 2.0 * ids-api-multipart-dispatcher-v1 under Apache License, Version 2.0 * ids-api-multipart-endpoint-v1 under Apache License, Version 2.0 @@ -99,7 +104,6 @@ The project maintains the following source code repositoriy: * ids-token-validation under Apache License, Version 2.0 * IntelliJ IDEA Annotations under The Apache Software License, Version 2.0 * Jackson datatype: JSR310 under The Apache Software License, Version 2.0 -* Jackson module: Afterburner under The Apache Software License, Version 2.0 * Jackson module: Jakarta XML Bind Annotations (jakarta.xml.bind) under The Apache Software License, Version 2.0 * Jackson-annotations under The Apache Software License, Version 2.0 * Jackson-core under The Apache Software License, Version 2.0 @@ -110,11 +114,10 @@ The project maintains the following source code repositoriy: * Jakarta Annotations API under EPL 2.0 or GPL2 w/ CPE * Jakarta Bean Validation API under Apache License 2.0 * Jakarta Dependency Injection under The Apache Software License, Version 2.0 -* Jakarta RESTful WS API under EPL-2.0 or GPL-2.0-with-classpath-exception * Jakarta XML Binding API under Eclipse Distribution License - v 1.0 * jakarta.transaction API under EPL 2.0 or GPL2 w/ CPE +* jakarta.ws.rs-api under EPL-2.0 or GPL-2.0-with-classpath-exception * java under Apache License, Version 2.0 -* java jwt under The MIT License (MIT) * Java Native Access under LGPL, version 2.1 or Apache License v2.0 * Java Native Access Platform under LGPL, version 2.1 or Apache License v2.0 * Javassist under MPL 1.1 or LGPL 2.1 or Apache License 2.0 @@ -156,27 +159,27 @@ The project maintains the following source code repositoriy: * Jetty :: XML utilities under Eclipse Public License - Version 2.0 or Apache Software License - Version 2.0 * jetty-micrometer under Apache License, Version 2.0 * JSON Small and Fast Parser under The Apache Software License, Version 2.0 +* JUL to SLF4J bridge under MIT License +* JUnit under Eclipse Public License 1.0 +* JUnit Jupiter (Aggregator) under Eclipse Public License v2.0 * JUnit Jupiter API under Eclipse Public License v2.0 * JUnit Jupiter Engine under Eclipse Public License v2.0 * JUnit Jupiter Params under Eclipse Public License v2.0 * JUnit Platform Commons under Eclipse Public License v2.0 * JUnit Platform Engine API under Eclipse Public License v2.0 -* JUnit Platform Launcher under Eclipse Public License v2.0 -* JUnit Platform Suite (Aggregator) under Eclipse Public License v2.0 -* JUnit Platform Suite API under Eclipse Public License v2.0 -* JUnit Platform Suite Commons under Eclipse Public License v2.0 -* JUnit Platform Suite Engine under Eclipse Public License v2.0 +* junit-extension under Apache License, Version 2.0 * LatencyUtils under Public Domain, per Creative Commons CC0 * lease-sql under Apache License, Version 2.0 -* Metrics Core under Apache License 2.0 +* Logback Classic Module under Eclipse Public License - v 1.0 or GNU Lesser General Public License +* Logback Core Module under Eclipse Public License - v 1.0 or GNU Lesser General Public License * micrometer-core under The Apache Software License, Version 2.0 * Microsoft Azure client library for Identity under The MIT License (MIT) * Microsoft Azure client library for KeyVault Secrets under The MIT License (MIT) * Microsoft Azure Java Core Library under The MIT License (MIT) * Microsoft Azure Netty HTTP Client Library under The MIT License (MIT) -* Microsoft Azure SDK for SQL API of Azure Cosmos DB Service under The MIT License (MIT) * MIME streaming extension under Eclipse Distribution License - v 1.0 -* Mockito under The MIT License +* mockito-core under The MIT License +* mockito-inline under The MIT License * msal4j under MIT License * msal4j-persistence-extension under MIT License * Netty/Buffer under Apache License, Version 2.0 @@ -207,6 +210,7 @@ The project maintains the following source code repositoriy: * OAuth 2.0 SDK with OpenID Connect extensions under Apache License, version 2.0 * oauth2-core under Apache License, Version 2.0 * oauth2-spi under Apache License, Version 2.0 +* Objenesis under Apache License, Version 2.0 * observability-api under Apache License, Version 2.0 * okhttp under The Apache Software License, Version 2.0 * Okio under The Apache Software License, Version 2.0 @@ -219,12 +223,12 @@ The project maintains the following source code repositoriy: * policy-engine under Apache License, Version 2.0 * policy-evaluator under Apache License, Version 2.0 * policy-spi under Apache License, Version 2.0 -* policy-store-cosmos under Apache License, Version 2.0 * policy-store-sql under Apache License, Version 2.0 * policydefinition-api under Apache License, Version 2.0 * PostgreSQL JDBC Driver under BSD-2-Clause * postgresql-migration under Apache License 2.0 * product-edc-parent under Apache License 2.0 +* Project Lombok under The MIT License * reactive-streams under CC0 * ServiceLocator Default Implementation under EPL 2.0 or GPL2 w/ CPE * SLF4J API Module under MIT License @@ -237,13 +241,15 @@ The project maintains the following source code repositoriy: * swagger-integration-jakarta under Apache License 2.0 * swagger-jaxrs2-jakarta under Apache License 2.0 * swagger-models-jakarta under Apache License 2.0 +* Testcontainers :: JUnit Jupiter Extension under MIT +* Testcontainers :: Vault under MIT +* Testcontainers Core under MIT * token-generation-lib under Apache License, Version 2.0 * token-validation-lib under Apache License, Version 2.0 * transaction-datasource-spi under Apache License, Version 2.0 * transaction-local under Apache License, Version 2.0 * transaction-spi under Apache License, Version 2.0 * transfer under Apache License, Version 2.0 -* transfer-process-store-cosmos under Apache License, Version 2.0 * transfer-process-store-sql under Apache License, Version 2.0 * transfer-spi under Apache License, Version 2.0 * transferprocess-api under Apache License, Version 2.0 diff --git a/README.md b/README.md index f7d2918d3..41728e38f 100644 --- a/README.md +++ b/README.md @@ -10,12 +10,21 @@ The Data-Plane does the heavy lifting of transferring and receiving data streams Depending on your environment there are different derivatives of the control-plane prepared: -* [edc-controlplane-memory](edc-controlplane/edc-controlplane-memory) -* [edc-controlplane-postgresql](edc-controlplane/edc-controlplane-postgresql) +* [edc-controlplane-memory](edc-controlplane/edc-controlplane-memory) with dependency onto + * [Azure Key Vault](https://azure.microsoft.com/en-us/services/key-vault/#product-overview) +* [edc-controlplane-postgresql](edc-controlplane/edc-controlplane-postgresql) with dependency onto + * [Azure Key Vault](https://azure.microsoft.com/en-us/services/key-vault/#product-overview) + * [PostgreSQL 8.2 or newer](https://www.postgresql.org/) +* [edc-controlplane-postgresql-hashicorp-vault](edc-controlplane/edc-controlplane-postgresql-hashicorp-vault) with dependency onto + * [Hashicorp Vault](https://www.vaultproject.io/) + * [PostgreSQL 8.2 or newer](https://www.postgresql.org/) Derivatives of the Data-Plane can be found here -* [edc-dataplane](edc-dataplane) +* [edc-dataplane-azure-vault](edc-dataplane/edc-dataplane-azure-vault) with dependency onto + * [Azure Key Vault](https://azure.microsoft.com/en-us/services/key-vault/#product-overview) +* [edc-dataplane-hashicorp-vault](edc-dataplane/edc-dataplane-hashicorp-vault) with dependency onto + * [Hashicorp Vault](https://www.vaultproject.io/) ## Prerequisites diff --git a/deployment/helm/edc-controlplane/README.md b/deployment/helm/edc-controlplane/README.md index a9f0bdc6d..9d60d16c7 100644 --- a/deployment/helm/edc-controlplane/README.md +++ b/deployment/helm/edc-controlplane/README.md @@ -23,7 +23,7 @@ $ helm install my-release catenax-ng-product-edc/edc-controlplane --version 0.0. | autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | | autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | | autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | -| configuration.properties | string | `"# edc.api.auth.key=\n# edc.api.control.auth.apikey.key=\n# edc.api.control.auth.apikey.value=\n# edc.atomikos.checkpoint.interval=\n# edc.atomikos.directory=\n# edc.atomikos.logging=\n# edc.atomikos.threaded2pc=\n# edc.atomikos.timeout=\n# edc.aws.access.key=\n# edc.aws.provision.retry.retries.max=\n# edc.aws.provision.role.duration.session.max=\n# edc.aws.secret.access.key=\n# edc.blobstore.endpoint=\n# edc.controlplane.validation-endpoint=\n# edc.core.retry.backoff.max=\n# edc.core.retry.backoff.min=\n# edc.core.retry.retries.max=\n# edc.core.system.health.check.liveness-period=\n# edc.core.system.health.check.readiness-period=\n# edc.core.system.health.check.startup-period=\n# edc.core.system.health.check.threadpool-size=\n# edc.dataplane.queue.capacity=\n# edc.dataplane.wait=\n# edc.dataplane.workers=\n# edc.datasource.asset.name=\"default\"\n# edc.datasource.contractdefinition.name=\"default\"\n# edc.datasource.contractnegotiation.name=\"default\"\n# edc.datasource.policy.name=\"default\"\n# edc.datasource.transferprocess.name=\"default\"\n# edc.datasource.default.pool.maxIdleConnections=\n# edc.datasource.default.pool.maxTotalConnections=\n# edc.datasource.default.pool.minIdleConnections=\n# edc.datasource.default.pool.testConnectionOnBorrow=\n# edc.datasource.default.pool.testConnectionOnCreate=\n# edc.datasource.default.pool.testConnectionOnReturn=\n# edc.datasource.default.pool.testConnectionWhileIdle=\n# edc.datasource.default.pool.testQuery=\n# edc.datasource.default.url=\n# edc.datasource.default.user=\n# edc.datasource.default.password=\n# edc.dpf.selector.url=\n# edc.events.topic.endpoint=\n# edc.events.topic.name=\n# edc.fs.config=\n# edc.hostname=\n# edc.identity.did.url=\n# edc.ids.catalog.id=\n# edc.ids.curator=\n# edc.ids.description=\n# edc.ids.endpoint=\n# edc.ids.id=\n# edc.ids.maintainer=\n# edc.ids.security.profile=\n# edc.ids.title=\n# edc.ids.validation.referringconnector=\n# edc.ion.crawler.did-type=\n# edc.ion.crawler.interval-minutes=\n# edc.ion.crawler.ion.url=\n# edc.metrics.enabled=\n# edc.metrics.executor.enabled=\n# edc.metrics.jersey.enabled=\n# edc.metrics.jetty.enabled=\n# edc.metrics.okhttp.enabled=\n# edc.metrics.system.enabled=\n# edc.negotiation.consumer.state-machine.batch-size=\n# edc.negotiation.provider.state-machine.batch-size=\n# edc.oauth.client.id=\n# edc.oauth.private.key.alias=\n# edc.oauth.provider.audience=\n# edc.oauth.provider.jwks.refresh=\n# edc.oauth.provider.jwks.url=\n# edc.oauth.public.key.alias=\n# edc.oauth.token.url=\n# edc.oauth.validation.nbf.leeway=\n# edc.receiver.http.auth-code=\n# edc.receiver.http.auth-key=\n# edc.receiver.http.endpoint=\n# edc.transfer.proxy.endpoint=\n# edc.transfer.dataplane.sync.token.validity=\n# edc.transfer.proxy.token.signer.privatekey.alias=\n# edc.transfer.functions.check.endpoint=\n# edc.transfer.functions.enabled.protocols=\n# edc.transfer.functions.transfer.endpoint=\n# edc.transfer-process-store.database.name=\n# edc.transfer.state-machine.batch-size=\n# edc.vault=\n# edc.vault.certificate=\n# edc.vault.clientid=\n# edc.vault.clientsecret=\n# edc.vault.name=\n# edc.vault.tenantid=\n# edc.webdid.doh.url=\n# edc.web.rest.cors.enabled=\n# edc.web.rest.cors.headers=\n# edc.web.rest.cors.methods=\n# edc.web.rest.cors.origins="` | EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) | +| configuration.properties | string | `"# edc.api.auth.key=\n# edc.api.control.auth.apikey.key=\n# edc.api.control.auth.apikey.value=\n# edc.atomikos.checkpoint.interval=\n# edc.atomikos.directory=\n# edc.atomikos.logging=\n# edc.atomikos.threaded2pc=\n# edc.atomikos.timeout=\n# edc.aws.access.key=\n# edc.aws.provision.retry.retries.max=\n# edc.aws.provision.role.duration.session.max=\n# edc.aws.secret.access.key=\n# edc.blobstore.endpoint=\n# edc.controlplane.validation-endpoint=\n# edc.core.retry.backoff.max=\n# edc.core.retry.backoff.min=\n# edc.core.retry.retries.max=\n# edc.core.system.health.check.liveness-period=\n# edc.core.system.health.check.readiness-period=\n# edc.core.system.health.check.startup-period=\n# edc.core.system.health.check.threadpool-size=\n# edc.dataplane.queue.capacity=\n# edc.dataplane.wait=\n# edc.dataplane.workers=\n# edc.datasource.asset.name=\"default\"\n# edc.datasource.contractdefinition.name=\"default\"\n# edc.datasource.contractnegotiation.name=\"default\"\n# edc.datasource.policy.name=\"default\"\n# edc.datasource.transferprocess.name=\"default\"\n# edc.datasource.default.pool.maxIdleConnections=\n# edc.datasource.default.pool.maxTotalConnections=\n# edc.datasource.default.pool.minIdleConnections=\n# edc.datasource.default.pool.testConnectionOnBorrow=\n# edc.datasource.default.pool.testConnectionOnCreate=\n# edc.datasource.default.pool.testConnectionOnReturn=\n# edc.datasource.default.pool.testConnectionWhileIdle=\n# edc.datasource.default.pool.testQuery=\n# edc.datasource.default.url=\n# edc.datasource.default.user=\n# edc.datasource.default.password=\n# edc.dpf.selector.url=\n# edc.events.topic.endpoint=\n# edc.events.topic.name=\n# edc.fs.config=\n# edc.hostname=\n# edc.identity.did.url=\n# edc.ids.catalog.id=\n# edc.ids.curator=\n# edc.ids.description=\n# edc.ids.endpoint=\n# edc.ids.id=\n# edc.ids.maintainer=\n# edc.ids.security.profile=\n# edc.ids.title=\n# edc.ids.validation.referringconnector=\n# edc.ion.crawler.did-type=\n# edc.ion.crawler.interval-minutes=\n# edc.ion.crawler.ion.url=\n# edc.metrics.enabled=\n# edc.metrics.executor.enabled=\n# edc.metrics.jersey.enabled=\n# edc.metrics.jetty.enabled=\n# edc.metrics.okhttp.enabled=\n# edc.metrics.system.enabled=\n# edc.negotiation.consumer.state-machine.batch-size=\n# edc.negotiation.provider.state-machine.batch-size=\n# edc.oauth.client.id=\n# edc.oauth.private.key.alias=\n# edc.oauth.provider.audience=\n# edc.oauth.provider.jwks.refresh=\n# edc.oauth.provider.jwks.url=\n# edc.oauth.public.key.alias=\n# edc.oauth.token.url=\n# edc.oauth.validation.nbf.leeway=\n# edc.receiver.http.auth-code=\n# edc.receiver.http.auth-key=\n# edc.receiver.http.endpoint=\n# edc.transfer.proxy.endpoint=\n# edc.transfer.dataplane.sync.token.validity=\n# edc.transfer.proxy.token.signer.privatekey.alias=\n# edc.transfer.functions.check.endpoint=\n# edc.transfer.functions.enabled.protocols=\n# edc.transfer.functions.transfer.endpoint=\n# edc.transfer-process-store.database.name=\n# edc.transfer.state-machine.batch-size=\n# edc.vault=\n# edc.vault.certificate=\n# edc.vault.clientid=\n# edc.vault.clientsecret=\n# edc.vault.name=\n# edc.vault.tenantid=\n# edc.vault.url=\n# edc.vault.token=\n# edc.vault.timeout.seconds=\n# edc.webdid.doh.url=\n# edc.web.rest.cors.enabled=\n# edc.web.rest.cors.headers=\n# edc.web.rest.cors.methods=\n# edc.web.rest.cors.origins="` | EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) | | edc.endpoints.control.path | string | `"/api/controlplane/control"` | The path mapping the "control" api is going to be exposed at | | edc.endpoints.control.port | string | `"9999"` | The network port, which the "control" api is going to be exposed by the container, pod and service | | edc.endpoints.data.path | string | `"/data"` | The path mapping the "data" management api is going to be exposed at | @@ -39,7 +39,7 @@ $ helm install my-release catenax-ng-product-edc/edc-controlplane --version 0.0. | env | object | `{}` | Container environment variables e.g. for configuring [JAVA_TOOL_OPTIONS](https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/envvars002.html) Ex.: JAVA_TOOL_OPTIONS: > -Dhttp.proxyHost=proxy -Dhttp.proxyPort=80 -Dhttp.nonProxyHosts="localhost|127.*|[::1]" -Dhttps.proxyHost=proxy -Dhttps.proxyPort=443 | | fullnameOverride | string | `""` | Overrides the releases full name | | image.pullPolicy | string | `"IfNotPresent"` | [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use | -| image.repository | string | `"ghcr.io/catenax-ng/product-edc/edc-controlplane-memory"` | Which derivate of the edc controlplane to use. One of: [ghcr.io/catenax-ng/product-edc/edc-controlplane-memory, ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql] | +| image.repository | string | `"ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql-hashicorp-vault"` | Which derivate of the edc control-plane to use. One of: [ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql-hashicorp-vault, ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql, ghcr.io/catenax-ng/product-edc/edc-controlplane-memory] | | image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion. | | imagePullSecret.dockerconfigjson | string | `""` | Image pull secret to create to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) Note: This value needs to adhere to the [(base64 encoded) .dockerconfigjson format](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#registry-secret-existing-credentials). Furthermore, if 'imagePullSecret.dockerconfigjson' is defined, it takes precedence over 'imagePullSecrets'. | | imagePullSecrets | list | `[]` | Existing image pull secret to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) | diff --git a/deployment/helm/edc-controlplane/values.yaml b/deployment/helm/edc-controlplane/values.yaml index 3b709fd69..778549497 100644 --- a/deployment/helm/edc-controlplane/values.yaml +++ b/deployment/helm/edc-controlplane/values.yaml @@ -8,9 +8,9 @@ replicaCount: 1 image: - # -- Which derivate of the edc controlplane to use. - # One of: [ghcr.io/catenax-ng/product-edc/edc-controlplane-memory, ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql] - repository: ghcr.io/catenax-ng/product-edc/edc-controlplane-memory + # -- Which derivate of the edc control-plane to use. + # One of: [ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql-hashicorp-vault, ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql, ghcr.io/catenax-ng/product-edc/edc-controlplane-memory] + repository: ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql-hashicorp-vault # -- [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use pullPolicy: IfNotPresent # -- Overrides the image tag whose default is the chart appVersion. @@ -328,6 +328,9 @@ configuration: # edc.vault.clientsecret= # edc.vault.name= # edc.vault.tenantid= + # edc.vault.hashicorp.url= + # edc.vault.hashicorp.token= + # edc.vault.hashicorp.timeout.seconds= # edc.webdid.doh.url= # edc.web.rest.cors.enabled= # edc.web.rest.cors.headers= diff --git a/deployment/helm/edc-dataplane/README.md b/deployment/helm/edc-dataplane/README.md index 76f8c727e..d2ae5e616 100644 --- a/deployment/helm/edc-dataplane/README.md +++ b/deployment/helm/edc-dataplane/README.md @@ -23,7 +23,7 @@ $ helm install my-release catenax-ng-product-edc/edc-dataplane --version 0.0.3 | autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | | autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | | autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | -| configuration.properties | string | `"# edc.api.control.auth.apikey.key=\n# edc.api.control.auth.apikey.value=\n# edc.atomikos.checkpoint.interval=\n# edc.atomikos.directory=\n# edc.atomikos.logging=\n# edc.atomikos.threaded2pc=\n# edc.atomikos.timeout=\n# edc.aws.access.key=\n# edc.aws.provision.retry.retries.max=\n# edc.aws.provision.role.duration.session.max=\n# edc.aws.secret.access.key=\n# edc.blobstore.endpoint=\n# edc.controlplane.validation-endpoint=\n# edc.core.retry.backoff.max=\n# edc.core.retry.backoff.min=\n# edc.core.retry.retries.max=\n# edc.core.system.health.check.liveness-period=\n# edc.core.system.health.check.readiness-period=\n# edc.core.system.health.check.startup-period=\n# edc.core.system.health.check.threadpool-size=\n# edc.dataplane.queue.capacity=\n# edc.dataplane.wait=\n# edc.dataplane.workers=\n# edc.datasource.asset.name=\"default\"\n# edc.datasource.contractdefinition.name=\"default\"\n# edc.datasource.contractnegotiation.name=\"default\"\n# edc.datasource.policy.name=\"default\"\n# edc.datasource.transferprocess.name=\"default\"\n# edc.datasource.default.pool.maxIdleConnections=\n# edc.datasource.default.pool.maxTotalConnections=\n# edc.datasource.default.pool.minIdleConnections=\n# edc.datasource.default.pool.testConnectionOnBorrow=\n# edc.datasource.default.pool.testConnectionOnCreate=\n# edc.datasource.default.pool.testConnectionOnReturn=\n# edc.datasource.default.pool.testConnectionWhileIdle=\n# edc.datasource.default.pool.testQuery=\n# edc.datasource.default.url=\n# edc.datasource.default.user=\n# edc.datasource.default.password=\n# edc.dpf.selector.url=\n# edc.events.topic.endpoint=\n# edc.events.topic.name=\n# edc.fs.config=\n# edc.hostname=\n# edc.identity.did.url=\n# edc.ids.catalog.id=\n# edc.ids.curator=\n# edc.ids.description=\n# edc.ids.endpoint=\n# edc.ids.id=\n# edc.ids.maintainer=\n# edc.ids.security.profile=\n# edc.ids.title=\n# edc.ids.validation.referringconnector=\n# edc.ion.crawler.did-type=\n# edc.ion.crawler.interval-minutes=\n# edc.ion.crawler.ion.url=\n# edc.metrics.enabled=\n# edc.metrics.executor.enabled=\n# edc.metrics.jersey.enabled=\n# edc.metrics.jetty.enabled=\n# edc.metrics.okhttp.enabled=\n# edc.metrics.system.enabled=\n# edc.negotiation.consumer.state-machine.batch-size=\n# edc.negotiation.provider.state-machine.batch-size=\n# edc.oauth.client.id=\n# edc.oauth.private.key.alias=\n# edc.oauth.provider.audience=\n# edc.oauth.provider.jwks.refresh=\n# edc.oauth.provider.jwks.url=\n# edc.oauth.public.key.alias=\n# edc.oauth.token.url=\n# edc.oauth.validation.nbf.leeway=\n# edc.receiver.http.auth-code=\n# edc.receiver.http.auth-key=\n# edc.receiver.http.endpoint=\n# edc.transfer.proxy.endpoint=\n# edc.transfer.dataplane.sync.token.validity=\n# edc.transfer.proxy.token.signer.privatekey.alias=\n# edc.transfer.functions.check.endpoint=\n# edc.transfer.functions.enabled.protocols=\n# edc.transfer.functions.transfer.endpoint=\n# edc.transfer-process-store.database.name=\n# edc.transfer.state-machine.batch-size=\n# edc.vault=\n# edc.vault.certificate=\n# edc.vault.clientid=\n# edc.vault.clientsecret=\n# edc.vault.name=\n# edc.vault.tenantid=\n# edc.webdid.doh.url=\n# edc.web.rest.cors.enabled=\n# edc.web.rest.cors.headers=\n# edc.web.rest.cors.methods=\n# edc.web.rest.cors.origins="` | EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) | +| configuration.properties | string | `"# edc.api.control.auth.apikey.key=\n# edc.api.control.auth.apikey.value=\n# edc.atomikos.checkpoint.interval=\n# edc.atomikos.directory=\n# edc.atomikos.logging=\n# edc.atomikos.threaded2pc=\n# edc.atomikos.timeout=\n# edc.aws.access.key=\n# edc.aws.provision.retry.retries.max=\n# edc.aws.provision.role.duration.session.max=\n# edc.aws.secret.access.key=\n# edc.blobstore.endpoint=\n# edc.controlplane.validation-endpoint=\n# edc.core.retry.backoff.max=\n# edc.core.retry.backoff.min=\n# edc.core.retry.retries.max=\n# edc.core.system.health.check.liveness-period=\n# edc.core.system.health.check.readiness-period=\n# edc.core.system.health.check.startup-period=\n# edc.core.system.health.check.threadpool-size=\n# edc.dataplane.queue.capacity=\n# edc.dataplane.wait=\n# edc.dataplane.workers=\n# edc.datasource.asset.name=\"default\"\n# edc.datasource.contractdefinition.name=\"default\"\n# edc.datasource.contractnegotiation.name=\"default\"\n# edc.datasource.policy.name=\"default\"\n# edc.datasource.transferprocess.name=\"default\"\n# edc.datasource.default.pool.maxIdleConnections=\n# edc.datasource.default.pool.maxTotalConnections=\n# edc.datasource.default.pool.minIdleConnections=\n# edc.datasource.default.pool.testConnectionOnBorrow=\n# edc.datasource.default.pool.testConnectionOnCreate=\n# edc.datasource.default.pool.testConnectionOnReturn=\n# edc.datasource.default.pool.testConnectionWhileIdle=\n# edc.datasource.default.pool.testQuery=\n# edc.datasource.default.url=\n# edc.datasource.default.user=\n# edc.datasource.default.password=\n# edc.dpf.selector.url=\n# edc.events.topic.endpoint=\n# edc.events.topic.name=\n# edc.fs.config=\n# edc.hostname=\n# edc.identity.did.url=\n# edc.ids.catalog.id=\n# edc.ids.curator=\n# edc.ids.description=\n# edc.ids.endpoint=\n# edc.ids.id=\n# edc.ids.maintainer=\n# edc.ids.security.profile=\n# edc.ids.title=\n# edc.ids.validation.referringconnector=\n# edc.ion.crawler.did-type=\n# edc.ion.crawler.interval-minutes=\n# edc.ion.crawler.ion.url=\n# edc.metrics.enabled=\n# edc.metrics.executor.enabled=\n# edc.metrics.jersey.enabled=\n# edc.metrics.jetty.enabled=\n# edc.metrics.okhttp.enabled=\n# edc.metrics.system.enabled=\n# edc.negotiation.consumer.state-machine.batch-size=\n# edc.negotiation.provider.state-machine.batch-size=\n# edc.oauth.client.id=\n# edc.oauth.private.key.alias=\n# edc.oauth.provider.audience=\n# edc.oauth.provider.jwks.refresh=\n# edc.oauth.provider.jwks.url=\n# edc.oauth.public.key.alias=\n# edc.oauth.token.url=\n# edc.oauth.validation.nbf.leeway=\n# edc.receiver.http.auth-code=\n# edc.receiver.http.auth-key=\n# edc.receiver.http.endpoint=\n# edc.transfer.proxy.endpoint=\n# edc.transfer.dataplane.sync.token.validity=\n# edc.transfer.proxy.token.signer.privatekey.alias=\n# edc.transfer.functions.check.endpoint=\n# edc.transfer.functions.enabled.protocols=\n# edc.transfer.functions.transfer.endpoint=\n# edc.transfer-process-store.database.name=\n# edc.transfer.state-machine.batch-size=\n# edc.vault=\n# edc.vault.certificate=\n# edc.vault.clientid=\n# edc.vault.clientsecret=\n# edc.vault.name=\n# edc.vault.tenantid=\n# edc.vault.url=\n# edc.vault.token=\n# edc.vault.timeout.seconds=\n# edc.webdid.doh.url=\n# edc.web.rest.cors.enabled=\n# edc.web.rest.cors.headers=\n# edc.web.rest.cors.methods=\n# edc.web.rest.cors.origins="` | EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) | | edc.endpoints.control.path | string | `"/api/dataplane/control"` | The path mapping the "control" api is going to be exposed by | | edc.endpoints.control.port | string | `"9999"` | The network port, which the "control" api is going to be exposed by the container, pod and service | | edc.endpoints.default.path | string | `"/api"` | The path mapping the "default" api is going to be exposed by | @@ -35,7 +35,7 @@ $ helm install my-release catenax-ng-product-edc/edc-dataplane --version 0.0.3 | env | object | `{}` | Container environment variables e.g. for configuring [JAVA_TOOL_OPTIONS](https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/envvars002.html) Ex.: JAVA_TOOL_OPTIONS: > -Dhttp.proxyHost=proxy -Dhttp.proxyPort=80 -Dhttp.nonProxyHosts="localhost|127.*|[::1]" -Dhttps.proxyHost=proxy -Dhttps.proxyPort=443 | | fullnameOverride | string | `""` | Overrides the releases full name | | image.pullPolicy | string | `"IfNotPresent"` | [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use | -| image.repository | string | `"ghcr.io/catenax-ng/product-edc/edc-dataplane"` | Which edc-dataplane container image to use | +| image.repository | string | `"ghcr.io/catenax-ng/product-edc/edc-dataplane-hashicorp-vault"` | Which derivate of the edc data-plane to use. One of: [ghcr.io/catenax-ng/product-edc/edc-dataplane-hashicorp-vault, ghcr.io/catenax-ng/product-edc/edc-dataplane-azure-vault] | | image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion | | imagePullSecret.dockerconfigjson | string | `""` | Image pull secret to create to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) Note: This value needs to adhere to the [(base64 encoded) .dockerconfigjson format](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#registry-secret-existing-credentials). Furthermore, if 'imagePullSecret.dockerconfigjson' is defined, it takes precedence over 'imagePullSecrets'. | | imagePullSecrets | list | `[]` | Existing image pull secret to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) | diff --git a/deployment/helm/edc-dataplane/values.yaml b/deployment/helm/edc-dataplane/values.yaml index 80e3bb5c6..ea8a9e8fe 100644 --- a/deployment/helm/edc-dataplane/values.yaml +++ b/deployment/helm/edc-dataplane/values.yaml @@ -8,8 +8,9 @@ replicaCount: 1 image: - # -- Which edc-dataplane container image to use - repository: ghcr.io/catenax-ng/product-edc/edc-dataplane + # -- Which derivate of the edc data-plane to use. + # One of: [ghcr.io/catenax-ng/product-edc/edc-dataplane-hashicorp-vault, ghcr.io/catenax-ng/product-edc/edc-dataplane-azure-vault] + repository: ghcr.io/catenax-ng/product-edc/edc-dataplane-hashicorp-vault # -- [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use pullPolicy: IfNotPresent # -- Overrides the image tag whose default is the chart appVersion @@ -293,6 +294,9 @@ configuration: # edc.vault.clientsecret= # edc.vault.name= # edc.vault.tenantid= + # edc.vault.hashicorp.url= + # edc.vault.hashicorp.token= + # edc.vault.hashicorp.timeout.seconds= # edc.webdid.doh.url= # edc.web.rest.cors.enabled= # edc.web.rest.cors.headers= diff --git a/edc-controlplane/edc-controlplane-memory/pom.xml b/edc-controlplane/edc-controlplane-memory/pom.xml index 0e84554eb..5a79b2d70 100644 --- a/edc-controlplane/edc-controlplane-memory/pom.xml +++ b/edc-controlplane/edc-controlplane-memory/pom.xml @@ -14,8 +14,8 @@ --> - edc-controlplane net.catenax.edc + edc-controlplane 0.0.4-SNAPSHOT 4.0.0 @@ -234,7 +234,6 @@ - + + + net.catenax.edc + edc-controlplane + 0.0.4-SNAPSHOT + + 4.0.0 + + edc-controlplane-postgresql-hashicorp-vault + jar + + + ${project.artifactId} + + + src/main/resources + + **/* + + + + ../../ + META-INF + + NOTICE.md + LICENSE + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + true + lib/ + org.eclipse.dataspaceconnector.boot.system.runtime.BaseRuntime + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + package + + copy-dependencies + + + ${project.build.directory}/lib + + + + + + + + + + + + net.catenax.edc.extensions + business-partner-validation + + + net.catenax.edc.extensions + postgresql-migration + + + + + org.eclipse.dataspaceconnector + filesystem-configuration + + + + + org.eclipse.dataspaceconnector + auth-tokenbased + + + + + net.catenax.edc.extensions + hashicorp-vault + + + + + org.eclipse.dataspaceconnector + control-api + + + org.eclipse.dataspaceconnector + data-management-api + + + org.eclipse.dataspaceconnector + observability-api + + + + + org.eclipse.dataspaceconnector + ids-api-configuration + + + org.eclipse.dataspaceconnector + ids-api-multipart-dispatcher-v1 + + + org.eclipse.dataspaceconnector + ids-api-multipart-endpoint-v1 + + + org.eclipse.dataspaceconnector + ids-api-transform-v1 + + + org.eclipse.dataspaceconnector + ids-core + + + org.eclipse.dataspaceconnector + ids-spi + + + org.eclipse.dataspaceconnector + ids-token-validation + + + + + org.eclipse.dataspaceconnector + asset-index-sql + + + org.eclipse.dataspaceconnector + contractdefinition-store-sql + + + org.eclipse.dataspaceconnector + contractnegotiation-store-sql + + + org.eclipse.dataspaceconnector + transfer-process-store-sql + + + org.eclipse.dataspaceconnector + policy-store-sql + + + + + org.eclipse.dataspaceconnector + core-base + + + org.eclipse.dataspaceconnector + core-boot + + + org.eclipse.dataspaceconnector + transfer + + + org.eclipse.dataspaceconnector + contract + + + + + org.eclipse.dataspaceconnector + data-plane-transfer-sync + + + + + org.eclipse.dataspaceconnector + oauth2-core + + + org.eclipse.dataspaceconnector + iam-daps + + + + + org.eclipse.dataspaceconnector + core-micrometer + + + org.eclipse.dataspaceconnector + jersey-micrometer + + + org.eclipse.dataspaceconnector + jetty-micrometer + + + + + org.eclipse.dataspaceconnector + jdk-logger-monitor + + + + + + org.eclipse.dataspaceconnector + http + + + + org.eclipse.dataspaceconnector + apache-commons-pool-sql + + + org.eclipse.dataspaceconnector + transaction-local + + + + + org.eclipse.dataspaceconnector + http-receiver + + + + + com.azure + azure-identity + + + com.azure + azure-security-keyvault-secrets + + + org.postgresql + postgresql + + + + + + + + with-docker-image + + + + org.codehaus.mojo + exec-maven-plugin + + + + docker-build-${project.artifactId}:${project.version} + package + + exec + + + docker + ${project.basedir} + + build + -f + src/main/docker/Dockerfile + --build-arg + JAR=target/${project.artifactId}.jar + --build-arg + LIB=target/lib + -t + ${project.artifactId}:${project.version} + . + + + + + docker-tag-${project.artifactId}:latest + package + + exec + + + docker + ${project.basedir} + + tag + ${project.artifactId}:${project.version} + ${project.artifactId}:latest + + + + + + + + + + diff --git a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile new file mode 100644 index 000000000..d4aa94d6b --- /dev/null +++ b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile @@ -0,0 +1,37 @@ +# +# Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +# +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# +# Contributors: +# Mercedes-Benz Tech Innovation GmbH - Initial Dockerfile +# +FROM alpine:3.16.0 as otel + +ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" + +RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar + +FROM gcr.io/distroless/java11-debian11 +ARG JAR +ARG LIB + +WORKDIR /app + +COPY --from=otel /tmp/opentelemetry-javaagent.jar . +COPY ${JAR} edc-controlplane.jar +COPY ${LIB} ./lib/ + +CMD ["-javaagent:/app/opentelemetry-javaagent.jar", \ + "-Dedc.fs.config=/app/configuration.properties", \ + "-Djava.util.logging.config.file=/app/logging.properties", \ + "-Dotel.javaagent.configuration-file=/app/opentelemetry.properties", \ + "-Dotel.metrics.exporter=prometheus", \ + "-Dotel.exporter.prometheus.port=9090", \ + "-Djava.security.edg=file:/dev/.urandom", \ + "-jar", \ + "edc-controlplane.jar"] diff --git a/edc-controlplane/edc-controlplane-postgresql/pom.xml b/edc-controlplane/edc-controlplane-postgresql/pom.xml index d8f0be46d..dd765bbd2 100644 --- a/edc-controlplane/edc-controlplane-postgresql/pom.xml +++ b/edc-controlplane/edc-controlplane-postgresql/pom.xml @@ -12,10 +12,11 @@ Mercedes-Benz Tech Innovation GmbH - Initial POM --> - + - edc-controlplane net.catenax.edc + edc-controlplane 0.0.4-SNAPSHOT 4.0.0 diff --git a/edc-controlplane/pom.xml b/edc-controlplane/pom.xml index 9ca158d85..f5a338e37 100644 --- a/edc-controlplane/pom.xml +++ b/edc-controlplane/pom.xml @@ -12,14 +12,14 @@ Mercedes-Benz Tech Innovation GmbH - Initial POM --> - - 4.0.0 - + net.catenax.edc product-edc-parent 0.0.4-SNAPSHOT + 4.0.0 edc-controlplane pom @@ -27,5 +27,6 @@ edc-controlplane-memory edc-controlplane-postgresql + edc-controlplane-postgresql-hashicorp-vault diff --git a/edc-dataplane/README.md b/edc-dataplane/edc-dataplane-azure-vault/README.md similarity index 93% rename from edc-dataplane/README.md rename to edc-dataplane/edc-dataplane-azure-vault/README.md index b1bcd48a0..c3947ca8e 100644 --- a/edc-dataplane/README.md +++ b/edc-dataplane/edc-dataplane-azure-vault/README.md @@ -1,14 +1,14 @@ -# EDC Data-Plane +# EDC Data-Plane with [Azure Key Vault](https://azure.microsoft.com/en-us/services/key-vault/#product-overview) ### Building ```shell -./mvnw -pl .,edc-dataplane -am package -Pwith-docker-image +./mvnw -pl .,edc-dataplane/edc-dataplane-azure-vault -am package -Pwith-docker-image ``` ### Configuration -Listed below are configuration keys needed to get the `edc-dataplane` up and running. +Listed below are configuration keys needed to get the `edc-dataplane-azure-vault` up and running. Details regarding each configuration property can be found at the [documentary section of the EDC](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/docs). | Key | Required | Example | Description | @@ -90,5 +90,5 @@ docker run \ -v ${CONFIGURATION_PROPERTIES_FILE:-/dev/null}:/app/configuration.properties \ -v ${LOGGING_PROPERTIES_FILE:-/dev/null}:/app/logging.properties \ -v ${OPENTELEMETRY_PROPERTIES_FILE:-/dev/null}:/app/opentelemetry.properties \ - -i edc-dataplane:latest + -i edc-dataplane-azure-vault:latest ``` diff --git a/edc-dataplane/edc-dataplane-azure-vault/pom.xml b/edc-dataplane/edc-dataplane-azure-vault/pom.xml new file mode 100644 index 000000000..eb485886f --- /dev/null +++ b/edc-dataplane/edc-dataplane-azure-vault/pom.xml @@ -0,0 +1,228 @@ + + + + + net.catenax.edc + edc-dataplane + 0.0.4-SNAPSHOT + + 4.0.0 + + edc-dataplane-azure-vault + jar + + + ${project.artifactId} + + + src/main/resources + + **/* + + + + ../../ + META-INF + + NOTICE.md + LICENSE + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + true + lib/ + org.eclipse.dataspaceconnector.boot.system.runtime.BaseRuntime + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + package + + copy-dependencies + + + ${project.build.directory}/lib + + + + + + + + + + + org.eclipse.dataspaceconnector + filesystem-configuration + + + + + org.eclipse.dataspaceconnector + azure-vault + + + com.azure + azure-security-keyvault-secrets + + + com.azure + azure-identity + + + + + + + org.eclipse.dataspaceconnector + data-plane-framework + + + org.eclipse.dataspaceconnector + data-plane-http + + + org.eclipse.dataspaceconnector + data-plane-api + + + org.eclipse.dataspaceconnector + observability-api + + + + + org.eclipse.dataspaceconnector + core-base + + + org.eclipse.dataspaceconnector + core-boot + + + + + org.eclipse.dataspaceconnector + core-micrometer + + + org.eclipse.dataspaceconnector + jersey-micrometer + + + org.eclipse.dataspaceconnector + jetty-micrometer + + + + + org.eclipse.dataspaceconnector + jdk-logger-monitor + + + + + + org.eclipse.dataspaceconnector + http + + + + + com.azure + azure-identity + + + com.azure + azure-security-keyvault-secrets + + + + + + + with-docker-image + + + + org.codehaus.mojo + exec-maven-plugin + + + + docker-build-${project.artifactId}:${project.version} + package + + exec + + + docker + ${project.basedir} + + build + -f + src/main/docker/Dockerfile + --build-arg + JAR=target/${project.artifactId}.jar + --build-arg + LIB=target/lib + -t + ${project.artifactId}:${project.version} + . + + + + + docker-tag-${project.artifactId}:latest + package + + exec + + + docker + ${project.basedir} + + tag + ${project.artifactId}:${project.version} + ${project.artifactId}:latest + + + + + + + + + + diff --git a/edc-dataplane/src/main/docker/Dockerfile b/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile similarity index 100% rename from edc-dataplane/src/main/docker/Dockerfile rename to edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile diff --git a/edc-dataplane/edc-dataplane-hashicorp-vault/README.md b/edc-dataplane/edc-dataplane-hashicorp-vault/README.md new file mode 100644 index 000000000..22a8d4d60 --- /dev/null +++ b/edc-dataplane/edc-dataplane-hashicorp-vault/README.md @@ -0,0 +1,92 @@ +# EDC Data-Plane [Hashicorp Vault](https://www.vaultproject.io/) + +### Building + +```shell +./mvnw -pl .,edc-dataplane/edc-dataplane-hashicorp-vault -am package -Pwith-docker-image +``` + +### Configuration + +Listed below are configuration keys needed to get the `edc-dataplane-hashicorp-vault` up and running. +Details regarding each configuration property can be found at the [documentary section of the EDC](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/docs). + +| Key | Required | Example | Description | +|--- |--- |--- |--- | +| web.http.default.port | X | 8080 | | +| web.http.default.path | X | /api | | +| web.http.public.port | X | 8181 | | +| web.http.public.path | X | | | +| web.http.control.port | X | 9999 | | +| web.http.control.path | X | /api/controlplane/control | | +| edc.receiver.http.endpoint | X | http://backend-service | | +| edc.hostname | | localhost | | +| edc.oauth.client.id | X | daps-oauth-client-id | | +| edc.vault.hashicorp.url | X | http://vault | | +| edc.vault.hashicorp.token | X | 55555555-6666-7777-8888-999999999999 | | +| edc.vault.hashicorp.timeout.seconds | | 30 | | +| edc.controlplane.validation-endpoint | X | http://controlplane:8182/validation/token | | + +#### Example configuration.properties + +JDK properties-style configuration of the EDC Control-Plane is expected to be mounted to `/app/configuration.properties` within the container. + +```shell +# Create configuration.properties +export CONFIGURATION_PROPERTIES_FILE=$(mktemp /tmp/configuration.properties.XXXXXX) +cat << 'EOF' > ${CONFIGURATION_PROPERTIES_FILE} + +web.http.default.port=8080 +web.http.default.path=/api +web.http.public.port=8185 +web.http.public.path=/public +web.http.control.port=9999 +web.http.control.path=/api/dataplane/control + +# Validation endpoint of controlplane +edc.controlplane.validation-endpoint=http://controlplane:8182/validation/token + +# EDC hostname +edc.hostname=localhost + +# HashiCorp vault related configuration +edc.vault.hashicorp.url=http://vault +edc.vault.hashicorp.token=55555555-6666-7777-8888-999999999999 +edc.vault.hashicorp.timeout.seconds=30 +EOF +``` + +#### Example logging.properties +```shell +# Create logging.properties +export LOGGING_PROPERTIES_FILE=$(mktemp /tmp/logging.properties.XXXXXX) +cat << 'EOF' > ${LOGGING_PROPERTIES_FILE} +.level=INFO +org.eclipse.dataspaceconnector.level=ALL +handlers=java.util.logging.ConsoleHandler +java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter +java.util.logging.ConsoleHandler.level=ALL +java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n +EOF +``` + +#### Example opentelemetry.properties +```shell +# Create opentelemetry.properties +export OPENTELEMETRY_PROPERTIES_FILE=$(mktemp /tmp/opentelemetry.properties.XXXXXX) +cat << 'EOF' > ${OPENTELEMETRY_PROPERTIES_FILE} +otel.javaagent.enabled=true +otel.javaagent.debug=false +EOF +``` + +### Running + +```shell +docker run \ + -p 8080:8080 -p 8185:8185 -p 9999:9999 -p 9090:9090 \ + -v ${CONFIGURATION_PROPERTIES_FILE:-/dev/null}:/app/configuration.properties \ + -v ${LOGGING_PROPERTIES_FILE:-/dev/null}:/app/logging.properties \ + -v ${OPENTELEMETRY_PROPERTIES_FILE:-/dev/null}:/app/opentelemetry.properties \ + -i edc-dataplane-hashicorp-vault:latest +``` \ No newline at end of file diff --git a/edc-dataplane/edc-dataplane-hashicorp-vault/pom.xml b/edc-dataplane/edc-dataplane-hashicorp-vault/pom.xml new file mode 100644 index 000000000..fe4859ada --- /dev/null +++ b/edc-dataplane/edc-dataplane-hashicorp-vault/pom.xml @@ -0,0 +1,210 @@ + + + + + net.catenax.edc + edc-dataplane + 0.0.4-SNAPSHOT + + 4.0.0 + + edc-dataplane-hashicorp-vault + jar + + + ${project.artifactId} + + + src/main/resources + + **/* + + + + ../../ + META-INF + + NOTICE.md + LICENSE + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + true + lib/ + + org.eclipse.dataspaceconnector.boot.system.runtime.BaseRuntime + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + package + + copy-dependencies + + + ${project.build.directory}/lib + + + + + + + + + + + org.eclipse.dataspaceconnector + filesystem-configuration + + + + + net.catenax.edc.extensions + hashicorp-vault + + + + + org.eclipse.dataspaceconnector + data-plane-framework + + + org.eclipse.dataspaceconnector + data-plane-http + + + org.eclipse.dataspaceconnector + data-plane-api + + + org.eclipse.dataspaceconnector + observability-api + + + + + org.eclipse.dataspaceconnector + core-base + + + org.eclipse.dataspaceconnector + core-boot + + + + + org.eclipse.dataspaceconnector + core-micrometer + + + org.eclipse.dataspaceconnector + jersey-micrometer + + + org.eclipse.dataspaceconnector + jetty-micrometer + + + + + org.eclipse.dataspaceconnector + jdk-logger-monitor + + + + + + org.eclipse.dataspaceconnector + http + + + + + + + with-docker-image + + + + org.codehaus.mojo + exec-maven-plugin + + + + docker-build-${project.artifactId}:${project.version} + package + + exec + + + docker + ${project.basedir} + + build + -f + src/main/docker/Dockerfile + --build-arg + JAR=target/${project.artifactId}.jar + --build-arg + LIB=target/lib + -t + ${project.artifactId}:${project.version} + . + + + + + docker-tag-${project.artifactId}:latest + package + + exec + + + docker + ${project.basedir} + + tag + ${project.artifactId}:${project.version} + ${project.artifactId}:latest + + + + + + + + + + diff --git a/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile b/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile new file mode 100644 index 000000000..0af3e0b55 --- /dev/null +++ b/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile @@ -0,0 +1,37 @@ +# +# Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +# +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# +# Contributors: +# Mercedes-Benz Tech Innovation GmbH - Initial Dockerfile +# +FROM alpine:3.16.0 as otel + +ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" + +RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar + +FROM gcr.io/distroless/java11-debian11 +ARG JAR +ARG LIB + +WORKDIR /app + +COPY --from=otel /tmp/opentelemetry-javaagent.jar . +COPY ${JAR} edc-dataplane.jar +COPY ${LIB} ./lib/ + +CMD ["-javaagent:/app/opentelemetry-javaagent.jar", \ + "-Dedc.fs.config=/app/configuration.properties", \ + "-Djava.util.logging.config.file=/app/logging.properties", \ + "-Dotel.javaagent.configuration-file=/app/opentelemetry.properties", \ + "-Dotel.metrics.exporter=prometheus", \ + "-Dotel.exporter.prometheus.port=9090", \ + "-Djava.security.edg=file:/dev/.urandom", \ + "-jar", \ + "edc-dataplane.jar"] diff --git a/edc-dataplane/pom.xml b/edc-dataplane/pom.xml index 6bac89696..92fab39fb 100644 --- a/edc-dataplane/pom.xml +++ b/edc-dataplane/pom.xml @@ -20,210 +20,12 @@ product-edc-parent 0.0.4-SNAPSHOT - edc-dataplane - jar - - - ${project.artifactId} - - - src/main/resources - - **/* - - - - ../ - META-INF - - NOTICE.md - LICENSE - - - - - - - org.apache.maven.plugins - maven-jar-plugin - - - - true - lib/ - - org.eclipse.dataspaceconnector.boot.system.runtime.BaseRuntime - - - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - package - - copy-dependencies - - - ${project.build.directory}/lib - - - - - - - - - - - org.eclipse.dataspaceconnector - filesystem-configuration - - - - - org.eclipse.dataspaceconnector - azure-vault - - - com.azure - azure-security-keyvault-secrets - - - com.azure - azure-identity - - - - - - - org.eclipse.dataspaceconnector - data-plane-framework - - - org.eclipse.dataspaceconnector - data-plane-http - - - org.eclipse.dataspaceconnector - data-plane-api - - - org.eclipse.dataspaceconnector - observability-api - - - - org.eclipse.dataspaceconnector - core-base - - - org.eclipse.dataspaceconnector - core-boot - - - - - org.eclipse.dataspaceconnector - core-micrometer - - - org.eclipse.dataspaceconnector - jersey-micrometer - - - org.eclipse.dataspaceconnector - jetty-micrometer - - - - - org.eclipse.dataspaceconnector - jdk-logger-monitor - - - - - - org.eclipse.dataspaceconnector - http - - - - - com.azure - azure-identity - - - com.azure - azure-security-keyvault-secrets - - + edc-dataplane + pom - - - - with-docker-image - - - - org.codehaus.mojo - exec-maven-plugin - - - - docker-build-${project.artifactId}:${project.version} - package - - exec - - - docker - ${project.basedir} - - build - -f - src/main/docker/Dockerfile - --build-arg - JAR=target/${project.artifactId}.jar - --build-arg - LIB=target/lib - -t - ${project.artifactId}:${project.version} - . - - - - - docker-tag-${project.artifactId}:latest - package - - exec - - - docker - ${project.basedir} - - tag - ${project.artifactId}:${project.version} - ${project.artifactId}:latest - - - - - - - - - - \ No newline at end of file + + edc-dataplane-azure-vault + edc-dataplane-hashicorp-vault + + diff --git a/edc-extensions/business-partner-validation/pom.xml b/edc-extensions/business-partner-validation/pom.xml index b106ac56c..54c3f3d1d 100644 --- a/edc-extensions/business-partner-validation/pom.xml +++ b/edc-extensions/business-partner-validation/pom.xml @@ -12,14 +12,15 @@ Mercedes-Benz Tech Innovation GmbH - Initial POM --> - - 4.0.0 - + net.catenax.edc.extensions edc-extensions 0.0.4-SNAPSHOT + 4.0.0 + business-partner-validation jar diff --git a/edc-extensions/hashicorp-vault/README.md b/edc-extensions/hashicorp-vault/README.md new file mode 100644 index 000000000..5eea7a23c --- /dev/null +++ b/edc-extensions/hashicorp-vault/README.md @@ -0,0 +1,9 @@ +# [HashiCorp Vault](https://www.vaultproject.io/) Extension + +## Configuration + +| Key | Description | Mandatory | +|:---|:---|---| +| edc.vault.hashicorp.url | URL to connect to the HashiCorp Vault | X | +| edc.vault.hashicorp.token | Value for [Token Authentication](https://www.vaultproject.io/docs/auth/token) with the vault | X | +| edc.vault.hashicorp.timeout.seconds | Request timeout in seconds when contacting the vault (default: 30) | | diff --git a/edc-extensions/hashicorp-vault/pom.xml b/edc-extensions/hashicorp-vault/pom.xml new file mode 100644 index 000000000..15e987e85 --- /dev/null +++ b/edc-extensions/hashicorp-vault/pom.xml @@ -0,0 +1,223 @@ + + + + + net.catenax.edc.extensions + edc-extensions + 0.0.4-SNAPSHOT + + 4.0.0 + + hashicorp-vault + jar + + + ${project.basedir}/src/main/java + ${originalSourceDirectory} + ${project.build.directory}/delombok + + + + ${sourceDirectory} + + + + src/main/resources + + **/* + + + + ../../ + META-INF + + NOTICE.md + LICENSE + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${maven.compiler.source} + ${maven.compiler.target} + ${project.build.sourceEncoding} + + + org.projectlombok + lombok + ${org.projectlombok.lombok.version} + + + + + + + org.projectlombok + lombok-maven-plugin + ${org.projectlombok.lombok.maven.plugin.version} + + + generate-sources + + delombok + + + + + ${originalSourceDirectory} + ${delombokSourceDirectory} + false + UTF-8 + + skip + + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar + + + + + + + + + + + org.eclipse.dataspaceconnector + core-spi + + + org.eclipse.dataspaceconnector + junit-extension + test + + + org.eclipse.dataspaceconnector + junit-extension + test-fixtures + test + + + + + org.projectlombok + lombok + + + org.bouncycastle + bcpkix-jdk15on + + + com.squareup.okhttp3 + okhttp + + + + + org.junit.jupiter + junit-jupiter + test + + + org.mockito + mockito-core + test + + + org.mockito + mockito-inline + test + + + org.testcontainers + junit-jupiter + test + + + org.testcontainers + vault + ${org.testcontainers.version} + test + + + ch.qos.logback + logback-core + test + + + org.slf4j + slf4j-api + test + + + org.slf4j + jul-to-slf4j + test + + + ch.qos.logback + logback-classic + test + + + org.hamcrest + hamcrest + 2.2 + test + + + + + + delombok + + ${delombokSourceDirectory} + + + + + failsafe + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + + integration-test + verify + + + + + + + + + diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpCertificateResolver.java b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpCertificateResolver.java new file mode 100644 index 000000000..f067aec92 --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpCertificateResolver.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.hashicorpvault; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.security.cert.X509Certificate; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import org.eclipse.dataspaceconnector.spi.EdcException; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.security.CertificateResolver; +import org.eclipse.dataspaceconnector.spi.security.Vault; + +/** Resolves an X.509 certificate in Hashicorp vault. */ +@RequiredArgsConstructor +public class HashicorpCertificateResolver implements CertificateResolver { + @NonNull private final Vault vault; + @NonNull private final Monitor monitor; + + @Override + public X509Certificate resolveCertificate(@NonNull String id) { + String certificateRepresentation = vault.resolveSecret(id); + if (certificateRepresentation == null) { + return null; + } + try (InputStream inputStream = + new ByteArrayInputStream(certificateRepresentation.getBytes(StandardCharsets.UTF_8))) { + X509Certificate x509Certificate = PemUtil.readX509Certificate(inputStream); + if (x509Certificate == null) { + monitor.warning( + String.format("Expected PEM certificate on key %s, but value not PEM.", id)); + } + return x509Certificate; + } catch (IOException e) { + throw new EdcException(e.getMessage(), e); + } + } +} diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVault.java b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVault.java new file mode 100644 index 000000000..6fe484081 --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVault.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.hashicorpvault; + +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.result.Result; +import org.eclipse.dataspaceconnector.spi.security.Vault; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** Implements a vault backed by Hashicorp Vault. */ +@RequiredArgsConstructor +class HashicorpVault implements Vault { + + @NonNull private final HashicorpVaultClient hashicorpVaultClient; + @NonNull private final Monitor monitor; + + @Override + public @Nullable String resolveSecret(@NonNull String key) { + Result result = hashicorpVaultClient.getSecretValue(key); + + return result.succeeded() ? result.getContent() : null; + } + + @Override + @NotNull + public Result storeSecret(@NotNull @NonNull String key, @NotNull @NonNull String value) { + Result result = + hashicorpVaultClient.setSecret(key, value); + + return result.succeeded() ? Result.success() : Result.failure(result.getFailureMessages()); + } + + @Override + public Result deleteSecret(@NotNull @NonNull String key) { + return hashicorpVaultClient.destroySecret(key); + } +} diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultClient.java b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultClient.java new file mode 100644 index 000000000..251633c5a --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultClient.java @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.hashicorpvault; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.net.URI; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.Objects; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import okhttp3.Headers; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import org.eclipse.dataspaceconnector.spi.result.Result; +import org.jetbrains.annotations.NotNull; + +@RequiredArgsConstructor +class HashicorpVaultClient { + static final String VAULT_DATA_ENTRY_NAME = "content"; + private static final String VAULT_TOKEN_HEADER = "X-Vault-Token"; + private static final String VAULT_REQUEST_HEADER = "X-Vault-Request"; + private static final MediaType MEDIA_TYPE_APPLICATION_JSON = MediaType.get("application/json"); + private static final String VAULT_API_VERSION = "v1"; + private static final String VAULT_SECRET_PATH = "secret"; + private static final String VAULT_SECRET_DATA_PATH = "data"; + private static final String VAULT_SECRET_METADATA_PATH = "metadata"; + private static final String CALL_UNSUCCESSFUL_ERROR_TEMPLATE = "Call unsuccessful: %s"; + @NonNull private final HashicorpVaultClientConfig config; + @NonNull private final OkHttpClient okHttpClient; + @NonNull private final ObjectMapper objectMapper; + + Result getSecretValue(@NonNull String key) { + String requestURI = getSecretUrl(key, VAULT_SECRET_DATA_PATH); + Headers headers = getHeaders(); + Request request = new Request.Builder().url(requestURI).headers(headers).get().build(); + + try (Response response = okHttpClient.newCall(request).execute()) { + + if (response.isSuccessful()) { + if (response.code() == 404) { + return Result.failure( + String.format(CALL_UNSUCCESSFUL_ERROR_TEMPLATE, "Secret not found")); + } + + String responseBody = Objects.requireNonNull(response.body()).string(); + HashicorpVaultGetEntryResponsePayload payload = + objectMapper.readValue(responseBody, HashicorpVaultGetEntryResponsePayload.class); + String value = + Objects.requireNonNull(payload.getData().getData().get(VAULT_DATA_ENTRY_NAME)); + + return Result.success(value); + } else { + return Result.failure(String.format(CALL_UNSUCCESSFUL_ERROR_TEMPLATE, response.code())); + } + + } catch (IOException e) { + return Result.failure(e.getMessage()); + } + } + + Result setSecret( + @NonNull String key, @NonNull String value) { + String requestURI = getSecretUrl(key, VAULT_SECRET_DATA_PATH); + Headers headers = getHeaders(); + HashicorpVaultCreateEntryRequestPayload requestPayload = + HashicorpVaultCreateEntryRequestPayload.builder() + .data(Collections.singletonMap(VAULT_DATA_ENTRY_NAME, value)) + .build(); + Request request = + new Request.Builder() + .url(requestURI) + .headers(headers) + .post(createRequestBody(requestPayload)) + .build(); + + try (Response response = okHttpClient.newCall(request).execute()) { + if (response.isSuccessful()) { + String responseBody = Objects.requireNonNull(response.body()).string(); + HashicorpVaultCreateEntryResponsePayload responsePayload = + objectMapper.readValue(responseBody, HashicorpVaultCreateEntryResponsePayload.class); + return Result.success(responsePayload); + } else { + return Result.failure(String.format(CALL_UNSUCCESSFUL_ERROR_TEMPLATE, response.code())); + } + } catch (IOException e) { + return Result.failure(e.getMessage()); + } + } + + Result destroySecret(@NonNull String key) { + String requestURI = getSecretUrl(key, VAULT_SECRET_METADATA_PATH); + Headers headers = getHeaders(); + Request request = new Request.Builder().url(requestURI).headers(headers).delete().build(); + + try (Response response = okHttpClient.newCall(request).execute()) { + return response.isSuccessful() || response.code() == 404 + ? Result.success() + : Result.failure(String.format(CALL_UNSUCCESSFUL_ERROR_TEMPLATE, response.code())); + } catch (IOException e) { + return Result.failure(e.getMessage()); + } + } + + @NotNull + private Headers getHeaders() { + Headers.Builder headersBuilder = + new Headers.Builder().add(VAULT_REQUEST_HEADER, Boolean.toString(true)); + if (config.getVaultToken() != null) { + headersBuilder = headersBuilder.add(VAULT_TOKEN_HEADER, config.getVaultToken()); + } + return headersBuilder.build(); + } + + private String getBaseUrl() { + String baseUrl = config.getVaultUrl(); + + if (baseUrl.endsWith("/")) { + baseUrl = baseUrl.substring(0, baseUrl.length() - 1); + } + + return baseUrl; + } + + private String getSecretUrl(String key, String entryType) { + + key = URLEncoder.encode(key, StandardCharsets.UTF_8); + return URI.create( + String.format( + "%s/%s/%s/%s/%s", + getBaseUrl(), VAULT_API_VERSION, VAULT_SECRET_PATH, entryType, key)) + .toString(); + } + + private RequestBody createRequestBody(Object requestPayload) { + String jsonRepresentation; + try { + jsonRepresentation = objectMapper.writeValueAsString(requestPayload); + } catch (JsonProcessingException e) { + throw new HashicorpVaultException(e.getMessage(), e); + } + return RequestBody.create(jsonRepresentation, MEDIA_TYPE_APPLICATION_JSON); + } +} diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultClientConfig.java b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultClientConfig.java new file mode 100644 index 000000000..2b3c886f7 --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultClientConfig.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.hashicorpvault; + +import java.time.Duration; +import lombok.Builder; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Builder +@Getter +@RequiredArgsConstructor +class HashicorpVaultClientConfig { + private final String vaultUrl; + private final String vaultToken; + private final Duration timeout; +} diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultCreateEntryRequestPayload.java b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultCreateEntryRequestPayload.java new file mode 100644 index 000000000..4882c5477 --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultCreateEntryRequestPayload.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.hashicorpvault; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Map; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +class HashicorpVaultCreateEntryRequestPayload { + + @JsonProperty("options") + private Options options; + + @JsonProperty("data") + private Map data; + + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Data + @JsonIgnoreProperties(ignoreUnknown = true) + static class Options { + @JsonProperty("cas") + private Integer cas; + } +} diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultCreateEntryResponsePayload.java b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultCreateEntryResponsePayload.java new file mode 100644 index 000000000..0818c77a7 --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultCreateEntryResponsePayload.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.hashicorpvault; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +class HashicorpVaultCreateEntryResponsePayload { + + @JsonProperty("data") + private HashicorpVaultEntryMetadata data; +} diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultEntryMetadata.java b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultEntryMetadata.java new file mode 100644 index 000000000..ce9f16b7b --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultEntryMetadata.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.hashicorpvault; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Map; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +class HashicorpVaultEntryMetadata { + + @JsonProperty("custom_metadata") + private Map customMetadata; + + @JsonProperty("destroyed") + private Boolean destroyed; + + @JsonProperty("version") + private Integer version; +} diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultException.java b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultException.java new file mode 100644 index 000000000..af106bb58 --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultException.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.hashicorpvault; + +import org.eclipse.dataspaceconnector.spi.EdcException; + +public class HashicorpVaultException extends EdcException { + + public HashicorpVaultException(String message) { + super(message); + } + + public HashicorpVaultException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultExtension.java b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultExtension.java new file mode 100644 index 000000000..6d886ac06 --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultExtension.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.hashicorpvault; + +import java.time.Duration; +import okhttp3.OkHttpClient; +import org.eclipse.dataspaceconnector.spi.EdcException; +import org.eclipse.dataspaceconnector.spi.EdcSetting; +import org.eclipse.dataspaceconnector.spi.security.CertificateResolver; +import org.eclipse.dataspaceconnector.spi.security.PrivateKeyResolver; +import org.eclipse.dataspaceconnector.spi.security.Vault; +import org.eclipse.dataspaceconnector.spi.security.VaultPrivateKeyResolver; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; +import org.eclipse.dataspaceconnector.spi.system.VaultExtension; + +public class HashicorpVaultExtension implements VaultExtension { + + @EdcSetting(required = true) + public static final String VAULT_URL = "edc.vault.hashicorp.url"; + + @EdcSetting(required = true) + public static final String VAULT_TOKEN = "edc.vault.hashicorp.token"; + + @EdcSetting + private static final String VAULT_TIMEOUT_SECONDS = "edc.vault.hashicorp.timeout.seconds"; + + private Vault vault; + private CertificateResolver certificateResolver; + private PrivateKeyResolver privateKeyResolver; + + @Override + public String name() { + return "Hashicorp Vault"; + } + + @Override + public Vault getVault() { + return vault; + } + + @Override + public PrivateKeyResolver getPrivateKeyResolver() { + return privateKeyResolver; + } + + @Override + public CertificateResolver getCertificateResolver() { + return certificateResolver; + } + + @Override + public void initializeVault(ServiceExtensionContext context) { + HashicorpVaultClientConfig config = loadHashicorpVaultClientConfig(context); + + OkHttpClient okHttpClient = createOkHttpClient(config); + HashicorpVaultClient client = + new HashicorpVaultClient(config, okHttpClient, context.getTypeManager().getMapper()); + + vault = new HashicorpVault(client, context.getMonitor()); + certificateResolver = new HashicorpCertificateResolver(vault, context.getMonitor()); + privateKeyResolver = new VaultPrivateKeyResolver(vault); + + context.getMonitor().info("HashicorpVaultExtension: authentication/initialization complete."); + } + + private OkHttpClient createOkHttpClient(HashicorpVaultClientConfig config) { + OkHttpClient.Builder builder = + new OkHttpClient.Builder() + .callTimeout(config.getTimeout()) + .readTimeout(config.getTimeout()); + + return builder.build(); + } + + private HashicorpVaultClientConfig loadHashicorpVaultClientConfig( + ServiceExtensionContext context) { + + String vaultUrl = context.getSetting(VAULT_URL, null); + if (vaultUrl == null) { + throw new HashicorpVaultException(String.format("Vault URL (%s) must be defined", VAULT_URL)); + } + + int vaultTimeoutSeconds = Math.max(0, context.getSetting(VAULT_TIMEOUT_SECONDS, 30)); + Duration vaultTimeoutDuration = Duration.ofSeconds(vaultTimeoutSeconds); + + String vaultToken = context.getSetting(VAULT_TOKEN, null); + + if (vaultToken == null) { + throw new EdcException( + String.format("For Vault authentication [%s] is required", VAULT_TOKEN)); + } + + return HashicorpVaultClientConfig.builder() + .vaultUrl(vaultUrl) + .vaultToken(vaultToken) + .timeout(vaultTimeoutDuration) + .build(); + } +} diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultGetEntryResponsePayload.java b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultGetEntryResponsePayload.java new file mode 100644 index 000000000..712c92421 --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultGetEntryResponsePayload.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.hashicorpvault; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Map; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +class HashicorpVaultGetEntryResponsePayload { + + @JsonProperty("data") + private GetVaultEntryData data; + + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Data + @JsonIgnoreProperties(ignoreUnknown = true) + static class GetVaultEntryData { + + @JsonProperty("data") + private Map data; + + @JsonProperty("metadata") + private HashicorpVaultEntryMetadata metadata; + } +} diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/PemUtil.java b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/PemUtil.java new file mode 100644 index 000000000..8e72fdfb1 --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/PemUtil.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.hashicorpvault; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.security.Provider; +import java.security.cert.X509Certificate; +import lombok.NonNull; +import lombok.SneakyThrows; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openssl.PEMParser; +import org.jetbrains.annotations.NotNull; + +final class PemUtil { + + private PemUtil() { + throw new IllegalStateException("Private constructor invocation disallowed"); + } + + private static final Provider PROVIDER = new BouncyCastleProvider(); + private static final JcaX509CertificateConverter X509_CONVERTER = + new JcaX509CertificateConverter().setProvider(PROVIDER); + + @SneakyThrows + public static X509Certificate readX509Certificate(@NotNull @NonNull InputStream inputStream) { + X509CertificateHolder x509CertificateHolder = parsePem(inputStream); + if (x509CertificateHolder == null) { + return null; + } + return X509_CONVERTER.getCertificate(x509CertificateHolder); + } + + @SuppressWarnings("unchecked") + private static T parsePem(@NotNull @NonNull InputStream inputStream) throws IOException { + try (Reader reader = new InputStreamReader(inputStream)) { + PEMParser pemParser = new PEMParser(reader); + return (T) pemParser.readObject(); + } + } +} diff --git a/edc-extensions/hashicorp-vault/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.VaultExtension b/edc-extensions/hashicorp-vault/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.VaultExtension new file mode 100644 index 000000000..4c244ca16 --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.VaultExtension @@ -0,0 +1,13 @@ +# +# Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +# +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# +# Contributors: +# Mercedes-Benz Tech Innovation GmbH - Initial ServiceExtension file +# +net.catenax.edc.hashicorpvault.HashicorpVaultExtension diff --git a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/AbstractHashicorpIT.java b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/AbstractHashicorpIT.java new file mode 100644 index 000000000..e7b4279e3 --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/AbstractHashicorpIT.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial Test + * + */ + +package net.catenax.edc.hashicorpvault; + +import static net.catenax.edc.hashicorpvault.HashicorpVaultClient.VAULT_DATA_ENTRY_NAME; +import static net.catenax.edc.hashicorpvault.HashicorpVaultExtension.VAULT_TOKEN; +import static net.catenax.edc.hashicorpvault.HashicorpVaultExtension.VAULT_URL; + +import java.util.HashMap; +import java.util.UUID; +import lombok.Getter; +import org.eclipse.dataspaceconnector.junit.launcher.EdcExtension; +import org.eclipse.dataspaceconnector.spi.security.CertificateResolver; +import org.eclipse.dataspaceconnector.spi.security.Vault; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtension; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; +import org.junit.ClassRule; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.vault.VaultContainer; + +@Testcontainers +@ExtendWith(EdcExtension.class) +class AbstractHashicorpIT { + static final String DOCKER_IMAGE_NAME = "vault:1.9.6"; + static final String VAULT_ENTRY_KEY = "testing"; + static final String VAULT_ENTRY_VALUE = UUID.randomUUID().toString(); + static final String TOKEN = UUID.randomUUID().toString(); + + private final TestExtension testExtension = new TestExtension(); + + protected Vault getVault() { + return testExtension.getVault(); + } + + protected CertificateResolver getCertificateResolver() { + return testExtension.getCertificateResolver(); + } + + @Container @ClassRule + private static final VaultContainer vaultContainer = + new VaultContainer<>(DockerImageName.parse(DOCKER_IMAGE_NAME)) + .withVaultToken(TOKEN) + .withSecretInVault( + "secret/" + VAULT_ENTRY_KEY, + String.format("%s=%s", VAULT_DATA_ENTRY_NAME, VAULT_ENTRY_VALUE)); + + @BeforeEach + final void beforeEach(EdcExtension extension) { + extension.setConfiguration( + new HashMap<>() { + { + put( + VAULT_URL, + String.format( + "http://%s:%s", vaultContainer.getHost(), vaultContainer.getFirstMappedPort())); + put(VAULT_TOKEN, TOKEN); + } + }); + extension.registerSystemExtension(ServiceExtension.class, testExtension); + } + + @Getter + private static class TestExtension implements ServiceExtension { + private Vault vault; + private CertificateResolver certificateResolver; + + @Override + public void initialize(ServiceExtensionContext context) { + vault = context.getService(Vault.class); + certificateResolver = context.getService(CertificateResolver.class); + } + } +} diff --git a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpCertificateResolverIT.java b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpCertificateResolverIT.java new file mode 100644 index 000000000..77d26e6df --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpCertificateResolverIT.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial Test + * + */ + +package net.catenax.edc.hashicorpvault; + +import java.security.cert.X509Certificate; +import java.util.UUID; +import lombok.SneakyThrows; +import org.eclipse.dataspaceconnector.spi.security.CertificateResolver; +import org.eclipse.dataspaceconnector.spi.security.Vault; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class HashicorpCertificateResolverIT extends AbstractHashicorpIT { + + @Test + @SneakyThrows + void resolveCertificate_success() { + String key = UUID.randomUUID().toString(); + X509Certificate certificateExpected = X509CertificateTestUtil.generateCertificate(5, "Test"); + String pem = X509CertificateTestUtil.convertToPem(certificateExpected); + + Vault vault = getVault(); + vault.storeSecret(key, pem); + CertificateResolver resolver = getCertificateResolver(); + X509Certificate certificateResult = resolver.resolveCertificate(key); + + Assertions.assertEquals(certificateExpected, certificateResult); + } + + @Test + @SneakyThrows + void resolveCertificate_malformed() { + String key = UUID.randomUUID().toString(); + String value = UUID.randomUUID().toString(); + Vault vault = getVault(); + vault.storeSecret(key, value); + + CertificateResolver resolver = getCertificateResolver(); + X509Certificate certificateResult = resolver.resolveCertificate(key); + Assertions.assertNull(certificateResult); + } +} diff --git a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpCertificateResolverTest.java b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpCertificateResolverTest.java new file mode 100644 index 000000000..ca86d71fa --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpCertificateResolverTest.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial Test + * + */ + +package net.catenax.edc.hashicorpvault; + +import java.security.cert.X509Certificate; +import lombok.SneakyThrows; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class HashicorpCertificateResolverTest { + private static final String key = "key"; + + // mocks + private HashicorpCertificateResolver certificateResolver; + private HashicorpVault vault; + + @BeforeEach + void setup() { + vault = Mockito.mock(HashicorpVault.class); + final Monitor monitor = Mockito.mock(Monitor.class); + certificateResolver = new HashicorpCertificateResolver(vault, monitor); + } + + @Test + @SneakyThrows + void resolveCertificate() { + // prepare + X509Certificate certificateExpected = X509CertificateTestUtil.generateCertificate(5, "Test"); + String pem = X509CertificateTestUtil.convertToPem(certificateExpected); + Mockito.when(vault.resolveSecret(key)).thenReturn(pem); + + // invoke + certificateResolver.resolveCertificate(key); + + // verify + Mockito.verify(vault, Mockito.times(1)).resolveSecret(key); + } +} diff --git a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultClientTest.java b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultClientTest.java new file mode 100644 index 000000000..ae0ca69c8 --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultClientTest.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial Test + * + */ + +package net.catenax.edc.hashicorpvault; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.UUID; +import lombok.SneakyThrows; +import okhttp3.Call; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; +import org.eclipse.dataspaceconnector.spi.result.Result; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class HashicorpVaultClientTest { + private static final String key = "key"; + private static final ObjectMapper objectMapper = new ObjectMapper(); + + @Test + @SneakyThrows + void getSecretValue() { + // prepare + String vaultUrl = "https://mock.url"; + String vaultToken = UUID.randomUUID().toString(); + HashicorpVaultClientConfig hashicorpVaultClientConfig = + HashicorpVaultClientConfig.builder().vaultUrl(vaultUrl).vaultToken(vaultToken).build(); + + OkHttpClient okHttpClient = Mockito.mock(OkHttpClient.class); + HashicorpVaultClient vaultClient = + new HashicorpVaultClient(hashicorpVaultClientConfig, okHttpClient, objectMapper); + Call call = Mockito.mock(Call.class); + Response response = Mockito.mock(Response.class); + ResponseBody body = Mockito.mock(ResponseBody.class); + HashicorpVaultGetEntryResponsePayload payload = new HashicorpVaultGetEntryResponsePayload(); + + Mockito.when(okHttpClient.newCall(Mockito.any(Request.class))).thenReturn(call); + Mockito.when(call.execute()).thenReturn(response); + Mockito.when(response.code()).thenReturn(200); + Mockito.when(response.body()).thenReturn(body); + Mockito.when(body.string()).thenReturn(payload.toString()); + + // invoke + Result result = vaultClient.getSecretValue(key); + + // verify + Assertions.assertNotNull(result); + Mockito.verify(okHttpClient, Mockito.times(1)) + .newCall( + Mockito.argThat( + request -> + request.method().equalsIgnoreCase("GET") + && request.url().encodedPath().contains("/v1/secret/data") + && request.url().encodedPathSegments().contains(key))); + } + + @Test + @SneakyThrows + void setSecretValue() { + // prepare + String vaultUrl = "https://mock.url"; + String vaultToken = UUID.randomUUID().toString(); + String secretValue = UUID.randomUUID().toString(); + HashicorpVaultClientConfig hashicorpVaultClientConfig = + HashicorpVaultClientConfig.builder().vaultUrl(vaultUrl).vaultToken(vaultToken).build(); + + OkHttpClient okHttpClient = Mockito.mock(OkHttpClient.class); + HashicorpVaultClient vaultClient = + new HashicorpVaultClient(hashicorpVaultClientConfig, okHttpClient, objectMapper); + HashicorpVaultCreateEntryResponsePayload payload = + new HashicorpVaultCreateEntryResponsePayload(); + + Call call = Mockito.mock(Call.class); + Response response = Mockito.mock(Response.class); + ResponseBody body = Mockito.mock(ResponseBody.class); + + Mockito.when(okHttpClient.newCall(Mockito.any(Request.class))).thenReturn(call); + Mockito.when(call.execute()).thenReturn(response); + Mockito.when(response.code()).thenReturn(200); + Mockito.when(response.body()).thenReturn(body); + Mockito.when(body.string()).thenReturn(payload.toString()); + + // invoke + Result result = + vaultClient.setSecret(key, secretValue); + + // verify + Assertions.assertNotNull(result); + Mockito.verify(okHttpClient, Mockito.times(1)) + .newCall( + Mockito.argThat( + request -> + request.method().equalsIgnoreCase("POST") + && request.url().encodedPath().contains("/v1/secret/data") + && request.url().encodedPathSegments().contains(key))); + } + + @Test + @SneakyThrows + void destroySecretValue() { + // prepare + String vaultUrl = "https://mock.url"; + String vaultToken = UUID.randomUUID().toString(); + HashicorpVaultClientConfig hashicorpVaultClientConfig = + HashicorpVaultClientConfig.builder().vaultUrl(vaultUrl).vaultToken(vaultToken).build(); + + OkHttpClient okHttpClient = Mockito.mock(OkHttpClient.class); + HashicorpVaultClient vaultClient = + new HashicorpVaultClient(hashicorpVaultClientConfig, okHttpClient, objectMapper); + + Call call = Mockito.mock(Call.class); + Response response = Mockito.mock(Response.class); + ResponseBody body = Mockito.mock(ResponseBody.class); + Mockito.when(okHttpClient.newCall(Mockito.any(Request.class))).thenReturn(call); + Mockito.when(call.execute()).thenReturn(response); + Mockito.when(response.code()).thenReturn(200); + Mockito.when(response.body()).thenReturn(body); + + // invoke + Result result = vaultClient.destroySecret(key); + + // verify + Assertions.assertNotNull(result); + Mockito.verify(okHttpClient, Mockito.times(1)) + .newCall( + Mockito.argThat( + request -> + request.method().equalsIgnoreCase("DELETE") + && request.url().encodedPath().contains("/v1/secret/metadata") + && request.url().encodedPathSegments().contains(key))); + } +} diff --git a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultIT.java b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultIT.java new file mode 100644 index 000000000..e23c5a9f0 --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultIT.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial Test + * + */ + +package net.catenax.edc.hashicorpvault; + +import java.util.UUID; +import org.eclipse.dataspaceconnector.spi.security.Vault; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class HashicorpVaultIT extends AbstractHashicorpIT { + + @Test + @DisplayName("Resolve a secret that exists") + void testResolveSecret_exists() { + Vault vault = getVault(); + String secretValue = vault.resolveSecret(VAULT_ENTRY_KEY); + Assertions.assertEquals(VAULT_ENTRY_VALUE, secretValue); + } + + @Test + @DisplayName("Resolve a secret that does not exist") + void testResolveSecret_doesNotExist() { + Vault vault = getVault(); + Assertions.assertNull(vault.resolveSecret("wrong_key")); + } + + @Test + @DisplayName("Update a secret that exists") + void testSetSecret_exists() { + String key = UUID.randomUUID().toString(); + String value1 = UUID.randomUUID().toString(); + String value2 = UUID.randomUUID().toString(); + + Vault vault = getVault(); + vault.storeSecret(key, value1); + vault.storeSecret(key, value2); + String secretValue = vault.resolveSecret(key); + Assertions.assertEquals(value2, secretValue); + } + + @Test + @DisplayName("Create a secret that does not exist") + void testSetSecret_doesNotExist() { + String key = UUID.randomUUID().toString(); + String value = UUID.randomUUID().toString(); + + Vault vault = getVault(); + vault.storeSecret(key, value); + String secretValue = vault.resolveSecret(key); + Assertions.assertEquals(value, secretValue); + } + + @Test + @DisplayName("Delete a secret that exists") + void testDeleteSecret_exists() { + String key = UUID.randomUUID().toString(); + String value = UUID.randomUUID().toString(); + + Vault vault = getVault(); + vault.storeSecret(key, value); + vault.deleteSecret(key); + + Assertions.assertNull(vault.resolveSecret(key)); + } + + @Test + @DisplayName("Try to delete a secret that does not exist") + void testDeleteSecret_doesNotExist() { + String key = UUID.randomUUID().toString(); + + Vault vault = getVault(); + vault.deleteSecret(key); + + Assertions.assertNull(vault.resolveSecret(key)); + } +} diff --git a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultTest.java b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultTest.java new file mode 100644 index 000000000..c082b76e9 --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultTest.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial Test + * + */ + +package net.catenax.edc.hashicorpvault; + +import java.util.UUID; +import lombok.SneakyThrows; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.result.Result; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class HashicorpVaultTest { + private static final String key = "key"; + + // mocks + private HashicorpVaultClient vaultClient; + private HashicorpVault vault; + + @BeforeEach + void setup() { + vaultClient = Mockito.mock(HashicorpVaultClient.class); + final Monitor monitor = Mockito.mock(Monitor.class); + vault = new HashicorpVault(vaultClient, monitor); + } + + @Test + @SneakyThrows + void getSecret() { + // prepare + String value = UUID.randomUUID().toString(); + Result result = Mockito.mock(Result.class); + Mockito.when(vaultClient.getSecretValue(key)).thenReturn(result); + Mockito.when(result.getContent()).thenReturn(value); + Mockito.when(result.succeeded()).thenReturn(true); + + // invoke + String returnValue = vault.resolveSecret(key); + + // verify + Mockito.verify(vaultClient, Mockito.times(1)).getSecretValue(key); + Assertions.assertEquals(value, returnValue); + } + + @Test + @SneakyThrows + void setSecret() { + // prepare + String value = UUID.randomUUID().toString(); + Result result = Mockito.mock(Result.class); + Mockito.when(vaultClient.setSecret(key, value)).thenReturn(result); + Mockito.when(result.succeeded()).thenReturn(true); + + // invoke + Result returnValue = vault.storeSecret(key, value); + + // verify + Mockito.verify(vaultClient, Mockito.times(1)).setSecret(key, value); + Assertions.assertTrue(returnValue.succeeded()); + } + + @Test + @SneakyThrows + void destroySecret() { + // prepare + Result result = Mockito.mock(Result.class); + Mockito.when(vaultClient.destroySecret(key)).thenReturn(result); + Mockito.when(result.succeeded()).thenReturn(true); + + // invoke + Result returnValue = vault.deleteSecret(key); + + // verify + Mockito.verify(vaultClient, Mockito.times(1)).destroySecret(key); + Assertions.assertTrue(returnValue.succeeded()); + } +} diff --git a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/X509CertificateTestUtil.java b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/X509CertificateTestUtil.java new file mode 100644 index 000000000..210928a88 --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/X509CertificateTestUtil.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.hashicorpvault; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.time.Duration; +import java.time.Instant; +import java.util.Date; +import java.util.Optional; +import lombok.SneakyThrows; +import lombok.experimental.UtilityClass; +import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier; +import org.bouncycastle.asn1.x509.BasicConstraints; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.SubjectKeyIdentifier; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.cert.X509ExtensionUtils; +import org.bouncycastle.cert.X509v3CertificateBuilder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.DigestCalculator; +import org.bouncycastle.operator.OperatorCreationException; +import org.bouncycastle.operator.bc.BcDigestCalculatorProvider; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.testcontainers.shaded.org.bouncycastle.openssl.jcajce.JcaPEMWriter; + +@UtilityClass +final class X509CertificateTestUtil { + private static final String SIGNATURE_ALGORITHM = "SHA256WithRSAEncryption"; + private static final Provider PROVIDER = new BouncyCastleProvider(); + private static final JcaX509CertificateConverter JCA_X509_CERTIFICATE_CONVERTER = + new JcaX509CertificateConverter().setProvider(PROVIDER); + + static X509Certificate generateCertificate(int validity, String cn) + throws CertificateException, OperatorCreationException, IOException, + NoSuchAlgorithmException { + + KeyPair keyPair = generateKeyPair(); + + Instant now = Instant.now(); + ContentSigner contentSigner = + new JcaContentSignerBuilder(SIGNATURE_ALGORITHM).build(keyPair.getPrivate()); + X500Name issuer = + new X500Name( + String.format( + "CN=%s", + Optional.ofNullable(cn) + .map(String::trim) + .filter(s -> !s.isEmpty()) + .orElse("rootCA"))); + BigInteger serial = BigInteger.valueOf(now.toEpochMilli()); + Date notBefore = Date.from(now); + Date notAfter = Date.from(now.plus(Duration.ofDays(validity))); + PublicKey publicKey = keyPair.getPublic(); + X509v3CertificateBuilder certificateBuilder = + new JcaX509v3CertificateBuilder(issuer, serial, notBefore, notAfter, issuer, publicKey); + certificateBuilder = + certificateBuilder.addExtension( + Extension.subjectKeyIdentifier, false, createSubjectKeyId(publicKey)); + certificateBuilder = + certificateBuilder.addExtension( + Extension.authorityKeyIdentifier, false, createAuthorityKeyId(publicKey)); + certificateBuilder = + certificateBuilder.addExtension( + Extension.basicConstraints, true, new BasicConstraints(true)); + return JCA_X509_CERTIFICATE_CONVERTER.getCertificate(certificateBuilder.build(contentSigner)); + } + + private static KeyPair generateKeyPair() throws NoSuchAlgorithmException { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", PROVIDER); + keyPairGenerator.initialize(1024, new SecureRandom()); + + return keyPairGenerator.generateKeyPair(); + } + + private static SubjectKeyIdentifier createSubjectKeyId(PublicKey publicKey) + throws OperatorCreationException { + SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()); + DigestCalculator digCalc = + new BcDigestCalculatorProvider().get(new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1)); + return new X509ExtensionUtils(digCalc).createSubjectKeyIdentifier(publicKeyInfo); + } + + private static AuthorityKeyIdentifier createAuthorityKeyId(PublicKey publicKey) + throws OperatorCreationException { + SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()); + DigestCalculator digCalc = + new BcDigestCalculatorProvider().get(new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1)); + return new X509ExtensionUtils(digCalc).createAuthorityKeyIdentifier(publicKeyInfo); + } + + @SneakyThrows + static String convertToPem(X509Certificate certificate) { + try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) { + try (OutputStreamWriter writer = new OutputStreamWriter(stream)) { + JcaPEMWriter pemWriter = new JcaPEMWriter(writer); + pemWriter.writeObject(certificate); + pemWriter.flush(); + } + return stream.toString(StandardCharsets.UTF_8); + } + } +} diff --git a/edc-extensions/hashicorp-vault/src/test/resources/logback.xml b/edc-extensions/hashicorp-vault/src/test/resources/logback.xml new file mode 100644 index 000000000..3347fcbae --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/test/resources/logback.xml @@ -0,0 +1,23 @@ + + + + + + %d{HH:mm:ss.SSS} [%thread] sdfsfs %-5level %logger{36} - %msg%n + + + + + + diff --git a/edc-extensions/pom.xml b/edc-extensions/pom.xml index daa03d35e..bbff69f84 100644 --- a/edc-extensions/pom.xml +++ b/edc-extensions/pom.xml @@ -12,14 +12,14 @@ Mercedes-Benz Tech Innovation GmbH - Initial POM --> - - 4.0.0 - + net.catenax.edc product-edc-parent 0.0.4-SNAPSHOT + 4.0.0 net.catenax.edc.extensions edc-extensions @@ -28,6 +28,7 @@ business-partner-validation postgresql-migration + hashicorp-vault diff --git a/edc-extensions/postgresql-migration/pom.xml b/edc-extensions/postgresql-migration/pom.xml index 1b1f5a06c..6e25fb754 100644 --- a/edc-extensions/postgresql-migration/pom.xml +++ b/edc-extensions/postgresql-migration/pom.xml @@ -12,7 +12,8 @@ Mercedes-Benz Tech Innovation GmbH - Initial POM --> - + edc-extensions net.catenax.edc.extensions diff --git a/misc/license-mappings.xml b/misc/license-mappings.xml index f26375c3e..e173088a7 100644 --- a/misc/license-mappings.xml +++ b/misc/license-mappings.xml @@ -752,4 +752,10 @@ Apache License, Version 2.0 0.0.1-SNAPSHOT + + org.eclipse.dataspaceconnector + junit-extension + Apache License, Version 2.0 + 0.0.1-SNAPSHOT + diff --git a/pom.xml b/pom.xml index 813f9905c..4fe84ed30 100644 --- a/pom.xml +++ b/pom.xml @@ -48,8 +48,14 @@ 3.0.0 3.4.0 2.0.0 + 2.22.2 3.0.0-M2 3.2.1 + 3.0.0-M6 + 3.10.1 + 3.2.0 + 1.18.20.0 + 1.0.6.1 0.0.1-SNAPSHOT @@ -61,6 +67,13 @@ 5.8.2 1.8.2 4.6.1 + 1.18.20 + 1.70 + 4.9.3 + 1.16.3 + 2.0.0-alpha1 + 1.2.6 + 1.2.6 @@ -107,6 +120,11 @@ maven-jar-plugin ${org.apache.maven.plugins.maven.jar.plugin.version} + + org.apache.maven.plugins + maven-resources-plugin + ${org.apache.maven.plugins.resources.version} + org.apache.maven.plugins maven-dependency-plugin @@ -145,8 +163,24 @@ ${org.apache.maven.plugins.source.version} + org.apache.maven.plugins + maven-compiler-plugin + ${org.apache.maven.plugins.compiler.version} + + + org.projectlombok + lombok-maven-plugin + ${org.projectlombok.lombok.maven.plugin.version} + + + org.apache.maven.plugins + maven-failsafe-plugin + ${org.apache.maven.plugins.failsafe.version} + + + org.apache.maven.plugins maven-surefire-plugin - 2.22.2 + ${org.apache.maven.plugins.surefire.version} org.codehaus.mojo @@ -241,6 +275,11 @@ postgresql-migration ${project.version} + + net.catenax.edc.extensions + hashicorp-vault + ${project.version} + @@ -253,6 +292,41 @@ flyway-core ${org.flywaydb.version} + + org.projectlombok + lombok + ${org.projectlombok.lombok.version} + + + org.bouncycastle + bcpkix-jdk15on + ${org.bouncycastle.bcpkix-jdk15on.version} + + + com.squareup.okhttp3 + okhttp + ${com.squareup.okhttp3.okhttp.version} + + + org.slf4j + slf4j-api + ${org.slf4j.version} + + + org.slf4j + jul-to-slf4j + ${org.slf4j.version} + + + ch.qos.logback + logback-classic + ${ch.qos.logback.logback-classic.version} + + + ch.qos.logback + logback-core + ${ch.qos.logback.logback-core.version} + @@ -880,6 +954,17 @@ web-spi ${org.eclipse.dataspaceconnector.version} + + org.eclipse.dataspaceconnector + junit-extension + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + junit-extension + ${org.eclipse.dataspaceconnector.version} + test-fixtures + @@ -903,6 +988,14 @@ pom import + + + + org.testcontainers + junit-jupiter + ${org.testcontainers.version} + test + @@ -917,17 +1010,17 @@ --> org.jasig.maven maven-notice-plugin - 1.1.0 + ${org.jasig.maven.notice.plugin.version} jakarta.xml.bind jakarta.xml.bind-api - 4.0.0 + 2.3.3 org.glassfish.jaxb jaxb-runtime - 3.0.2 + 2.3.3 From 2e8848ab78ff7cf7f63b02baebc32879091f66cf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Jun 2022 16:57:59 +0200 Subject: [PATCH 128/433] Bump actions/setup-python from 3 to 4 (#196) --- .github/workflows/helm-lint.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/helm-lint.yaml b/.github/workflows/helm-lint.yaml index 8b49c30b7..cd366ea2d 100644 --- a/.github/workflows/helm-lint.yaml +++ b/.github/workflows/helm-lint.yaml @@ -37,7 +37,7 @@ jobs: version: v3.8.1 - name: python (setup) - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: python-version: 3.7 - From bf9257919d5f9fb4d2482eca42d1f37a3ed1b5f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Jun 2022 16:58:10 +0200 Subject: [PATCH 129/433] Bump maven-surefire-plugin from 3.0.0-M6 to 3.0.0-M7 (#198) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4fe84ed30..e1adb678b 100644 --- a/pom.xml +++ b/pom.xml @@ -51,7 +51,7 @@ 2.22.2 3.0.0-M2 3.2.1 - 3.0.0-M6 + 3.0.0-M7 3.10.1 3.2.0 1.18.20.0 From 79ead2818ff97a32c4546f31b9220792ea2b9799 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Jun 2022 16:58:21 +0200 Subject: [PATCH 130/433] Bump logback-classic from 1.2.6 to 1.2.11 (#200) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e1adb678b..28f659769 100644 --- a/pom.xml +++ b/pom.xml @@ -72,7 +72,7 @@ 4.9.3 1.16.3 2.0.0-alpha1 - 1.2.6 + 1.2.11 1.2.6 From cf12880fb49efd876f28e2cb491ed4b6b7cea5c5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Jun 2022 16:58:41 +0200 Subject: [PATCH 131/433] Bump org.testcontainers.version from 1.16.3 to 1.17.2 (#199) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 28f659769..f910fc556 100644 --- a/pom.xml +++ b/pom.xml @@ -70,7 +70,7 @@ 1.18.20 1.70 4.9.3 - 1.16.3 + 1.17.2 2.0.0-alpha1 1.2.11 1.2.6 From cc5705f91c61626f4ef4ff8a5a44049eea0184e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Jun 2022 17:02:32 +0200 Subject: [PATCH 132/433] Bump lombok from 1.18.20 to 1.18.24 (#201) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f910fc556..46911b866 100644 --- a/pom.xml +++ b/pom.xml @@ -67,7 +67,7 @@ 5.8.2 1.8.2 4.6.1 - 1.18.20 + 1.18.24 1.70 4.9.3 1.17.2 From b4124d9217af84c7760c40258a84a7edc67f989d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Jun 2022 17:12:41 +0200 Subject: [PATCH 133/433] Bump maven-notice-plugin from 1.0.6.1 to 1.1.0 (#205) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 46911b866..f4af80f35 100644 --- a/pom.xml +++ b/pom.xml @@ -55,7 +55,7 @@ 3.10.1 3.2.0 1.18.20.0 - 1.0.6.1 + 1.1.0 0.0.1-SNAPSHOT From 93a17b9d67570b2e20bebc45b2671d943f6d6084 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Jun 2022 17:13:08 +0200 Subject: [PATCH 134/433] Bump logback-core from 1.2.6 to 1.2.9 (#203) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f4af80f35..c6879027a 100644 --- a/pom.xml +++ b/pom.xml @@ -73,7 +73,7 @@ 1.17.2 2.0.0-alpha1 1.2.11 - 1.2.6 + 1.2.9 From 9272a520525b036f3f143bba9b5d95acf577cbbc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Jun 2022 17:34:45 +0200 Subject: [PATCH 135/433] Bump org.slf4j.version from 2.0.0-alpha1 to 2.0.0-alpha7 (#202) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c6879027a..da9b877f7 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ 1.70 4.9.3 1.17.2 - 2.0.0-alpha1 + 2.0.0-alpha7 1.2.11 1.2.9 From 07eaaa8b27c6699b3c3958cc0de7cb622ac439eb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Jun 2022 17:37:13 +0200 Subject: [PATCH 136/433] Bump logback-core from 1.2.6 to 1.2.11 (#206) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index da9b877f7..b2502bb2a 100644 --- a/pom.xml +++ b/pom.xml @@ -73,7 +73,7 @@ 1.17.2 2.0.0-alpha7 1.2.11 - 1.2.9 + 1.2.11 From 511dddabfaae32741e9fc145a12ff8c1273211e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Jun 2022 17:37:59 +0200 Subject: [PATCH 137/433] Bump jakarta.xml.bind-api from 2.3.3 to 4.0.0 (#204) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b2502bb2a..5884514bb 100644 --- a/pom.xml +++ b/pom.xml @@ -1015,7 +1015,7 @@ jakarta.xml.bind jakarta.xml.bind-api - 2.3.3 + 4.0.0 org.glassfish.jaxb From 861ee95c5fd8cdff7ca0833a4cfcdf9631f513b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jun 2022 10:24:07 +0200 Subject: [PATCH 138/433] Bump postgresql from 42.3.6 to 42.4.0 (#208) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5884514bb..a3f3c1aaa 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ 0.0.1-SNAPSHOT 1.2.2 - 42.3.6 + 42.4.0 8.5.12 From c0d3374c3a1272d5f0e1f1f4ff8cd21cb6fce1f9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jun 2022 09:39:27 +0200 Subject: [PATCH 139/433] Bump spotless-maven-plugin from 2.22.6 to 2.22.8 (#211) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a3f3c1aaa..257c3cd78 100644 --- a/pom.xml +++ b/pom.xml @@ -44,7 +44,7 @@ 3.3.0 3.2.2 - 2.22.6 + 2.22.8 3.0.0 3.4.0 2.0.0 From 75a13c71650f91ea9484003f926d8abaa338d69e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Jun 2022 08:19:24 +0200 Subject: [PATCH 140/433] Bump flyway-core from 8.5.12 to 8.5.13 (#217) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 257c3cd78..dbdde913d 100644 --- a/pom.xml +++ b/pom.xml @@ -61,7 +61,7 @@ 0.0.1-SNAPSHOT 1.2.2 42.4.0 - 8.5.12 + 8.5.13 5.8.2 From d6af5cc9ada28650250130a4fddf3976c7cb4f53 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Tue, 21 Jun 2022 10:58:20 +0200 Subject: [PATCH 141/433] Introduce controlplane-base and dataplane-base module (#209) --- .../edc-controlplane-base/README.md | 7 + .../edc-controlplane-base/pom.xml | 154 +++++++++++++++ .../edc-controlplane-memory/pom.xml | 120 +----------- .../pom.xml | 184 +----------------- .../edc-controlplane-postgresql/pom.xml | 120 +----------- edc-controlplane/pom.xml | 3 + .../edc-dataplane-azure-vault/pom.xml | 61 +----- edc-dataplane/edc-dataplane-base/README.md | 7 + edc-dataplane/edc-dataplane-base/pom.xml | 97 +++++++++ .../edc-dataplane-hashicorp-vault/pom.xml | 61 +----- edc-dataplane/pom.xml | 2 + edc-extensions/hashicorp-vault/pom.xml | 5 - pom.xml | 25 ++- 13 files changed, 311 insertions(+), 535 deletions(-) create mode 100644 edc-controlplane/edc-controlplane-base/README.md create mode 100644 edc-controlplane/edc-controlplane-base/pom.xml create mode 100644 edc-dataplane/edc-dataplane-base/README.md create mode 100644 edc-dataplane/edc-dataplane-base/pom.xml diff --git a/edc-controlplane/edc-controlplane-base/README.md b/edc-controlplane/edc-controlplane-base/README.md new file mode 100644 index 000000000..98888200a --- /dev/null +++ b/edc-controlplane/edc-controlplane-base/README.md @@ -0,0 +1,7 @@ +# EDC Control-Plane Base Module + +### Building + +```shell +./mvnw -pl .,edc-controlplane/edc-controlplane-base -am package +``` diff --git a/edc-controlplane/edc-controlplane-base/pom.xml b/edc-controlplane/edc-controlplane-base/pom.xml new file mode 100644 index 000000000..8e5cf983a --- /dev/null +++ b/edc-controlplane/edc-controlplane-base/pom.xml @@ -0,0 +1,154 @@ + + + + edc-controlplane + net.catenax.edc + 0.0.4-SNAPSHOT + + 4.0.0 + + edc-controlplane-base + jar + + + ${project.artifactId} + + + src/main/resources + + **/* + + + + ../../ + META-INF + + NOTICE.md + LICENSE + + + + + + + + + net.catenax.edc.extensions + business-partner-validation + + + + + org.eclipse.dataspaceconnector + filesystem-configuration + + + + + org.eclipse.dataspaceconnector + auth-tokenbased + + + + + org.eclipse.dataspaceconnector + control-api + + + org.eclipse.dataspaceconnector + data-management-api + + + org.eclipse.dataspaceconnector + observability-api + + + + + org.eclipse.dataspaceconnector + ids-api-configuration + + + org.eclipse.dataspaceconnector + ids-api-multipart-dispatcher-v1 + + + org.eclipse.dataspaceconnector + ids-api-multipart-endpoint-v1 + + + org.eclipse.dataspaceconnector + ids-api-transform-v1 + + + org.eclipse.dataspaceconnector + ids-core + + + org.eclipse.dataspaceconnector + ids-spi + + + org.eclipse.dataspaceconnector + ids-token-validation + + + + + org.eclipse.dataspaceconnector + core-base + + + org.eclipse.dataspaceconnector + core-boot + + + org.eclipse.dataspaceconnector + transfer + + + org.eclipse.dataspaceconnector + contract + + + + + org.eclipse.dataspaceconnector + data-plane-transfer-sync + + + + + org.eclipse.dataspaceconnector + core-micrometer + + + org.eclipse.dataspaceconnector + jersey-micrometer + + + org.eclipse.dataspaceconnector + jetty-micrometer + + + + + org.eclipse.dataspaceconnector + jdk-logger-monitor + + + + + + org.eclipse.dataspaceconnector + http + + + + org.eclipse.dataspaceconnector + http-receiver + + + \ No newline at end of file diff --git a/edc-controlplane/edc-controlplane-memory/pom.xml b/edc-controlplane/edc-controlplane-memory/pom.xml index 5a79b2d70..1a4b3125a 100644 --- a/edc-controlplane/edc-controlplane-memory/pom.xml +++ b/edc-controlplane/edc-controlplane-memory/pom.xml @@ -77,22 +77,10 @@ - + - net.catenax.edc.extensions - business-partner-validation - - - - - org.eclipse.dataspaceconnector - filesystem-configuration - - - - - org.eclipse.dataspaceconnector - auth-tokenbased + net.catenax.edc + edc-controlplane-base @@ -111,74 +99,6 @@ - - - org.eclipse.dataspaceconnector - control-api - - - org.eclipse.dataspaceconnector - data-management-api - - - org.eclipse.dataspaceconnector - observability-api - - - - - org.eclipse.dataspaceconnector - ids-api-configuration - - - org.eclipse.dataspaceconnector - ids-api-multipart-dispatcher-v1 - - - org.eclipse.dataspaceconnector - ids-api-multipart-endpoint-v1 - - - org.eclipse.dataspaceconnector - ids-api-transform-v1 - - - org.eclipse.dataspaceconnector - ids-core - - - org.eclipse.dataspaceconnector - ids-spi - - - org.eclipse.dataspaceconnector - ids-token-validation - - - - - org.eclipse.dataspaceconnector - core-base - - - org.eclipse.dataspaceconnector - core-boot - - - org.eclipse.dataspaceconnector - transfer - - - org.eclipse.dataspaceconnector - contract - - - - - org.eclipse.dataspaceconnector - data-plane-transfer-sync - - org.eclipse.dataspaceconnector @@ -189,40 +109,6 @@ iam-daps - - - org.eclipse.dataspaceconnector - core-micrometer - - - org.eclipse.dataspaceconnector - jersey-micrometer - - - org.eclipse.dataspaceconnector - jetty-micrometer - - - - - - org.eclipse.dataspaceconnector - jdk-logger-monitor - - - - - - org.eclipse.dataspaceconnector - http - - - - - org.eclipse.dataspaceconnector - http-receiver - - com.azure diff --git a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/pom.xml b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/pom.xml index b7326cf94..59f8cf258 100644 --- a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/pom.xml +++ b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/pom.xml @@ -77,26 +77,16 @@ - + - net.catenax.edc.extensions - business-partner-validation - - - net.catenax.edc.extensions - postgresql-migration - - - - - org.eclipse.dataspaceconnector - filesystem-configuration - - - - - org.eclipse.dataspaceconnector - auth-tokenbased + net.catenax.edc + edc-controlplane-postgresql + + + org.eclipse.dataspaceconnector + azure-vault + + @@ -104,162 +94,6 @@ net.catenax.edc.extensions hashicorp-vault - - - - org.eclipse.dataspaceconnector - control-api - - - org.eclipse.dataspaceconnector - data-management-api - - - org.eclipse.dataspaceconnector - observability-api - - - - - org.eclipse.dataspaceconnector - ids-api-configuration - - - org.eclipse.dataspaceconnector - ids-api-multipart-dispatcher-v1 - - - org.eclipse.dataspaceconnector - ids-api-multipart-endpoint-v1 - - - org.eclipse.dataspaceconnector - ids-api-transform-v1 - - - org.eclipse.dataspaceconnector - ids-core - - - org.eclipse.dataspaceconnector - ids-spi - - - org.eclipse.dataspaceconnector - ids-token-validation - - - - - org.eclipse.dataspaceconnector - asset-index-sql - - - org.eclipse.dataspaceconnector - contractdefinition-store-sql - - - org.eclipse.dataspaceconnector - contractnegotiation-store-sql - - - org.eclipse.dataspaceconnector - transfer-process-store-sql - - - org.eclipse.dataspaceconnector - policy-store-sql - - - - - org.eclipse.dataspaceconnector - core-base - - - org.eclipse.dataspaceconnector - core-boot - - - org.eclipse.dataspaceconnector - transfer - - - org.eclipse.dataspaceconnector - contract - - - - - org.eclipse.dataspaceconnector - data-plane-transfer-sync - - - - - org.eclipse.dataspaceconnector - oauth2-core - - - org.eclipse.dataspaceconnector - iam-daps - - - - - org.eclipse.dataspaceconnector - core-micrometer - - - org.eclipse.dataspaceconnector - jersey-micrometer - - - org.eclipse.dataspaceconnector - jetty-micrometer - - - - - org.eclipse.dataspaceconnector - jdk-logger-monitor - - - - - - org.eclipse.dataspaceconnector - http - - - - org.eclipse.dataspaceconnector - apache-commons-pool-sql - - - org.eclipse.dataspaceconnector - transaction-local - - - - - org.eclipse.dataspaceconnector - http-receiver - - - - - com.azure - azure-identity - - - com.azure - azure-security-keyvault-secrets - - - org.postgresql - postgresql - diff --git a/edc-controlplane/edc-controlplane-postgresql/pom.xml b/edc-controlplane/edc-controlplane-postgresql/pom.xml index dd765bbd2..35c48297b 100644 --- a/edc-controlplane/edc-controlplane-postgresql/pom.xml +++ b/edc-controlplane/edc-controlplane-postgresql/pom.xml @@ -79,28 +79,18 @@ - + - net.catenax.edc.extensions - business-partner-validation + net.catenax.edc + edc-controlplane-base + + net.catenax.edc.extensions postgresql-migration - - - org.eclipse.dataspaceconnector - filesystem-configuration - - - - - org.eclipse.dataspaceconnector - auth-tokenbased - - org.eclipse.dataspaceconnector @@ -117,50 +107,6 @@ - - - org.eclipse.dataspaceconnector - control-api - - - org.eclipse.dataspaceconnector - data-management-api - - - org.eclipse.dataspaceconnector - observability-api - - - - - org.eclipse.dataspaceconnector - ids-api-configuration - - - org.eclipse.dataspaceconnector - ids-api-multipart-dispatcher-v1 - - - org.eclipse.dataspaceconnector - ids-api-multipart-endpoint-v1 - - - org.eclipse.dataspaceconnector - ids-api-transform-v1 - - - org.eclipse.dataspaceconnector - ids-core - - - org.eclipse.dataspaceconnector - ids-spi - - - org.eclipse.dataspaceconnector - ids-token-validation - - org.eclipse.dataspaceconnector @@ -183,30 +129,6 @@ policy-store-sql - - - org.eclipse.dataspaceconnector - core-base - - - org.eclipse.dataspaceconnector - core-boot - - - org.eclipse.dataspaceconnector - transfer - - - org.eclipse.dataspaceconnector - contract - - - - - org.eclipse.dataspaceconnector - data-plane-transfer-sync - - org.eclipse.dataspaceconnector @@ -217,33 +139,8 @@ iam-daps - - - org.eclipse.dataspaceconnector - core-micrometer - - - org.eclipse.dataspaceconnector - jersey-micrometer - - - org.eclipse.dataspaceconnector - jetty-micrometer - - - - - org.eclipse.dataspaceconnector - jdk-logger-monitor - - - - org.eclipse.dataspaceconnector - http - - org.eclipse.dataspaceconnector apache-commons-pool-sql @@ -253,12 +150,6 @@ transaction-local - - - org.eclipse.dataspaceconnector - http-receiver - - com.azure @@ -274,7 +165,6 @@ - + - org.eclipse.dataspaceconnector - filesystem-configuration + net.catenax.edc + edc-dataplane-base @@ -98,61 +98,6 @@ - - - org.eclipse.dataspaceconnector - data-plane-framework - - - org.eclipse.dataspaceconnector - data-plane-http - - - org.eclipse.dataspaceconnector - data-plane-api - - - org.eclipse.dataspaceconnector - observability-api - - - - - org.eclipse.dataspaceconnector - core-base - - - org.eclipse.dataspaceconnector - core-boot - - - - - org.eclipse.dataspaceconnector - core-micrometer - - - org.eclipse.dataspaceconnector - jersey-micrometer - - - org.eclipse.dataspaceconnector - jetty-micrometer - - - - - org.eclipse.dataspaceconnector - jdk-logger-monitor - - - - - - org.eclipse.dataspaceconnector - http - - com.azure diff --git a/edc-dataplane/edc-dataplane-base/README.md b/edc-dataplane/edc-dataplane-base/README.md new file mode 100644 index 000000000..448937507 --- /dev/null +++ b/edc-dataplane/edc-dataplane-base/README.md @@ -0,0 +1,7 @@ +# EDC Data-Plane Base Module + +### Building + +```shell +./mvnw -pl .,edc-dataplane/edc-dataplane-base -am package +``` diff --git a/edc-dataplane/edc-dataplane-base/pom.xml b/edc-dataplane/edc-dataplane-base/pom.xml new file mode 100644 index 000000000..cac73ab05 --- /dev/null +++ b/edc-dataplane/edc-dataplane-base/pom.xml @@ -0,0 +1,97 @@ + + + + edc-dataplane + net.catenax.edc + 0.0.4-SNAPSHOT + + 4.0.0 + + edc-dataplane-base + jar + + + ${project.artifactId} + + + src/main/resources + + **/* + + + + ../../ + META-INF + + NOTICE.md + LICENSE + + + + + + + + + org.eclipse.dataspaceconnector + filesystem-configuration + + + + + org.eclipse.dataspaceconnector + data-plane-framework + + + org.eclipse.dataspaceconnector + data-plane-http + + + org.eclipse.dataspaceconnector + data-plane-api + + + org.eclipse.dataspaceconnector + observability-api + + + + + org.eclipse.dataspaceconnector + core-base + + + org.eclipse.dataspaceconnector + core-boot + + + + + org.eclipse.dataspaceconnector + core-micrometer + + + org.eclipse.dataspaceconnector + jersey-micrometer + + + org.eclipse.dataspaceconnector + jetty-micrometer + + + + + org.eclipse.dataspaceconnector + jdk-logger-monitor + + + + + + org.eclipse.dataspaceconnector + http + + + diff --git a/edc-dataplane/edc-dataplane-hashicorp-vault/pom.xml b/edc-dataplane/edc-dataplane-hashicorp-vault/pom.xml index fe4859ada..c21cdfc73 100644 --- a/edc-dataplane/edc-dataplane-hashicorp-vault/pom.xml +++ b/edc-dataplane/edc-dataplane-hashicorp-vault/pom.xml @@ -78,10 +78,10 @@ - + - org.eclipse.dataspaceconnector - filesystem-configuration + net.catenax.edc + edc-dataplane-base @@ -89,61 +89,6 @@ net.catenax.edc.extensions hashicorp-vault - - - - org.eclipse.dataspaceconnector - data-plane-framework - - - org.eclipse.dataspaceconnector - data-plane-http - - - org.eclipse.dataspaceconnector - data-plane-api - - - org.eclipse.dataspaceconnector - observability-api - - - - - org.eclipse.dataspaceconnector - core-base - - - org.eclipse.dataspaceconnector - core-boot - - - - - org.eclipse.dataspaceconnector - core-micrometer - - - org.eclipse.dataspaceconnector - jersey-micrometer - - - org.eclipse.dataspaceconnector - jetty-micrometer - - - - - org.eclipse.dataspaceconnector - jdk-logger-monitor - - - - - - org.eclipse.dataspaceconnector - http - diff --git a/edc-dataplane/pom.xml b/edc-dataplane/pom.xml index 92fab39fb..a6bb0e464 100644 --- a/edc-dataplane/pom.xml +++ b/edc-dataplane/pom.xml @@ -25,6 +25,8 @@ pom + edc-dataplane-base + edc-dataplane-azure-vault edc-dataplane-hashicorp-vault diff --git a/edc-extensions/hashicorp-vault/pom.xml b/edc-extensions/hashicorp-vault/pom.xml index 15e987e85..51b8753cc 100644 --- a/edc-extensions/hashicorp-vault/pom.xml +++ b/edc-extensions/hashicorp-vault/pom.xml @@ -179,11 +179,6 @@ jul-to-slf4j test - - ch.qos.logback - logback-classic - test - org.hamcrest hamcrest diff --git a/pom.xml b/pom.xml index dbdde913d..b8e485200 100644 --- a/pom.xml +++ b/pom.xml @@ -71,8 +71,7 @@ 1.70 4.9.3 1.17.2 - 2.0.0-alpha7 - 1.2.11 + 2.0.0-alpha1 1.2.11 @@ -281,6 +280,23 @@ ${project.version} + + + net.catenax.edc + edc-controlplane-base + ${project.version} + + + net.catenax.edc + edc-controlplane-postgresql + ${project.version} + + + net.catenax.edc + edc-dataplane-base + ${project.version} + + org.postgresql @@ -317,11 +333,6 @@ jul-to-slf4j ${org.slf4j.version} - - ch.qos.logback - logback-classic - ${ch.qos.logback.logback-classic.version} - ch.qos.logback logback-core From 527f11d7b6a80d1b5ee79ae4136c0af3ff82a17e Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Wed, 22 Jun 2022 14:24:05 +0200 Subject: [PATCH 142/433] Remove Control-Api Extension (#222) * remove control api extension Signed-off-by: Dominik Pinsel Co-authored-by: Florian Rusch (ZF Friedrichshafen AG) --- deployment/helm/edc-controlplane/README.md | 2 +- deployment/helm/edc-controlplane/values.yaml | 2 - deployment/helm/edc-dataplane/README.md | 2 +- deployment/helm/edc-dataplane/values.yaml | 2 - edc-controlplane/README.md | 39 ------------------- .../edc-controlplane-base/pom.xml | 4 -- .../edc-controlplane-memory/README.md | 8 ++-- .../README.md | 8 ++-- .../edc-controlplane-postgresql/README.md | 8 ++-- 9 files changed, 11 insertions(+), 64 deletions(-) diff --git a/deployment/helm/edc-controlplane/README.md b/deployment/helm/edc-controlplane/README.md index 9d60d16c7..e17ad0398 100644 --- a/deployment/helm/edc-controlplane/README.md +++ b/deployment/helm/edc-controlplane/README.md @@ -23,7 +23,7 @@ $ helm install my-release catenax-ng-product-edc/edc-controlplane --version 0.0. | autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | | autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | | autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | -| configuration.properties | string | `"# edc.api.auth.key=\n# edc.api.control.auth.apikey.key=\n# edc.api.control.auth.apikey.value=\n# edc.atomikos.checkpoint.interval=\n# edc.atomikos.directory=\n# edc.atomikos.logging=\n# edc.atomikos.threaded2pc=\n# edc.atomikos.timeout=\n# edc.aws.access.key=\n# edc.aws.provision.retry.retries.max=\n# edc.aws.provision.role.duration.session.max=\n# edc.aws.secret.access.key=\n# edc.blobstore.endpoint=\n# edc.controlplane.validation-endpoint=\n# edc.core.retry.backoff.max=\n# edc.core.retry.backoff.min=\n# edc.core.retry.retries.max=\n# edc.core.system.health.check.liveness-period=\n# edc.core.system.health.check.readiness-period=\n# edc.core.system.health.check.startup-period=\n# edc.core.system.health.check.threadpool-size=\n# edc.dataplane.queue.capacity=\n# edc.dataplane.wait=\n# edc.dataplane.workers=\n# edc.datasource.asset.name=\"default\"\n# edc.datasource.contractdefinition.name=\"default\"\n# edc.datasource.contractnegotiation.name=\"default\"\n# edc.datasource.policy.name=\"default\"\n# edc.datasource.transferprocess.name=\"default\"\n# edc.datasource.default.pool.maxIdleConnections=\n# edc.datasource.default.pool.maxTotalConnections=\n# edc.datasource.default.pool.minIdleConnections=\n# edc.datasource.default.pool.testConnectionOnBorrow=\n# edc.datasource.default.pool.testConnectionOnCreate=\n# edc.datasource.default.pool.testConnectionOnReturn=\n# edc.datasource.default.pool.testConnectionWhileIdle=\n# edc.datasource.default.pool.testQuery=\n# edc.datasource.default.url=\n# edc.datasource.default.user=\n# edc.datasource.default.password=\n# edc.dpf.selector.url=\n# edc.events.topic.endpoint=\n# edc.events.topic.name=\n# edc.fs.config=\n# edc.hostname=\n# edc.identity.did.url=\n# edc.ids.catalog.id=\n# edc.ids.curator=\n# edc.ids.description=\n# edc.ids.endpoint=\n# edc.ids.id=\n# edc.ids.maintainer=\n# edc.ids.security.profile=\n# edc.ids.title=\n# edc.ids.validation.referringconnector=\n# edc.ion.crawler.did-type=\n# edc.ion.crawler.interval-minutes=\n# edc.ion.crawler.ion.url=\n# edc.metrics.enabled=\n# edc.metrics.executor.enabled=\n# edc.metrics.jersey.enabled=\n# edc.metrics.jetty.enabled=\n# edc.metrics.okhttp.enabled=\n# edc.metrics.system.enabled=\n# edc.negotiation.consumer.state-machine.batch-size=\n# edc.negotiation.provider.state-machine.batch-size=\n# edc.oauth.client.id=\n# edc.oauth.private.key.alias=\n# edc.oauth.provider.audience=\n# edc.oauth.provider.jwks.refresh=\n# edc.oauth.provider.jwks.url=\n# edc.oauth.public.key.alias=\n# edc.oauth.token.url=\n# edc.oauth.validation.nbf.leeway=\n# edc.receiver.http.auth-code=\n# edc.receiver.http.auth-key=\n# edc.receiver.http.endpoint=\n# edc.transfer.proxy.endpoint=\n# edc.transfer.dataplane.sync.token.validity=\n# edc.transfer.proxy.token.signer.privatekey.alias=\n# edc.transfer.functions.check.endpoint=\n# edc.transfer.functions.enabled.protocols=\n# edc.transfer.functions.transfer.endpoint=\n# edc.transfer-process-store.database.name=\n# edc.transfer.state-machine.batch-size=\n# edc.vault=\n# edc.vault.certificate=\n# edc.vault.clientid=\n# edc.vault.clientsecret=\n# edc.vault.name=\n# edc.vault.tenantid=\n# edc.vault.url=\n# edc.vault.token=\n# edc.vault.timeout.seconds=\n# edc.webdid.doh.url=\n# edc.web.rest.cors.enabled=\n# edc.web.rest.cors.headers=\n# edc.web.rest.cors.methods=\n# edc.web.rest.cors.origins="` | EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) | +| configuration.properties | string | `"# edc.api.auth.key=\n# edc.atomikos.checkpoint.interval=\n# edc.atomikos.directory=\n# edc.atomikos.logging=\n# edc.atomikos.threaded2pc=\n# edc.atomikos.timeout=\n# edc.aws.access.key=\n# edc.aws.provision.retry.retries.max=\n# edc.aws.provision.role.duration.session.max=\n# edc.aws.secret.access.key=\n# edc.blobstore.endpoint=\n# edc.controlplane.validation-endpoint=\n# edc.core.retry.backoff.max=\n# edc.core.retry.backoff.min=\n# edc.core.retry.retries.max=\n# edc.core.system.health.check.liveness-period=\n# edc.core.system.health.check.readiness-period=\n# edc.core.system.health.check.startup-period=\n# edc.core.system.health.check.threadpool-size=\n# edc.dataplane.queue.capacity=\n# edc.dataplane.wait=\n# edc.dataplane.workers=\n# edc.datasource.asset.name=\"default\"\n# edc.datasource.contractdefinition.name=\"default\"\n# edc.datasource.contractnegotiation.name=\"default\"\n# edc.datasource.policy.name=\"default\"\n# edc.datasource.transferprocess.name=\"default\"\n# edc.datasource.default.pool.maxIdleConnections=\n# edc.datasource.default.pool.maxTotalConnections=\n# edc.datasource.default.pool.minIdleConnections=\n# edc.datasource.default.pool.testConnectionOnBorrow=\n# edc.datasource.default.pool.testConnectionOnCreate=\n# edc.datasource.default.pool.testConnectionOnReturn=\n# edc.datasource.default.pool.testConnectionWhileIdle=\n# edc.datasource.default.pool.testQuery=\n# edc.datasource.default.url=\n# edc.datasource.default.user=\n# edc.datasource.default.password=\n# edc.dpf.selector.url=\n# edc.events.topic.endpoint=\n# edc.events.topic.name=\n# edc.fs.config=\n# edc.hostname=\n# edc.identity.did.url=\n# edc.ids.catalog.id=\n# edc.ids.curator=\n# edc.ids.description=\n# edc.ids.endpoint=\n# edc.ids.id=\n# edc.ids.maintainer=\n# edc.ids.security.profile=\n# edc.ids.title=\n# edc.ids.validation.referringconnector=\n# edc.ion.crawler.did-type=\n# edc.ion.crawler.interval-minutes=\n# edc.ion.crawler.ion.url=\n# edc.metrics.enabled=\n# edc.metrics.executor.enabled=\n# edc.metrics.jersey.enabled=\n# edc.metrics.jetty.enabled=\n# edc.metrics.okhttp.enabled=\n# edc.metrics.system.enabled=\n# edc.negotiation.consumer.state-machine.batch-size=\n# edc.negotiation.provider.state-machine.batch-size=\n# edc.oauth.client.id=\n# edc.oauth.private.key.alias=\n# edc.oauth.provider.audience=\n# edc.oauth.provider.jwks.refresh=\n# edc.oauth.provider.jwks.url=\n# edc.oauth.public.key.alias=\n# edc.oauth.token.url=\n# edc.oauth.validation.nbf.leeway=\n# edc.receiver.http.auth-code=\n# edc.receiver.http.auth-key=\n# edc.receiver.http.endpoint=\n# edc.transfer.proxy.endpoint=\n# edc.transfer.dataplane.sync.token.validity=\n# edc.transfer.proxy.token.signer.privatekey.alias=\n# edc.transfer.functions.check.endpoint=\n# edc.transfer.functions.enabled.protocols=\n# edc.transfer.functions.transfer.endpoint=\n# edc.transfer-process-store.database.name=\n# edc.transfer.state-machine.batch-size=\n# edc.vault=\n# edc.vault.certificate=\n# edc.vault.clientid=\n# edc.vault.clientsecret=\n# edc.vault.name=\n# edc.vault.tenantid=\n# edc.vault.url=\n# edc.vault.token=\n# edc.vault.timeout.seconds=\n# edc.webdid.doh.url=\n# edc.web.rest.cors.enabled=\n# edc.web.rest.cors.headers=\n# edc.web.rest.cors.methods=\n# edc.web.rest.cors.origins="` | EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) | | edc.endpoints.control.path | string | `"/api/controlplane/control"` | The path mapping the "control" api is going to be exposed at | | edc.endpoints.control.port | string | `"9999"` | The network port, which the "control" api is going to be exposed by the container, pod and service | | edc.endpoints.data.path | string | `"/data"` | The path mapping the "data" management api is going to be exposed at | diff --git a/deployment/helm/edc-controlplane/values.yaml b/deployment/helm/edc-controlplane/values.yaml index 778549497..a13f729b1 100644 --- a/deployment/helm/edc-controlplane/values.yaml +++ b/deployment/helm/edc-controlplane/values.yaml @@ -238,8 +238,6 @@ configuration: # -- EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) properties: |- # edc.api.auth.key= - # edc.api.control.auth.apikey.key= - # edc.api.control.auth.apikey.value= # edc.atomikos.checkpoint.interval= # edc.atomikos.directory= # edc.atomikos.logging= diff --git a/deployment/helm/edc-dataplane/README.md b/deployment/helm/edc-dataplane/README.md index d2ae5e616..6fdfbc9b2 100644 --- a/deployment/helm/edc-dataplane/README.md +++ b/deployment/helm/edc-dataplane/README.md @@ -23,7 +23,7 @@ $ helm install my-release catenax-ng-product-edc/edc-dataplane --version 0.0.3 | autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | | autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | | autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | -| configuration.properties | string | `"# edc.api.control.auth.apikey.key=\n# edc.api.control.auth.apikey.value=\n# edc.atomikos.checkpoint.interval=\n# edc.atomikos.directory=\n# edc.atomikos.logging=\n# edc.atomikos.threaded2pc=\n# edc.atomikos.timeout=\n# edc.aws.access.key=\n# edc.aws.provision.retry.retries.max=\n# edc.aws.provision.role.duration.session.max=\n# edc.aws.secret.access.key=\n# edc.blobstore.endpoint=\n# edc.controlplane.validation-endpoint=\n# edc.core.retry.backoff.max=\n# edc.core.retry.backoff.min=\n# edc.core.retry.retries.max=\n# edc.core.system.health.check.liveness-period=\n# edc.core.system.health.check.readiness-period=\n# edc.core.system.health.check.startup-period=\n# edc.core.system.health.check.threadpool-size=\n# edc.dataplane.queue.capacity=\n# edc.dataplane.wait=\n# edc.dataplane.workers=\n# edc.datasource.asset.name=\"default\"\n# edc.datasource.contractdefinition.name=\"default\"\n# edc.datasource.contractnegotiation.name=\"default\"\n# edc.datasource.policy.name=\"default\"\n# edc.datasource.transferprocess.name=\"default\"\n# edc.datasource.default.pool.maxIdleConnections=\n# edc.datasource.default.pool.maxTotalConnections=\n# edc.datasource.default.pool.minIdleConnections=\n# edc.datasource.default.pool.testConnectionOnBorrow=\n# edc.datasource.default.pool.testConnectionOnCreate=\n# edc.datasource.default.pool.testConnectionOnReturn=\n# edc.datasource.default.pool.testConnectionWhileIdle=\n# edc.datasource.default.pool.testQuery=\n# edc.datasource.default.url=\n# edc.datasource.default.user=\n# edc.datasource.default.password=\n# edc.dpf.selector.url=\n# edc.events.topic.endpoint=\n# edc.events.topic.name=\n# edc.fs.config=\n# edc.hostname=\n# edc.identity.did.url=\n# edc.ids.catalog.id=\n# edc.ids.curator=\n# edc.ids.description=\n# edc.ids.endpoint=\n# edc.ids.id=\n# edc.ids.maintainer=\n# edc.ids.security.profile=\n# edc.ids.title=\n# edc.ids.validation.referringconnector=\n# edc.ion.crawler.did-type=\n# edc.ion.crawler.interval-minutes=\n# edc.ion.crawler.ion.url=\n# edc.metrics.enabled=\n# edc.metrics.executor.enabled=\n# edc.metrics.jersey.enabled=\n# edc.metrics.jetty.enabled=\n# edc.metrics.okhttp.enabled=\n# edc.metrics.system.enabled=\n# edc.negotiation.consumer.state-machine.batch-size=\n# edc.negotiation.provider.state-machine.batch-size=\n# edc.oauth.client.id=\n# edc.oauth.private.key.alias=\n# edc.oauth.provider.audience=\n# edc.oauth.provider.jwks.refresh=\n# edc.oauth.provider.jwks.url=\n# edc.oauth.public.key.alias=\n# edc.oauth.token.url=\n# edc.oauth.validation.nbf.leeway=\n# edc.receiver.http.auth-code=\n# edc.receiver.http.auth-key=\n# edc.receiver.http.endpoint=\n# edc.transfer.proxy.endpoint=\n# edc.transfer.dataplane.sync.token.validity=\n# edc.transfer.proxy.token.signer.privatekey.alias=\n# edc.transfer.functions.check.endpoint=\n# edc.transfer.functions.enabled.protocols=\n# edc.transfer.functions.transfer.endpoint=\n# edc.transfer-process-store.database.name=\n# edc.transfer.state-machine.batch-size=\n# edc.vault=\n# edc.vault.certificate=\n# edc.vault.clientid=\n# edc.vault.clientsecret=\n# edc.vault.name=\n# edc.vault.tenantid=\n# edc.vault.url=\n# edc.vault.token=\n# edc.vault.timeout.seconds=\n# edc.webdid.doh.url=\n# edc.web.rest.cors.enabled=\n# edc.web.rest.cors.headers=\n# edc.web.rest.cors.methods=\n# edc.web.rest.cors.origins="` | EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) | +| configuration.properties | string | `"# edc.atomikos.checkpoint.interval=\n# edc.atomikos.directory=\n# edc.atomikos.logging=\n# edc.atomikos.threaded2pc=\n# edc.atomikos.timeout=\n# edc.aws.access.key=\n# edc.aws.provision.retry.retries.max=\n# edc.aws.provision.role.duration.session.max=\n# edc.aws.secret.access.key=\n# edc.blobstore.endpoint=\n# edc.controlplane.validation-endpoint=\n# edc.core.retry.backoff.max=\n# edc.core.retry.backoff.min=\n# edc.core.retry.retries.max=\n# edc.core.system.health.check.liveness-period=\n# edc.core.system.health.check.readiness-period=\n# edc.core.system.health.check.startup-period=\n# edc.core.system.health.check.threadpool-size=\n# edc.dataplane.queue.capacity=\n# edc.dataplane.wait=\n# edc.dataplane.workers=\n# edc.datasource.asset.name=\"default\"\n# edc.datasource.contractdefinition.name=\"default\"\n# edc.datasource.contractnegotiation.name=\"default\"\n# edc.datasource.policy.name=\"default\"\n# edc.datasource.transferprocess.name=\"default\"\n# edc.datasource.default.pool.maxIdleConnections=\n# edc.datasource.default.pool.maxTotalConnections=\n# edc.datasource.default.pool.minIdleConnections=\n# edc.datasource.default.pool.testConnectionOnBorrow=\n# edc.datasource.default.pool.testConnectionOnCreate=\n# edc.datasource.default.pool.testConnectionOnReturn=\n# edc.datasource.default.pool.testConnectionWhileIdle=\n# edc.datasource.default.pool.testQuery=\n# edc.datasource.default.url=\n# edc.datasource.default.user=\n# edc.datasource.default.password=\n# edc.dpf.selector.url=\n# edc.events.topic.endpoint=\n# edc.events.topic.name=\n# edc.fs.config=\n# edc.hostname=\n# edc.identity.did.url=\n# edc.ids.catalog.id=\n# edc.ids.curator=\n# edc.ids.description=\n# edc.ids.endpoint=\n# edc.ids.id=\n# edc.ids.maintainer=\n# edc.ids.security.profile=\n# edc.ids.title=\n# edc.ids.validation.referringconnector=\n# edc.ion.crawler.did-type=\n# edc.ion.crawler.interval-minutes=\n# edc.ion.crawler.ion.url=\n# edc.metrics.enabled=\n# edc.metrics.executor.enabled=\n# edc.metrics.jersey.enabled=\n# edc.metrics.jetty.enabled=\n# edc.metrics.okhttp.enabled=\n# edc.metrics.system.enabled=\n# edc.negotiation.consumer.state-machine.batch-size=\n# edc.negotiation.provider.state-machine.batch-size=\n# edc.oauth.client.id=\n# edc.oauth.private.key.alias=\n# edc.oauth.provider.audience=\n# edc.oauth.provider.jwks.refresh=\n# edc.oauth.provider.jwks.url=\n# edc.oauth.public.key.alias=\n# edc.oauth.token.url=\n# edc.oauth.validation.nbf.leeway=\n# edc.receiver.http.auth-code=\n# edc.receiver.http.auth-key=\n# edc.receiver.http.endpoint=\n# edc.transfer.proxy.endpoint=\n# edc.transfer.dataplane.sync.token.validity=\n# edc.transfer.proxy.token.signer.privatekey.alias=\n# edc.transfer.functions.check.endpoint=\n# edc.transfer.functions.enabled.protocols=\n# edc.transfer.functions.transfer.endpoint=\n# edc.transfer-process-store.database.name=\n# edc.transfer.state-machine.batch-size=\n# edc.vault=\n# edc.vault.certificate=\n# edc.vault.clientid=\n# edc.vault.clientsecret=\n# edc.vault.name=\n# edc.vault.tenantid=\n# edc.vault.url=\n# edc.vault.token=\n# edc.vault.timeout.seconds=\n# edc.webdid.doh.url=\n# edc.web.rest.cors.enabled=\n# edc.web.rest.cors.headers=\n# edc.web.rest.cors.methods=\n# edc.web.rest.cors.origins="` | EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) | | edc.endpoints.control.path | string | `"/api/dataplane/control"` | The path mapping the "control" api is going to be exposed by | | edc.endpoints.control.port | string | `"9999"` | The network port, which the "control" api is going to be exposed by the container, pod and service | | edc.endpoints.default.path | string | `"/api"` | The path mapping the "default" api is going to be exposed by | diff --git a/deployment/helm/edc-dataplane/values.yaml b/deployment/helm/edc-dataplane/values.yaml index ea8a9e8fe..7f3ac94d5 100644 --- a/deployment/helm/edc-dataplane/values.yaml +++ b/deployment/helm/edc-dataplane/values.yaml @@ -204,8 +204,6 @@ opentelemetry: configuration: # -- EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) properties: |- - # edc.api.control.auth.apikey.key= - # edc.api.control.auth.apikey.value= # edc.atomikos.checkpoint.interval= # edc.atomikos.directory= # edc.atomikos.logging= diff --git a/edc-controlplane/README.md b/edc-controlplane/README.md index 32fe2099b..98ab9d0c9 100644 --- a/edc-controlplane/README.md +++ b/edc-controlplane/README.md @@ -471,45 +471,6 @@ Get Contract Definition curl -X GET "$__connectorUrl/$__dataMgmtPath/contractdefinitions/$__contractDefinitionId" --header "$__apiKey: $__apiKeyValue" --header "Content-Type: application/json" | jq ``` -### 4. Get Contract Offer Catalog - -The last call is not (yet) part of the Data Management API. Instead, the deprecated Control API is used. The extension -for the control API is part of the Catena-X images and usable. - ----- - -**Please Note** - -Don't confuse the deprecated Control API with another Control API of the connector, that is not deprecated. - ----- - -#### Bash Script - -```bash -# Variables -__connectorUrl=http://localhost:8181 -__targetConnectorUrl=http://localhost:9292 -__targetConnectorIdsPath=api/v1/ids -__defaultApiPath=api -__apiKey=X-Api-Key -__apiKeyValue=pwd - -# Call Control API -curl -G -X GET $__connectorUrl/$__defaultApiPath/control/catalog --header "$__apiKey: $__apiKeyValue" --data-urlencode "provider=$__targetConnectorUrl/$__targetConnectorIdsPath/data" --header "Content-Type: application/json" -s | jq -``` - -#### Bash Parameters - -| Name | Description | -| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | -| $__connectorUrl | URL of the Connector with the Control API port configured in `web.http.default.port` | -| $__defaultApiPath | Path of the Control API as configured in `web.http.default.path` | -| $__apiKey | The API Key as configured in `edc.api.control.auth.apikey.key` | -| $__apiKeyValue | The API Key Value as configured in `edc.api.control.auth.apikey.value` | -| $__targetConnectorUrl | URL of the Connector of the target connector with the IDS API port configured in `web.http.ids.port`(in the configuration of the other connector) | -| $__targetConnectorIdsPath | The IDS Path as configured in `web.http.ids.path` (in the configuration of the other connector) | - # Secure your connector ## API Security diff --git a/edc-controlplane/edc-controlplane-base/pom.xml b/edc-controlplane/edc-controlplane-base/pom.xml index 8e5cf983a..3fe6e8dc5 100644 --- a/edc-controlplane/edc-controlplane-base/pom.xml +++ b/edc-controlplane/edc-controlplane-base/pom.xml @@ -52,10 +52,6 @@ - - org.eclipse.dataspaceconnector - control-api - org.eclipse.dataspaceconnector data-management-api diff --git a/edc-controlplane/edc-controlplane-memory/README.md b/edc-controlplane/edc-controlplane-memory/README.md index bf617c21f..b3115a5c3 100644 --- a/edc-controlplane/edc-controlplane-memory/README.md +++ b/edc-controlplane/edc-controlplane-memory/README.md @@ -13,6 +13,7 @@ Details regarding each configuration property can be found at the [documentary s | Key | Required | Example | Description | |--- |--- |--- |--- | +| edc.api.auth.key | | password | default value: random UUID | | web.http.default.port | X | 8080 | | | web.http.default.path | X | /api | | | web.http.data.port | X | 8181 | | @@ -33,8 +34,6 @@ Details regarding each configuration property can be found at the [documentary s | edc.ids.curator | | http://localhost | | | edc.ids.catalog.id | | urn:catalog:default | | | ids.webhook.address | | http://localhost:8282/api/v1/ids | | -| edc.api.control.auth.apikey.key | | X-Api-Key | | -| edc.api.control.auth.apikey.value | | super-strong-api-key | | | edc.hostname | | localhost | | | edc.oauth.token.url | X | https://daps.catena-x.net | | | edc.oauth.public.key.alias | X | key-to-daps-certificate-in-keyvault | | @@ -79,11 +78,10 @@ edc.ids.curator=http://localhost edc.ids.catalog.id=urn:catalog:default ids.webhook.address=http://localhost:8282/api/v1/ids -edc.api.control.auth.apikey.key=X-Api-Key -edc.api.control.auth.apikey.value=pass - edc.hostname=localhost +edc.api.auth.key=password + # OAuth / DAPS related configuration edc.oauth.token.url=https://daps.catena-x.net edc.oauth.public.key.alias=key-to-daps-certificate-in-keyvault diff --git a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/README.md b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/README.md index 1b50b1825..08a1ecf3c 100644 --- a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/README.md +++ b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/README.md @@ -13,6 +13,7 @@ Details regarding each configuration property can be found at the [documentary s | Key | Required | Example | Description | |--- |--- |--- |--- | +| edc.api.auth.key | | password | default value: random UUID | | web.http.default.port | X | 8080 | | | web.http.default.path | X | /api | | | web.http.data.port | X | 8181 | | @@ -33,8 +34,6 @@ Details regarding each configuration property can be found at the [documentary s | edc.ids.curator | | http://localhost | | | edc.ids.catalog.id | | urn:catalog:default | | | ids.webhook.address | | http://localhost:8282/api/v1/ids | | -| edc.api.control.auth.apikey.key | | X-Api-Key | | -| edc.api.control.auth.apikey.value | | super-strong-api-key | | | edc.hostname | | localhost | | | edc.oauth.token.url | X | https://daps.catena-x.net | | | edc.oauth.public.key.alias | X | key-to-daps-certificate-in-keyvault | | @@ -98,11 +97,10 @@ edc.ids.curator=http://localhost edc.ids.catalog.id=urn:catalog:default ids.webhook.address=http://localhost:8282/api/v1/ids -edc.api.control.auth.apikey.key=X-Api-Key -edc.api.control.auth.apikey.value=pass - edc.hostname=localhost +edc.api.auth.key=password + # OAuth / DAPS related configuration edc.oauth.token.url=https://daps.catena-x.net edc.oauth.public.key.alias=key-to-daps-certificate-in-keyvault diff --git a/edc-controlplane/edc-controlplane-postgresql/README.md b/edc-controlplane/edc-controlplane-postgresql/README.md index af44e6a12..b6f18f171 100644 --- a/edc-controlplane/edc-controlplane-postgresql/README.md +++ b/edc-controlplane/edc-controlplane-postgresql/README.md @@ -13,6 +13,7 @@ Details regarding each configuration property can be found at the [documentary s | Key | Required | Example | Description | |--- |--- |--- |--- | +| edc.api.auth.key | | password | default value: random UUID | | web.http.default.port | X | 8080 | | | web.http.default.path | X | /api | | | web.http.data.port | X | 8181 | | @@ -33,8 +34,6 @@ Details regarding each configuration property can be found at the [documentary s | edc.ids.curator | | http://localhost | | | edc.ids.catalog.id | | urn:catalog:default | | | ids.webhook.address | | http://localhost:8282/api/v1/ids | | -| edc.api.control.auth.apikey.key | | X-Api-Key | | -| edc.api.control.auth.apikey.value | | super-strong-api-key | | | edc.hostname | | localhost | | | edc.oauth.token.url | X | https://daps.catena-x.net | | | edc.oauth.public.key.alias | X | key-to-daps-certificate-in-keyvault | | @@ -99,11 +98,10 @@ edc.ids.curator=http://localhost edc.ids.catalog.id=urn:catalog:default ids.webhook.address=http://localhost:8282/api/v1/ids -edc.api.control.auth.apikey.key=X-Api-Key -edc.api.control.auth.apikey.value=pass - edc.hostname=localhost +edc.api.auth.key=password + # OAuth / DAPS related configuration edc.oauth.token.url=https://daps.catena-x.net edc.oauth.public.key.alias=key-to-daps-certificate-in-keyvault From c334a63b9098f0fe64e443c8ba8f7a7da537332b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Jun 2022 08:38:41 +0200 Subject: [PATCH 143/433] Bump actions/setup-java from 3.3.0 to 3.4.0 (#223) --- .github/workflows/build.yaml | 12 ++++++------ .github/workflows/draft-new-release.yaml | 2 +- .github/workflows/publish-new-release.yml | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 49da6a393..c25a0b621 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -43,7 +43,7 @@ jobs: fetch-depth: 0 - name: Set up JDK 11 - uses: actions/setup-java@v3.3.0 + uses: actions/setup-java@v3.4.0 with: java-version: '11' distribution: 'adopt' @@ -76,7 +76,7 @@ jobs: password: ${{ secrets.CXNG_GHCR_PAT }} - name: Set up JDK 11 - uses: actions/setup-java@v3.3.0 + uses: actions/setup-java@v3.4.0 with: java-version: '11' distribution: 'adopt' @@ -161,7 +161,7 @@ jobs: password: ${{ secrets.CXNG_GHCR_PAT }} - name: Set up JDK 11 - uses: actions/setup-java@v3.3.0 + uses: actions/setup-java@v3.4.0 with: java-version: '11' distribution: 'adopt' @@ -246,7 +246,7 @@ jobs: password: ${{ secrets.CXNG_GHCR_PAT }} - name: Set up JDK 11 - uses: actions/setup-java@v3.3.0 + uses: actions/setup-java@v3.4.0 with: java-version: '11' distribution: 'adopt' @@ -331,7 +331,7 @@ jobs: password: ${{ secrets.CXNG_GHCR_PAT }} - name: Set up JDK 11 - uses: actions/setup-java@v3.3.0 + uses: actions/setup-java@v3.4.0 with: java-version: '11' distribution: 'adopt' @@ -416,7 +416,7 @@ jobs: password: ${{ secrets.CXNG_GHCR_PAT }} - name: Set up JDK 11 - uses: actions/setup-java@v3.3.0 + uses: actions/setup-java@v3.4.0 with: java-version: '11' distribution: 'adopt' diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index 264694ce2..890df7e31 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -29,7 +29,7 @@ jobs: git config user.email noreply@github.com - name: Set up JDK 11 - uses: actions/setup-java@v3.3.0 + uses: actions/setup-java@v3.4.0 with: java-version: '11' distribution: 'adopt' diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index cb4997e3f..bedaf133a 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -71,7 +71,7 @@ jobs: submodules: recursive - name: Set up JDK 11 - uses: actions/setup-java@v3.3.0 + uses: actions/setup-java@v3.4.0 with: java-version: '11' distribution: 'adopt' @@ -180,7 +180,7 @@ jobs: prerelease: false - name: Set up JDK 11 - uses: actions/setup-java@v3.3.0 + uses: actions/setup-java@v3.4.0 with: java-version: '11' distribution: 'adopt' From 195d573e448c6e4fe16bbfdfd5fe4c9f32a6e679 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jun 2022 08:07:38 +0200 Subject: [PATCH 144/433] Bump mikefarah/yq from 4.25.2 to 4.25.3 (#225) --- .github/workflows/draft-new-release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index 890df7e31..7acc3be1c 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -43,7 +43,7 @@ jobs: GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} - name: Bump version in deployment/helm - uses: mikefarah/yq@v4.25.2 + uses: mikefarah/yq@v4.25.3 with: cmd: |- find deployment/helm -name Chart.yaml | xargs -n1 yq -i '.appVersion = "${{ github.event.inputs.version }}" | .version = "${{ github.event.inputs.version }}"' From 23639ab0133a79829a0633730320458669a3a329 Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Mon, 27 Jun 2022 10:14:17 +0200 Subject: [PATCH 145/433] bump edc to fix for 50 catalog limit for 50 contract definitions (#229) --- edc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc b/edc index acd02337a..1b3f3a86e 160000 --- a/edc +++ b/edc @@ -1 +1 @@ -Subproject commit acd02337a16d9498727bee12e49e715e9be960e3 +Subproject commit 1b3f3a86e3cef781d325788a30bf32efb18af98b From 0d970b917aae16b8a5176ddbdbb991cbc1ab998c Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Tue, 28 Jun 2022 11:47:25 +0200 Subject: [PATCH 146/433] new all-in-one deployment for tests (#194) --- deployment/helm/edc-controlplane/values.yaml | 1 + docs/Transfer Data.md | 161 ++++++ docs/diagrams/transfer_sequence_1.png | Bin 0 -> 29595 bytes docs/diagrams/transfer_sequence_1.puml | 34 ++ docs/diagrams/transfer_sequence_2.png | Bin 0 -> 29371 bytes docs/diagrams/transfer_sequence_2.puml | 28 + docs/diagrams/transfer_sequence_3.png | Bin 0 -> 34859 bytes docs/diagrams/transfer_sequence_3.puml | 33 ++ docs/diagrams/transfer_sequence_4.png | Bin 0 -> 60428 bytes docs/diagrams/transfer_sequence_4.puml | 44 ++ docs/diagrams/transfer_sequence_5.png | Bin 0 -> 21812 bytes docs/diagrams/transfer_sequence_5.puml | 27 + edc-controlplane/README.md | 11 +- .../deployment/helm/all-in-one/.gitignore | 4 + .../deployment/helm/all-in-one/.helmignore | 24 + .../deployment/helm/all-in-one/Chart.yaml | 76 +++ .../deployment/helm/all-in-one/README.md | 94 ++++ .../diagrams/deployed_components.png | Bin 0 -> 39746 bytes .../diagrams/deployed_components.puml | 47 ++ .../deployment/helm/all-in-one/values.yaml | 514 ++++++++++++++++++ .../helm/backend-application/.helmignore | 23 + .../helm/backend-application/Chart.yaml | 25 + .../helm/backend-application/README.md | 3 + .../templates/_helpers.tpl | 62 +++ .../templates/configmap.yaml | 9 + .../templates/deployment.yaml | 81 +++ .../backend-application/templates/hpa.yaml | 28 + .../templates/service.yaml | 15 + .../templates/serviceaccount.yaml | 12 + .../helm/backend-application/values.yaml | 153 ++++++ .../deployment/helm/omejdn/.helmignore | 23 + .../deployment/helm/omejdn/Chart.yaml | 25 + .../deployment/helm/omejdn/README.md | 20 + .../helm/omejdn/templates/_helpers.tpl | 62 +++ .../helm/omejdn/templates/configmap.yaml | 75 +++ .../helm/omejdn/templates/deployment.yaml | 121 +++++ .../deployment/helm/omejdn/templates/hpa.yaml | 28 + .../omejdn/templates/imagepullsecret.yaml | 13 + .../helm/omejdn/templates/service.yaml | 15 + .../helm/omejdn/templates/serviceaccount.yaml | 12 + .../deployment/helm/omejdn/values.yaml | 89 +++ lintconf.yaml | 2 +- 42 files changed, 1990 insertions(+), 4 deletions(-) create mode 100644 docs/Transfer Data.md create mode 100644 docs/diagrams/transfer_sequence_1.png create mode 100644 docs/diagrams/transfer_sequence_1.puml create mode 100644 docs/diagrams/transfer_sequence_2.png create mode 100644 docs/diagrams/transfer_sequence_2.puml create mode 100644 docs/diagrams/transfer_sequence_3.png create mode 100644 docs/diagrams/transfer_sequence_3.puml create mode 100644 docs/diagrams/transfer_sequence_4.png create mode 100644 docs/diagrams/transfer_sequence_4.puml create mode 100644 docs/diagrams/transfer_sequence_5.png create mode 100644 docs/diagrams/transfer_sequence_5.puml create mode 100644 edc-tests/src/main/resources/deployment/helm/all-in-one/.gitignore create mode 100644 edc-tests/src/main/resources/deployment/helm/all-in-one/.helmignore create mode 100644 edc-tests/src/main/resources/deployment/helm/all-in-one/Chart.yaml create mode 100644 edc-tests/src/main/resources/deployment/helm/all-in-one/README.md create mode 100644 edc-tests/src/main/resources/deployment/helm/all-in-one/diagrams/deployed_components.png create mode 100644 edc-tests/src/main/resources/deployment/helm/all-in-one/diagrams/deployed_components.puml create mode 100644 edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml create mode 100644 edc-tests/src/main/resources/deployment/helm/backend-application/.helmignore create mode 100644 edc-tests/src/main/resources/deployment/helm/backend-application/Chart.yaml create mode 100644 edc-tests/src/main/resources/deployment/helm/backend-application/README.md create mode 100644 edc-tests/src/main/resources/deployment/helm/backend-application/templates/_helpers.tpl create mode 100644 edc-tests/src/main/resources/deployment/helm/backend-application/templates/configmap.yaml create mode 100644 edc-tests/src/main/resources/deployment/helm/backend-application/templates/deployment.yaml create mode 100644 edc-tests/src/main/resources/deployment/helm/backend-application/templates/hpa.yaml create mode 100644 edc-tests/src/main/resources/deployment/helm/backend-application/templates/service.yaml create mode 100644 edc-tests/src/main/resources/deployment/helm/backend-application/templates/serviceaccount.yaml create mode 100644 edc-tests/src/main/resources/deployment/helm/backend-application/values.yaml create mode 100644 edc-tests/src/main/resources/deployment/helm/omejdn/.helmignore create mode 100644 edc-tests/src/main/resources/deployment/helm/omejdn/Chart.yaml create mode 100644 edc-tests/src/main/resources/deployment/helm/omejdn/README.md create mode 100644 edc-tests/src/main/resources/deployment/helm/omejdn/templates/_helpers.tpl create mode 100644 edc-tests/src/main/resources/deployment/helm/omejdn/templates/configmap.yaml create mode 100644 edc-tests/src/main/resources/deployment/helm/omejdn/templates/deployment.yaml create mode 100644 edc-tests/src/main/resources/deployment/helm/omejdn/templates/hpa.yaml create mode 100644 edc-tests/src/main/resources/deployment/helm/omejdn/templates/imagepullsecret.yaml create mode 100644 edc-tests/src/main/resources/deployment/helm/omejdn/templates/service.yaml create mode 100644 edc-tests/src/main/resources/deployment/helm/omejdn/templates/serviceaccount.yaml create mode 100644 edc-tests/src/main/resources/deployment/helm/omejdn/values.yaml diff --git a/deployment/helm/edc-controlplane/values.yaml b/deployment/helm/edc-controlplane/values.yaml index a13f729b1..e172f6e17 100644 --- a/deployment/helm/edc-controlplane/values.yaml +++ b/deployment/helm/edc-controlplane/values.yaml @@ -334,3 +334,4 @@ configuration: # edc.web.rest.cors.headers= # edc.web.rest.cors.methods= # edc.web.rest.cors.origins= + # ids.webhook.address= diff --git a/docs/Transfer Data.md b/docs/Transfer Data.md new file mode 100644 index 000000000..471761a81 --- /dev/null +++ b/docs/Transfer Data.md @@ -0,0 +1,161 @@ +# Transfer Data + +This document will showcase a data transfer between two connectors. + +--- + +Before running the commands setup the all-in-one deployment from the. This is documented in it's +[README.md](../README.md#Setup). + +Please install [Bash jq](https://linuxhint.com/bash_jq_command/), as it is used in the bash calls of this document. + +--- + +For this transfer connector **Plato** will act as data provider, and connector **Sokrates** will act as data +consumer. But the roles could be inverse as well. + +**Contents** + +0. Before running the demo + 1. Ensure all pods are running + 2. Set environment variables +1. Setup Data Offer +2. Request Contract Offers +3. Negotiate Contract +4. Transfer Data +5. Verify Data Transfer + +## 0. Before Running the demo + +### 0.1 Wait until all pods are running + +Get all the pods and wait until all pods are in a `Running` state before executing the next steps. +Please ignore that the EDC applications will crash 2-3 times during the start-up phase. This is normal. + +**Run** + +```bash +minikube kubectl -- -n edc-all-in-one get pods +``` + +### 0.2 Set environment variables used in subsequent calls + +Initialize the following environment variables, that are used in the upcoming API calls. + +**Run** + +```bash +export PLATO_DATAMGMT_URL=$(minikube service plato-edc-controlplane -n edc-all-in-one --url | sed -n 3p) +export PLATO_IDS_URL=$(minikube service plato-edc-controlplane -n edc-all-in-one --url | sed -n 5p) +export SOKRATES_DATAMGMT_URL=$(minikube service sokrates-edc-controlplane -n edc-all-in-one --url | sed -n 3p) +``` + +## 1. Setup Data Offer + +Set up a data offer in **Plato**, so that **Sokrates** has something to consume. + +In case you are unfamiliar with the EDC terms `Asset`, `Policy` or `ContractDefinition` please have a look the official open +source documentation ([link](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/blob/main/docs/architecture/domain-model.md)). + +![Sequence 1](./diagrams/transfer_sequence_1.png) + +**Run** + +The following commands will create an Asset, a Policy and a Contract Definition. +For simplicity `https://jsonplaceholder.typicode.com/todos/1` is used as data source of the asset, but could be any +other API, that is reachable from the Data Plane. + +```bash +curl -X POST "$PLATO_DATAMGMT_URL/data/assets" --header "X-Api-Key: password" --header "Content-Type: application/json" --data "{ \"asset\": { \"properties\": { \"asset:prop:id\": \"1\", \"asset:prop:description\": \"Product EDC Demo Asset\" } }, \"dataAddress\": { \"properties\": { \"type\": \"HttpData\", \"endpoint\": \"https://jsonplaceholder.typicode.com/todos/1\" } } }" -s -o /dev/null -w 'Response Code: %{http_code}\n' +``` + +```bash +curl -X POST "$PLATO_DATAMGMT_URL/data/policies" --header "X-Api-Key: password" --header "Content-Type: application/json" --data "{ \"uid\": \"1\", \"prohibitions\": [], \"obligations\": [], \"permissions\": [ { \"edctype\": \"dataspaceconnector:permission\", \"action\": { \"type\": \"USE\" }, \"constraints\": [] } ] }" -s -o /dev/null -w 'Response Code: %{http_code}\n' +``` + +```bash +curl -X POST "$PLATO_DATAMGMT_URL/data/contractdefinitions" --header "X-Api-Key: password" --header "Content-Type: application/json" --data "{ \"id\": \"1\", \"criteria\": [ { \"left\": \"asset:prop:id\", \"op\": \"=\", \"right\": \"1\" } ], \"accessPolicyId\": \"1\", \"contractPolicyId\": \"1\" }" -s -o /dev/null -w 'Response Code: %{http_code}\n' +``` + +## 2. Request Contract Offer Catalog + +In this step Sokrates gets told to request contract offers from another connector (in this case Plato). Sokrates will +then request the catalog over IDS messaging. + +For IDS messaging connectors will identify each other using the configured IDS DAPS. Therefore, it is important that +connectors, that intent to send messages to each other, have the same DAPS instance configured. + +![Sequence 1](./diagrams/transfer_sequence_2.png) + +**Run** + +```bash +curl -G -X GET "$SOKRATES_DATAMGMT_URL/data/catalog" --data-urlencode "providerUrl=$PLATO_IDS_URL/api/v1/ids/data" --header "X-Api-Key: password" --header "Content-Type: application/json" -s | jq +``` + +## 3. Negotiate Contract + +Initiate a contract negotiation for the asset (from step 1). Part of the negotiation payload is the contract +offer (received in step 2). + +In the diagram the IDS contract negotiation is marked as simplified, because the EDC is exchanging multiple messages +during contract negotiation. But the inter-controlplane communication is not in the scope of this document. + +After the negotiation is initiated ensure that is has concluded. This is done by requesting the negotiation from the API +and checking whether the `contractAgreementId` is set. This might take a few seconds. + +![Sequence 1](./diagrams/transfer_sequence_3.png) + +**Run** + +```bash +export NEGOTIATION_ID=$(curl -X POST "$SOKRATES_DATAMGMT_URL/data/contractnegotiations" --header "X-Api-Key: password" --header "Content-Type: application/json" --data "{ \"connectorId\": \"foo\", \"connectorAddress\": \"$PLATO_IDS_URL/api/v1/ids/data\", \"offer\": { \"offerId\": \"1:foo\", \"assetId\": \"1\", \"policy\": { \"uid\": \"1\", \"prohibitions\": [], \"obligations\": [], \"permissions\": [ { \"edctype\": \"dataspaceconnector:permission\", \"action\": { \"type\": \"USE\" }, \"target\": \"1\", \"constraints\": [] } ] } } }" -s | jq -r '.id') +``` + +```bash +curl -X GET "$SOKRATES_DATAMGMT_URL/data/contractnegotiations/$NEGOTIATION_ID" --header "X-Api-Key: password" --header "Content-Type: application/json" -s | jq +``` + +## 4. Transfer Data + +Initiate a data transfer using the contract agreement from the negotiation (from step 3). Then wait until the state of +the +transfer process is `COMPLETED`. + +![Sequence 1](./diagrams/transfer_sequence_4.png) + +**Run** + +```bash +export CONTRACT_AGREEMENT_ID=$(curl -X GET "$SOKRATES_DATAMGMT_URL/data/contractnegotiations/$NEGOTIATION_ID" --header "X-Api-Key: password" --header "Content-Type: application/json" -s | jq -r '.contractAgreementId') +``` + +```bash +export TRANSFER_PROCESS_ID=$(tr -dc '[:alnum:]' < /dev/urandom | head -c20) +export TRANSFER_ID=$(curl -X POST "$SOKRATES_DATAMGMT_URL/data/transferprocess" --header "X-Api-Key: password" --header "Content-Type: application/json" --data "{ \"id\": \"${TRANSFER_PROCESS_ID}\", \"connectorId\": \"foo\", \"connectorAddress\": \"${PLATO_IDS_URL}/api/v1/ids/data\", \"contractId\": \"${CONTRACT_AGREEMENT_ID}\", \"assetId\": \"1\", \"managedResources\": \"false\", \"dataDestination\": { \"type\": \"HttpProxy\" } }" -s | jq -r '.id') +``` + +```bash +curl -X GET "$SOKRATES_DATAMGMT_URL/data/transferprocess/$TRANSFER_ID" --header "X-Api-Key: password" --header "Content-Type: application/json" -s | jq +``` + +## 5. Verify Data Transfer + +After the transfer is complete the Backend Application has downloaded the data. The Backend Application stores the data +locally. In this demo the transfer can be verified by executing a simple `cat` call in the Pod. + +![Sequence 1](./diagrams/transfer_sequence_5.png) + +```bash +echo $(kubectl exec -n edc-all-in-one --stdin --tty `kubectl get pod -n edc-all-in-one -l app.kubernetes.io/name=sokrates-backend-application --template "{{ with index .items ${POD_INDEX:-0} }}{{ .metadata.name }}{{ end }}"` -- /usr/bin/cat /tmp/data/${TRANSFER_PROCESS_ID}) | jq +``` + +# Delete All Data + +```bash +minikube kubectl -- delete pvc -n edc-all-in-one --all +``` + +```bash +minikube kubectl -- delete pv -n edc-all-in-one --all +``` \ No newline at end of file diff --git a/docs/diagrams/transfer_sequence_1.png b/docs/diagrams/transfer_sequence_1.png new file mode 100644 index 0000000000000000000000000000000000000000..f52ecbb70a0aadca9e555a2481581d11c86ca1d6 GIT binary patch literal 29595 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 S*d-L>t**AAR=(!-d;bAdoUX6{ literal 0 HcmV?d00001 diff --git a/docs/diagrams/transfer_sequence_1.puml b/docs/diagrams/transfer_sequence_1.puml new file mode 100644 index 000000000..a159447ee --- /dev/null +++ b/docs/diagrams/transfer_sequence_1.puml @@ -0,0 +1,34 @@ +@startuml + +!define sokratesColor 66CCFF +!define platoColor CCFF99 +!define dapsColor FFFF99 +!define noteColor 9999FF + +actor User as "User" + +box Sokrates + participant SokratesControlPlane as "Control Plane" #sokratesColor + participant SokratesBackendService as "Backend Application" #sokratesColor + participant SokratesDataPlane as "Data Plane" #sokratesColor +end box + +box Plato + participant PlatoControlPlane as "Control Plane" #platoColor + participant PlatoDataPlane as "Data Plane" #platoColor +end box + +participant JsonPlaceHolder as "JsonPlaceHolder" + + +User -> PlatoControlPlane ++ : Create Asset +return 204 + +User -> PlatoControlPlane ++ : Create Policy +return 204 + +User -> PlatoControlPlane ++ : Create Contract Definition +return 204 + + +@enduml diff --git a/docs/diagrams/transfer_sequence_2.png b/docs/diagrams/transfer_sequence_2.png new file mode 100644 index 0000000000000000000000000000000000000000..ed2f4dd90855fddd2db0b7841d109524f5628246 GIT binary patch literal 29371 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 literal 0 HcmV?d00001 diff --git a/docs/diagrams/transfer_sequence_2.puml b/docs/diagrams/transfer_sequence_2.puml new file mode 100644 index 000000000..805bcbeec --- /dev/null +++ b/docs/diagrams/transfer_sequence_2.puml @@ -0,0 +1,28 @@ +@startuml + +!define sokratesColor 66CCFF +!define platoColor CCFF99 +!define dapsColor FFFF99 +!define noteColor 9999FF + +actor User as "User" + +box Sokrates + participant SokratesControlPlane as "Control Plane" #sokratesColor + participant SokratesBackendService as "Backend Application" #sokratesColor + participant SokratesDataPlane as "Data Plane" #sokratesColor +end box + +box Plato + participant PlatoControlPlane as "Control Plane" #platoColor + participant PlatoDataPlane as "Data Plane" #platoColor +end box + +participant JsonPlaceHolder as "JsonPlaceHolder" + +User -> SokratesControlPlane ++ : Request Contract Offers from Plato + SokratesControlPlane -> PlatoControlPlane ++ : IDS Description Request Message + return Description +return Contract Offers + +@enduml diff --git a/docs/diagrams/transfer_sequence_3.png b/docs/diagrams/transfer_sequence_3.png new file mode 100644 index 0000000000000000000000000000000000000000..b1d56ec6c24a1c7ec2913088ff82c1cda4350404 GIT binary patch literal 34859 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 WQZ!@|bb$L0DY1KZb3`A%{Qm%Q&K}?Z literal 0 HcmV?d00001 diff --git a/docs/diagrams/transfer_sequence_3.puml b/docs/diagrams/transfer_sequence_3.puml new file mode 100644 index 000000000..43707af36 --- /dev/null +++ b/docs/diagrams/transfer_sequence_3.puml @@ -0,0 +1,33 @@ +@startuml + +!define sokratesColor 66CCFF +!define platoColor CCFF99 +!define dapsColor FFFF99 +!define noteColor 9999FF + +actor User as "User" + +box Sokrates + participant SokratesControlPlane as "Control Plane" #sokratesColor + participant SokratesBackendService as "Backend Application" #sokratesColor + participant SokratesDataPlane as "Data Plane" #sokratesColor +end box + +box Plato + participant PlatoControlPlane as "Control Plane" #platoColor + participant PlatoDataPlane as "Data Plane" #platoColor +end box + +participant JsonPlaceHolder as "JsonPlaceHolder" + + +User -> SokratesControlPlane ++ : Negotiate Contract for Offer X +SokratesControlPlane --> User: Negotiation ID + SokratesControlPlane -> PlatoControlPlane ++ : IDS Contract Negotiation (simplified) + return Contract Agreement +deactivate SokratesControlPlane + +User -> SokratesControlPlane ++ : Request Negotiation by ID +return Contract Negotiation + +@enduml diff --git a/docs/diagrams/transfer_sequence_4.png b/docs/diagrams/transfer_sequence_4.png new file mode 100644 index 0000000000000000000000000000000000000000..43701e1ba1486f8dfa8171ba46b782bea61da0ea GIT binary patch literal 60428 zcmdSBcT|)6)-?(!Dk!LsfDI84NT>=bQUnX3x6nI^bfougLm&uJLKA7BcMy;c7C<^k zQKSljNbey1uIzU9IcL9P-1ml+iVk_=lQj@=A3J;1Sly;Qc<3vBq1T8l9m!z zCLuX^jf8}(C(Lm zta3|_NNKv*e6QUht6pWEmCt$?sw=I~QoA4%P#AZdtb4udR=TUp$jZR+Tn7uTFV|w( zNRRf#p89y3`IJ+BIi_1Zpv%^=$J(XRS3tf@%^}L+93y>Rbox=HQk^v^W4n35wC_L3 z8dzjI1P)!bJs>>)F}P?X)8LY>)R9x2($dNP;?1dEtHlw;+N@8W(pG=qJ@$=Tc=1hG zy-B>o`Jv?Jei^3BFZsFUY$MYG{L7Fsq6zsQxOv6P)8%o4oTx7%6OXcII2P{e&vQ4? z$$x#j_%w&_O-iQRCjl8{*QbnXG`w3SMk~21Z4X}M*mTYFJhcrH;GQ2VW$8MfrJ#m-x5pN-?ox>b{DzD1;td7h0RFu4VWAE`G|Kr!D%u59N0C zdqhFW;-Mw?y@I#Y#kkH>$L);`j*Dn@*Of=NpYY^7Phlw`Xa2K+?7O>usJy$vwYGvu z9_?fFd$_KF(9eLN$UFN#p0(9F@ebt)&N~&=SbzS~w&i1`{?XJXkLTXA z{<3Z7f-i$M*EY0En9_yu7Otho54RQcQtHT?861=Rw&SF}eW@&^_G?U<=ly#*bS^QK zK~A41`KiCe6dU50Jf*Sg!U^^V+6LszScjYpcL%bWzx^z*<1rIZrjnkWL(Y7QHVt4E zPcf|MXBvE75wtNI)*{`&UKbQrf?S$DyiE1groeFd?v$EO0tv}O5^3>Us;>I8aTK0Z z8rvVAT=eh5oUnY__XU-k8rH9^bMZ~TMP{c3XJnL2U@@!A118lqZY#GZbZGtketp9r ze=q5a>q^|Ysfd!{eU|jJ@EGCmKYlcO&5v*FzETwBH*J3<=rG^(YBE-s-;R%X9*BR= zzQUjW{T0bwu|(7#FC}hb|9JKMA>of#r$W^*($dm#S2-<~$2-vSF?>$*gXQT8amsYW zqe}eKQlO9R>f-VBmG=+c6GBfJYWK^Bnz@a>w-MbO?{10W<#Zvr8QUScJ<9^0mUx=C zSk`_(-TyMh_4}!W*#VbtAC9xbL#l<*x(T_=mnw>G&MuCAXdWWze8@v*3cqsdq%;

1za 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 e@4pd*Y SokratesControlPlane ++ : Request Negotiation by ID +return Contract Negotiation\n(containing Contract Agreement ID) + +User -> SokratesControlPlane ++ : Initiate Transfer with Agreement ID +SokratesControlPlane --> User : Transfer Process ID + SokratesControlPlane -> PlatoControlPlane ++ : IDS Data Transfer (simplified) +return +SokratesControlPlane -> SokratesBackendService ++ : Data Plane Endpoint + Token + SokratesBackendService -> SokratesDataPlane ++ : Request Data with Token + SokratesDataPlane -> PlatoDataPlane ++ : Request Data + PlatoDataPlane -> JsonPlaceHolder ++ : Request Data + return data + return data + return data + SokratesBackendService -> SokratesBackendService : Write Data to File +return ok +deactivate SokratesControlPlane + +User -> SokratesControlPlane ++ : Request Transfer Process by ID +return Transfer Process + +@enduml diff --git a/docs/diagrams/transfer_sequence_5.png b/docs/diagrams/transfer_sequence_5.png new file mode 100644 index 0000000000000000000000000000000000000000..080c26335df9d5ab8a381d22594c99bcd9492f6d GIT binary patch literal 21812 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 IbxsLQ0Pl#yT>t<8 literal 0 HcmV?d00001 diff --git a/docs/diagrams/transfer_sequence_5.puml b/docs/diagrams/transfer_sequence_5.puml new file mode 100644 index 000000000..b64f2222b --- /dev/null +++ b/docs/diagrams/transfer_sequence_5.puml @@ -0,0 +1,27 @@ +@startuml + +!define sokratesColor 66CCFF +!define platoColor CCFF99 +!define dapsColor FFFF99 +!define noteColor 9999FF + +actor User as "User" + +box Sokrates + participant SokratesControlPlane as "Control Plane" #sokratesColor + participant SokratesBackendService as "Backend Application" #sokratesColor + participant SokratesDataPlane as "Data Plane" #sokratesColor +end box + +box Plato + participant PlatoControlPlane as "Control Plane" #platoColor + participant PlatoDataPlane as "Data Plane" #platoColor +end box + +participant JsonPlaceHolder as "JsonPlaceHolder" + +User -> SokratesBackendService ++ : Get File Content +return data + + +@enduml diff --git a/edc-controlplane/README.md b/edc-controlplane/README.md index 98ab9d0c9..794ff6030 100644 --- a/edc-controlplane/README.md +++ b/edc-controlplane/README.md @@ -111,7 +111,6 @@ Pleaste know that, in the past there were some DAPS issues with the client certi ``` --- - ### (optional) 1. Key / Certificate Generation In the first step generate a PKSC8 Key and the corresponding certificate. This step is optional, because it might be possible that this key is provided by the DAPS maintainers. @@ -222,8 +221,7 @@ az keyvault secret set --name my-data-plane-public-key --vault-name tree512 --fi edc.transfer.proxy.token.verifier.publickey.alias=my-data-plane-public-key ``` - -# Short Overview of the EDC Domain +# EDC Domain This chapter gives a short overview of the EDC domain. The idea is to get a basic understanding of the domain objects and their roles.
The complete EDC documentation can be found in the official open source [EDC GitHub Repository](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector). @@ -489,6 +487,13 @@ Please have look at all the open issues in the open source repository. The list only contains the most important issues. EDC Github Repository https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues +--- + +**Please note** that some of these issues might already be fixed on the EDC main branch, but are not part of the specific +EDC commit the Product-EDC uses. + +--- + ## Contract negotiation not working when `web.http.ids.path` is configured/changed https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1249 diff --git a/edc-tests/src/main/resources/deployment/helm/all-in-one/.gitignore b/edc-tests/src/main/resources/deployment/helm/all-in-one/.gitignore new file mode 100644 index 000000000..8681aba50 --- /dev/null +++ b/edc-tests/src/main/resources/deployment/helm/all-in-one/.gitignore @@ -0,0 +1,4 @@ +# ignore downloaded helm depdencies +charts/ + +Chart.lock diff --git a/edc-tests/src/main/resources/deployment/helm/all-in-one/.helmignore b/edc-tests/src/main/resources/deployment/helm/all-in-one/.helmignore new file mode 100644 index 000000000..8c60d7821 --- /dev/null +++ b/edc-tests/src/main/resources/deployment/helm/all-in-one/.helmignore @@ -0,0 +1,24 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ +docs diff --git a/edc-tests/src/main/resources/deployment/helm/all-in-one/Chart.yaml b/edc-tests/src/main/resources/deployment/helm/all-in-one/Chart.yaml new file mode 100644 index 000000000..1875499ba --- /dev/null +++ b/edc-tests/src/main/resources/deployment/helm/all-in-one/Chart.yaml @@ -0,0 +1,76 @@ +--- +apiVersion: v2 +name: all-in-one +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.16.0" + +dependencies: + # IDS DAPS + - name: ids-daps + version: 0.0.1 + repository: "file://../omejdn" + alias: ids-daps + + # PLATO CONNECTOR + - name: edc-controlplane + version: 0.0.3 + repository: "file://../../../../../../../deployment/helm/edc-controlplane" + alias: plato-edc-controlplane + - name: edc-dataplane + version: 0.0.3 + repository: "file://../../../../../../../deployment/helm/edc-dataplane" + alias: plato-edc-dataplane + - name: backend-application + version: 0.0.1 + repository: "file://../backend-application" + alias: plato-backend-application + - name: vault + version: 0.20.0 + repository: https://helm.releases.hashicorp.com + alias: plato-vault + - name: postgresql + version: 11.2.4 + repository: https://charts.bitnami.com/bitnami + alias: plato-postgresql + + # SOKRATES CONNECTOR + - name: edc-controlplane + version: 0.0.3 + repository: "file://../../../../../../../deployment/helm/edc-controlplane" + alias: sokrates-edc-controlplane + - name: edc-dataplane + version: 0.0.3 + repository: "file://../../../../../../../deployment/helm/edc-dataplane" + alias: sokrates-edc-dataplane + - name: backend-application + version: 0.0.1 + repository: "file://../backend-application" + alias: sokrates-backend-application + - name: vault + version: 0.20.0 + repository: https://helm.releases.hashicorp.com + alias: sokrates-vault + - name: postgresql + version: 11.2.4 + repository: https://charts.bitnami.com/bitnami + alias: sokrates-postgresql diff --git a/edc-tests/src/main/resources/deployment/helm/all-in-one/README.md b/edc-tests/src/main/resources/deployment/helm/all-in-one/README.md new file mode 100644 index 000000000..e639dd387 --- /dev/null +++ b/edc-tests/src/main/resources/deployment/helm/all-in-one/README.md @@ -0,0 +1,94 @@ +# All-In-One Deployment + +The Product EDC Demo Deployment creates a complete, independent and already configured EDC test environment. + +This deployment may function as + +- reference setup for teams, that want to create their own connector +- standalone test environment to try different things out + +## Setup + +Follow these steps to get a fully functional EDC demo environment out of the box. + +### Requirements + +Install on your machine: + +- Minikube + - Documentation https://minikube.sigs.k8s.io/docs/start/ +- Helm + - Documentation https://helm.sh/docs/intro/install/ + +## Start Demo Environment + +**Update Helm Dependencies** + +```bash +helm dependency update +``` + +**Install Demo Chart** + +```bash +helm install edc-all-in-one --namespace edc-all-in-one --create-namespace . +``` + +This will deploy the following components: + +![Deployed Components](diagrams/deployed_components.png) + +## Stop Demo Environment + +**Uninstall Demo Chart** + +```bash +helm uninstall edc-all-in-one --namespace edc-all-in-one +``` + +## Components + +Overview of the installed components. + +### EDC Control Plane + +The EDC Control Plane does + +- data/contract offering +- contract negotiation +- data transfer coordination + +Two control planes always talk to each other using IDS messages. Therefore, when telling one connector to talk to +another connector, the target endpoint must point to the IDS API (e.g `http://[myTargetConnector].com/api/v1/ids`). + +The connector owner should only talk to the control plane via the Data Management API. The API is not only used for +simple data management, but for initiating inter-connector communication as well. + +### EDC Data Plane + +The EDC Data Plane is used for the actual data transfer. + +At the time of writing the Data Plane may only function as HTTP proxy and does not support any other type of +transfer. Additional transfer capabilities could be added by including new EDC extensions in the Data Plane application. + +### PostgreSQL + +This database is used to persist the state of the Control Plane. + +### HashiCorp Vault + +The Control- and Data Plane will persist confidential in the vault and persist and communicate using only the secret +names. + +### Backend Application + +After a Data Transfer is successfully prepared the control plane will contact the a configurable endpoint with the +information it needs to initiate the data transfer. This transfer flow, where something like a Backend Application is +required, is unique to the HTTP Proxy data transfer flow. + +The Backend Application has an API endpoint, that is configured in the control plane. After it gets called with the data +transfer information, it will do the actual data transfer and store the data on disk. + +### Omejdn DAPS + +Instead of the Catena-X DAPS this demo configures and deploys it's own DAPS instance. diff --git a/edc-tests/src/main/resources/deployment/helm/all-in-one/diagrams/deployed_components.png b/edc-tests/src/main/resources/deployment/helm/all-in-one/diagrams/deployed_components.png new file mode 100644 index 0000000000000000000000000000000000000000..b91338ef6a88be9d52117cbdb0d47498bdedce05 GIT binary patch literal 39746 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 literal 0 HcmV?d00001 diff --git a/edc-tests/src/main/resources/deployment/helm/all-in-one/diagrams/deployed_components.puml b/edc-tests/src/main/resources/deployment/helm/all-in-one/diagrams/deployed_components.puml new file mode 100644 index 000000000..9880baf85 --- /dev/null +++ b/edc-tests/src/main/resources/deployment/helm/all-in-one/diagrams/deployed_components.puml @@ -0,0 +1,47 @@ +@startuml + +!define sokratesColor 66CCFF +!define platoColor CCFF99 +!define dapsColor FFFF99 + +node PlatoSetup as "Plato Connector Setup" { + database PlatoPsql as "PostgreSQL" #platoColor + database PlatoKeyVault as "HashiCorp Vault" #platoColor + component PlatoConnector as "Eclipse Dataspace Connector" { + artifact PlatoControlPlane as "Control Plane" #platoColor + artifact PlatoDataPlane as "Data Plane" #platoColor + } + component PlatoBackendService as "Backend Application" #platoColor +} + +PlatoControlPlane -- PlatoPsql +PlatoControlPlane -- PlatoKeyVault +PlatoDataPlane -- PlatoKeyVault +PlatoDataPlane -left- PlatoControlPlane +PlatoControlPlane -left- PlatoBackendService + +node SokratesSetup as "Sokrates Connector Setup" { + database SokratesPsql as "PostgreSQL" #sokratesColor + database SokratesKeyVault as "HashiCorp Vault" #sokratesColor + component SokratesConnector as "Eclipse Dataspace Connector" { + artifact SokratesControlPlane as "Control Plane" #sokratesColor + artifact SokratesDataPlane as "Data Plane" #sokratesColor + } + component SokratesBackendService as "Backend Application" #sokratesColor +} + +SokratesControlPlane -- SokratesPsql +SokratesControlPlane -- SokratesKeyVault +SokratesDataPlane -- SokratesKeyVault +SokratesDataPlane -left- SokratesControlPlane +SokratesControlPlane -left- SokratesBackendService + + +node IdentityProvider as "Identity Provider" { + component OmejdnDaps as "Omejdn DAPS" #dapsColor +} + +PlatoPsql -[hidden]down- OmejdnDaps +SokratesControlPlane -[hidden]up- OmejdnDaps + +@enduml diff --git a/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml b/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml new file mode 100644 index 000000000..d7b76163b --- /dev/null +++ b/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml @@ -0,0 +1,514 @@ +--- +# Default values for all-in-one deployment. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +ids-daps: + fullnameOverride: "ids-daps" + connectors: + - id: &sokratesDapsClientId E7:07:2D:74:56:66:31:F0:7B:10:EA:B6:03:06:4C:23:7F:ED:A6:65:keyid:E7:07:2D:74:56:66:31:F0:7B:10:EA:B6:03:06:4C:23:7F:ED:A6:65 + name: sokrates + # Must be the same certificate that is stores in section 'sokrates-vault' + certificate: |- + -----BEGIN CERTIFICATE----- + MIIEAzCCAuugAwIBAgIUXFgjbN7jxGRUDkoUvEwcN3zcew8wDQYJKoZIhvcNAQEL + BQAwgZAxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZCZXJsaW4xDzANBgNVBAcMBkJl + cmxpbjEMMAoGA1UECgwDQk1XMSAwHgYDVQQLDBdlZGMtcGxheWdyb3VuZC1wYXJ0 + bmVyMTEvMC0GA1UEAwwmc29rcmF0ZXMtZWRjLmRlbW8uY2F0ZW5hLXgubmV0L0JQ + TjEyMzQwHhcNMjIwNTEwMDc1NzMzWhcNMjMwNTEwMDc1NzMzWjCBkDELMAkGA1UE + BhMCREUxDzANBgNVBAgMBkJlcmxpbjEPMA0GA1UEBwwGQmVybGluMQwwCgYDVQQK + DANCTVcxIDAeBgNVBAsMF2VkYy1wbGF5Z3JvdW5kLXBhcnRuZXIxMS8wLQYDVQQD + DCZzb2tyYXRlcy1lZGMuZGVtby5jYXRlbmEteC5uZXQvQlBOMTIzNDCCASIwDQYJ + KoZIhvcNAQEBBQADggEPADCCAQoCggEBAK/41S8rumkk+IzBk9pBDETvjlPmlXfw + 78yRrLmbzaed3kGgygJ2GFFPLcR/Lv0WG8F8au4UEssbOxAU4RRjncCVt66ajaCa + llIqMlH8zaJ8rgxNpGeJU5YvmYRxlIo+Gwi0qnF0tqJh8Hry7OqSo0gK2YBBFJyV + grMsEz3EcsS3ENYJufNgUIeg4QsaL49M0gWxSexPdC4pon96Nvju90D8RlvAJB21 + PInqLniMaFlSnRYzCrUaja6HMmzKA+ZPZ1r9lllzsE00RASxRIxlKkwfzTtMb9O6 + ey2i2vM7hKGGlXjNsnYVX9WXEfvK4JrCadHzgX8qdez19RxFKtB+5gECAwEAAaNT + MFEwHQYDVR0OBBYEFOcHLXRWZjHwexDqtgMGTCN/7aZlMB8GA1UdIwQYMBaAFOcH + LXRWZjHwexDqtgMGTCN/7aZlMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL + BQADggEBAD2a5kuIdICNXfYLpSe7AIONwZVucaArYtpXBxHEy5lMJsTEJgjZzypd + iIMU7onEQGVbii6yVNpWfIpJYM4e8ytVdJuk5evclVKZs/lZ2IshLyWFVj+ITh2E + 28X4C/Hnmt4MPBCNowQf71nMp4LEziBgXp54qFV9C+qSTEVdrherRE0PU/zKyX10 + S/P5o42weTHnAO/pBN/8AmL3AymynKVgcPaW46IjjRAuc6kfZWCrYQ0M4+/7Ws5r + uM55Zae/L+C82OTNNaaK324ogsCkORPeQ23OCrRD8rZJmQ9bpoOGglPminfwEOhB + UHtyKgmvqCyOV3G/4G93W/xsLV0kxLA= + -----END CERTIFICATE----- + - id: &platoDapsClientId 99:83:A7:17:86:FF:98:93:CE:A0:DD:A1:F1:36:FA:F6:0F:75:0A:23:keyid:99:83:A7:17:86:FF:98:93:CE:A0:DD:A1:F1:36:FA:F6:0F:75:0A:23 + name: plato + # Must be the same certificate that is stores in section 'plato-vault' + certificate: |- + -----BEGIN CERTIFICATE----- + MIID7TCCAtWgAwIBAgIUJ0bwxUc7n3YK89mOyGXrLx2KO0YwDQYJKoZIhvcNAQEL + BQAwgYUxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZCZXJsaW4xDzANBgNVBAcMBkJl + cmxpbjEMMAoGA1UECgwDQk1XMSAwHgYDVQQLDBdlZGMtcGxheWdyb3VuZC1wYXJ0 + bmVyMjEkMCIGA1UEAwwbcGxhdG8tZWRjLmRlbW8uY2F0ZW5hLXgubmV0MB4XDTIy + MDQyNTEwMjgwMloXDTIzMDQyNTEwMjgwMlowgYUxCzAJBgNVBAYTAkRFMQ8wDQYD + VQQIDAZCZXJsaW4xDzANBgNVBAcMBkJlcmxpbjEMMAoGA1UECgwDQk1XMSAwHgYD + VQQLDBdlZGMtcGxheWdyb3VuZC1wYXJ0bmVyMjEkMCIGA1UEAwwbcGxhdG8tZWRj + LmRlbW8uY2F0ZW5hLXgubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC + AQEAm1/UvHXuRU1peEGHULZBP8j8gorXAQvUz8Znb1iVNtldI29GCSXkTHxph66x + TcegdF8aeaoU1mPf3LzMQUU1koZHUq6sRC+50uFcZJ2AjF5IXKQlDPNWR5tPXP56 + RZyqXxjPFHeuTA+YsYyrzEVhzEieOiNaxJDM3uV5pv+FTRHz+xMOgNBonR1QyMh6 + tcwB+EQagoeFl0DjEXAel9WG4hOG/rDiXArTMaVjnTG/ycmF0HeSnbRC+3/+fh/C + hzQJyEbviX67ymyYRJTyynt/Mtrqg5/ssdISexjw3ZmiFNemZIOhIdepoSwnJHFM + 4Jj8B5lq0a1jY5Rc9lDj710RXQIDAQABo1MwUTAdBgNVHQ4EFgQUmYOnF4b/mJPO + oN2h8Tb69g91CiMwHwYDVR0jBBgwFoAUmYOnF4b/mJPOoN2h8Tb69g91CiMwDwYD + VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAKxv/MTIEKNkzReOqrzpt + LM00X6JsDdfxa3rZ0Uq17PjO0R63IPsqzexhfZUML0e/Dwpe97xpvftCOEuICMBA + wOHhQc77MgwyF4dqgRgfJysxw37ACZxU6GI/K2JpKXQLgEhP14oHUIWOzCAbgDhR + jwOx3ZP176Vjxx90pW3hOphRVnq/BRqqEDtFwRzKtGnGvP8ecmC2iY4dXEA3QEp1 + gzg03eglvZSoedEPY5o5y/4n6TplaDmaeoo0QrvAiWik1gY85Lg21aBWVsP45wVS + tFn3m1FCCV8XYIj/EEUAh8VEhphLVEViE6m9Mm4deFDavXcGBb63BCiOQtnjd3eY + zQ== + -----END CERTIFICATE----- + +######### +# PLATO # +######### +plato-backend-application: + fullnameOverride: "plato-backend-application" + service: + port: 80 +plato-postgresql: + fullnameOverride: "plato-postgresql" + auth: + username: &psqlUsername "postgresql_sandbox_user" + password: &psqlPassword "psql_password" + database: &psqlDatabase "edc" + persistence: + enabled: false +plato-vault: + fullnameOverride: "plato-vault" + injector: + enabled: false + server: + dev: + enabled: true + devRootToken: &platoVaultToken "root" + + # Must be the same certificate that is configured in section 'ids-daps' + postStart: + - "sh" + - "-c" + - | + { + + sleep 1 + + cat << EOF | /bin/vault kv put secret/my-plato-daps-key content=- + -----BEGIN PRIVATE KEY----- + MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCbX9S8de5FTWl4 + QYdQtkE/yPyCitcBC9TPxmdvWJU22V0jb0YJJeRMfGmHrrFNx6B0Xxp5qhTWY9/c + vMxBRTWShkdSrqxEL7nS4VxknYCMXkhcpCUM81ZHm09c/npFnKpfGM8Ud65MD5ix + jKvMRWHMSJ46I1rEkMze5Xmm/4VNEfP7Ew6A0GidHVDIyHq1zAH4RBqCh4WXQOMR + cB6X1YbiE4b+sOJcCtMxpWOdMb/JyYXQd5KdtEL7f/5+H8KHNAnIRu+JfrvKbJhE + lPLKe38y2uqDn+yx0hJ7GPDdmaIU16Zkg6Eh16mhLCckcUzgmPwHmWrRrWNjlFz2 + UOPvXRFdAgMBAAECggEAN2yd5IRk9I/CucUWUfJRoEE/4glI3PSte1iY+R0uTRyI + nuVIpGbB447VzjLAyLAXSqvKM/A58qg56PHoIrhffd8sfhAVH1WvAcymOrX8bxYK + 1hEvrkj3VB/Q1alpUH+sPrQI2pI+uJ8vptY5SmrNkiOtXavS6x+EFVbiaHHpyS26 + ASaCoRpdBoNTm0SAiDBTK6MqTs4vRpqKseGdC76F+jKimYrTJY19ZctSIAMjrnqd + qzRL+jfob5vMqKC22AjInkZ8BZWll1ZoTnv37bq2NAb9lvdY73REm42Wpm5S7PET + Eixe69gvi/IwaSe27S36+kcrQoYHnxbb31+Xt+0pQQKBgQDJfA2ZnYmcA3yvVQhi + e76I3rq6AEfcG4EDhf+JRO2QHKMMXLwfFAdSR8QflxNUWy1y6q/783EpgLJ1Kv8h + uNkTH6JyV7kFhwfvxWreAWx2jRQRACqnuaLnJ/28vd8Il0kc3/BQsWzbg6YTERrq + 0Au2RW/c9blrKS0MyurtOtZsiQKBgQDFaezSCWUspeNci5lrdvMiHBLOUgR2guQm + Gtf9RdBmzvtBqpdkP8AEMhRW7oSGcKpDldd0Klyml7s/CDYTL7sflHtKRiTQmWuJ + +p3uvyylAxr/Swfw56hj5Y4/Oj2CLIuUlglewo40JnvvM5icT7RGvbyaIIhYzIsR + HTv3t8eRNQKBgA4l8eaJk3IrJIRDWlVgDx8ZVM9e2azxGXwf2rPO7UejWyexE1yz + UVhLxc/aEfdod6aMKFNu4tFhQibMICJEEqovHH8e/dUPiFUj7b8tJmqkuXYAJv6k + IHZO7phkVNcLmIy4hO2Fp/k6I11PZC588XWZJqPDdYO63nj5fsmtygTRAoGBAJ72 + YH/wmMuO+Ll4n51tNvJscKg6WuWjGFumme2T3fArEx8ZYraSruex+7bUcVpgNnod + mlQsGFb9LwXecsyYTrFrOqvgN5zRLUr5x1qMDkMBcSfJHyfZIjruidBX8Vd0zyBi + gEERoLhVlM5UWbrkY2HjPo9NSv1WF1U8mSErl0NRAoGAYC3RxEfGxD9+Qi08nQgg + s/48hLdD2k2q4t3FrDsIGPAIEs52CGp9JWil9RyIQxBXWejETwDz+PgmD6U86Mhh + Qf5css6pcP/w1XF8vsyXfPnecgPSyOE4CgLtnQLxNriMiy5pfALELLyxoBQ+nquz + fMNLPC4K85ps/Uu9uzSatl0= + -----END PRIVATE KEY----- + EOF + + cat << EOF | /bin/vault kv put secret/my-plato-daps-crt content=- + -----BEGIN CERTIFICATE----- + MIID7TCCAtWgAwIBAgIUJ0bwxUc7n3YK89mOyGXrLx2KO0YwDQYJKoZIhvcNAQEL + BQAwgYUxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZCZXJsaW4xDzANBgNVBAcMBkJl + cmxpbjEMMAoGA1UECgwDQk1XMSAwHgYDVQQLDBdlZGMtcGxheWdyb3VuZC1wYXJ0 + bmVyMjEkMCIGA1UEAwwbcGxhdG8tZWRjLmRlbW8uY2F0ZW5hLXgubmV0MB4XDTIy + MDQyNTEwMjgwMloXDTIzMDQyNTEwMjgwMlowgYUxCzAJBgNVBAYTAkRFMQ8wDQYD + VQQIDAZCZXJsaW4xDzANBgNVBAcMBkJlcmxpbjEMMAoGA1UECgwDQk1XMSAwHgYD + VQQLDBdlZGMtcGxheWdyb3VuZC1wYXJ0bmVyMjEkMCIGA1UEAwwbcGxhdG8tZWRj + LmRlbW8uY2F0ZW5hLXgubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC + AQEAm1/UvHXuRU1peEGHULZBP8j8gorXAQvUz8Znb1iVNtldI29GCSXkTHxph66x + TcegdF8aeaoU1mPf3LzMQUU1koZHUq6sRC+50uFcZJ2AjF5IXKQlDPNWR5tPXP56 + RZyqXxjPFHeuTA+YsYyrzEVhzEieOiNaxJDM3uV5pv+FTRHz+xMOgNBonR1QyMh6 + tcwB+EQagoeFl0DjEXAel9WG4hOG/rDiXArTMaVjnTG/ycmF0HeSnbRC+3/+fh/C + hzQJyEbviX67ymyYRJTyynt/Mtrqg5/ssdISexjw3ZmiFNemZIOhIdepoSwnJHFM + 4Jj8B5lq0a1jY5Rc9lDj710RXQIDAQABo1MwUTAdBgNVHQ4EFgQUmYOnF4b/mJPO + oN2h8Tb69g91CiMwHwYDVR0jBBgwFoAUmYOnF4b/mJPOoN2h8Tb69g91CiMwDwYD + VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAKxv/MTIEKNkzReOqrzpt + LM00X6JsDdfxa3rZ0Uq17PjO0R63IPsqzexhfZUML0e/Dwpe97xpvftCOEuICMBA + wOHhQc77MgwyF4dqgRgfJysxw37ACZxU6GI/K2JpKXQLgEhP14oHUIWOzCAbgDhR + jwOx3ZP176Vjxx90pW3hOphRVnq/BRqqEDtFwRzKtGnGvP8ecmC2iY4dXEA3QEp1 + gzg03eglvZSoedEPY5o5y/4n6TplaDmaeoo0QrvAiWik1gY85Lg21aBWVsP45wVS + tFn3m1FCCV8XYIj/EEUAh8VEhphLVEViE6m9Mm4deFDavXcGBb63BCiOQtnjd3eY + zQ== + -----END CERTIFICATE----- + EOF + } + + ui: + enabled: true + externalPort: 8200 + targetPort: 8200 +plato-edc-dataplane: + fullnameOverride: "plato-edc-dataplane" + image: + repository: &edcDataPlaneImage ghcr.io/catenax-ng/product-edc/edc-dataplane-hashicorp-vault + tag: &edcDataPlaneImageTag develop + edc: + endpoints: + public: + port: 8185 + path: /api/public + opentelemetry: + properties: |- + otel.javaagent.enabled=false + otel.javaagent.debug=false + env: + JAVA_TOOL_OPTIONS: "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=1044" + + ############# + ## GENERAL ## + ############# + + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/data-plane/data-plane-api + EDC_CONTROLPLANE_VALIDATION-ENDPOINT: http://plato-edc-controlplane:8182/validation/token + + ############### + ## KEY VAULT ## + ############### + + # see extension https://github.com/catenax-ng/product-edc/tree/develop/edc-extensions/hashicorp-vault + EDC_VAULT_HASHICORP_URL: http://plato-vault + EDC_VAULT_HASHICORP_TOKEN: *platoVaultToken +plato-edc-controlplane: + fullnameOverride: "plato-edc-controlplane" + service: + type: NodePort + image: + repository: &edcControlPlaneImage ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql-hashicorp-vault + tag: &edcControlPlaneImageTag develop + opentelemetry: + properties: |- + otel.javaagent.enabled=false + otel.javaagent.debug=false + env: + JAVA_TOOL_OPTIONS: "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=1044" + + ######################## + ## DAPS CONFIGURATION ## + ######################## + + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/iam/oauth2/oauth2-core + EDC_OAUTH_CLIENT_ID: *platoDapsClientId + EDC_OAUTH_PROVIDER_JWKS_URL: &edcControlPlaneOauthJwksUrl "http://ids-daps:4567/.well-known/jwks.json" + EDC_OAUTH_TOKEN_URL: &edcControlPlaneOauthTokenUrl "http://ids-daps:4567/token" + EDC_OAUTH_PRIVATE_KEY_ALIAS: my-plato-daps-key + EDC_OAUTH_PUBLIC_KEY_ALIAS: my-plato-daps-crt + EDC_OAUTH_PROVIDER_AUDIENCE: &edcControlPlaneOauthAudience idsc:IDS_CONNECTORS_ALL + + ############# + ## GENERAL ## + ############# + + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/data-protocols/ids/ids-api-multipart-dispatcher-v1 + IDS_WEBHOOK_ADDRESS: http://plato-edc-controlplane:8282 + + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/data-protocols/ids/ids-core + EDC_IDS_ENDPOINT: http://plato-edc-controlplane:8282/api/v1/ids + EDC_IDS_DESCRIPTION: "Plato Control Plane" + + ######################### + ## DATA MANAGEMENT API ## + ######################### + + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/api/auth-tokenbased + EDC_API_AUTH_KEY: &edcControlPlaneAuthKey "password" + + ################ + ## POSTGRESQL ## + ################ + + # the psql is configured in section 'plato-postgresql' + # password and username are taken from there + + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/asset-index-sql + EDC_DATASOURCE_ASSET_NAME: asset + EDC_DATASOURCE_ASSET_PASSWORD: *psqlPassword + EDC_DATASOURCE_ASSET_USER: *psqlUsername + EDC_DATASOURCE_ASSET_URL: &platoPsqlConStr "jdbc:postgresql://plato-postgresql:5432/edc" + + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/contract-definition-store-sql + EDC_DATASOURCE_CONTRACTDEFINITION_NAME: contractdefinition + EDC_DATASOURCE_CONTRACTDEFINITION_PASSWORD: *psqlPassword + EDC_DATASOURCE_CONTRACTDEFINITION_USER: *psqlUsername + EDC_DATASOURCE_CONTRACTDEFINITION_URL: *platoPsqlConStr + + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/contract-negotiation-store-sql + EDC_DATASOURCE_CONTRACTNEGOTIATION_NAME: contractnegotiation + EDC_DATASOURCE_CONTRACTNEGOTIATION_PASSWORD: *psqlPassword + EDC_DATASOURCE_CONTRACTNEGOTIATION_USER: *psqlUsername + EDC_DATASOURCE_CONTRACTNEGOTIATION_URL: *platoPsqlConStr + + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/policy-store-sql + EDC_DATASOURCE_POLICY_NAME: policy + EDC_DATASOURCE_POLICY_PASSWORD: *psqlPassword + EDC_DATASOURCE_POLICY_USER: *psqlUsername + EDC_DATASOURCE_POLICY_URL: *platoPsqlConStr + + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/transfer-process-store-sql + EDC_DATASOURCE_TRANSFERPROCESS_NAME: transferprocess + EDC_DATASOURCE_TRANSFERPROCESS_PASSWORD: *psqlPassword + EDC_DATASOURCE_TRANSFERPROCESS_USER: *psqlUsername + EDC_DATASOURCE_TRANSFERPROCESS_URL: *platoPsqlConStr + + ################ + ## DATA PLANE ## + ################ + + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/data-plane-transfer + EDC_TRANSFER_PROXY_ENDPOINT: http://plato-edc-dataplane:8185/api/public + EDC_TRANSFER_PROXY_TOKEN_SIGNER_PRIVATEKEY_ALIAS: my-plato-daps-key # for simplicity this example re-uses the DAPS keys. + EDC_TRANSFER_PROXY_TOKEN_VERIFIER_PUBLICKEY_ALIAS: my-plato-daps-crt # for simplicity this example re-uses the DAPS keys. + + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/http-receiver + EDC_RECEIVER_HTTP_ENDPOINT: http://plato-backend-application + + ############### + ## KEY VAULT ## + ############### + + # see extension https://github.com/catenax-ng/product-edc/tree/develop/edc-extensions/hashicorp-vault + EDC_VAULT_HASHICORP_URL: http://plato-vault:8200 + EDC_VAULT_HASHICORP_TOKEN: *platoVaultToken + +############ +# SOKRATES # +############ +sokrates-backend-application: + fullnameOverride: "sokrates-backend-application" + service: + port: 80 +sokrates-postgresql: + fullnameOverride: "sokrates-postgresql" + auth: + username: *psqlUsername + password: *psqlPassword + database: *psqlDatabase +sokrates-vault: + fullnameOverride: "sokrates-vault" + injector: + enabled: false + server: + dev: + enabled: true + devRootToken: &sokratesVaultToken "root" + + # Must be the same certificate that is configured in section 'ids-daps' + postStart: + - "sh" + - "-c" + - | + { + + sleep 1 + + cat << EOF | /bin/vault kv put secret/my-sokrates-daps-key content=- + -----BEGIN PRIVATE KEY----- + MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCv+NUvK7ppJPiM + wZPaQQxE745T5pV38O/Mkay5m82nnd5BoMoCdhhRTy3Efy79FhvBfGruFBLLGzsQ + FOEUY53Albeumo2gmpZSKjJR/M2ifK4MTaRniVOWL5mEcZSKPhsItKpxdLaiYfB6 + 8uzqkqNICtmAQRSclYKzLBM9xHLEtxDWCbnzYFCHoOELGi+PTNIFsUnsT3QuKaJ/ + ejb47vdA/EZbwCQdtTyJ6i54jGhZUp0WMwq1Go2uhzJsygPmT2da/ZZZc7BNNEQE + sUSMZSpMH807TG/TunstotrzO4ShhpV4zbJ2FV/VlxH7yuCawmnR84F/KnXs9fUc + RSrQfuYBAgMBAAECggEAO+KjsjTgcG3bhBNQnMLsSP15Y0Yicbn18ZlVvaivGS7Z + d14fwSytY+ZdPfTGaey/L16HCVSdfK9cr0Fbw9OO2P5ajzobnp9dLsMbctlkpbpm + hNtbarzKTF8QkIkSsuUl0BWjt46vpJ1N+Jl5VO7oUFkY4dPEDvG2lAEY3zlekWDm + cQeOC/YgpoW4xfRwPPS6QE0w3Q+H5NfNjfz+mSHeItTlVfTKDRliWQLPWeRZFuXh + FlRFUQnTmEE/9wpIe3Hn7WXJ3fQqcYDzxU7/zwwY9I7bB15SgVHlR0ENDPAD5X8F + MVZ3EcLlqGBy+WvTWALp6pc8YfhW3fiTWyuamXtNrQKBgQDonsIzBKEOOKdKGW0e + uyw79ErmnmzkY5nuMrMxrmTA4WKCfJ/YRRA+4sxiltWsIJ3UkHe3OBCSSCdj79hb + ugb/+UzE70hOdgrct2NUQqbrj3gvsVvU8ZRQgTRMqKpmC0zY7KOMx6NU85z3IvS1 + z5fjszcUv4kLQlldYGSAuqPy+wKBgQDBqIkc8p/wcw7ygo1q/GerNeszfoxiIFp8 + h4RWLVhkwrcXFz30wBlUWuv5/kxU8tmJcmXxe72EmUstd6wvNOAnYwCiile6zQiJ + vsr1axavZnGOtNGUp6DUAsd2iviBl7IZ7kAcqCrQo4ivGhfHmahH3hmg8wuAMjYB + 8f+FSPgaMwKBgQC7W4tMrjDOFIFhJEOIWfcRvvxI7VcFSNelS76aiDzsQVwnfxr7 + hPzFucQmsBgfUBHvMADMWGK4f1cCnh5kGtwidXgIsjVJxLeQ+EAPkLOCzQZfW3l8 + dKshgD9QcxTzpaxal5ZPAEikVqaZQtVYToCmzCTUGETYBbOWitnH+Qut2wKBgQC6 + Y6DcSLUhc0xOotLDxv1sbu/aVxF8nFEbDD+Vxf0Otc4MnmUWPRHj+8KlkVkcZcR0 + IrP1kThd+EDAGS+TG9wmbIY+6tH3S8HM+eJUBWcHGJ1xUZ1p61DC3Y3nDWiTKlLT + 3Fi+fCkBOHSku4Npq/2odh7Kp0JJd4o9oxJg0VNhuwKBgQDSFn7dqFE0Xmwc40Vr + 0wJH8cPWXKGt7KJENpj894buk2DniLD4w2x874dzTjrOFi6fKxEzbBNA9Rq9UPo8 + u9gKvl/IyWmV0c4zFCNMjRwVdnkMEte/lXcJZ67T4FXZByqAZlhrr/v0FD442Z9B + AjWFbUiBCFOo+gpAFcQGrkOQHA== + -----END PRIVATE KEY----- + EOF + + cat << EOF | /bin/vault kv put secret/my-sokrates-daps-crt content=- + -----BEGIN CERTIFICATE----- + MIIEAzCCAuugAwIBAgIUXFgjbN7jxGRUDkoUvEwcN3zcew8wDQYJKoZIhvcNAQEL + BQAwgZAxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZCZXJsaW4xDzANBgNVBAcMBkJl + cmxpbjEMMAoGA1UECgwDQk1XMSAwHgYDVQQLDBdlZGMtcGxheWdyb3VuZC1wYXJ0 + bmVyMTEvMC0GA1UEAwwmc29rcmF0ZXMtZWRjLmRlbW8uY2F0ZW5hLXgubmV0L0JQ + TjEyMzQwHhcNMjIwNTEwMDc1NzMzWhcNMjMwNTEwMDc1NzMzWjCBkDELMAkGA1UE + BhMCREUxDzANBgNVBAgMBkJlcmxpbjEPMA0GA1UEBwwGQmVybGluMQwwCgYDVQQK + DANCTVcxIDAeBgNVBAsMF2VkYy1wbGF5Z3JvdW5kLXBhcnRuZXIxMS8wLQYDVQQD + DCZzb2tyYXRlcy1lZGMuZGVtby5jYXRlbmEteC5uZXQvQlBOMTIzNDCCASIwDQYJ + KoZIhvcNAQEBBQADggEPADCCAQoCggEBAK/41S8rumkk+IzBk9pBDETvjlPmlXfw + 78yRrLmbzaed3kGgygJ2GFFPLcR/Lv0WG8F8au4UEssbOxAU4RRjncCVt66ajaCa + llIqMlH8zaJ8rgxNpGeJU5YvmYRxlIo+Gwi0qnF0tqJh8Hry7OqSo0gK2YBBFJyV + grMsEz3EcsS3ENYJufNgUIeg4QsaL49M0gWxSexPdC4pon96Nvju90D8RlvAJB21 + PInqLniMaFlSnRYzCrUaja6HMmzKA+ZPZ1r9lllzsE00RASxRIxlKkwfzTtMb9O6 + ey2i2vM7hKGGlXjNsnYVX9WXEfvK4JrCadHzgX8qdez19RxFKtB+5gECAwEAAaNT + MFEwHQYDVR0OBBYEFOcHLXRWZjHwexDqtgMGTCN/7aZlMB8GA1UdIwQYMBaAFOcH + LXRWZjHwexDqtgMGTCN/7aZlMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL + BQADggEBAD2a5kuIdICNXfYLpSe7AIONwZVucaArYtpXBxHEy5lMJsTEJgjZzypd + iIMU7onEQGVbii6yVNpWfIpJYM4e8ytVdJuk5evclVKZs/lZ2IshLyWFVj+ITh2E + 28X4C/Hnmt4MPBCNowQf71nMp4LEziBgXp54qFV9C+qSTEVdrherRE0PU/zKyX10 + S/P5o42weTHnAO/pBN/8AmL3AymynKVgcPaW46IjjRAuc6kfZWCrYQ0M4+/7Ws5r + uM55Zae/L+C82OTNNaaK324ogsCkORPeQ23OCrRD8rZJmQ9bpoOGglPminfwEOhB + UHtyKgmvqCyOV3G/4G93W/xsLV0kxLA= + -----END CERTIFICATE----- + EOF + } + + ui: + enabled: true + externalPort: 8200 + targetPort: 8200 +sokrates-edc-dataplane: + fullnameOverride: "sokrates-edc-dataplane" + image: + repository: *edcDataPlaneImage + tag: *edcDataPlaneImageTag + edc: + endpoints: + public: + port: 8185 + path: /api/public + opentelemetry: + properties: |- + otel.javaagent.enabled=false + otel.javaagent.debug=false + env: + JAVA_TOOL_OPTIONS: "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=1044" + + ############# + ## GENERAL ## + ############# + + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/data-plane/data-plane-api + EDC_CONTROLPLANE_VALIDATION-ENDPOINT: http://sokrates-edc-controlplane:8182/validation/token + + ############### + ## KEY VAULT ## + ############### + + # see extension https://github.com/catenax-ng/product-edc/tree/develop/edc-extensions/hashicorp-vault + EDC_VAULT_HASHICORP_URL: http://sokrates-vault-0 + EDC_VAULT_HASHICORP_TOKEN: *sokratesVaultToken +sokrates-edc-controlplane: + fullnameOverride: "sokrates-edc-controlplane" + service: + type: NodePort + image: + repository: *edcControlPlaneImage + tag: *edcControlPlaneImageTag + opentelemetry: + properties: |- + otel.javaagent.enabled=false + otel.javaagent.debug=false + env: + JAVA_TOOL_OPTIONS: "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=1044" + + ######################## + ## DAPS CONFIGURATION ## + ######################## + + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/iam/oauth2/oauth2-core + EDC_OAUTH_CLIENT_ID: *sokratesDapsClientId + EDC_OAUTH_PROVIDER_JWKS_URL: *edcControlPlaneOauthJwksUrl + EDC_OAUTH_TOKEN_URL: *edcControlPlaneOauthTokenUrl + EDC_OAUTH_PRIVATE_KEY_ALIAS: my-sokrates-daps-key + EDC_OAUTH_PUBLIC_KEY_ALIAS: my-sokrates-daps-crt + EDC_OAUTH_PROVIDER_AUDIENCE: *edcControlPlaneOauthAudience + + ############# + ## GENERAL ## + ############# + IDS_WEBHOOK_ADDRESS: http://sokrates-edc-controlplane:8282 + EDC_IDS_ENDPOINT: http://sokrates-edc-controlplane:8282/api/v1/ids + EDC_IDS_DESCRIPTION: "Sokrates Control Plane" + + ######################### + ## DATA MANAGEMENT API ## + ######################### + + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/api/auth-tokenbased + EDC_API_AUTH_KEY: *edcControlPlaneAuthKey + + ################ + ## POSTGRESQL ## + ################ + + # the psql is configured in section 'sokrates-postgresql' + # password and username are taken from there + + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/asset-index-sql + EDC_DATASOURCE_ASSET_NAME: asset + EDC_DATASOURCE_ASSET_PASSWORD: *psqlPassword + EDC_DATASOURCE_ASSET_USER: *psqlUsername + EDC_DATASOURCE_ASSET_URL: &SokratesPsqlConStr "jdbc:postgresql://sokrates-postgresql:5432/edc" + + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/contract-definition-store-sql + EDC_DATASOURCE_CONTRACTDEFINITION_NAME: contractdefinition + EDC_DATASOURCE_CONTRACTDEFINITION_PASSWORD: *psqlPassword + EDC_DATASOURCE_CONTRACTDEFINITION_USER: *psqlUsername + EDC_DATASOURCE_CONTRACTDEFINITION_URL: *SokratesPsqlConStr + + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/contract-negotiation-store-sql + EDC_DATASOURCE_CONTRACTNEGOTIATION_NAME: contractnegotiation + EDC_DATASOURCE_CONTRACTNEGOTIATION_PASSWORD: *psqlPassword + EDC_DATASOURCE_CONTRACTNEGOTIATION_USER: *psqlUsername + EDC_DATASOURCE_CONTRACTNEGOTIATION_URL: *SokratesPsqlConStr + + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/policy-store-sql + EDC_DATASOURCE_POLICY_NAME: policy + EDC_DATASOURCE_POLICY_PASSWORD: *psqlPassword + EDC_DATASOURCE_POLICY_USER: *psqlUsername + EDC_DATASOURCE_POLICY_URL: *SokratesPsqlConStr + + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/transfer-process-store-sql + EDC_DATASOURCE_TRANSFERPROCESS_NAME: transferprocess + EDC_DATASOURCE_TRANSFERPROCESS_PASSWORD: *psqlPassword + EDC_DATASOURCE_TRANSFERPROCESS_USER: *psqlUsername + EDC_DATASOURCE_TRANSFERPROCESS_URL: *SokratesPsqlConStr + + ################ + ## DATA PLANE ## + ################ + + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/data-plane-transfer + EDC_TRANSFER_PROXY_ENDPOINT: http://sokrates-edc-dataplane:8185/api/public + EDC_TRANSFER_PROXY_TOKEN_SIGNER_PRIVATEKEY_ALIAS: my-sokrates-daps-key # for simplicity this example re-uses the DAPS keys. + EDC_TRANSFER_PROXY_TOKEN_VERIFIER_PUBLICKEY_ALIAS: my-sokrates-daps-crt # for simplicity this example re-uses the DAPS keys. + + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/http-receiver + EDC_RECEIVER_HTTP_ENDPOINT: http://sokrates-backend-application + + ############### + ## KEY VAULT ## + ############### + + # see extension https://github.com/catenax-ng/product-edc/tree/develop/edc-extensions/hashicorp-vault + EDC_VAULT_HASHICORP_URL: http://sokrates-vault:8200 + EDC_VAULT_HASHICORP_TOKEN: *sokratesVaultToken diff --git a/edc-tests/src/main/resources/deployment/helm/backend-application/.helmignore b/edc-tests/src/main/resources/deployment/helm/backend-application/.helmignore new file mode 100644 index 000000000..0e8a0eb36 --- /dev/null +++ b/edc-tests/src/main/resources/deployment/helm/backend-application/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/edc-tests/src/main/resources/deployment/helm/backend-application/Chart.yaml b/edc-tests/src/main/resources/deployment/helm/backend-application/Chart.yaml new file mode 100644 index 000000000..cdfd13060 --- /dev/null +++ b/edc-tests/src/main/resources/deployment/helm/backend-application/Chart.yaml @@ -0,0 +1,25 @@ +--- +apiVersion: v2 +name: backend-application +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.0.1 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "0.0.1" diff --git a/edc-tests/src/main/resources/deployment/helm/backend-application/README.md b/edc-tests/src/main/resources/deployment/helm/backend-application/README.md new file mode 100644 index 000000000..45b9855b1 --- /dev/null +++ b/edc-tests/src/main/resources/deployment/helm/backend-application/README.md @@ -0,0 +1,3 @@ +# Backend Application + +The Eclipse Dataspace Connector requires the Backend Application to transfer data using the HTTP-TransferMethod. diff --git a/edc-tests/src/main/resources/deployment/helm/backend-application/templates/_helpers.tpl b/edc-tests/src/main/resources/deployment/helm/backend-application/templates/_helpers.tpl new file mode 100644 index 000000000..84ec93472 --- /dev/null +++ b/edc-tests/src/main/resources/deployment/helm/backend-application/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "backend-application.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "backend-application.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "backend-application.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "backend-application.labels" -}} +helm.sh/chart: {{ include "backend-application.chart" . }} +{{ include "backend-application.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "backend-application.selectorLabels" -}} +app.kubernetes.io/name: {{ include "backend-application.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "backend-application.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "backend-application.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/edc-tests/src/main/resources/deployment/helm/backend-application/templates/configmap.yaml b/edc-tests/src/main/resources/deployment/helm/backend-application/templates/configmap.yaml new file mode 100644 index 000000000..4f9d006ed --- /dev/null +++ b/edc-tests/src/main/resources/deployment/helm/backend-application/templates/configmap.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "backend-application.fullname" . }} + labels: + {{- include "backend-application.labels" . | nindent 4 }} +data: + handler.sh: |- + {{- .Values.script.content | nindent 4 }} diff --git a/edc-tests/src/main/resources/deployment/helm/backend-application/templates/deployment.yaml b/edc-tests/src/main/resources/deployment/helm/backend-application/templates/deployment.yaml new file mode 100644 index 000000000..1d8751e2d --- /dev/null +++ b/edc-tests/src/main/resources/deployment/helm/backend-application/templates/deployment.yaml @@ -0,0 +1,81 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "backend-application.fullname" . }} + labels: + {{- include "backend-application.labels" . | nindent 4 }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "backend-application.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "backend-application.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "backend-application.serviceAccountName" . }} + automountServiceAccountToken: {{ .Values.automountServiceAccountToken }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + {{ if .Values.image.command -}} + command: + {{ toYaml .Values.image.command | nindent 12 }} + {{ end -}} + {{ if .Values.livenessProbe -}} + livenessProbe: + {{ toYaml .Values.livenessProbe | nindent 12 }} + {{ end -}} + {{ if .Values.readinessProbe -}} + readinessProbe: + {{ toYaml .Values.readinessProbe | nindent 12 }} + {{ end -}} + volumeMounts: + - name: configmap + mountPath: {{ .Values.script.path }} + subPath: handler.sh + env: + - name: TCP_SERVER_SCRIPT_PATH + value: {{ .Values.script.path | quote }} + - name: TCP_SERVER_PORT + value: {{ .Values.container.port | quote}} + ports: + - containerPort: {{ .Values.container.port }} + protocol: TCP + name: http + volumes: + - name: configmap + configMap: + name: {{ include "backend-application.fullname" . }} + items: + - key: handler.sh + path: handler.sh + defaultMode: 0744 + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/edc-tests/src/main/resources/deployment/helm/backend-application/templates/hpa.yaml b/edc-tests/src/main/resources/deployment/helm/backend-application/templates/hpa.yaml new file mode 100644 index 000000000..7e01b6053 --- /dev/null +++ b/edc-tests/src/main/resources/deployment/helm/backend-application/templates/hpa.yaml @@ -0,0 +1,28 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2beta1 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "backend-application.fullname" . }} + labels: + {{- include "backend-application.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "backend-application.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/edc-tests/src/main/resources/deployment/helm/backend-application/templates/service.yaml b/edc-tests/src/main/resources/deployment/helm/backend-application/templates/service.yaml new file mode 100644 index 000000000..9328b68c8 --- /dev/null +++ b/edc-tests/src/main/resources/deployment/helm/backend-application/templates/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "backend-application.fullname" . }} + labels: + {{- include "backend-application.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "backend-application.selectorLabels" . | nindent 4 }} diff --git a/edc-tests/src/main/resources/deployment/helm/backend-application/templates/serviceaccount.yaml b/edc-tests/src/main/resources/deployment/helm/backend-application/templates/serviceaccount.yaml new file mode 100644 index 000000000..f3f87ff4f --- /dev/null +++ b/edc-tests/src/main/resources/deployment/helm/backend-application/templates/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "backend-application.serviceAccountName" . }} + labels: + {{- include "backend-application.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/edc-tests/src/main/resources/deployment/helm/backend-application/values.yaml b/edc-tests/src/main/resources/deployment/helm/backend-application/values.yaml new file mode 100644 index 000000000..20e356fd4 --- /dev/null +++ b/edc-tests/src/main/resources/deployment/helm/backend-application/values.yaml @@ -0,0 +1,153 @@ +--- +# Default values for backend-application. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + # -- Which container image to use + repository: ubuntu + # -- [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use + pullPolicy: IfNotPresent + # -- Overrides the image tag whose default is the chart appVersion + tag: "22.04" + command: + - /bin/bash + - -c + - apt-get update && apt-get install -y ucspi-tcp curl jq && rm -rf /var/lib/apt/lists/* && tcpserver -v 0.0.0.0 "${TCP_SERVER_PORT}" "${TCP_SERVER_SCRIPT_PATH}" + +# -- Image pull secret to create to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) +imagePullSecrets: [] + +# -- Overrides the charts name +nameOverride: "" + +# -- Overrides the releases full name +fullnameOverride: "" + +serviceAccount: + # -- Specifies whether a [service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) should be created per release + create: true + # -- [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/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 release's fullname template + name: "" + +# -- Whether to [automount kubernetes API credentials](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server) into the pod +automountServiceAccountToken: false + +# -- [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) added to deployed [pods](https://kubernetes.io/docs/concepts/workloads/pods/) +podAnnotations: {} + +# The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment +podSecurityContext: {} + +# The [container security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) defines privilege and access control settings for a Container within a pod +securityContext: {} + +service: + # -- [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. + type: ClusterIP + # -- [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service) to expose the running application on a set of Pods as a network service. + port: 80 + +container: + port: 8080 + +# -- [Resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) applied to the deployed pod +resources: {} + +autoscaling: + # -- Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) + enabled: false + # -- Minimal replicas if resource consumption falls below resource threshholds + minReplicas: 1 + # -- Maximum replicas if resource consumption exceeds resource threshholds + maxReplicas: 100 + # -- targetAverageUtilization of cpu provided to a pod + targetCPUUtilizationPercentage: 80 + # -- targetAverageUtilization of memory provided to a pod + targetMemoryUtilizationPercentage: 80 + +# -- [Liveness-Probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-a-liveness-command) to detect and remedy broken applications +livenessProbe: + # -- exec command for liveness check + exec: + command: + - /bin/bash + - -c + - /usr/bin/ps -ef | grep tcpserver | grep -v grep + # -- initialDelaySeconds before performing the first probe + initialDelaySeconds: 10 + # -- periodSeconds between each probe + periodSeconds: 10 + +# -- [Readiness-Probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-readiness-probes) to detect ready applications to receive traffic +readinessProbe: + # -- exec command for readiness check + exec: + command: + - /bin/bash + - -c + - /usr/bin/ps -ef | grep tcpserver | grep -v grep + # -- initialDelaySeconds before performing the first probe + initialDelaySeconds: 10 + # -- periodSeconds between each probe + periodSeconds: 10 + +# -- [Node-Selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) to constrain the Pod to nodes with specific labels. +nodeSelector: {} + +# -- [Tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) are applied to Pods to schedule onto nodes with matching taints. +tolerations: [] + +# -- [Affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) constrains which nodes the Pod can be scheduled on based on node labels. +affinity: {} + +# -- script invoked on http calls +script: + path: /opt/tcpserver/handler.sh + content: |- + #!/bin/bash + + PAYLOAD="" + PAYLOAD_INCOMING=0 + export TMOUT=3.5 + while IFS= read -r LINE || [ "$LINE" ]; do + if [ $PAYLOAD_INCOMING -eq 1 ]; then + PAYLOAD="${PAYLOAD}${LINE}" + break + fi + + if [[ "${#LINE}" = "1" && "$(printf "%d" "'${LINE}")" = "13" ]]; then + PAYLOAD_INCOMING=1 + fi + done + + if [ -z "$PAYLOAD" ]; then + echo -ne "HTTP/1.1 400 Bad Request\r\nContent-Length: 2\r\nContent-Type: application/json\r\nConnection: close\r\n\r\n{}" + exit 1 + fi + + ENDPOINT=$(echo $PAYLOAD | jq -r '.endpoint') + if [ -z "$ENDPOINT" ]; then + echo -ne "HTTP/1.1 400 Bad Request\r\nContent-Length: 2\r\nContent-Type: application/json\r\nConnection: close\r\n\r\n{}" + exit 1 + fi + + ID=$(echo $PAYLOAD | jq -r '.id') + AUTH_KEY=$(echo $PAYLOAD | jq -r '.authKey') + AUTH_CODE=$(echo $PAYLOAD | jq -r '.authCode') + + mkdir -p /tmp/data/ + echo "${AUTH_KEY}: ${AUTH_CODE}" >| header.txt + + curl -L -H @header.txt -o "/tmp/data/${ID}" ${ENDPOINT} + if [ ! $? -eq 0 ]; then + echo "calling endpoint ($ENDPOINT) failed ($?)" 1>&2 + echo -ne "HTTP/1.1 400 Bad Request\r\nContent-Length: 2\r\nContent-Type: application/json\r\nConnection: close\r\n\r\n{}" + exit 1 + fi + + echo -ne "HTTP/1.1 200 OK\r\nContent-Length: 2\r\nContent-Type: application/json\r\nConnection: close\r\n\r\n{}" diff --git a/edc-tests/src/main/resources/deployment/helm/omejdn/.helmignore b/edc-tests/src/main/resources/deployment/helm/omejdn/.helmignore new file mode 100644 index 000000000..0e8a0eb36 --- /dev/null +++ b/edc-tests/src/main/resources/deployment/helm/omejdn/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/edc-tests/src/main/resources/deployment/helm/omejdn/Chart.yaml b/edc-tests/src/main/resources/deployment/helm/omejdn/Chart.yaml new file mode 100644 index 000000000..613b1f45c --- /dev/null +++ b/edc-tests/src/main/resources/deployment/helm/omejdn/Chart.yaml @@ -0,0 +1,25 @@ +--- +apiVersion: v2 +name: ids-daps +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.0.1 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "0.0.1" diff --git a/edc-tests/src/main/resources/deployment/helm/omejdn/README.md b/edc-tests/src/main/resources/deployment/helm/omejdn/README.md new file mode 100644 index 000000000..2fe8128db --- /dev/null +++ b/edc-tests/src/main/resources/deployment/helm/omejdn/README.md @@ -0,0 +1,20 @@ +# Omejdn DAPS + +This chart deployes an [IDS Omejdn DAPS](https://github.com/Fraunhofer-AISEC/omejdn-server). + +Two Eclipse Dataspace Connectors need to be registered at the same DAPS instance, to be able to talk to each other. Each connector is registered in the DAPS by an unique client ID and a correpsonding client certificate. + +New connectors are configured in the omejdn _values.yaml_. + +In each Eclipse Dataspace Connector configure the following properties to use the DAPS. +```properties + edc.oauth.client.id= + + edc.oauth.provider.jwks.url="http://:4567/.well-known/jwks.json" + edc.oauth.token.url="http://:4567/token" + + edc.oauth.private.key.alias= + edc.oauth.public.key.alias= + + edc.oauth.provider.audience=idsc:IDS_CONNECTORS_ALL +``` \ No newline at end of file diff --git a/edc-tests/src/main/resources/deployment/helm/omejdn/templates/_helpers.tpl b/edc-tests/src/main/resources/deployment/helm/omejdn/templates/_helpers.tpl new file mode 100644 index 000000000..95b115eee --- /dev/null +++ b/edc-tests/src/main/resources/deployment/helm/omejdn/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "omejdn.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "omejdn.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "omejdn.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "omejdn.labels" -}} +helm.sh/chart: {{ include "omejdn.chart" . }} +{{ include "omejdn.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "omejdn.selectorLabels" -}} +app.kubernetes.io/name: {{ include "omejdn.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "omejdn.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "omejdn.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/edc-tests/src/main/resources/deployment/helm/omejdn/templates/configmap.yaml b/edc-tests/src/main/resources/deployment/helm/omejdn/templates/configmap.yaml new file mode 100644 index 000000000..c6321c849 --- /dev/null +++ b/edc-tests/src/main/resources/deployment/helm/omejdn/templates/configmap.yaml @@ -0,0 +1,75 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "omejdn.fullname" . }} + labels: + {{- include "omejdn.labels" . | nindent 4 }} +data: + omejdn.yml: |- + --- + host: http://localhost:4567 + path_prefix: '' + bind_to: 0.0.0.0 + allow_origin: "*" + app_env: debug + openid: true + token: + expiration: 3600 + signing_key: omejdn_priv.pem + jwks_additions: + - omejdn_priv.pem.cert + algorithm: RS256 + audience: idsc:IDS_CONNECTORS_ALL + issuer: http://localhost:4567 + id_token: + expiration: 3600 + signing_key: omejdn_priv.pem + jwks_additions: + - omejdn_priv.pem.cert + algorithm: RS256 + issuer: http://localhost:4567 + user_backend: + - yaml + user_backend_default: yaml + user_selfservice: + enabled: true + allow_deletion: true + allow_password_change: true + editable_attributes: + - name + - family_name + - given_name + - middle_name + - nickname + - preferred_username + - profile + - picture + - website + - gender + - birthdate + - zoneinfo + - locale + - updated_at + - email + - email_verified + - address + - phone_number + - phone_number_verified + accept_audience: idsc:IDS_CONNECTORS_ALL + + clients.yml: |- + --- +{{- range $i, $val := .Values.connectors }} + - client_id: {{ quote $val.id }} + name: {{ quote $val.name }} + allowed_scopes: + - idsc:IDS_CONNECTOR_ATTRIBUTES_ALL + attributes: + - key: securityProfile + value: idsc:BASE_SECURITY_PROFILE + redirect_uri: http://localhost:4200 +{{ end -}} + +{{- range $i, $val := .Values.connectors }} + {{ $val.name }}: {{ quote $val.certificate | toString }} +{{ end -}} diff --git a/edc-tests/src/main/resources/deployment/helm/omejdn/templates/deployment.yaml b/edc-tests/src/main/resources/deployment/helm/omejdn/templates/deployment.yaml new file mode 100644 index 000000000..9244ddd6c --- /dev/null +++ b/edc-tests/src/main/resources/deployment/helm/omejdn/templates/deployment.yaml @@ -0,0 +1,121 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "omejdn.fullname" . }} + labels: + {{- include "omejdn.labels" . | nindent 4 }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "omejdn.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "omejdn.selectorLabels" . | nindent 8 }} + spec: + {{- if .Values.imagePullSecret.dockerconfigjson }} + imagePullSecrets: + - name: {{ include "omejdn.fullname" . }}-imagepullsecret + {{- else }} + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- end }} + serviceAccountName: {{ include "omejdn.serviceAccountName" . }} + automountServiceAccountToken: {{ .Values.automountServiceAccountToken }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + initContainers: + - name: init-daps-pvc + image: alpine + command: + - "sh" + - "-c" + args: + - | + cp /opt/config/omejdn.yml /etc/daps/omejdn.yml + cp /opt/config/clients.yml /etc/daps/clients.yml + volumeMounts: + - name: config-dir + mountPath: /etc/daps + - mountPath: /opt/config/omejdn.yml + name: omejdn-config + subPath: omejdn.yml + - mountPath: /opt/config/clients.yml + name: clients-config + subPath: clients.yml + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + volumeMounts: + - mountPath: /opt/config/omejdn.yml + name: config-dir + subPath: omejdn.yml + - mountPath: /opt/config/clients.yml + name: config-dir + subPath: clients.yml + - mountPath: /opt/keys/ + name: client-certificates + ports: + - name: http + containerPort: 4567 + protocol: TCP + livenessProbe: + httpGet: + path: /.well-known/jwks.json + port: http + readinessProbe: + httpGet: + path: /.well-known/jwks.json + port: http + resources: + {{- toYaml .Values.resources | nindent 12 }} + env: + - name: OMEJDN_JWT_AUD_OVERRIDE + value: "idsc:IDS_CONNECTORS_ALL" + volumes: + - name: config-dir + emptyDir: {} + - name: omejdn-config + configMap: + name: {{ include "omejdn.fullname" . }} + items: + - key: omejdn.yml + path: omejdn.yml + - name: clients-config + configMap: + name: {{ include "omejdn.fullname" . }} + items: + - key: clients.yml + path: clients.yml + - name: client-certificates + configMap: + name: {{ include "omejdn.fullname" . }} + items: + {{- range $i, $val := .Values.connectors }} + - key: {{ $val.name }} + path: {{ $val.id | b64enc }}.cert + {{- end }} diff --git a/edc-tests/src/main/resources/deployment/helm/omejdn/templates/hpa.yaml b/edc-tests/src/main/resources/deployment/helm/omejdn/templates/hpa.yaml new file mode 100644 index 000000000..ce2a70957 --- /dev/null +++ b/edc-tests/src/main/resources/deployment/helm/omejdn/templates/hpa.yaml @@ -0,0 +1,28 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2beta1 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "omejdn.fullname" . }} + labels: + {{- include "omejdn.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "omejdn.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/edc-tests/src/main/resources/deployment/helm/omejdn/templates/imagepullsecret.yaml b/edc-tests/src/main/resources/deployment/helm/omejdn/templates/imagepullsecret.yaml new file mode 100644 index 000000000..d7c1d31d7 --- /dev/null +++ b/edc-tests/src/main/resources/deployment/helm/omejdn/templates/imagepullsecret.yaml @@ -0,0 +1,13 @@ +{{- if .Values.imagePullSecret.dockerconfigjson }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "edc-dataplane.fullname" . }}-imagepullsecret + namespace: {{ .Release.Namespace | default "default" | quote }} + labels: + {{- include "edc-dataplane.labels" . | nindent 4 }} +data: + .dockerconfigjson: {{ .Values.imagePullSecret.dockerconfigjson }} +type: kubernetes.io/dockerconfigjson +{{- end }} diff --git a/edc-tests/src/main/resources/deployment/helm/omejdn/templates/service.yaml b/edc-tests/src/main/resources/deployment/helm/omejdn/templates/service.yaml new file mode 100644 index 000000000..57dfe3921 --- /dev/null +++ b/edc-tests/src/main/resources/deployment/helm/omejdn/templates/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "omejdn.fullname" . }} + labels: + {{- include "omejdn.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "omejdn.selectorLabels" . | nindent 4 }} diff --git a/edc-tests/src/main/resources/deployment/helm/omejdn/templates/serviceaccount.yaml b/edc-tests/src/main/resources/deployment/helm/omejdn/templates/serviceaccount.yaml new file mode 100644 index 000000000..17baf8239 --- /dev/null +++ b/edc-tests/src/main/resources/deployment/helm/omejdn/templates/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "omejdn.serviceAccountName" . }} + labels: + {{- include "omejdn.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/edc-tests/src/main/resources/deployment/helm/omejdn/values.yaml b/edc-tests/src/main/resources/deployment/helm/omejdn/values.yaml new file mode 100644 index 000000000..09c1d9592 --- /dev/null +++ b/edc-tests/src/main/resources/deployment/helm/omejdn/values.yaml @@ -0,0 +1,89 @@ +--- +# Default values for omejdn. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +# -- Specifies how many replicas of a deployed pod shall be created during the deployment +# Note: If horizontal pod autoscaling is enabled this setting has no effect +replicaCount: 1 + +image: + # -- Which omjedn container image to use + repository: ghcr.io/fraunhofer-aisec/omejdn-server + # -- [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use + pullPolicy: IfNotPresent + # -- Overrides the image tag whose default is the chart appVersion + tag: "1.4.2" + +imagePullSecret: + # -- Image pull secret to create to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) + # Note: This value needs to adhere to the [(base64 encoded) .dockerconfigjson format](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#registry-secret-existing-credentials). + # Furthermore, if 'imagePullSecret.dockerconfigjson' is defined, it takes precedence over 'imagePullSecrets'. + dockerconfigjson: "" + +# -- Overrides the charts name +nameOverride: "" + +# -- Overrides the releases full name +fullnameOverride: "" + +serviceAccount: + # -- Specifies whether a [service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) should be created per release + create: true + # -- [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/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 release's fullname template + name: "" + +# -- Whether to [automount kubernetes API credentials](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server) into the pod +automountServiceAccountToken: false + +# -- [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) added to deployed [pods](https://kubernetes.io/docs/concepts/workloads/pods/) +podAnnotations: {} + +# The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment +podSecurityContext: {} + +# The [container security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) defines privilege and access control settings for a Container within a pod +securityContext: {} + +service: + # -- [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. + type: ClusterIP + # -- [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service) to expose the running application on a set of Pods as a network service. + port: 4567 + +# -- [Resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) applied to the deployed pod +resources: {} + +autoscaling: + # -- Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) + enabled: false + # -- Minimal replicas if resource consumption falls below resource threshholds + minReplicas: 1 + # -- Maximum replicas if resource consumption exceeds resource threshholds + maxReplicas: 100 + # -- targetAverageUtilization of cpu provided to a pod + targetCPUUtilizationPercentage: 80 + # -- targetAverageUtilization of memory provided to a pod + targetMemoryUtilizationPercentage: 80 + +# -- [Node-Selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) to constrain the Pod to nodes with specific labels. +nodeSelector: {} + +# -- [Tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) are applied to Pods to schedule onto nodes with matching taints. +tolerations: [] + +# -- [Affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) constrains which nodes the Pod can be scheduled on based on node labels. +affinity: {} + +# List of connector clients. Certificate and Client-ID must be configured in parallel. +#
+# Example Connector: +# - id: grMsEz3EcsS3ENYJufNgUIeg4QsaL49M0gWxSexPdC4pon96Nvju90D8RlvAJB21 +# name: my-connector +# certificate: |- +# -----BEGIN CERTIFICATE----- +# foo +# -----END CERTIFICATE----- +connectors: [] diff --git a/lintconf.yaml b/lintconf.yaml index 71d5b1db7..45c708042 100644 --- a/lintconf.yaml +++ b/lintconf.yaml @@ -21,7 +21,7 @@ rules: require-starting-space: true min-spaces-from-content: 1 document-end: disable - document-start: true # No --- to start a file + document-start: enable # No --- to start a file empty-lines: max: 2 max-start: 0 From 5c10bfb9df4b6f58239453fba67cf41a551f1bb6 Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Tue, 28 Jun 2022 11:48:52 +0200 Subject: [PATCH 147/433] BPN validation by list of numbers (#221) --- CHANGELOG.md | 3 + .../business-partner-validation/README.md | 37 ++++- .../AbstractBusinessPartnerValidation.java | 142 +++++++++++++++--- ...AbstractBusinessPartnerValidationTest.java | 35 +++-- 4 files changed, 182 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aecbcee2d..8bed3014b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed +- BusinessPartnerNumber constraint supports List structure + ## [0.0.4] - 2022-06-27 ### Added diff --git a/edc-extensions/business-partner-validation/README.md b/edc-extensions/business-partner-validation/README.md index fb7d53ce6..9d51fc945 100644 --- a/edc-extensions/business-partner-validation/README.md +++ b/edc-extensions/business-partner-validation/README.md @@ -42,6 +42,7 @@ The most simple BPN policy would allow the usage of certain data to a single Bus shown below. In this example the `edctype` properties are added, so that this policy may even be sent to the Data Management API. +**Example 1 for single BPN:** ```json { "uid": "", @@ -72,6 +73,37 @@ Management API. } ``` +**Example 2 for multiple BPN:** +```json +{ + "uid": "", + "prohibitions": [], + "obligations": [], + "permissions": [ + { + "edctype": "dataspaceconnector:permission", + "action": { + "type": "USE" + }, + "constraints": [ + { + "edctype": "AtomicConstraint", + "leftExpression": { + "edctype": "dataspaceconnector:literalexpression", + "value": "BusinessPartnerNumber" + }, + "rightExpression": { + "edctype": "dataspaceconnector:literalexpression", + "value": [ "", "" ] + }, + "operator": "IN" + } + ] + } + ] +} +``` + The business partner number of another connector is part of the DAPS token. Once a BPN constraint is used in an access policy the connector checks the token before sending out contract offers. @@ -81,8 +113,7 @@ Please be aware that the EDC ignores all Rules and Constraint it does not unders --- -Example 1 for accidentially public: - +**Example 3 for accidentially public:** ```json { "uid": "1", @@ -117,7 +148,7 @@ This policy is public available, even though the constraint is described correct --- -Example 2 for accidentially public: +**Example 4 for accidentially public:** ```json { diff --git a/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java b/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java index c776e6042..d92200119 100644 --- a/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java +++ b/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java @@ -9,6 +9,7 @@ * * Contributors: * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Mercedes-Benz Tech Innovation GmbH - Right value of constraint can now contain iterable of BPNs * */ @@ -27,6 +28,19 @@ */ public abstract class AbstractBusinessPartnerValidation { + // Developer Note: + // Problems reported to the policy context are not logged. Therefore, everything + // that is reported to the policy context should be logged, too. + + private static final String SKIP_EVALUATION_BECAUSE_ITERABLE_VALUE_NOT_STRING_S = + "Skipping evaluation of iterable value in BusinessPartnerNumber constraint. Right values used in an iterable must be of type 'String'. Unsupported Type: '%s'"; + private static final String FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_STRING_S = + "Failing evaluation because of invalid BusinessPartnerNumber constraint. For operator 'EQ' right value must be of type 'String'. Unsupported Type: '%s'"; + private static final String FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_ITERABLE_S = + "Failing evaluation because of invalid BusinessPartnerNumber constraint. For operator 'IN' right value must be of type 'Iterable'. Unsupported Type: '%s'"; + private static final String FAIL_EVALUATION_BECAUSE_UNSUPPORTED_OPERATOR_S = + "Failing evaluation because of invalid BusinessPartnerNumber constraint. As operator only 'EQ' or 'IN' are supported. Unsupported operator: '%s'"; + private final Monitor monitor; protected AbstractBusinessPartnerValidation(Monitor monitor) { @@ -40,7 +54,7 @@ protected AbstractBusinessPartnerValidation(Monitor monitor) { * number is part of the 'referringConnector' claim in the IDS DAT token. This will probably * change for the next release. */ - private static final String BUSINESS_PARTNER_NUMBER_CLAIM_KEY = "referringConnector"; + private static final String REFERRING_CONNECTOR_CLAIM = "referringConnector"; /** * Evaluation funtion to decide whether a claim belongs to a specific business partner. @@ -48,7 +62,7 @@ protected AbstractBusinessPartnerValidation(Monitor monitor) { * @param operator operator of the constraint * @param rightValue right value fo the constraint, that contains the business partner number * (e.g. BPNLCDQ90000X42KU) - * @param claims claims of the participant / business partner + * @param policyContext context of the policy with claims * @return true if claims are from the constrained business partner */ protected boolean evaluate( @@ -56,37 +70,129 @@ protected boolean evaluate( if (policyContext.hasProblems() && policyContext.getProblems().size() > 0) { String problems = String.join(", ", policyContext.getProblems()); - String logMessage = + String message = String.format( "BusinessPartnerNumberValidation: Rejecting PolicyContext with problems. Problems: %s", problems); - monitor.debug(logMessage); + monitor.debug(message); return false; } final ParticipantAgent participantAgent = policyContext.getParticipantAgent(); final Map claims = participantAgent.getClaims(); - if (!claims.containsKey(BUSINESS_PARTNER_NUMBER_CLAIM_KEY)) { + + if (!claims.containsKey(REFERRING_CONNECTOR_CLAIM)) { + return false; + } + + String referringConnectorClaim = claims.get(REFERRING_CONNECTOR_CLAIM); + if (referringConnectorClaim == null || referringConnectorClaim.isEmpty()) { + return false; + } + + if (operator == Operator.EQ) { + return isBusinessPartnerNumber(referringConnectorClaim, rightValue, policyContext); + } else if (operator == Operator.IN) { + return containsBusinessPartnerNumber(referringConnectorClaim, rightValue, policyContext); + } else { + final String message = + String.format(FAIL_EVALUATION_BECAUSE_UNSUPPORTED_OPERATOR_S, operator); + monitor.warning(message); + policyContext.reportProblem(message); + return false; + } + } + + /** + * @param referingConnectorClaim of the participant + * @param businessPartnerNumber object + * @return true if object is an iterable and constains a string that is successfully evaluated + * against the claim + */ + private boolean containsBusinessPartnerNumber( + String referingConnectorClaim, Object businessPartnerNumbers, PolicyContext policyContext) { + if (businessPartnerNumbers == null) { + final String message = + String.format(FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_ITERABLE_S, "null"); + monitor.warning(message); + policyContext.reportProblem(message); + return false; + } + if (!(businessPartnerNumbers instanceof Iterable)) { + final String message = + String.format( + FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_ITERABLE_S, + businessPartnerNumbers.getClass().getName()); + monitor.warning(message); + policyContext.reportProblem(message); return false; } - if (operator != Operator.EQ) { - throw new UnsupportedOperationException( - "Operator for BusinessPartnerNumber must always be 'EQ'"); + for (Object businessPartnerNumber : (Iterable) businessPartnerNumbers) { + if (businessPartnerNumber == null) { + final String message = + String.format(SKIP_EVALUATION_BECAUSE_ITERABLE_VALUE_NOT_STRING_S, "null"); + monitor.warning(message); + policyContext.reportProblem(message); + continue; + } + if (!(businessPartnerNumber instanceof String)) { + final String message = + String.format( + SKIP_EVALUATION_BECAUSE_ITERABLE_VALUE_NOT_STRING_S, + businessPartnerNumber.getClass().getName()); + monitor.warning(message); + policyContext.reportProblem(message); + continue; + } + if (isCorrectBusinessPartner(referingConnectorClaim, (String) businessPartnerNumber)) { + return true; // iterable does contain at least one matching value + } } - if (!(rightValue instanceof String)) { - throw new UnsupportedOperationException( - "Right value of BusinessPartnerNumber constraint must be of type 'String'"); + return false; + } + + /** + * @param referingConnectorClaim of the participant + * @param businessPartnerNumber object + * @return true if object is string and successfully evaluated against the claim + */ + private boolean isBusinessPartnerNumber( + String referingConnectorClaim, Object businessPartnerNumber, PolicyContext policyContext) { + if (businessPartnerNumber == null) { + final String message = + String.format(FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_STRING_S, "null"); + monitor.warning(message); + policyContext.reportProblem(message); + return false; + } + if (!(businessPartnerNumber instanceof String)) { + final String message = + String.format( + FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_STRING_S, + businessPartnerNumber.getClass().getName()); + monitor.warning(message); + policyContext.reportProblem(message); + return false; } - String claimValue = claims.get(BUSINESS_PARTNER_NUMBER_CLAIM_KEY); + return isCorrectBusinessPartner(referingConnectorClaim, (String) businessPartnerNumber); + } - // At the time of writing the business partner number is part of the - // 'referingConnector' claim, which contains a connector URL. - // As the CX projects are not further alligned about the URL formatting, the - // enforcement can only be done by checking whether the URL _contains_ the - // number. - return claimValue.contains((String) rightValue); + /** + * At the time of writing (11. April 2022) the business partner number is part of the + * 'referingConnector' claim, which contains a connector URL. As the CX projects are not further + * alligned about the URL formatting, the enforcement can only be done by checking whether the URL + * _contains_ the number. As this introduces some insecurities when validation business partner + * numbers, this should be addresses in the long term. + * + * @param referingConnectorClaim describing URL with business partner number + * @param businessPartnerNumber of the constraint + * @return true if claim contains the business partner number + */ + private static boolean isCorrectBusinessPartner( + String referingConnectorClaim, String businessPartnerNumber) { + return referingConnectorClaim.contains(businessPartnerNumber); } } diff --git a/edc-extensions/business-partner-validation/src/test/java/net/catenax/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidationTest.java b/edc-extensions/business-partner-validation/src/test/java/net/catenax/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidationTest.java index 451e79868..ac8a68ebb 100644 --- a/edc-extensions/business-partner-validation/src/test/java/net/catenax/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidationTest.java +++ b/edc-extensions/business-partner-validation/src/test/java/net/catenax/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidationTest.java @@ -49,9 +49,9 @@ void BeforeEach() { @ParameterizedTest @EnumSource(Operator.class) - void testThrowsOnUnsupportedOperations(Operator operator) { + void testFailsOnUnsupportedOperations(Operator operator) { - if (operator == Operator.EQ) { // only allowed operator + if (operator == Operator.EQ || operator == Operator.IN) { // only allowed operator return; } @@ -60,25 +60,19 @@ void testThrowsOnUnsupportedOperations(Operator operator) { prepareBusinessPartnerClaim("yes"); // invoke & assert - Assertions.assertThrows( - UnsupportedOperationException.class, - () -> validation.evaluate(operator, "null", policyContext)); + Assertions.assertFalse(validation.evaluate(operator, "foo", policyContext)); } @Test - void testThrowsOnUnsupportedRightValue() { + void testFailsOnUnsupportedRightValue() { // prepare prepareContextProblems(null); prepareBusinessPartnerClaim("yes"); // invoke & assert - Assertions.assertThrows( - UnsupportedOperationException.class, - () -> validation.evaluate(Operator.EQ, 1, policyContext)); - Assertions.assertThrows( - UnsupportedOperationException.class, - () -> validation.evaluate(Operator.EQ, new Object(), policyContext)); + Assertions.assertFalse(validation.evaluate(Operator.EQ, 1, policyContext)); + Assertions.assertFalse(validation.evaluate(Operator.IN, "foo", policyContext)); } @Test @@ -95,7 +89,7 @@ void testValidationFailsWhenClaimMissing() { } @Test - void testValidationSuccedesWhenClaimContainsNumber() { + void testValidationSucceedsWhenClaimContainsValue() { // prepare prepareContextProblems(null); @@ -128,7 +122,7 @@ void testValidationWhenParticipantHasProblems() { } @Test - void testValidationWhenParticipantIsValid() { + void testValidationWhenSingleParticipantIsValid() { // prepare prepareContextProblems(null); @@ -141,6 +135,19 @@ void testValidationWhenParticipantIsValid() { Assertions.assertTrue(isContainedTrue); } + @Test + void testValidationForMultipleParticipants() { + + // prepare + prepareContextProblems(null); + prepareBusinessPartnerClaim("foo"); + + // invoke & verify + Assertions.assertTrue(validation.evaluate(Operator.IN, List.of("foo", "bar"), policyContext)); + Assertions.assertTrue(validation.evaluate(Operator.IN, List.of(1, "foo"), policyContext)); + Assertions.assertFalse(validation.evaluate(Operator.IN, List.of("bar", "bar"), policyContext)); + } + private void prepareContextProblems(List problems) { Mockito.when(policyContext.getProblems()).thenReturn(problems); From 669a4ac83711968268228062c3faef7be5acfced Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Tue, 28 Jun 2022 14:32:11 +0200 Subject: [PATCH 148/433] Update edc chart to versions 0.0.4 in all-in-one deployment (#232) --- .../main/resources/deployment/helm/all-in-one/Chart.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/edc-tests/src/main/resources/deployment/helm/all-in-one/Chart.yaml b/edc-tests/src/main/resources/deployment/helm/all-in-one/Chart.yaml index 1875499ba..8ebf25167 100644 --- a/edc-tests/src/main/resources/deployment/helm/all-in-one/Chart.yaml +++ b/edc-tests/src/main/resources/deployment/helm/all-in-one/Chart.yaml @@ -33,11 +33,11 @@ dependencies: # PLATO CONNECTOR - name: edc-controlplane - version: 0.0.3 + version: 0.0.4 repository: "file://../../../../../../../deployment/helm/edc-controlplane" alias: plato-edc-controlplane - name: edc-dataplane - version: 0.0.3 + version: 0.0.4 repository: "file://../../../../../../../deployment/helm/edc-dataplane" alias: plato-edc-dataplane - name: backend-application @@ -55,11 +55,11 @@ dependencies: # SOKRATES CONNECTOR - name: edc-controlplane - version: 0.0.3 + version: 0.0.4 repository: "file://../../../../../../../deployment/helm/edc-controlplane" alias: sokrates-edc-controlplane - name: edc-dataplane - version: 0.0.3 + version: 0.0.4 repository: "file://../../../../../../../deployment/helm/edc-dataplane" alias: sokrates-edc-dataplane - name: backend-application From cd1ff27d6719f66528495186486326e25cd28826 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Jun 2022 21:37:04 +0200 Subject: [PATCH 149/433] Bump azure/setup-helm from 2.1 to 3.0 (#233) --- .github/workflows/helm-lint.yaml | 2 +- .github/workflows/publish-new-release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/helm-lint.yaml b/.github/workflows/helm-lint.yaml index cd366ea2d..b5cafca05 100644 --- a/.github/workflows/helm-lint.yaml +++ b/.github/workflows/helm-lint.yaml @@ -32,7 +32,7 @@ jobs: fetch-depth: 0 - name: helm (setup) - uses: azure/setup-helm@v2.1 + uses: azure/setup-helm@v3.0 with: version: v3.8.1 - diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index bedaf133a..0c96c390d 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -109,7 +109,7 @@ jobs: fetch-depth: 0 - name: Install Helm - uses: azure/setup-helm@v1 + uses: azure/setup-helm@v3.0 with: version: v3.8.1 - From 66def2b7ed102daad4b7cf0782dd4d320c38a136 Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Wed, 29 Jun 2022 11:42:44 +0200 Subject: [PATCH 150/433] improve BPN documentation (#237) Signed-off-by: Dominik Pinsel --- .../business-partner-validation/README.md | 9 +++--- .../AbstractBusinessPartnerValidation.java | 30 +++++++++---------- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/edc-extensions/business-partner-validation/README.md b/edc-extensions/business-partner-validation/README.md index 9d51fc945..daf374e96 100644 --- a/edc-extensions/business-partner-validation/README.md +++ b/edc-extensions/business-partner-validation/README.md @@ -19,6 +19,9 @@ corresponding documentation can be found in the [EDC GitHub Repository](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector). For a simplified overview of the EDC domain please have a look at the Catena-X Control Plane documentation. +The business partner number of another connector is part of the DAPS token. Once a BPN constraint is used in an access +policy the connector checks the token before sending out contract offers. + Example of business partner constraint: ```json @@ -35,8 +38,7 @@ Example of business partner constraint: The `leftExpression` must always contain 'BusinessPartner', so that the policy functions of this extension are invoked. Additionally, the only `operator` that is supported by these policy functions is 'EQ'. Finally, the `rightExpression` -must contain -the Business Partner Number. +must contain the Business Partner Number. The most simple BPN policy would allow the usage of certain data to a single Business Partner. An example `Policy` is shown below. In this example the `edctype` properties are added, so that this policy may even be sent to the Data @@ -104,9 +106,6 @@ Management API. } ``` -The business partner number of another connector is part of the DAPS token. Once a BPN constraint is used in an access -policy the connector checks the token before sending out contract offers. - # Important: EDC Policies are input sensitive Please be aware that the EDC ignores all Rules and Constraint it does not understand. This could cause your constrained policies to be public. diff --git a/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java b/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java index d92200119..f2bc61640 100644 --- a/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java +++ b/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java @@ -32,13 +32,13 @@ public abstract class AbstractBusinessPartnerValidation { // Problems reported to the policy context are not logged. Therefore, everything // that is reported to the policy context should be logged, too. - private static final String SKIP_EVALUATION_BECAUSE_ITERABLE_VALUE_NOT_STRING_S = - "Skipping evaluation of iterable value in BusinessPartnerNumber constraint. Right values used in an iterable must be of type 'String'. Unsupported Type: '%s'"; - private static final String FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_STRING_S = - "Failing evaluation because of invalid BusinessPartnerNumber constraint. For operator 'EQ' right value must be of type 'String'. Unsupported Type: '%s'"; - private static final String FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_ITERABLE_S = - "Failing evaluation because of invalid BusinessPartnerNumber constraint. For operator 'IN' right value must be of type 'Iterable'. Unsupported Type: '%s'"; - private static final String FAIL_EVALUATION_BECAUSE_UNSUPPORTED_OPERATOR_S = + private static final String SKIP_EVALUATION_BECAUSE_ITERABLE_VALUE_NOT_STRING = + "Skipping evaluation of iterable value in BusinessPartnerNumber constraint. Right values used in an iterable must be of type 'String'. Unsupported type: '%s'"; + private static final String FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_STRING = + "Failing evaluation because of invalid BusinessPartnerNumber constraint. For operator 'EQ' right value must be of type 'String'. Unsupported type: '%s'"; + private static final String FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_ITERABLE = + "Failing evaluation because of invalid BusinessPartnerNumber constraint. For operator 'IN' right value must be of type 'Iterable'. Unsupported type: '%s'"; + private static final String FAIL_EVALUATION_BECAUSE_UNSUPPORTED_OPERATOR = "Failing evaluation because of invalid BusinessPartnerNumber constraint. As operator only 'EQ' or 'IN' are supported. Unsupported operator: '%s'"; private final Monitor monitor; @@ -95,8 +95,7 @@ protected boolean evaluate( } else if (operator == Operator.IN) { return containsBusinessPartnerNumber(referringConnectorClaim, rightValue, policyContext); } else { - final String message = - String.format(FAIL_EVALUATION_BECAUSE_UNSUPPORTED_OPERATOR_S, operator); + final String message = String.format(FAIL_EVALUATION_BECAUSE_UNSUPPORTED_OPERATOR, operator); monitor.warning(message); policyContext.reportProblem(message); return false; @@ -113,7 +112,7 @@ private boolean containsBusinessPartnerNumber( String referingConnectorClaim, Object businessPartnerNumbers, PolicyContext policyContext) { if (businessPartnerNumbers == null) { final String message = - String.format(FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_ITERABLE_S, "null"); + String.format(FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_ITERABLE, "null"); monitor.warning(message); policyContext.reportProblem(message); return false; @@ -121,7 +120,7 @@ private boolean containsBusinessPartnerNumber( if (!(businessPartnerNumbers instanceof Iterable)) { final String message = String.format( - FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_ITERABLE_S, + FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_ITERABLE, businessPartnerNumbers.getClass().getName()); monitor.warning(message); policyContext.reportProblem(message); @@ -131,7 +130,7 @@ private boolean containsBusinessPartnerNumber( for (Object businessPartnerNumber : (Iterable) businessPartnerNumbers) { if (businessPartnerNumber == null) { final String message = - String.format(SKIP_EVALUATION_BECAUSE_ITERABLE_VALUE_NOT_STRING_S, "null"); + String.format(SKIP_EVALUATION_BECAUSE_ITERABLE_VALUE_NOT_STRING, "null"); monitor.warning(message); policyContext.reportProblem(message); continue; @@ -139,7 +138,7 @@ private boolean containsBusinessPartnerNumber( if (!(businessPartnerNumber instanceof String)) { final String message = String.format( - SKIP_EVALUATION_BECAUSE_ITERABLE_VALUE_NOT_STRING_S, + SKIP_EVALUATION_BECAUSE_ITERABLE_VALUE_NOT_STRING, businessPartnerNumber.getClass().getName()); monitor.warning(message); policyContext.reportProblem(message); @@ -161,8 +160,7 @@ private boolean containsBusinessPartnerNumber( private boolean isBusinessPartnerNumber( String referingConnectorClaim, Object businessPartnerNumber, PolicyContext policyContext) { if (businessPartnerNumber == null) { - final String message = - String.format(FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_STRING_S, "null"); + final String message = String.format(FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_STRING, "null"); monitor.warning(message); policyContext.reportProblem(message); return false; @@ -170,7 +168,7 @@ private boolean isBusinessPartnerNumber( if (!(businessPartnerNumber instanceof String)) { final String message = String.format( - FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_STRING_S, + FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_STRING, businessPartnerNumber.getClass().getName()); monitor.warning(message); policyContext.reportProblem(message); From 5379fc7cab203ca5fb43e8e534274e0ebe873d6b Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Wed, 29 Jun 2022 12:38:48 +0200 Subject: [PATCH 151/433] Simplify trivy gh workflow (#239) Signed-off-by: Denis Neuling --- .github/workflows/trivy.yaml | 151 ++++------------------------------- 1 file changed, 16 insertions(+), 135 deletions(-) diff --git a/.github/workflows/trivy.yaml b/.github/workflows/trivy.yaml index d5fdb5cfa..21a9fbca7 100644 --- a/.github/workflows/trivy.yaml +++ b/.github/workflows/trivy.yaml @@ -2,6 +2,9 @@ name: "Trivy" on: + schedule: + - cron: "0 0 * * *" + workflow_dispatch: workflow_run: workflows: ["Build"] branches: @@ -27,7 +30,7 @@ jobs: run: | echo "::set-output name=SHA7::${GITHUB_SHA::7}" - analyze-config: + trivy-analyze-config: runs-on: ubuntu-latest permissions: actions: read @@ -55,152 +58,30 @@ jobs: with: sarif_file: "trivy-results-config.sarif" - ############################### - ### edc-controlplane-memory ### - ############################### - analyze-edc-controlplane-memory: + trivy: needs: [ git-sha7 ] - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - steps: - - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Run Trivy vulnerability scanner - if: always() - uses: aquasecurity/trivy-action@master - with: - image-ref: "ghcr.io/${{ github.repository }}/edc-controlplane-memory:sha-${{ needs.git-sha7.outputs.value }}" - format: "sarif" - output: "trivy-results-edc-controlplane-memory.sarif" - exit-code: "1" - severity: "CRITICAL,HIGH" - - - name: Upload Trivy scan results to GitHub Security tab - if: always() - uses: github/codeql-action/upload-sarif@v2 - with: - sarif_file: "trivy-results-edc-controlplane-memory.sarif" - - ################################### - ### edc-controlplane-postgresql ### - ################################### - analyze-edc-controlplane-postgresql: - needs: [ git-sha7 ] - runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write - steps: - - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Run Trivy vulnerability scanner - if: always() - uses: aquasecurity/trivy-action@master - with: - image-ref: "ghcr.io/${{ github.repository }}/edc-controlplane-postgresql:sha-${{ needs.git-sha7.outputs.value }}" - format: "sarif" - output: "trivy-results-edc-controlplane-postgresql.sarif" - exit-code: "1" - severity: "CRITICAL,HIGH" - - - name: Upload Trivy scan results to GitHub Security tab - if: always() - uses: github/codeql-action/upload-sarif@v2 - with: - sarif_file: "trivy-results-edc-controlplane-postgresql.sarif" - - ################################################### - ### edc-controlplane-postgresql-hashicorp-vault ### - ################################################### - analyze-edc-controlplane-postgresql-hashicorp-vault: - needs: [ git-sha7 ] runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write + strategy: + matrix: + image: + - edc-controlplane-memory + - edc-controlplane-postgresql + - edc-controlplane-postgresql-hashicorp-vault + - edc-dataplane-azure-vault + - edc-dataplane-hashicorp-vault steps: - - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Run Trivy vulnerability scanner - if: always() - uses: aquasecurity/trivy-action@master - with: - image-ref: "ghcr.io/${{ github.repository }}/edc-controlplane-postgresql-hashicorp-vault:sha-${{ needs.git-sha7.outputs.value }}" - format: "sarif" - output: "trivy-results-edc-controlplane-postgresql-hashicorp-vault.sarif" - exit-code: "1" - severity: "CRITICAL,HIGH" - - - name: Upload Trivy scan results to GitHub Security tab - if: always() - uses: github/codeql-action/upload-sarif@v2 - with: - sarif_file: "trivy-results-edc-controlplane-postgresql-hashicorp-vault.sarif" - - ################################# - ### edc-dataplane-azure-vault ### - ################################# - analyze-edc-dataplane-azure-vault: - needs: [ git-sha7 ] - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - steps: - - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Run Trivy vulnerability scanner - if: always() - uses: aquasecurity/trivy-action@master - with: - image-ref: "ghcr.io/${{ github.repository }}/edc-dataplane-azure-vault:sha-${{ needs.git-sha7.outputs.value }}" - format: "sarif" - output: "trivy-results-edc-dataplane-azure-vault.sarif" - exit-code: "1" - severity: "CRITICAL,HIGH" - - - name: Upload Trivy scan results to GitHub Security tab - if: always() - uses: github/codeql-action/upload-sarif@v2 - with: - sarif_file: "trivy-results-edc-dataplane-azure-vault.sarif" - - ##################################### - ### edc-dataplane-hashicorp-vault ### - ##################################### - analyze-edc-dataplane-hashicorp-vault: - needs: [ git-sha7 ] - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - steps: - - - name: Checkout repository - uses: actions/checkout@v2 - name: Run Trivy vulnerability scanner if: always() uses: aquasecurity/trivy-action@master with: - image-ref: "ghcr.io/${{ github.repository }}/edc-dataplane-hashicorp-vault:sha-${{ needs.git-sha7.outputs.value }}" + image-ref: "ghcr.io/${{ github.repository }}/${{ matrix.image }}:sha-${{ needs.git-sha7.outputs.value }}" format: "sarif" - output: "trivy-results-edc-dataplane-hashicorp-vault.sarif" + output: "trivy-results-${{ matrix.image }}.sarif" exit-code: "1" severity: "CRITICAL,HIGH" - @@ -208,4 +89,4 @@ jobs: if: always() uses: github/codeql-action/upload-sarif@v2 with: - sarif_file: "trivy-results-edc-dataplane-hashicorp-vault.sarif" + sarif_file: "trivy-results-${{ matrix.image }}.sarif" From 363ee926a70f7520474b8f5447d1ff988d7250c8 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Wed, 29 Jun 2022 12:40:44 +0200 Subject: [PATCH 152/433] Use semver version range >=0.0.1 for locally present dependencies (#238) --- .../resources/deployment/helm/all-in-one/Chart.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/edc-tests/src/main/resources/deployment/helm/all-in-one/Chart.yaml b/edc-tests/src/main/resources/deployment/helm/all-in-one/Chart.yaml index 8ebf25167..9deeb5792 100644 --- a/edc-tests/src/main/resources/deployment/helm/all-in-one/Chart.yaml +++ b/edc-tests/src/main/resources/deployment/helm/all-in-one/Chart.yaml @@ -33,15 +33,15 @@ dependencies: # PLATO CONNECTOR - name: edc-controlplane - version: 0.0.4 + version: ">=0.0.1" repository: "file://../../../../../../../deployment/helm/edc-controlplane" alias: plato-edc-controlplane - name: edc-dataplane - version: 0.0.4 + version: ">=0.0.1" repository: "file://../../../../../../../deployment/helm/edc-dataplane" alias: plato-edc-dataplane - name: backend-application - version: 0.0.1 + version: ">=0.0.1" repository: "file://../backend-application" alias: plato-backend-application - name: vault @@ -55,15 +55,15 @@ dependencies: # SOKRATES CONNECTOR - name: edc-controlplane - version: 0.0.4 + version: ">=0.0.1" repository: "file://../../../../../../../deployment/helm/edc-controlplane" alias: sokrates-edc-controlplane - name: edc-dataplane - version: 0.0.4 + version: ">=0.0.1" repository: "file://../../../../../../../deployment/helm/edc-dataplane" alias: sokrates-edc-dataplane - name: backend-application - version: 0.0.1 + version: ">=0.0.1" repository: "file://../backend-application" alias: sokrates-backend-application - name: vault From 3c4b4c369f69e5f980bdd2a4d41bfd3dd32ea0e7 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Wed, 29 Jun 2022 13:35:23 +0200 Subject: [PATCH 153/433] Enable checkov for workflow dispatch events (#240) --- .github/workflows/checkov.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/checkov.yaml b/.github/workflows/checkov.yaml index 5bdc9aeb0..f1f2004d6 100644 --- a/.github/workflows/checkov.yaml +++ b/.github/workflows/checkov.yaml @@ -2,6 +2,7 @@ name: "Checkov" on: + workflow_dispatch: push: branches: - main From 792c12776373f747487574dfb90fa272421bbe56 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Wed, 29 Jun 2022 14:43:00 +0200 Subject: [PATCH 154/433] Add env configuration from k8s secret (#236) --- deployment/helm/edc-controlplane/README.md | 3 ++- deployment/helm/edc-controlplane/templates/deployment.yaml | 4 ++++ deployment/helm/edc-controlplane/values.yaml | 3 +++ deployment/helm/edc-dataplane/README.md | 1 + deployment/helm/edc-dataplane/templates/deployment.yaml | 4 ++++ deployment/helm/edc-dataplane/values.yaml | 3 +++ 6 files changed, 17 insertions(+), 1 deletion(-) diff --git a/deployment/helm/edc-controlplane/README.md b/deployment/helm/edc-controlplane/README.md index 14f891d77..47982434b 100644 --- a/deployment/helm/edc-controlplane/README.md +++ b/deployment/helm/edc-controlplane/README.md @@ -23,7 +23,7 @@ $ helm install my-release catenax-ng-product-edc/edc-controlplane --version 0.0. | autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | | autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | | autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | -| configuration.properties | string | `"# edc.api.auth.key=\n# edc.atomikos.checkpoint.interval=\n# edc.atomikos.directory=\n# edc.atomikos.logging=\n# edc.atomikos.threaded2pc=\n# edc.atomikos.timeout=\n# edc.aws.access.key=\n# edc.aws.provision.retry.retries.max=\n# edc.aws.provision.role.duration.session.max=\n# edc.aws.secret.access.key=\n# edc.blobstore.endpoint=\n# edc.controlplane.validation-endpoint=\n# edc.core.retry.backoff.max=\n# edc.core.retry.backoff.min=\n# edc.core.retry.retries.max=\n# edc.core.system.health.check.liveness-period=\n# edc.core.system.health.check.readiness-period=\n# edc.core.system.health.check.startup-period=\n# edc.core.system.health.check.threadpool-size=\n# edc.dataplane.queue.capacity=\n# edc.dataplane.wait=\n# edc.dataplane.workers=\n# edc.datasource.asset.name=\"default\"\n# edc.datasource.contractdefinition.name=\"default\"\n# edc.datasource.contractnegotiation.name=\"default\"\n# edc.datasource.policy.name=\"default\"\n# edc.datasource.transferprocess.name=\"default\"\n# edc.datasource.default.pool.maxIdleConnections=\n# edc.datasource.default.pool.maxTotalConnections=\n# edc.datasource.default.pool.minIdleConnections=\n# edc.datasource.default.pool.testConnectionOnBorrow=\n# edc.datasource.default.pool.testConnectionOnCreate=\n# edc.datasource.default.pool.testConnectionOnReturn=\n# edc.datasource.default.pool.testConnectionWhileIdle=\n# edc.datasource.default.pool.testQuery=\n# edc.datasource.default.url=\n# edc.datasource.default.user=\n# edc.datasource.default.password=\n# edc.dpf.selector.url=\n# edc.events.topic.endpoint=\n# edc.events.topic.name=\n# edc.fs.config=\n# edc.hostname=\n# edc.identity.did.url=\n# edc.ids.catalog.id=\n# edc.ids.curator=\n# edc.ids.description=\n# edc.ids.endpoint=\n# edc.ids.id=\n# edc.ids.maintainer=\n# edc.ids.security.profile=\n# edc.ids.title=\n# edc.ids.validation.referringconnector=\n# edc.ion.crawler.did-type=\n# edc.ion.crawler.interval-minutes=\n# edc.ion.crawler.ion.url=\n# edc.metrics.enabled=\n# edc.metrics.executor.enabled=\n# edc.metrics.jersey.enabled=\n# edc.metrics.jetty.enabled=\n# edc.metrics.okhttp.enabled=\n# edc.metrics.system.enabled=\n# edc.negotiation.consumer.state-machine.batch-size=\n# edc.negotiation.provider.state-machine.batch-size=\n# edc.oauth.client.id=\n# edc.oauth.private.key.alias=\n# edc.oauth.provider.audience=\n# edc.oauth.provider.jwks.refresh=\n# edc.oauth.provider.jwks.url=\n# edc.oauth.public.key.alias=\n# edc.oauth.token.url=\n# edc.oauth.validation.nbf.leeway=\n# edc.receiver.http.auth-code=\n# edc.receiver.http.auth-key=\n# edc.receiver.http.endpoint=\n# edc.transfer.proxy.endpoint=\n# edc.transfer.dataplane.sync.token.validity=\n# edc.transfer.proxy.token.signer.privatekey.alias=\n# edc.transfer.functions.check.endpoint=\n# edc.transfer.functions.enabled.protocols=\n# edc.transfer.functions.transfer.endpoint=\n# edc.transfer-process-store.database.name=\n# edc.transfer.state-machine.batch-size=\n# edc.vault=\n# edc.vault.certificate=\n# edc.vault.clientid=\n# edc.vault.clientsecret=\n# edc.vault.name=\n# edc.vault.tenantid=\n# edc.vault.hashicorp.url=\n# edc.vault.hashicorp.token=\n# edc.vault.hashicorp.timeout.seconds=\n# edc.webdid.doh.url=\n# edc.web.rest.cors.enabled=\n# edc.web.rest.cors.headers=\n# edc.web.rest.cors.methods=\n# edc.web.rest.cors.origins="` | EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) | +| configuration.properties | string | `"# edc.api.auth.key=\n# edc.atomikos.checkpoint.interval=\n# edc.atomikos.directory=\n# edc.atomikos.logging=\n# edc.atomikos.threaded2pc=\n# edc.atomikos.timeout=\n# edc.aws.access.key=\n# edc.aws.provision.retry.retries.max=\n# edc.aws.provision.role.duration.session.max=\n# edc.aws.secret.access.key=\n# edc.blobstore.endpoint=\n# edc.controlplane.validation-endpoint=\n# edc.core.retry.backoff.max=\n# edc.core.retry.backoff.min=\n# edc.core.retry.retries.max=\n# edc.core.system.health.check.liveness-period=\n# edc.core.system.health.check.readiness-period=\n# edc.core.system.health.check.startup-period=\n# edc.core.system.health.check.threadpool-size=\n# edc.dataplane.queue.capacity=\n# edc.dataplane.wait=\n# edc.dataplane.workers=\n# edc.datasource.asset.name=\"default\"\n# edc.datasource.contractdefinition.name=\"default\"\n# edc.datasource.contractnegotiation.name=\"default\"\n# edc.datasource.policy.name=\"default\"\n# edc.datasource.transferprocess.name=\"default\"\n# edc.datasource.default.pool.maxIdleConnections=\n# edc.datasource.default.pool.maxTotalConnections=\n# edc.datasource.default.pool.minIdleConnections=\n# edc.datasource.default.pool.testConnectionOnBorrow=\n# edc.datasource.default.pool.testConnectionOnCreate=\n# edc.datasource.default.pool.testConnectionOnReturn=\n# edc.datasource.default.pool.testConnectionWhileIdle=\n# edc.datasource.default.pool.testQuery=\n# edc.datasource.default.url=\n# edc.datasource.default.user=\n# edc.datasource.default.password=\n# edc.dpf.selector.url=\n# edc.events.topic.endpoint=\n# edc.events.topic.name=\n# edc.fs.config=\n# edc.hostname=\n# edc.identity.did.url=\n# edc.ids.catalog.id=\n# edc.ids.curator=\n# edc.ids.description=\n# edc.ids.endpoint=\n# edc.ids.id=\n# edc.ids.maintainer=\n# edc.ids.security.profile=\n# edc.ids.title=\n# edc.ids.validation.referringconnector=\n# edc.ion.crawler.did-type=\n# edc.ion.crawler.interval-minutes=\n# edc.ion.crawler.ion.url=\n# edc.metrics.enabled=\n# edc.metrics.executor.enabled=\n# edc.metrics.jersey.enabled=\n# edc.metrics.jetty.enabled=\n# edc.metrics.okhttp.enabled=\n# edc.metrics.system.enabled=\n# edc.negotiation.consumer.state-machine.batch-size=\n# edc.negotiation.provider.state-machine.batch-size=\n# edc.oauth.client.id=\n# edc.oauth.private.key.alias=\n# edc.oauth.provider.audience=\n# edc.oauth.provider.jwks.refresh=\n# edc.oauth.provider.jwks.url=\n# edc.oauth.public.key.alias=\n# edc.oauth.token.url=\n# edc.oauth.validation.nbf.leeway=\n# edc.receiver.http.auth-code=\n# edc.receiver.http.auth-key=\n# edc.receiver.http.endpoint=\n# edc.transfer.proxy.endpoint=\n# edc.transfer.dataplane.sync.token.validity=\n# edc.transfer.proxy.token.signer.privatekey.alias=\n# edc.transfer.functions.check.endpoint=\n# edc.transfer.functions.enabled.protocols=\n# edc.transfer.functions.transfer.endpoint=\n# edc.transfer-process-store.database.name=\n# edc.transfer.state-machine.batch-size=\n# edc.vault=\n# edc.vault.certificate=\n# edc.vault.clientid=\n# edc.vault.clientsecret=\n# edc.vault.name=\n# edc.vault.tenantid=\n# edc.vault.hashicorp.url=\n# edc.vault.hashicorp.token=\n# edc.vault.hashicorp.timeout.seconds=\n# edc.webdid.doh.url=\n# edc.web.rest.cors.enabled=\n# edc.web.rest.cors.headers=\n# edc.web.rest.cors.methods=\n# edc.web.rest.cors.origins=\n# ids.webhook.address="` | EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) | | edc.endpoints.control.path | string | `"/api/controlplane/control"` | The path mapping the "control" api is going to be exposed at | | edc.endpoints.control.port | string | `"9999"` | The network port, which the "control" api is going to be exposed by the container, pod and service | | edc.endpoints.data.path | string | `"/data"` | The path mapping the "data" management api is going to be exposed at | @@ -37,6 +37,7 @@ $ helm install my-release catenax-ng-product-edc/edc-controlplane --version 0.0. | edc.endpoints.validation.path | string | `"/validation"` | The path mapping the "validation" api is going to be exposed at | | edc.endpoints.validation.port | string | `"8182"` | The network port, which the "validation" api is going to be exposed by the container, pod and service | | env | object | `{}` | Container environment variables e.g. for configuring [JAVA_TOOL_OPTIONS](https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/envvars002.html) Ex.: JAVA_TOOL_OPTIONS: > -Dhttp.proxyHost=proxy -Dhttp.proxyPort=80 -Dhttp.nonProxyHosts="localhost|127.*|[::1]" -Dhttps.proxyHost=proxy -Dhttps.proxyPort=443 | +| envSecretName | string | `nil` | Secret name to load environment variables from | | fullnameOverride | string | `""` | Overrides the releases full name | | image.pullPolicy | string | `"IfNotPresent"` | [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use | | image.repository | string | `"ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql-hashicorp-vault"` | Which derivate of the edc control-plane to use. One of: [ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql-hashicorp-vault, ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql, ghcr.io/catenax-ng/product-edc/edc-controlplane-memory] | diff --git a/deployment/helm/edc-controlplane/templates/deployment.yaml b/deployment/helm/edc-controlplane/templates/deployment.yaml index 8b69aac9f..266ae9cab 100644 --- a/deployment/helm/edc-controlplane/templates/deployment.yaml +++ b/deployment/helm/edc-controlplane/templates/deployment.yaml @@ -85,6 +85,10 @@ spec: envFrom: - configMapRef: name: {{ include "edc-controlplane.fullname" . }}-env + {{- if .Values.envSecretName }} + - secretRef: + name: {{ .Values.envSecretName | quote }} + {{- end }} resources: {{- toYaml .Values.resources | nindent 12 }} volumeMounts: diff --git a/deployment/helm/edc-controlplane/values.yaml b/deployment/helm/edc-controlplane/values.yaml index e172f6e17..7da20cfbe 100644 --- a/deployment/helm/edc-controlplane/values.yaml +++ b/deployment/helm/edc-controlplane/values.yaml @@ -218,6 +218,9 @@ affinity: {} # -Dhttp.proxyHost=proxy -Dhttp.proxyPort=80 -Dhttp.nonProxyHosts="localhost|127.*|[::1]" -Dhttps.proxyHost=proxy -Dhttps.proxyPort=443 env: {} +# -- [Kubernetes Secret Resource](https://kubernetes.io/docs/concepts/configuration/secret/) name to load environment variables from +envSecretName: + logging: # -- EDC logging.properties configuring the [java.util.logging subsystem](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html#a1.8) properties: |- diff --git a/deployment/helm/edc-dataplane/README.md b/deployment/helm/edc-dataplane/README.md index 7f18a8705..3f087eece 100644 --- a/deployment/helm/edc-dataplane/README.md +++ b/deployment/helm/edc-dataplane/README.md @@ -33,6 +33,7 @@ $ helm install my-release catenax-ng-product-edc/edc-dataplane --version 0.0.4 | edc.endpoints.public.path | string | `"/api/public"` | The path mapping the "public" api is going to be exposed by | | edc.endpoints.public.port | string | `"8185"` | The network port, which the "public" api is going to be exposed by the container, pod and service | | env | object | `{}` | Container environment variables e.g. for configuring [JAVA_TOOL_OPTIONS](https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/envvars002.html) Ex.: JAVA_TOOL_OPTIONS: > -Dhttp.proxyHost=proxy -Dhttp.proxyPort=80 -Dhttp.nonProxyHosts="localhost|127.*|[::1]" -Dhttps.proxyHost=proxy -Dhttps.proxyPort=443 | +| envSecretName | string | `nil` | [Kubernetes Secret Resource](https://kubernetes.io/docs/concepts/configuration/secret/) name to load environment variables from | | fullnameOverride | string | `""` | Overrides the releases full name | | image.pullPolicy | string | `"IfNotPresent"` | [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use | | image.repository | string | `"ghcr.io/catenax-ng/product-edc/edc-dataplane-hashicorp-vault"` | Which derivate of the edc data-plane to use. One of: [ghcr.io/catenax-ng/product-edc/edc-dataplane-hashicorp-vault, ghcr.io/catenax-ng/product-edc/edc-dataplane-azure-vault] | diff --git a/deployment/helm/edc-dataplane/templates/deployment.yaml b/deployment/helm/edc-dataplane/templates/deployment.yaml index 914c78866..9f434308c 100644 --- a/deployment/helm/edc-dataplane/templates/deployment.yaml +++ b/deployment/helm/edc-dataplane/templates/deployment.yaml @@ -79,6 +79,10 @@ spec: envFrom: - configMapRef: name: {{ include "edc-dataplane.fullname" . }}-env + {{- if .Values.envSecretName }} + - secretRef: + name: {{ .Values.envSecretName | quote }} + {{- end }} resources: {{- toYaml .Values.resources | nindent 12 }} volumeMounts: diff --git a/deployment/helm/edc-dataplane/values.yaml b/deployment/helm/edc-dataplane/values.yaml index 7f3ac94d5..c82c0aa53 100644 --- a/deployment/helm/edc-dataplane/values.yaml +++ b/deployment/helm/edc-dataplane/values.yaml @@ -185,6 +185,9 @@ affinity: {} # -Dhttp.proxyHost=proxy -Dhttp.proxyPort=80 -Dhttp.nonProxyHosts="localhost|127.*|[::1]" -Dhttps.proxyHost=proxy -Dhttps.proxyPort=443 env: {} +# -- [Kubernetes Secret Resource](https://kubernetes.io/docs/concepts/configuration/secret/) name to load environment variables from +envSecretName: + logging: # -- EDC logging.properties configuring the [java.util.logging subsystem](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html#a1.8) properties: |- From 2ca393bf6481ccd90f87043039dad5ec13c6cdea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Jun 2022 16:10:43 +0200 Subject: [PATCH 155/433] Bump org.testcontainers.version from 1.17.2 to 1.17.3 (#242) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cf738acd8..0c532a9e0 100644 --- a/pom.xml +++ b/pom.xml @@ -70,7 +70,7 @@ 1.18.24 1.70 4.9.3 - 1.17.2 + 1.17.3 2.0.0-alpha1 1.2.11 From 6647067bc1a9e05bc94744bc361f8b85d1b31987 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Thu, 30 Jun 2022 08:44:13 +0200 Subject: [PATCH 156/433] Add missing checkout step for trivy (#243) --- .github/workflows/trivy.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/trivy.yaml b/.github/workflows/trivy.yaml index 21a9fbca7..765713e23 100644 --- a/.github/workflows/trivy.yaml +++ b/.github/workflows/trivy.yaml @@ -74,6 +74,9 @@ jobs: - edc-dataplane-azure-vault - edc-dataplane-hashicorp-vault steps: + - + name: Checkout + uses: actions/checkout@v3 - name: Run Trivy vulnerability scanner if: always() From 2b3f649612e598c6dd57652b22e8dd2e40013291 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Thu, 30 Jun 2022 10:53:21 +0200 Subject: [PATCH 157/433] Disable fail-fast behaviour for trivy matrix scans (#244) --- .github/workflows/trivy.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/trivy.yaml b/.github/workflows/trivy.yaml index 765713e23..a8cf21ec3 100644 --- a/.github/workflows/trivy.yaml +++ b/.github/workflows/trivy.yaml @@ -66,6 +66,7 @@ jobs: security-events: write runs-on: ubuntu-latest strategy: + fail-fast: false # continue scanning other images although if the other has been vulnerable matrix: image: - edc-controlplane-memory From f6e1e8fbf58328f7c399cc08661e8f3bb8b94b2b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Jul 2022 08:31:47 +0200 Subject: [PATCH 158/433] Bump spotless-maven-plugin from 2.22.8 to 2.23.0 (#247) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0c532a9e0..74fb2f252 100644 --- a/pom.xml +++ b/pom.xml @@ -44,7 +44,7 @@ 3.3.0 3.2.2 - 2.22.8 + 2.23.0 3.0.0 3.4.0 2.0.0 From bd4a6765910124ed8d01ee49c95b813e6c8aa1f1 Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Thu, 7 Jul 2022 12:02:30 +0200 Subject: [PATCH 159/433] update documentation (#231) --- README.md | 8 +- docs/README.md | 56 ++ docs/Transfer Data.md | 3 +- docs/data-transfer/Transfer Data.md | 160 ++++++ .../diagrams/transfer_sequence_1.png | Bin 0 -> 29595 bytes .../diagrams/transfer_sequence_1.puml | 34 ++ .../diagrams/transfer_sequence_2.png | Bin 0 -> 29371 bytes .../diagrams/transfer_sequence_2.puml | 28 + .../diagrams/transfer_sequence_3.png | Bin 0 -> 34859 bytes .../diagrams/transfer_sequence_3.puml | 33 ++ .../diagrams/transfer_sequence_4.png | Bin 0 -> 60428 bytes .../diagrams/transfer_sequence_4.puml | 44 ++ .../diagrams/transfer_sequence_5.png | Bin 0 -> 21812 bytes .../diagrams/transfer_sequence_5.puml | 27 + edc-controlplane/README.md | 497 +----------------- 15 files changed, 404 insertions(+), 486 deletions(-) create mode 100644 docs/README.md create mode 100644 docs/data-transfer/Transfer Data.md create mode 100644 docs/data-transfer/diagrams/transfer_sequence_1.png create mode 100644 docs/data-transfer/diagrams/transfer_sequence_1.puml create mode 100644 docs/data-transfer/diagrams/transfer_sequence_2.png create mode 100644 docs/data-transfer/diagrams/transfer_sequence_2.puml create mode 100644 docs/data-transfer/diagrams/transfer_sequence_3.png create mode 100644 docs/data-transfer/diagrams/transfer_sequence_3.puml create mode 100644 docs/data-transfer/diagrams/transfer_sequence_4.png create mode 100644 docs/data-transfer/diagrams/transfer_sequence_4.puml create mode 100644 docs/data-transfer/diagrams/transfer_sequence_5.png create mode 100644 docs/data-transfer/diagrams/transfer_sequence_5.puml diff --git a/README.md b/README.md index 41728e38f..25fd1732c 100644 --- a/README.md +++ b/README.md @@ -49,8 +49,6 @@ cd edc && ./gradlew publishToMavenLocal ### Milestone 3 -The Catena-X milestone 3 release can be found in the `release/0.0.1` branch. -You can download the container image from our [repository](https://github.com/catenax-ng/product-edc/pkgs/container/product-edc%2Fedc-controlplane-postgresql). -```bash -docker pull ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql:0.0.1 -``` +The Catena-X milestone 3 release can be found in the `release/0.0.4` branch. + +https://github.com/catenax-ng/product-edc/releases/tag/0.0.4 diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..f0544aaeb --- /dev/null +++ b/docs/README.md @@ -0,0 +1,56 @@ +# Product EDC + +The Catena-X Product EDC Repository creates runnable applications out of EDC extensions from the [Eclipse DataSpace Connector](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) repository. + +When running a EDC connector from the Product EDC repository there are three setups to choose from. They only vary by using different extensions for +- Resolving of Connector-Identities +- Persistence of the Control-Plane-State +- Persistence of Secrets (Vault) + +## Connector Setup + +The three supported setups are. + +- Setup 1: In Memory & Azure Vault + - [Control Plane](../edc-controlplane/edc-controlplane-memory/README.md) + - [IDS DAPS Extensions](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/iam/daps) + - In Memory Persistence done by using no extension + - [Azure Key Vault Extension](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/azure/vault) + - [Data Plane](../edc-dataplane/edc-dataplane-azure-vault/README.md) + - [Azure Key Vault Extension](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/azure/vault) +- Setup 2: PostgreSQL & Azure Vault + - [Control Plane](../edc-controlplane/edc-controlplane-postgresql/README.md) + - [IDS DAPS Extensions](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/iam/daps) + - [PostgreSQL Persistence Extensions](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql) + - [Azure Key Vault Extension](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/azure/vault) + - [Data Plane](../edc-dataplane/edc-dataplane-azure-vault/README.md) + - [Azure Key Vault Extension](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/azure/vault) +- Setup 3: PostgreSQL & HashiCorp Vault + - [Control Plane](../edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/README.md) + - [IDS DAPS Extensions](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/iam/daps) + - [PostgreSQL Persistence Extensions](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql) + - [HashiCorp Vault Extension](../edc-extensions/hashicorp-vault/README.md) + - [Data Plane](../edc-dataplane/edc-dataplane-hashicorp-vault/README.md) + - [HashiCorp Vault Extension](../edc-extensions/hashicorp-vault/README.md) + +## Recommended Documentation + +**This Repository** + +- [Application: Control Plane](../edc-controlplane) +- [Application: Data Plane](../edc-dataplane) +- [Extension: Business Partner Numbers](../edc-extensions/business-partner-validation/README.md) +- [Example: Connector Configuration (Helm)](../edc-tests/src/main/resources/deployment/helm/all-in-one/README.md) +- [Example: Data Transfer](./data-transfer/Transfer%20Data.md) + +**Eclipse Dataspace Connector** + +- [EDC Domain Model](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/blob/main/docs/architecture/domain-model.md) +- [EDC Open API Spec](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/blob/main/resources/openapi/openapi.yaml) +- [HTTP Receiver Extension](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/http-receiver) + +**Catena-X** + +_Only accessible for Catena-X Members._ + +- [DAPS](https://confluence.catena-x.net/display/ARTI/Connector+Configuration) diff --git a/docs/Transfer Data.md b/docs/Transfer Data.md index 471761a81..78f848bf2 100644 --- a/docs/Transfer Data.md +++ b/docs/Transfer Data.md @@ -4,8 +4,7 @@ This document will showcase a data transfer between two connectors. --- -Before running the commands setup the all-in-one deployment from the. This is documented in it's -[README.md](../README.md#Setup). +Before running the commands setup the all-in-one deployment from the. This is documented in it's [README.md](../edc-tests/src/main/resources/deployment/helm/all-in-one/README.md#Setup). Please install [Bash jq](https://linuxhint.com/bash_jq_command/), as it is used in the bash calls of this document. diff --git a/docs/data-transfer/Transfer Data.md b/docs/data-transfer/Transfer Data.md new file mode 100644 index 000000000..70449ecfa --- /dev/null +++ b/docs/data-transfer/Transfer Data.md @@ -0,0 +1,160 @@ +# Transfer Data + +This document will showcase a data transfer between two connectors. It uses two connectors from the *All-in-one deployment* of this repository. + +--- + +Before running the commands setup the all-in-one deployment from the. This is documented in it's +[README.md](../../edc-tests/src/main/resources/deployment/helm/all-in-one/README.md). + +Please install [jq](https://stedolan.github.io/jq/), as it is used in the bash calls of this document. + +--- + +For this transfer connector **Plato** will act as data provider, and connector **Sokrates** will act as data +consumer. But the roles could be inverse as well. + +**Contents** + +0. Before running the demo + 1. Ensure all pods are running + 2. Set environment variables +1. Setup Data Offer +2. Request Contract Offers +3. Negotiate Contract +4. Transfer Data +5. Verify Data Transfer + +## 0. Before Running the demo + +### 0.1 Wait until all pods are running + +Get all the pods and wait until all pods are in a `Running` state before executing the next steps. +Please ignore that the EDC applications will crash 2-3 times during the start-up phase. This is normal. + +**Run** + +```bash +minikube kubectl -- -n edc-all-in-one get pods +``` + +### 0.2 Set environment variables used in subsequent calls + +Initialize the following environment variables, that are used in the upcoming API calls. + +**Run** + +```bash +export PLATO_DATAMGMT_URL=$(minikube service plato-edc-controlplane -n edc-all-in-one --url | sed -n 3p) +export PLATO_IDS_URL=$(minikube service plato-edc-controlplane -n edc-all-in-one --url | sed -n 5p) +export SOKRATES_DATAMGMT_URL=$(minikube service sokrates-edc-controlplane -n edc-all-in-one --url | sed -n 3p) +``` + +## 1. Setup Data Offer + +Set up a data offer in **Plato**, so that **Sokrates** has something to consume. + +In case you are unfamiliar with the EDC terms `Asset`, `Policy` or `ContractDefinition` please have a look at the official open +source documentation ([link](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/blob/main/docs/architecture/domain-model.md)). + +![Sequence 1](diagrams/transfer_sequence_1.png) + +**Run** + +The following commands will create an Asset, a Policy and a Contract Definition. +For simplicity `https://jsonplaceholder.typicode.com/todos/1` is used as data source of the asset, but could be any +other API, that is reachable from the Provider Data Plane. + +```bash +curl -X POST "$PLATO_DATAMGMT_URL/data/assets" --header "X-Api-Key: password" --header "Content-Type: application/json" --data "{ \"asset\": { \"properties\": { \"asset:prop:id\": \"1\", \"asset:prop:description\": \"Product EDC Demo Asset\" } }, \"dataAddress\": { \"properties\": { \"type\": \"HttpData\", \"endpoint\": \"https://jsonplaceholder.typicode.com/todos/1\" } } }" -s -o /dev/null -w 'Response Code: %{http_code}\n' +``` + +```bash +curl -X POST "$PLATO_DATAMGMT_URL/data/policies" --header "X-Api-Key: password" --header "Content-Type: application/json" --data "{ \"uid\": \"1\", \"prohibitions\": [], \"obligations\": [], \"permissions\": [ { \"edctype\": \"dataspaceconnector:permission\", \"action\": { \"type\": \"USE\" }, \"constraints\": [] } ] }" -s -o /dev/null -w 'Response Code: %{http_code}\n' +``` + +```bash +curl -X POST "$PLATO_DATAMGMT_URL/data/contractdefinitions" --header "X-Api-Key: password" --header "Content-Type: application/json" --data "{ \"id\": \"1\", \"criteria\": [ { \"left\": \"asset:prop:id\", \"op\": \"=\", \"right\": \"1\" } ], \"accessPolicyId\": \"1\", \"contractPolicyId\": \"1\" }" -s -o /dev/null -w 'Response Code: %{http_code}\n' +``` + +## 2. Request Contract Offer Catalog + +In this step Sokrates gets told to request contract offers from another connector (in this case Plato). Sokrates will +then request the catalog over IDS messaging. + +For IDS messaging connectors will identify each other using the configured IDS DAPS. Therefore, it is important that +connectors, that intent to send messages to each other, have the same DAPS instance configured. + +![Sequence 1](diagrams/transfer_sequence_2.png) + +**Run** + +```bash +curl -G -X GET "$SOKRATES_DATAMGMT_URL/data/catalog" --data-urlencode "providerUrl=$PLATO_IDS_URL/api/v1/ids/data" --header "X-Api-Key: password" --header "Content-Type: application/json" -s | jq +``` + +## 3. Negotiate Contract + +Initiate a contract negotiation for the asset (from step 1). Part of the negotiation payload is the contract +offer (received in step 2). + +In the diagram the IDS contract negotiation is marked as simplified, because the EDC is exchanging multiple messages +during contract negotiation. But the inter-controlplane communication is not in the scope of this document. + +After the negotiation is initiated ensure that is has concluded. This is done by requesting the negotiation from the API +and checking whether the `contractAgreementId` is set. This might take a few seconds. + +![Sequence 1](diagrams/transfer_sequence_3.png) + +**Run** + +```bash +export NEGOTIATION_ID=$(curl -X POST "$SOKRATES_DATAMGMT_URL/data/contractnegotiations" --header "X-Api-Key: password" --header "Content-Type: application/json" --data "{ \"connectorId\": \"foo\", \"connectorAddress\": \"$PLATO_IDS_URL/api/v1/ids/data\", \"offer\": { \"offerId\": \"1:foo\", \"assetId\": \"1\", \"policy\": { \"uid\": \"1\", \"prohibitions\": [], \"obligations\": [], \"permissions\": [ { \"edctype\": \"dataspaceconnector:permission\", \"action\": { \"type\": \"USE\" }, \"target\": \"1\", \"constraints\": [] } ] } } }" -s | jq -r '.id') +``` + +```bash +curl -X GET "$SOKRATES_DATAMGMT_URL/data/contractnegotiations/$NEGOTIATION_ID" --header "X-Api-Key: password" --header "Content-Type: application/json" -s | jq +``` + +## 4. Transfer Data + +Initiate a data transfer using the contract agreement from the negotiation (from step 3). Then wait until the state of +the transfer process is `COMPLETED`. + +![Sequence 1](diagrams/transfer_sequence_4.png) + +**Run** + +```bash +export CONTRACT_AGREEMENT_ID=$(curl -X GET "$SOKRATES_DATAMGMT_URL/data/contractnegotiations/$NEGOTIATION_ID" --header "X-Api-Key: password" --header "Content-Type: application/json" -s | jq -r '.contractAgreementId') +``` + +```bash +export TRANSFER_PROCESS_ID=$(tr -dc '[:alnum:]' < /dev/urandom | head -c20) +export TRANSFER_ID=$(curl -X POST "$SOKRATES_DATAMGMT_URL/data/transferprocess" --header "X-Api-Key: password" --header "Content-Type: application/json" --data "{ \"id\": \"${TRANSFER_PROCESS_ID}\", \"connectorId\": \"foo\", \"connectorAddress\": \"${PLATO_IDS_URL}/api/v1/ids/data\", \"contractId\": \"${CONTRACT_AGREEMENT_ID}\", \"assetId\": \"1\", \"managedResources\": \"false\", \"dataDestination\": { \"type\": \"HttpProxy\" } }" -s | jq -r '.id') +``` + +```bash +curl -X GET "$SOKRATES_DATAMGMT_URL/data/transferprocess/$TRANSFER_ID" --header "X-Api-Key: password" --header "Content-Type: application/json" -s | jq +``` + +## 5. Verify Data Transfer + +After the transfer is complete the Backend Application has downloaded the data. The Backend Application stores the data +locally. In this demo the transfer can be verified by executing a simple `cat` call in the Pod. + +![Sequence 1](diagrams/transfer_sequence_5.png) + +```bash +echo $(kubectl exec -n edc-all-in-one --stdin --tty `kubectl get pod -n edc-all-in-one -l app.kubernetes.io/name=sokrates-backend-application --template "{{ with index .items ${POD_INDEX:-0} }}{{ .metadata.name }}{{ end }}"` -- /usr/bin/cat /tmp/data/${TRANSFER_PROCESS_ID}) | jq +``` + +# Delete All Data + +```bash +minikube kubectl -- delete pvc -n edc-all-in-one --all +``` + +```bash +minikube kubectl -- delete pv -n edc-all-in-one --all +``` \ No newline at end of file diff --git a/docs/data-transfer/diagrams/transfer_sequence_1.png b/docs/data-transfer/diagrams/transfer_sequence_1.png new file mode 100644 index 0000000000000000000000000000000000000000..f52ecbb70a0aadca9e555a2481581d11c86ca1d6 GIT binary patch literal 29595 zcmeFZbySt>+BYhlA_gFh0VpCVh@@a3Ap$y`-yFGJX2JVK1@nSdg#!h!!oz7D<3+9 z$9U)vju9~~{G{9;Cm#Me|M-T=V?7H?2UC56$A_f#&GoHx9_v3~(RE-kdi>Z@n48(c5%z*Q2MPS;u5L4g>AHN3pET# zdgx!6hs{vNP~YHEekavbID7k(Zgl9*K-dcrPh&ILAWg0Knfd|SN@Y`#P)1Vjq^?O@ zs~OqWfS2#YCP(hYb*(y{K;N9sF^<{8v?sY(9>VdCx=S?LdD2|$ZSlo?d^gKoTjLBP zwoSK&%tN`1^060u=;!oK1>a63>hjSJS(x0itQ6dwix2!B^^xqpQR?Eu4^uC;?y3ux z9lV-T`ApI~sY^khb$H$<@tWJ{j&E%_{>AO9wc$7n+trpQ$z6!sLoQfui4T-MonFfh zkID!k(aSG7Kcur``h?!QMOiIp3TO9RQOxbi%Q!;e^smD-tRMExIes!f$++2$AL%!D z%Lc`LlFD7*{Mn0i`9{Q{7Idmx&D1$0Yu2r3Ickp2U7Oylb2Q%iCw!4Qm!2Lv^hiwR zy5v23)Lbl)%88!-ElJ0tREp`Iq37_)UOnZlCY-4YB9t>#QPlHK?Xf~UEw4B4bbeOv zNk_HI`OJ@sioo1DpcJ#Sjt*;Dsod{zTKoW? z6Sc(i#r<{S8DdYK%ch4DX`fZ$IZ(OEFQ;2n?olMkX58J~S}4_;ZjChT*Sru_#~CQ* zv=P8?CD*V$&WrGH)D`O~4?H4o0ojfONjwRO%ER=6#C9ido0~60@*8q#Rd5bUQi?XB zAE;z&ORkW?rRTWreW$-{o~>E$EcK1wh;9P0b-oZuMH4O`D|~lGfl8ucZ(|xhl{ok^ zE_|JzYMy~=*xN^6>T6_^m)F+3ja$OGX%={*3d{y-XbCA8&i(oVVluMh&Q>Pf84gRM zlCh8C6~B{55yG7`&m**)A6f5x|Dfy7cQW2Nw>&#r(cUjNLACWws!}4MJVr)FrXh$? z?#`Wf3Ag>HN6!?M+bs-UarwTMrCFMro4YVv-a@*aCwNZ?Gu@wWLc^uuet0W!`}_Lr z!-o%-hTU+IYoOd?nEtzcX6%^WLF_jdCU%SvWoxX`GS7Mm=-JjqEGfT9|o?Pk{ zv0K<(>$uY%E7HKte(8uq7dj<+SB<3C!t5FU0NCK3NR zJ{L7hULGWrIDdV7Q0ThnICj3-(}1^SY-4@^k$%tPo6YigU5$(I5KP6E+wsfhgIixe zB?=E`9L}>`94^-p;o(`D>&wev*vHMfpGjw0h2IIYwP1N+kM2-66&+nOxA*#?HWD{O zUteEqAH|S|s-~u#m@I^plxKFBx9us+OAi;pQoF@ARLwL6N?1f>R@NzOIbJfYU8LH# zj8tNBBRw>9Ti5NSpWFWG<>Q1nA9ShfOfcp3ZC;X`RQr9+3t&iCbb{^f`fezNBEDmYYqALF+2?lS{Nu?oca(Q^77>?PSsqq zfdT=O?#?VtdRta@_LQU~e0=;W!PYHU?UAAmR<5q%BGm6lf^^U;ZkXYQUpwEv1AU#-;kqN%#Nx>m_!l5~9ogWPf+ z$vjp1DUB`e)=Hktyt_FoOk$3f>_j?_2YZ|B3Nb>u@1G*IT&Onc>+5H(x7%i@7gC`5 zdwVm~@=1s7W;)a6%EkA$^tLc)^eoJs&RMbw=CeN*$oMqcy$g-R4*83C$;ft*%^>ff zGTFm$p=B_}7Z&-Mi0ehd3b{`PI3!!$HB_s3eeD<72J&rqoz3dd!WkEn|E z4&zqIb}w&jX<1!g_jyStU|i+hwozd8Ns2wHqtEHt73VFU&Yb-GN!jlk^E=~1#Pios ztaz*GYWc=Dq@}$$SlQTeeYu55h7!?p(e9ouoV= z;SkDpad>E^E{5s&alC=OolQf5KBvBcfq2+3pUK+d#MD((h@Hlk0s;b>LfG>NHA-y* z8E+Udt#Hudwk*m>uFI!cRe~wIZw{oJ|bGj;}dNoPA-$%m1X?>2?rpQ9(jA8a(wWQCj`m zkU!H_HUpGBo_ch^0eQ3Yu#aJ(X(M_GJ60o@)1k>wKIa?|ga_03*wV1iTM)Ki?GHP} zKwtlx>`uo_7rL+VT+C&QxGJ>v&4tdi<01@fCi^4JmT?9jqCBcpzcE|+zU)Z3Q)tpd zG3?WYQOoDpWp!Ems>IFBeP1`(Ef=-V?m!$UiDLEQMIo}<6FR43N3WrL;;ozzBzwcc zuP@mx4u5EA!F$%!)O7T~=r-&i`J%x)SHi>R=H~9ED3Hjpo;!E0v9;Cv+Ljw@Ucn*;#rp~E{^pGnzd6ZpLUcm5wbkASL& zGQfvvt~q_Js?Xbk1?sLR3r}`Hncr1H`Vf4B<_(TBN<4j+{Ey;$;9v)Vlq>%_t0cC) z>zw@m|Nm<|UI!q>Tz88sMkSzs06qSm21L8Bjn9tqVoKb zVNP7fE4H|Ofp5wrK25xj=PU9}dRD|Eq*6l!7((}~d=g6EqXf*pJ?*H>D!G^YqGm#+ zbdh;Q@1<}gv**Gwr>@;?IrCvo4vz0(HJmwm4Z}mFcB}l#r!c}+6O9!vJNQ0|xRahW zR@L!!qrtb92c?y=PP(MoLzmD9_j|3-ZK=4?LWTVx#LDOW@vz`G&PuzlcO#M%xTum# z<>nB1hV93>H%@S0tk_xT@gbvG;nyueYL?oX?R{UbQ5I9AaPmpMSsP!c#?F1b-Q~pO z;ccSpsUi7{aOnheY&SxuUd+F~bDH&v%#eW^BehcG%yWoOB2q|LdKN=vF3D`z`1n*yg;63!j6+F+v_>@ zS9vFGatpk2wX!HNIzPS_ZkpQLUVh~ihR+F#MDsddtI#PyM(*N~61oLR5(+pXv}YGEYyn5Z zWr^66T5y7y`7wEsONEZhfP|4?V0@3 z9v*oA>_XtN`gec6zv1k@f}oz=>-BUB#&N-^TXqC>!1x@5yaSHi*d%Lm%@PDJ5quK8Mvoy&W8atF0&=Q-A9&? z7ks{3SKs}C`<&I+0G+1S0=+pvp*D2Q>{YL^{Y6DPIm0$d9!Z^fl zzo{#ghYLr=m!;3gn2ihHe4^cnSTZF{^z8F}J*$%V)*`RKLjFM8zAb({YMf_2(R-;@ znF}MlkD1(Kuiql-d1sfMIf>j`SHM)bX$KTVEid1kCfs9tM-xteT$#)*y!xUl`#|sg zWwI3p;V%|_m6)(JEg4ua$-&B;*yb{{GKf7wyJ!3NOAqO$3u_%3mrrnX5wnO_fKU#d=T*o zUE>rS^p(GDIA1M(ZD727@-RUMJO50mAF07X<{b;EjcB&y{?Z}`=?c{;!&6P2{!&{y zyu)bm(!$=-pwc_;4HANfd#xVzPm$ei;QTtaFv*tn(77@7kou6ml<^pm60rqS(Pqo0 zt!BKKnTwl<(}3h%;YRFqGKVO~whHWbM5je7+>w=?pBbMzxouz+S^h|-fL|Y1$L?9r zc=#eBkWS!=4aa70&fNBD_&{G@Fr7ffuVLd2NAh}|kS;EtNr`ubAxmxD-)yxM9HTf| zZ;#L(h-+gSzNRoVO+(SV`^=2Iaw%!7s!0*&?Nrp*hZ1K?Pc)vKNCsxTM=USPtKV46C<#fKw5hMpjBF?a0jntc44)Q0lg zzK;d3nii4hWIZc20BjjQSC$Ez#;1={e=#{6% zct&a8%U0`|eRYmfL`dQs@>a@zTbwwB`NGG}qs8sbgRI+P3GaR6*2C;Om)qI+haZ&O zVDNjueA;z@b(TX=X>lJhvgQ9W{ro}DNpa44C&WP93#MW7lm*NvjlurA05c8yT=UTh z3Pl|1sU-s(=9q}aE{9B)J@GI7w)FkY<@XUw+0}PNrl>56-k093aZiRJQlpsc64i(* zaJaGmPMJ)CVK`}hRZq2Qr<)>;HMR1=NGhR{+3H6BJW+9!@kq5-@S}pcgPbS!~jSv(S4#Q;nT1Xs*N1-BB^n?!p*uAA9rC756K(JhYh~eo86AOWM`A9miWX;UOr`Yx^i@;*1@11rC4ar zQ zBKQ_5G;s{w1*`JknQjwS(rN(15wsZI-CEYuNt3B^7&Wa?KC17C(Czl(5G=q1YMvXe z!|mo*2^7v09=!ZT;rht3;0DFuE?Ee7S%7L0eRrd`7y*xD16N;*Ey;f4lPHAgyjuG{ z@vFl386y*SOp=h*Ws5VK(u&N3Z)$VNg%FEF+cpDrPTl?!-1EwrW_-GW8N!cDg4QH7 zI&~hFxOB}}9(#nR0{x0m`OF9R!CpUZUqDit2aVXxO809S1US@>(OyK}*K8 zYC(=mmYqXgLO>QOU!4te&N@64DWh2=uSIf`@xYYt%k8)7@0FJ4tj9)j6Opw&eFe)j ztan40i6uC1IwHEgx&#Xl^;~o3hwF4dhE8wf%`6oKh*<9gSr{&Aw`5jiuhq!8pfAsz zzvVOO&@}wQG<=krtn*!dUgX{zTRK-d%FJ*EQ4jb8v!XykebYI_;@1id6J3t!o!t z?68tFZ0?*Os#)NxMpx{v#B1N{NU5}29{m{LwyLDGz`7N>2ix699rOrWIl`qa28TM}UF2}$6ef3?D~z0B5ik+61R z&c8f;%v}bm;CS=OUu&tWjoYA_==#jV^tq9qn(?Sy2Og(LWZ9{6gTCrtbiM^H^gl;4 zM1KrW&#pre=uA9pSxX;GVRnAJuxP%(^$0KV!Hidxs=E5(-ip|Ua(ezWGsfm|ftKBz zra4mCxaa<(quBdp4j~lTe%WZHLLPBq_S@at8mb?yA{TK81hzf0OXZi+t*5i9xA4Wr z7t5`uiI##VTYB=sM_ipGpp<-ko!Be){8>bVg2p75rlXZ@P3*=j#!cpqgmSrK;MP{f zjn5-R9{yM3V}!%V1_~_>m`@JJT#>`KAtiIOw6eneGA7HW5YwHdNmTG5?rZM(!_$Zr zni|kzaMLr@@}D_1&)k1rGvnMe*GC$RW;RbQ zjGTUDRy#3ZIr_kyrnO9x*CNL(2f3`(9Dm<v|AX4zjeGS_GfXC8QFs7>bThE`*lOYa4b2`_wsIC`-ZH5 z2*9-qdxa9?$7?2wI#-A^-8Za#R(ei%;aRvi;-yQ{7im5x^Fno9{z9>K;NE`Fg!1g| za>yeq>{;9{EY~Xzw~Vyxe{IOzyvX+z!AHZ@g_L=t)DU~ywM@pwznbUD4q@5M{DY4s z1!lW9ny`EJi2n;znCFlI@_=4RWd2=?xn<7u2EZ z+|IkHrTN6WoVj23RyK^II~&KMx*}tuM`CH+bD>tv=h70M)}u`;uN#l%KarS*-!rk( zh-{i2sw7UGW;H)Vw$1x6JA_-tzH6X~SHK0GXBPR2O!)3wHVI1Ga1s-APhNq#fqVjtTV}anrZN1=Gv>fHx90t z^<;hNLfiSvIw5Q~Y!qL8U#~UkTG?33AaKcbaS{%7+vuYe^1ebXA$}l=erzbpE+iD{ z)bQl+8Ge3l%N(moLNpDE)$No9I*8c~b(pRm;U(OPo)}>)@yxrF_R$krJ(9*Zf|pBh zUT5TD7CxKqK`Mo5^bp^eZS{nDp(c01#SISE+uI$@_EdL|3t4_Gme#mZ>6$N$slxrJ z*K$Mg6^?WTM%SdlMUt(nf6FX9f!9*4dO*f!m(w6SB;@*q*{6esfKYDVY&GCcj{4C5 zDm&$>vs6(`m7v(ww$tB(rb#C;H`0kBkmY-QV&*hS?Fq>0vHRQ$B{>4o7*)&t?5!8D zN0?)>#~s2txoT0YE@R3rVQ1-Va4|3GER=_Ic}_Hm`}bq)#w&;}?Z0_oyn;98TXzf7 z{M{_9b<IX{q|art-q?6t zqBy5_PcBUNU{?KTI1kjh6ht)MeQn&7Go3fR&#)^;o2*tk|C!u~Jm&tAtukQ#!?r7| zBpAo?4!268(1*nd{QB_iI(Ctx2+sXYx8OT5LYYd5Y+PJiL`UKt2wTnEV?yGbJtJJg zI6xSAQtZ2?IZdjJhQ{TEcdb^Vy#s{@!RXq}^n?V;B_8_*e;NzV`o8{tv*9wb<|EAF zW~?RF^@zeM zOqf$sHh$P+y?m!~XJirQFjsl_T6+z9fLY7@vCbcp`mD=a>mbj`|249MY&zGD#7Uj32b}1{*kn!7wH=*ioJa~{dRy(4j z6%YQ!D=OEW`MuFmqq`J&EzG zysv4zja)w*5Vx%pe8(#8psl9YXK<0Ts{hCU0%u8H!-E7GH4VlWJ z3-kT-cYB^8A9MYf;qDQMtyw#2AeAbZ0mWGkM2Sm8*uDFCXMEeT?H>8ZtxLFUj=1b^ zZFJgX#FR34s5NN-8mt=oSSHEtN;qwAHh5ZPr4H4lv%L?{wtaSqREs{Z>VrhYo*u7I zWo({FqU7C#v>dq&sl4s>Ou2ce-ZIB%xJHnkN(~axt@a_`s2|&59lg9nD`|awq)AYKG3`k((Db{iS?0mTO=M>WImY( z>~h#K<-;mbYbC16R^}qCh7n7j%SXJUMh%PaNKj&zL=(zptVy`XSiw!JgQnolz7s)N zT7JGw1p#1FJ^3NM~{tG&eP1jv$5heNW0~@e|B9>2%qkY%23B*{>lB2 zV$YvSjM!q0yFh+78e^jp)ixjYQ1G0b|FT?MN_}@*@+)RLlHC@MuKuQSYCF7}GinbU zwZCpRBsgv$(4~$&B#Q#+=B4&I$m-P5IGJ%=YGl#rSK>l$Ui-&qHyLnfzK-6%wj@HP zT$)rSJ5!_oU6yBN%P@r2f^X<_5UByPH`U9PT|3E?%;^KiNGy%GyA;%mWwliAbC|=| zqdyLFH+HlQQ`4Os%0HE{XG)V%fyY+oiTBKnQqtg?*wzQ!=Y-ORPH39SJeDPn$d0Hv z1f@w(0ET`laZxXxK*U$1Sm@*#Qc5?ifg=2TyzJ78*TX_P-*0nhH9Id4qbb62#X)%I6Fy}^ zQc_Na#;yn%8(eRJ*^ZRdjDv$ity@plUSnThO7$-(1Ce!otwQk|X*5Ohv zMggUz4fGc8#+c61R3>iOQ73U-hcR;bPHt(9o|>MP(60JextDq2U=y7*yr*y9H68CZ zoFsD`JV)?k3XQw`_9PfJe&ZW_6CMG7c{&!=| z`aGY}=K?^vXniT7YT8~ur;Nnvw&Mb=w=i#b#Q#(ygF}%Xl^RVc1W7pWo@D< zWW;qR&`s@pBl^Uz@4OlJz&7o|xt&SUWxAVKjWbwg=7HqXhO^8$NPT@x)!YYxnVIu` zRP^tUU43nTuMy3XmaU|yST(W(l-tm7TCXAK5o=mn+M`F0OiRm|%)@zqYOoQkcDVtf zO@H1`09I^e4(V+2qzsl3*Tooju+0D3_M*xim*wtbQ1j#C zHRWaY%euzK#;a2wG&MD?t*yB=9rssxwzs#-&g_%fcXg)S^-0eCpG9Qwe7@8Nl97`; ze_uO~ zVGx{Ko}Qfz>c?xhgMIKnOP#m*h(j_n&D7NB=9=5v@7%r(9^B5f#6e_a3(SW~!1~V4dx6v;-c}6|RvnP+<3@s#yuwj;%~f8RolHuU zZTmSgQVd4r*xqHAoSd9|<1Q;}Ye+-ThH#8RAR)-#e|2Rgx3CbSUbx`2SdP@tXtmAM zEEVMBByScI6VubxEo$h!@D*WZ^ciCrPMc}8P3K7aEm_(l=_grIp*Vw_LZOB;L=-sY8w zZ9O7s_pID_W6ojDPW*i9F)e4M#)_gZRSO7o$Hu0L%~jDorIWO^LBsTy9BVSw3yZj@ zuEl0_{jeB@SU1w&?H%OJRJplz$GjG+ct?E2<8MV+T5osid22&K+0H7V)y*GSruo+DNHgq)PClc`DiOHZ$)`@Q+|ojzsFFRS}P zz-_$sahM8b=Y&oEtB*>}ik|%EClIg5;j1mvZg_5F-QvY4FMZ;VNmM0`X%{@kr`Svh zFEL)@h!IP>X}OWK!FgTM;i{)DDw$;l>$!g9fZs_C7`3C{5jjvvQhMt&qKQqPBgcHp z{8)%{?X1zFUn82suS8Am>pA^%@Chp%a{t#ubAAdzD(?8i z`jVW-YTr#MLRng=ALH(m$KFNU7S|c)bMD-cTZ*md2lg9ts*aA|iynPBcKkT(R;MrZ zhjju!pXB$~EV4W$A)%#WjPHAdSwTTT+HDRmuy;;31H}RdG6=fPaD|I{`gLblFSr^h{$J+ook_>8THhnpdf0Tz7=;-4 z2)I5g>+5EplVo75U}H*MyH6VVWGld)j%)+QcMJ_7u5^aHa9IkCgAk8iY%6W znV7)mK6Cms7=KNzt>o~Bg2EiC(}4;OG?=lmG3{HU2#BwEAEV4yh!uuTUcOvUpFR8G z^2mwsuV>r1%?Twb)jWSC2TrVVcm>BzPE4q%s%locY8G8PVo#;g$z8dNJ5X0wr=+Bm zXYhgJQ@bvUSb{jDq1<)w4Gq4K1f;-?CQ1i)hwbAoEDV)G0ttSg&%ND1>YixO7DGaR z&6grDFtF3?G%UGavjb1vB=e0l7;}<+Gdh`_ti)D$cPM;JHH}eTL!|iop%V6 zbWjw4on8GPUfhMQEIl>#){pBcIy$gFgT^FGK@ctrRW9tjwFH9<%DT-+g^O^xAW#=G z=;;u@;N|6oB?DQcx?z1-c0^iLj4ji7?c#YkIZYov_{_sq0gbH@e9+m}X1+4f1i2OM zuJ^2b)4m3e=ljDDXF5GgUdfQ*{J5?egoJ$T1p_&= z_~55cyl2mzWnp3A<$d8mb@Gm-CC^Fqj<>gMzK=}fU$nNhr6468XKul9a@v?{;>HmK_4VLHN#-hsu|(>0LNvBvlF$>B81D;y0SC}8XDM7pVn=Qx$=0&4>BZw z{C)xZdjbLi54G)`9WT94^B2gl0Yz-%4UUVnqrSi`6mNF+K|<&$d_!d(`>>#t`@@d7 zxVR6@wf?x@X#{CA-Z3aK8y@Ts;6+hXS64&Q6TGLIut*+?yq$NF@1_yZj_b-~tFx-F zL?tUCO-LA%$ytR$Jf~d|SFaulNrIrL?~^Bj4?jNuFf`{%b%JzSk!b?KrngZ>fBm1@ zIe{1QkurfW0X@WS3r4p&R?=l_${fO_{^_-Kb0`Ak$;HLRFJEdaC0X6e(qHoE4eKlZ zw|+mh&attB7^lC73mulERZm&&SJyl7UL)@$WRjWr9mAF@3l&iMO24)K)~|^AYw)B0 zRr@F3=43lGofqg${sRG#I)t%$ImjE6{0V(-iu;rNHCPb9Xc}!Tg(TOpcXl@>5L7ob zGg%*p@`aAd|9C6^;(rD=Wf%Sa%*`iH*Y@i^1`#H+Ikfo9j%QLaoWg8Nl6I{>#Fu5+Ma~K zH>nh4nXUm25^J49K-%z3VQfsyAQo!uaZ)of4xy3Uf&s6%wH9V()PRB==Ij-5`Tlr? z!;^)B>W^q$9&%X~V$}BbLZ+<|ytPmscD{2> z8u8EC8_|&TD)p)8=yhHU-<3WXFJLkjP?y~H){*!59)3P+`4&>-mS>u(t96D-Y}5vYG#gAnT) z1YIqCc%ocfT&g2`d2p?9eS{y4zkhX6J*wgmg+~qaw$9GZ&xs5AUaNW1zg?x!s(HV% zKP{}3E9L1$MfQ!)PkDHFK=C;Eyb8A;{?#ytSFawQfIt(tSX-=!nB&^hfPfPL7ez<# z#&_qA{zB5ft}MIrv@~3q0>*J^)C2L5fq_AGE$?K+eR=tS#6&#^iC&YEQiu~he^Pw{ z;obDNoBH|IqzGi#_SZgWRfQHK`vCrk?VgU_gd)E2>^M7w(A6C^#&G|AcRk~ql(acF zh2R5PN=|p_91uEDnWA>eMnlLca^hpxPX8*$ywWq2&B6X2)rI?)j6PO1G^7BlyeW$y z`Hn5SWFEhO6-OPmJ|8=FYYk`$_>#mAJxKZcSA91~xsJfwq^t&lRg>f*p zEC2Q(&?+DfM@UG>7d!6$5|YWaLhfdQbOC)pLcPoc!>iq1SR7##?$o|wmN+zAYKN(| z>KFVX*w(J(>?~?p8*uh+!`#5I{_8XHo^k4KS3X+LzPy^dI8s?@IaZUM%>$ik!@(Tj zr@ehq!6GsZo$no_{Nxz)thzl8oL}$jNytY73GuMP^lYiJQZ$TxfCRk$R|eIe zM>(&llx$DsRqB#F?{*~hUtchue19Gbr9PpB+TtY+0N~Rf91>~!#Od>|M5UQCp97ZX z&zX!QsVvMQ{cUFe*8yNe2c)e(@AY5{R$^!JdqG0(5>+%$&P*T;#5XMZG0v>xzn$ z2X18cuy5dMd$%CF2=Wg;_nlbb^^Q9dl1-4m=GLkZeb7i*U7nhek@4=`JD?ZC#nwb9 zsaQB5Qu}}?E;>BCVdexiHItC#q5YcYCBXzJ0z-oVKu_;-bFt@1Oh-y=$WTnekH11M zG3A~{Sgv5t$4J@Ff{1aIfAsd)B*gz;)0)zp5fLc`l&K{}>7D*-5yUsIrvr2avY9b6C;qc&TBuI2^_V$ z0Rvivt_tkkfTJ4(0_`zZtf7#P)%rrVB-^+v9pu@LZ}|^lRl!mRSs>`yGaRwcVH+G9 zTo7Ux-NIc^5w~Os?Mb`aPz#{FkCuUppZ_?;M1{s>%|Ykl%}Fb6-irz5|&o|2FRxogp-n8Q1S^wG~ral&6i43yk^-%!})2^-{wKQ%P!)awU%WQx`>(l6m{C*6#4-2KE;=Bpq|IY#kHb!b#-CA)HB)Rsuh^h($F;Qv$EK6 zTAULH)mf4M^yxQRm9E)1>oB>4We(P{ucy1!0b9b_jJ|A9BY2BxGj_B-kUlmxc7A@I zTeD z59Slr(kxKi8pdQeZdVQG=(045YnWv~w`mAwrWx@(_B=k`ZLXKMI;Z}Sn(gxV4V^UN zh%ZoEwhum24u5KDdI&;CnHs+B`}X0Z2B4?_(GYftFI#E80M*)C;x@z@p-~#+8{RQ) zmue0r>Utf4JpZ)VAZI1lX0{t9F>l8ZU()Evsw36Xx0LwrJ3BjDTbE3_O`)~04OX1K z^aKk5KonQ-rCluORk%1kbER%+Y=jkd2!lHUPR;RvMQ9<8j*e=os*Ixc&$Y~{e?yK~ z8vwglcy2TCK!=K~50=<(G-GFH2Xb3NyYFo-8H}4h8Z=XyZL5efumUgYySZrT^95CC3nM``d<4 zB-Hbb*C(2e+he=6!M_NTm*JMjR9 z3gIYh<^J~z7a}h`JSNluC9mkk#OP=?P#2J)N&wId7A*r&(x`C0(g~+{$7aJ-tF}$g z{5BWbOd)DD4;-%ggBYNe8!zT02!`IxK)OA^0UD=TbzyUk%A20_xZ)udEA&VgV9{-@ zL%vsa?CtHrOZ=v^nJXzieN6YKpYg+u7{0A;%$rz#fiDFPEz=yKGGP7rn&$QFkn0T>Y_QMbJCerTsE42w|2ByHtRJY`f!jI_ z!3aWU_!#2Kl`AmJ6l7#AZEe&G{P>rnUhn@-9}R0+`-6JIS#n_5 zWX#O*?(T=qB&Xx={j;Q#Bbv%sSLSyIjypr~$Hl{g>gMd?f)!}L{CL;jHh$@ z8T`#NZD?qy6>Po5XVY3SuSqQ|bq&NRSU0%P6fWz&C$>DlusR5v3E*sr)(uK;IxH`& zd4oM@rVAOxHB%U8a0uix)e6luQTfU}oKODQ7eaml=J_<_!%)S_@-hi2DIY2m$kuOs zGZ9NZBcM(F#t;iEMu5^<9CxjhrTRBg)vAnEhO)q2i#|C%q8^S5HT1WNkCn^v*joTe z`EF!=opcCW9%cBT{BIxftNj0zG{@i-|A{L7k}(OCdaz~qJ|M`W{Wq}+)cx2GYp}qq3>q`s*lNjawu(PvUN(UW`f|d`z;Ie5iO9qnKvrzDDKH-aE{I5CL z+1YR=A8OfU4vyyT?gBXW3j$M(V=|PhhaVHFzd;5b7^K6>1RMeU&sX=;$-pYkGoSLn zGz3v<`JmAKznOz4Vh#mJTb^ORaN)wmi}PSY0A}?FS4@G&FYtUFyA5domDrk^8Z0*+ zDn-t)Q3}b<_veEGTJ#c39c(Q#-RaI$2c9Yltq_W_sti2T0oort6`=qi3GA?3sbzJy zwNKKDhZP7eK4A6i=F{GRh=d{q5uNA}<|U4=_zSEtqz~5S3rD78-_ww>I|B+Yv0J2I z-yQeo9*z0P5&Ir-D8sXecF;pOap$_ts53(s7TW)298;fWJu-6xxpAG6{_VjZF9e>cdAF4T36;r_t4nM$Ykn+o53F zfer_X0`MiSAKZ>}CS1$a1A;EPmO*e*HVoSuj@-F($M%c&(CDXc7Tb^KdLONQ1s8Dy zHB|0&nUBwE4i0;<9;i?(2^x`v;NkfA_&^JXbvLIEf=2c8(U?(6mTMpF&uJ8z$NKw| zl^&ek8YfTp80T^OOly2G25ZNWkdVw-+LxO2WPQ6nJ$7az{fus%~x>V8?$ggGC!ql2fe8X8`wT>Kp=>!QxxfCP~a*7 z6>-_w__o&n4h&I{i~!X%ynpX*{Z)40kW)C=*-XwjcAU}>nCax*I%IEPz&1Bt5uj)2 z6*Wq^^frT1jlid`uTS{J)2sOa7anofWs2F%oM3Pot0BmuNYp^pspT8CGe!D`t#EiD zPc0%Q=Z|}SJo|^&UW`Hjg_g6pfB)4FAwrR|;P6dEvTRt8qo=3moAqo=AxPKEOibcB zHQu42p#VLqYcLng@K|UqIYqp$Ep=@{dre>k7jc}1?`kn9B}L(|!xj0(#26WLw^t^2 zYeLxX%v?WRgL%u}sb<771CurIz#z`=2&>9#5En7ikD|<&~a11OsJ?>-% z&x7u(+S)A0585WGtD}{Jr^u@vww{)!11d>~7Vh&G_<+X$pgjp=V&m~o)^@09Dh(7J zj=r7E#lpNiEZGHkA-g6~dPu--z8|67k9G%gUcTO~4gqU#)w#d1YtG?0t@-L>3*Wvq zm2t4K89-`guvQF`0RjI0dIv&5BCjr=h|hcU<)*s&K;^;S;@rct+3`>j;RzjdGf-*W zUsMq?bb(6=?#ygb;Ps_$@E$Y-(z@Z96>q?~pG&j0*V>3p7!`G{LKBjoJQQv{*&GJ6 z8uD`2S|qk7ju#+jbZ@V6Nb;*VIP6^4{5RR+FZF=Ho!0xw6E$^pCnqPcUhTepCLol4 z)`-SFRi}4yW@ZL53R6?mOiXS24hKtPwTz66An1d02vL#9G>{kJ(7?%nOjv@#d%^0S zudaxQ5J8a+rh=r<6Oc`eWIjX*jHw7sz?Kl+J3`xu(PADhgs8;mad_0kXBiRi(addB za){A9-0zz=ZxS4U=4QgfTKLbxl({2Szj-XA!x3(0=Uu?!#pTL0CQ(R|sOqqA^qo$E zUy=yFBLV=KAJqlO8*mINQ5!tWWCM7@CzmPvX_%^<9HTo3rVdnfc;*E4!nqHSM*u8r zGuceFf2+BrMFdXhZ&8Q3!wD-GU6LTG5KuBABbyeOmT|O-?U&yqWXr#g7fb7|IYF{` zVawN#O>d&^K4r&u{43+3f3ocMv!Oa4LYlX?FNN0FoIk~m;pkT-!{ z&B0*|Ed)#BsR{lP4Hh}?qkaQd5ku$%by!tbQ?@Rr;y*_sj{RLE;BV7qOZd~Es4GJe z%sLtw8NpH^`1Sg!=2Fpr^i2)cIKH@9#A3~2x#5<7G@w) zuC5gj%7>2WRQ#an*it%#YECb6{1*PhT9#A1X{;PvM|vUn#(~9FP|X1H5)6QxPa|{} zy0SJ;HR0D2^2b$GRqe6Kzsx@!Y(@L7c(?sNz~SHuh&im_V-;QMm~Q|w2XC#ctiT37 zW%|Cp9yGVRDKtODfj>>`N~D;}_jjOf!?T(8)_b&6o4~24iUd2iHt;MFkuQ`zIHue+ zG~@!ih1qp$3_@ewJKo;pYZihm>OJNx_Pq55I z9ZyjftZZ?r4{%X7N1&J2HBfi(I{cnJd-V8m2G}jPZZ(3>L8G>|HgWK=ffvXHU(gu+SHn5HxHFvsYg{^YK{gOh zsWoqHZGk;Jv-^-Q3)O=O-kv{Nnjn{}LauRFr@57p;ZD zxbTVJ)(Z|y+#GfUZpCo)s7~3vu!Q(J{NK3eALAj}p4n18l%RW)fSg`1qf8m$=}LWu zxJHTvY|GbTn9odp4qY%DUY?$#{ByIjEe;3}sJk&*N4^{?=riIm5aHsYDCpB5JV?gU z{eHm~=y`v$g)ia%D4!5@uG|`cs1?MUnT{SkN=;1|ZwNOuI;#DzydNCv7a<|FuDyBC zpIu#DClMF^_H-dA?c>tzMFU8XgK%(g>d|2(2E4!Rm*2v&WwM+{jIj_+MzGZ7My1vb zmOqWxUt+*--8N#$X*T^|hV{R7!XKi{M$$hCygw2#|3f#VDl%>CW&p&1DyZ>1qIsY>^dqTbdzT~g; zF^rZ?J~jXUZEl&k6scL{_$Bk9zlZ{=|Iz1TF`oe zOfiDtO!oqSu#1ac=e>nhYq2?e;u5+4r@eEJhU)$II7tenlshS*8bc+Q5F!e>mN6Jc zlDmi)_mK)ULXF%aisUjeG_GY#?zuJOk~`&^doD8!#(Adi`t|#Lf9ssJzQ1+OTIZ~F ze)EqtGxoEe{h4R){XCz~`~BLpFhB!(LHFs(#6}&GZ0I8r7~T$9^_>hQIx9!~P=$r5 z>55dCg_!iM1u#Qi7huSxAE-wbzbG<2Pc?ZaHB@eO-e=nMPl9_Oj{oup-N(wad@bZ3 zngn#BNWMT!0`G7zHSG_)&qoGD@C>*yp&J223@jY!o6AOM+i^e;`Ul7l>)#{Mb`W>m;ePo zbobB1F(3}3WM{*JLB0UsAUwUia=`@;q+kGfNaX}nMh5xb+u7Lo36Q|}`KPMIB8 zPpN|xDG){l`uTy=H(V+q^smr*RzX_ybS{uLfhrbAp>M<*sR2PEvT=FA*Ab*fg_XQ^ zHg$A#Xq3y!%JvkFz)&a@@Wr%))fdnTDAJuX|H6Shfooi~%>Xra5*4Jm!4(*+qek_> znja(~dr*O@12T4hsTGhJrcSlj0F5k|*a8>EIzV68F2qT}-)eQ@;4vZ`SLJQ1chY;C z{sO1S4UdV6ngi2Y(oIl&23FXCW^h9XL`A^H80hMfS5%||C&rPh|KxN5X}TQ<-w=7* z$7NQKCIso;QT0e-ks&28FL<=-THQEC>vn|N>Ibfa3Ghpc9a(OWI8`AI@m@tGNQTi( zKw`>i$65M%JUjg+>i71NKYiZ`eBj7~`%LXu^jqBRS?+)F4b1vOJ#|jS*veh=U$M@_ zO~UR+Z{sw!AGy=30JcM>c+-V6lry@)R@?PBM%lwVdP-5l%3WW_U<>ap(BeJ6^Kck#Vb!{JIs;y}P%=wPml8Z@SIPs%Dzsqol@@3{?Y$-PN+kjKXo~a!5P{(adflbSu-wr+tq*j z(HVM`{G5oJ>pCoi*ntE7$Yc2qK^5)g8l*jQb>sC9I;)56Z(r$d1iX#Sd*>rQuOTQ4 zuJ+S#hDRpHa6+;2QfqE&Uw1OSs$VGteI)|#Bhnjt@;)_0kl}}_3U(=+_sqEPMsp-8~K6H1I8POCEfkM4+-<}#KZG+VzpsI3K z?;K2skKgqHBuB4Zy9SbZ^mTHGgM&kWpPyS_(VnJjSFc{k11V8|kn9F)9Z)WD2kXyS zx41EwM+l1C^&uK(bYVqZVYSDruwjA&A7wEzHZQ?I4ncRhp%eDxGC? zbye|A9{z^ouv6QiBwE~|B|@U<@jFowQ}2Hic`T!%yHq1=HZNLrd;-xAqaGG%cT6F= z7OVXztqW`xVt-@g(0P)Fr-hD48+tISXrNc@>$w~qL}(#vVsrSy7He))z=Kg9g}x%b zX-qL8Zh{5l--{UjRExHn!Da)ijLlR6i7&9Adi^mT-o{ zw?;6jAd5+Isz)NQSEfp|*O-9Gv!3Y{ty+ptTut6|j#_~vQmu(IyQl9Xyv^g8v!;nE zBw#ou2Rz-(w9YLCM2c|Oao~2QM?@+^4NCErB8FlNkcUOf_2b9m+&4#Jw|BXE=8P*s zH?|A8lN|1kU$}-K)60fK@qzBG!FGKCW}p3eggMl9x~#&3fBa(EFc%$nA1r(jT&k%| zxqnd4pQqwQgH#lD#bvj^SV+?9`DWmcUzNf0KWp#8p_<2d?B}sdkQ|PLA5vA>qiF^X z6^-7J=OC%UGMfn0ip!dEzrsHvEdmPq>Pwn>!c;+IIG-;Kpv^=MCwMx#5C5E-6>%%? z;)_T6<#?=ytFq=4oIlm8f}P_YxDooi1f@Vw7u=~Fx7xqF9h}<22#xtFai)x8n%w)m z3~DEr;^K?e`>^&Y;A!!zTW$&Zu9sWpcYH3K__S67EQ4=iaxDi1{s_)5%E==^a=Lv3 zT&VJJONqUu%#?o8^UVuA){=f*2{!bDk6mahR8WTZr>$i2NGXg>p!bZA$c>00N`lz$ zVH3|a4Go1JJo03ZUpabf0+11?jj>ACb%Ye$cv9k7rxJkl#>DwP!0`g1+WcLDy#sQs zfB1bR&E?`bu6|Zc7Dq&hn>C8EdY{eca~dfnjc9KwY>ubbqP4~F z*z)~o-1UUgQgXD205IYEEDQx94&qw;0#C$Q6&v!gFxAgFG7vZ6)7`K?FDnzqb zP_M(q6M^v~2G#&djmQ1DW=Cu^kY3bm_3|vP7`?@g?tEJ zqTY5!kU1g8CQGui=tA;!MXA-TjXaW$=6+NB9@tx14Oq6R_m#nFv!uCOYYn>=O0vxf zp=B)4F+!p(HiI2S|5-1+T}TtxrgBl*W9+3OzL3^>EeV7BYE?Tn&mAhai|+i*1cvhcY2jb#|+*dyyGN1M*>aHV-lbSL_C&1+k> z`Hh&MxL0u9eFIRqNi{4hN|H3x^5q7|hW*7_d84^yR z3RUiA(;IkF;>yxXQkN_DrAls7i6@vF)A8+YalJ$6AUBN62Wn35OAO=CzrMD`67(yk zyF5iiL(;_%@v}+^`npEJClX~8D=?-RR}%PioIq(*&oDO5CtlEZ_MDE;AW`MY%rM?& zj6g1NY`)2TF`_ajPfXtbTiD^g0yWn7QN$v(=wgISLrjS8Iu*Ivs<zYV8#T^JY#SD(o*e!2bn1=AllJua$^^K9-kAoFeYY5#L!RJ?J98qeLs&0N@b zS~n*)XzQ+upIRsiL7Ah2ymHV|CBhT(jd0y)Zph4&8mb=Ku3~=f2DDuNtr8ESy1Mi} zorr5VYc4E-v?7uz-xN~^Wof}72feis+F9f_+|;NVSEY}gJOXbaht61hWjV>mx3Jg_ zkhBmxysY|df|d3jxX2{T;cK#mC1*`Mft*e|OLnC)=ZQXhouzhYv-w6ljzWKT!AtbX zp0+*$7wf~O#f}$31bYY(Z!n#nm;R;yl_N(9G$5`rE6Ds2T;AQ{&T3=MN)7}Skv~>{dS>K#~mlK*47hJO21XgKA)RwdduG@AQ<1DV(|&DTO;n6@^eTO1c*1Ry;p3B&Ky^Pyq<$f%G}>L99N*g5Tz73w!0YSl-^6qJ zaAhI2znvgg!EA=_TpQ*u-)hKaQ#7U@E}cbhJROE~yY#%?=BH%zj?M5Bwxnna$IDWe zY$cEqyvKt>WLZQ9YewEaSC;0PjlYIF&{6kv??tu9B#GWO+;eTi$JqIjF%-Xwuxt#t zUKG8TQs?d9JT$;IOPhD-Hrz1FcQI(k?GmM(auz;$ZlgO&dz@xeB6#Maka{-qU|vYG z?H=03T?ZT|v1#_BM7dYcCKdid;rV93*(;H@JzkUy_R1$6sfbdv@lq;nf2m(cQThhs z7e3Lq>L**~5~ivs5~J!|xYgkM?(jfCB!ah5FlMvlwOLIY-R|%Zn;evA!Ah>C9_~dV z>QPsRj@vwSDf3e@ZtRG?_+CI#(@$6 zoznkvl5E4LcmdcyqktsxrlaGsC?T8&pB|F8>w&4%wpL!rwV{Q=?@vZmIgUBHELgnk zGitV;$X_5%=#L_gu^(O|elklks5D`vo;&O@fC<2+?Ntg&m^mrheG4xbG=j?U;xRvi zzBReGFWcx5x8W7-XQKj7b4;aE1=LC8_bI6Yy|*N#xuxFoE?Kld8(D`%2SUi2G2?7P z-lFE!UrRSO0y9$PP|KlbGI#9guSd60BNv2YJd7-B`-*G6b?SLRv7BuU&QvkqO|8%E zwAP!D^>nxodXi%pi;h-%U*GK^&T58hMq50tr=CLOTa4}O>oPp)>QuT)&P&ddUCBKW z%aSmO`7phbp_{L$YgxOvILlYzXv;-SWu0M@&mKuQ(;DD~Fr7OU>4Wd{Q&M_unR(m= zmbyg0%M&4)C7LMbH6hg($SZan^=iC)X4w3WgC+jJoG@3Sjs6W>i=KyBl}LVlo{Ndm z5~0SI?~dwt&HFhRoBJsU{8dYBSg^DIWR!nVJGX@1IUZI_cFrlR)Z?__Kx;$IOCSTG zz14Fh{3uDX6+62|(Fr71r@&3FcEG7 z9w1X`RlaocBnLCjQeGIJu zULQ~oVfDf{#*SWzhvul3%Jc>t_3r{{HM<*cFc)V=`-*LJ@&-1#n=o(_&K?>Itovhu z@~m&4Ye}$f;Gw>s9McCzb0h`m_ulu8^@r3*K!ngSajX7|sjQ!I;*3Zg8sv2_dpr1W zUno6D=Ep&jEVG#&7(%+!rnKF2Ep|NjYb<>`)svdTf_TqfkqsOQ(~y@RBj>4*ky&|h zvkE7huVq{n&Z;M?9}(AA*}3)2xLD@kq4Z#8I}{i=oC6_|rg+nJ%($kj+`Q6TRK)Fe z+*EE`s_vJ27%7nhD#3E-Sh}51rR;-!1>V;s=2bZya4mp=4lWzt^Od)purmf&-ZSUV zZ-~G3zAC)H8fDl)BDT;pbbk+-^d4GG3OXZ=19x!NrYQzhFn7fuheCjv1|O^U6=UPJ zd3fX`QAkGOsoZ(9l4s=mROoG_ga9~q(#;czey>IHL7AUDtZ(@C#03Ns_u8(VCKjlw}=5=*p&nyJUiDy z&EYc1#;LYfXOkjGAvx1suF*n@NkqBOohKkJ;;wwdgpm@s$y=-P5+rdKEcd~ew49vg zrg&+bKhG^?Ny%52qAby9`m0y4RfMj7iouq%Y@tWpb>++Ap?;J(O2DDUQ9^CEJbGzJgI5nWFbU0P_$`!r_`NuGv zEKq5$A5CG#zf&C}^oS}oa{FU{x58i1M0sKpt0T+zT|VdXN-i-lCi69PWqBF+LLQkX z94Lv%LdrXSzSohppq@Q1Cu2=J3LLwl)dGuxQ3mRQ?Sf}!z9LEUo6@!=ptXtl;+VudM9#@EH zfrw;qjGa^WHZa~4J;NMntUJfpQpS1_aw=@|xfWP~;&oLQbH!qSGHizYuo>w?ohR-& z_{Q8nq$`X=9aw^*wkueH0ejN3ahBM#yVP#1yBS~yItdbMjj=;wC{p#A)27?F%z1*AvIx;JlR zI@Ie8nF`W?RB(mQXt8K&Gy;`;at0e}KfG9qo`)&EppSoML zy*fHN;NdJ9hSAMjxp%JfvM^;cRj_C8s?ZBQzXVZLNOfzu!NkOK7tY*OP}sTS6f=GY z6O-Kt?1H=-wD==17BDe=EvAxdYt~)L4oF8W}%@_J4%#Vloa@T71Ke5aIMsNQw zZ2CYI3G&u?plbFdZIiw9SG0b`#V?N5GXeuv4KNTGy2&lw=1rHcRlno*$M@dP0Z+|2 zlwLIc%@SK>-$hf4Wa@3q<{!`2B z7p~rW{`Bnz642{j#K+f{@?MxLiuWkybU4Fwm;EE|uFFSUXM+JAXwt6e>yIIP+AODQ zN-bFk{7e^E83SqrHa!{*2ak(6D=EoPT!gHG^M&c| znSbq#Njnb9#B_n#eD5fN`GhivWJWq@2E5K=7^f(B%`zid8Go;HF~%-<1+J-^npM-t S*d-L>t**AAR=(!-d;bAdoUX6{ literal 0 HcmV?d00001 diff --git a/docs/data-transfer/diagrams/transfer_sequence_1.puml b/docs/data-transfer/diagrams/transfer_sequence_1.puml new file mode 100644 index 000000000..a159447ee --- /dev/null +++ b/docs/data-transfer/diagrams/transfer_sequence_1.puml @@ -0,0 +1,34 @@ +@startuml + +!define sokratesColor 66CCFF +!define platoColor CCFF99 +!define dapsColor FFFF99 +!define noteColor 9999FF + +actor User as "User" + +box Sokrates + participant SokratesControlPlane as "Control Plane" #sokratesColor + participant SokratesBackendService as "Backend Application" #sokratesColor + participant SokratesDataPlane as "Data Plane" #sokratesColor +end box + +box Plato + participant PlatoControlPlane as "Control Plane" #platoColor + participant PlatoDataPlane as "Data Plane" #platoColor +end box + +participant JsonPlaceHolder as "JsonPlaceHolder" + + +User -> PlatoControlPlane ++ : Create Asset +return 204 + +User -> PlatoControlPlane ++ : Create Policy +return 204 + +User -> PlatoControlPlane ++ : Create Contract Definition +return 204 + + +@enduml diff --git a/docs/data-transfer/diagrams/transfer_sequence_2.png b/docs/data-transfer/diagrams/transfer_sequence_2.png new file mode 100644 index 0000000000000000000000000000000000000000..ed2f4dd90855fddd2db0b7841d109524f5628246 GIT binary patch literal 29371 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 literal 0 HcmV?d00001 diff --git a/docs/data-transfer/diagrams/transfer_sequence_2.puml b/docs/data-transfer/diagrams/transfer_sequence_2.puml new file mode 100644 index 000000000..805bcbeec --- /dev/null +++ b/docs/data-transfer/diagrams/transfer_sequence_2.puml @@ -0,0 +1,28 @@ +@startuml + +!define sokratesColor 66CCFF +!define platoColor CCFF99 +!define dapsColor FFFF99 +!define noteColor 9999FF + +actor User as "User" + +box Sokrates + participant SokratesControlPlane as "Control Plane" #sokratesColor + participant SokratesBackendService as "Backend Application" #sokratesColor + participant SokratesDataPlane as "Data Plane" #sokratesColor +end box + +box Plato + participant PlatoControlPlane as "Control Plane" #platoColor + participant PlatoDataPlane as "Data Plane" #platoColor +end box + +participant JsonPlaceHolder as "JsonPlaceHolder" + +User -> SokratesControlPlane ++ : Request Contract Offers from Plato + SokratesControlPlane -> PlatoControlPlane ++ : IDS Description Request Message + return Description +return Contract Offers + +@enduml diff --git a/docs/data-transfer/diagrams/transfer_sequence_3.png b/docs/data-transfer/diagrams/transfer_sequence_3.png new file mode 100644 index 0000000000000000000000000000000000000000..b1d56ec6c24a1c7ec2913088ff82c1cda4350404 GIT binary patch literal 34859 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 WQZ!@|bb$L0DY1KZb3`A%{Qm%Q&K}?Z literal 0 HcmV?d00001 diff --git a/docs/data-transfer/diagrams/transfer_sequence_3.puml b/docs/data-transfer/diagrams/transfer_sequence_3.puml new file mode 100644 index 000000000..43707af36 --- /dev/null +++ b/docs/data-transfer/diagrams/transfer_sequence_3.puml @@ -0,0 +1,33 @@ +@startuml + +!define sokratesColor 66CCFF +!define platoColor CCFF99 +!define dapsColor FFFF99 +!define noteColor 9999FF + +actor User as "User" + +box Sokrates + participant SokratesControlPlane as "Control Plane" #sokratesColor + participant SokratesBackendService as "Backend Application" #sokratesColor + participant SokratesDataPlane as "Data Plane" #sokratesColor +end box + +box Plato + participant PlatoControlPlane as "Control Plane" #platoColor + participant PlatoDataPlane as "Data Plane" #platoColor +end box + +participant JsonPlaceHolder as "JsonPlaceHolder" + + +User -> SokratesControlPlane ++ : Negotiate Contract for Offer X +SokratesControlPlane --> User: Negotiation ID + SokratesControlPlane -> PlatoControlPlane ++ : IDS Contract Negotiation (simplified) + return Contract Agreement +deactivate SokratesControlPlane + +User -> SokratesControlPlane ++ : Request Negotiation by ID +return Contract Negotiation + +@enduml diff --git a/docs/data-transfer/diagrams/transfer_sequence_4.png b/docs/data-transfer/diagrams/transfer_sequence_4.png new file mode 100644 index 0000000000000000000000000000000000000000..43701e1ba1486f8dfa8171ba46b782bea61da0ea GIT binary patch literal 60428 zcmdSBcT|)6)-?(!Dk!LsfDI84NT>=bQUnX3x6nI^bfougLm&uJLKA7BcMy;c7C<^k zQKSljNbey1uIzU9IcL9P-1ml+iVk_=lQj@=A3J;1Sly;Qc<3vBq1T8l9m!z zCLuX^jf8}(C(Lm zta3|_NNKv*e6QUht6pWEmCt$?sw=I~QoA4%P#AZdtb4udR=TUp$jZR+Tn7uTFV|w( zNRRf#p89y3`IJ+BIi_1Zpv%^=$J(XRS3tf@%^}L+93y>Rbox=HQk^v^W4n35wC_L3 z8dzjI1P)!bJs>>)F}P?X)8LY>)R9x2($dNP;?1dEtHlw;+N@8W(pG=qJ@$=Tc=1hG zy-B>o`Jv?Jei^3BFZsFUY$MYG{L7Fsq6zsQxOv6P)8%o4oTx7%6OXcII2P{e&vQ4? z$$x#j_%w&_O-iQRCjl8{*QbnXG`w3SMk~21Z4X}M*mTYFJhcrH;GQ2VW$8MfrJ#m-x5pN-?ox>b{DzD1;td7h0RFu4VWAE`G|Kr!D%u59N0C zdqhFW;-Mw?y@I#Y#kkH>$L);`j*Dn@*Of=NpYY^7Phlw`Xa2K+?7O>usJy$vwYGvu z9_?fFd$_KF(9eLN$UFN#p0(9F@ebt)&N~&=SbzS~w&i1`{?XJXkLTXA z{<3Z7f-i$M*EY0En9_yu7Otho54RQcQtHT?861=Rw&SF}eW@&^_G?U<=ly#*bS^QK zK~A41`KiCe6dU50Jf*Sg!U^^V+6LszScjYpcL%bWzx^z*<1rIZrjnkWL(Y7QHVt4E zPcf|MXBvE75wtNI)*{`&UKbQrf?S$DyiE1groeFd?v$EO0tv}O5^3>Us;>I8aTK0Z z8rvVAT=eh5oUnY__XU-k8rH9^bMZ~TMP{c3XJnL2U@@!A118lqZY#GZbZGtketp9r ze=q5a>q^|Ysfd!{eU|jJ@EGCmKYlcO&5v*FzETwBH*J3<=rG^(YBE-s-;R%X9*BR= zzQUjW{T0bwu|(7#FC}hb|9JKMA>of#r$W^*($dm#S2-<~$2-vSF?>$*gXQT8amsYW zqe}eKQlO9R>f-VBmG=+c6GBfJYWK^Bnz@a>w-MbO?{10W<#Zvr8QUScJ<9^0mUx=C zSk`_(-TyMh_4}!W*#VbtAC9xbL#l<*x(T_=mnw>G&MuCAXdWWze8@v*3cqsdq%;

1za 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 e@4pd*Y SokratesControlPlane ++ : Request Negotiation by ID +return Contract Negotiation\n(containing Contract Agreement ID) + +User -> SokratesControlPlane ++ : Initiate Transfer with Agreement ID +SokratesControlPlane --> User : Transfer Process ID + SokratesControlPlane -> PlatoControlPlane ++ : IDS Data Transfer (simplified) +return +SokratesControlPlane -> SokratesBackendService ++ : Data Plane Endpoint + Token + SokratesBackendService -> SokratesDataPlane ++ : Request Data with Token + SokratesDataPlane -> PlatoDataPlane ++ : Request Data + PlatoDataPlane -> JsonPlaceHolder ++ : Request Data + return data + return data + return data + SokratesBackendService -> SokratesBackendService : Write Data to File +return ok +deactivate SokratesControlPlane + +User -> SokratesControlPlane ++ : Request Transfer Process by ID +return Transfer Process + +@enduml diff --git a/docs/data-transfer/diagrams/transfer_sequence_5.png b/docs/data-transfer/diagrams/transfer_sequence_5.png new file mode 100644 index 0000000000000000000000000000000000000000..080c26335df9d5ab8a381d22594c99bcd9492f6d GIT binary patch literal 21812 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 IbxsLQ0Pl#yT>t<8 literal 0 HcmV?d00001 diff --git a/docs/data-transfer/diagrams/transfer_sequence_5.puml b/docs/data-transfer/diagrams/transfer_sequence_5.puml new file mode 100644 index 000000000..b64f2222b --- /dev/null +++ b/docs/data-transfer/diagrams/transfer_sequence_5.puml @@ -0,0 +1,27 @@ +@startuml + +!define sokratesColor 66CCFF +!define platoColor CCFF99 +!define dapsColor FFFF99 +!define noteColor 9999FF + +actor User as "User" + +box Sokrates + participant SokratesControlPlane as "Control Plane" #sokratesColor + participant SokratesBackendService as "Backend Application" #sokratesColor + participant SokratesDataPlane as "Data Plane" #sokratesColor +end box + +box Plato + participant PlatoControlPlane as "Control Plane" #platoColor + participant PlatoDataPlane as "Data Plane" #platoColor +end box + +participant JsonPlaceHolder as "JsonPlaceHolder" + +User -> SokratesBackendService ++ : Get File Content +return data + + +@enduml diff --git a/edc-controlplane/README.md b/edc-controlplane/README.md index 794ff6030..7c0fe39b5 100644 --- a/edc-controlplane/README.md +++ b/edc-controlplane/README.md @@ -7,472 +7,6 @@ While the **Data Plane** handles the actual Data Transfer, the **Control Plane** - Contract Offering & Contract Negotiation - Data Transfer Coordination / Management -# Control Plane Setup - -This chapter is about integration the Control Plane with the Azure KeyVault and IDS DAPS. - - ----- - -**Please note** -
-The documentation operates the Azure Key Vault using the Azure CLI. Please visit the Microsoft has documented to learn how the Azure CLI is installed. -
-https://docs.microsoft.com/en-us/cli/azure/install-azure-cli - ----- - -## Azure Key Vault Setup - -The Eclipse Dataspace Connector requires a key vault, where it can store and retrieve secrets and certificates.
-At the time of writing the only key vault, the EDC is supporting, is the Azure Key vault. - -### 1. Register a new App - -In the Azure Portal: - -1. Open **App registrations** page and create a new app -2. Choose a unique name and click _register_ -3. The new App has a **Client ID** (also called Application ID). This ID must be configured in the connector - setting `edc.vault.clientid` - -For further information have a look at the official documentation
-https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app - -### 2. Create App Secret - -In the Azure Portal: - -1. Open the page of the newly created app -2. On the left side select _certificates & secrets_ -3. Create a new _client secret_ -4. Add the secret value to the connector setting `edc.vault.clientsecret` - -For further information have a look at the official documentation
-https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app#add-credentials - -### 3. Create Azure Key Vault - -In the Azure Portal: - -1. Open **Key vaults** page and create a new Azure Key Vault -2. Fill out the mandatory fields, choose a unique key vault name and click _review + create_ -3. The chosen name must be configured in the connector setting `edc.vault.name` -4. The directory ID of the key vault (also called tenant ID) must be configured in the `edc.vault.tenantid` - -For further information have a look at the official documentation
-https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app - -### 4. Provide the newly created App access to the Key Vault - -In the Azure Portal: - -1. Open the page of the newly created key vault -2. On the left side select _access policies_ -3. Create new _access policy_ and select the appropriate permissions -5. Under _select principal_ add the newly created app from step 1 -6. Click _add_ - -For further information have a look at the official documentation
-https://docs.microsoft.com/en-us/azure/key-vault/general/assign-access-policy?tabs=azure-portal - -### 5. Summary - -The complete Azure Key Vault configuration in the EDC should look something like this - -```properties -edc.vault.tenantid= -edc.vault.clientid= -edc.vault.clientsecret= -edc.vault.name= -``` - -Please note that the key vault could also be configured using the `edc.vault.certificate`, which is not covered by this -documentation. - -## IDS DAPS Setup - -The Eclipse Dataspace Connector is able to retrieve an identity token from the IDS DAPS. This token is part of all IDS -messages. - -The DAPS application requires a certificate from the Eclipse Dataspace Connector. This certificate may then be used by -the EDC connector to retrieve its identity token and prove its identity to other connectors. - -When writing this guidance these step were tested out using the open source omejdn DAPS of the Fraunhofer -AISEC ([GitHub](https://github.com/International-Data-Spaces-Association/omejdn-daps)). - ---- -**Client Unknown Issue:** -
-Pleaste know that, in the past there were some DAPS issues with the client certificate. If you see this error, please contact the DAPS Team so that they can support you. - -```json -{ "error":"invalid_client","error_description":"Client unknown"} -``` ---- - -### (optional) 1. Key / Certificate Generation - -In the first step generate a PKSC8 Key and the corresponding certificate. This step is optional, because it might be possible that this key is provided by the DAPS maintainers. - -```bash -# Generate Private Key -openssl genpkey -out daps_key.pem -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -``` - -```bash -# Generate Certificate -openssl req -new -x509 -key daps_key.pem -nodes -days 365 -out daps_cert.pem -``` - -In case the certificate was not generated by the DAPS maintainers themself, it needs to be send to them. After the DAPS maintainers have registered a new client, it gets a unique client ID. Configure the DAPS client ID in `edc.oauth.client.id`. - -### 2. Azure Key Vault Upload - -```bash -# Upload Private Key -az keyvault secret set --name my-daps-key --vault-name tree512 --file daps_key.pem -``` - -```bash -# Upload Certificate -az keyvault secret set --name my-daps-cert --vault-name tree512 --file daps_cert.pem -``` - -### 3. EDC Configuration - -Configure the private key alias in the `edc.oauth.private.key.alias`, and the certificate alias in `edc.oauth.public.key.alias`. - -In this example it would be -```properties -edc.oauth.private.key.alias=my-daps-key -edc.oauth.public.key.alias=my-daps-cert -``` - -Additionally these properties must be requested from the DAPS maintainers: - -- DAPS Connector Client ID must be configured in `edc.oauth.client.id` -- DAPS Token URL must be configured in `edc.oauth.token.url` -- DAPS JWKS URL must be configured in `edc.oauth.provider.jwks.url` -- Token Audience must be configured in `edc.oauth.provider.audience` - -### 4. Summary - -The complete EDC configuration could look like this: - -```properties -# DAPS Properties -edc.oauth.token.url=http://localhost:4567/token -edc.oauth.client.id= -edc.oauth.provider.audience= -edc.oauth.provider.jwks.url=http://localhost:4567/.well-known/jwks.json -# OAUTH Properties -edc.oauth.private.key.alias= -edc.oauth.public.key.alias= -``` - -## Dataplane Setup - -Configure the control plane so that is able to communicate with an EDC data plane instance. - ----- - -**Please note** -
-This chapter contains only the mandatory data plane configuration. - ----- - -### Encryption - -The communication between dataplane and controlplane is encrypted and needs some keys in the key vault and configuration in the EDC. - -#### Private Key - -```bash -# Generate Private Key -openssl genpkey -out my-data-plane-private-key.pem -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -``` - -```bash -# Upload Private Key -az keyvault secret set --name my-data-plane-private-key --vault-name tree512 --file my-data-plane-private-key.pem -``` - -```properties -# Configuration -edc.transfer.proxy.token.signer.privatekey.alias=my-data-plane-private-key -``` - -#### Public Key - -```bash -# Generate Public Key -openssl rsa -in my-data-plane-private-key.pem -out my-data-plane-public-key.pem -pubout -outform PEM -``` - -```bash -# Upload Public Key -az keyvault secret set --name my-data-plane-public-key --vault-name tree512 --file my-data-plane-public-key.pem -``` - -```properties -# Configuration -edc.transfer.proxy.token.verifier.publickey.alias=my-data-plane-public-key -``` - -# EDC Domain - -This chapter gives a short overview of the EDC domain. The idea is to get a basic understanding of the domain objects and their roles.
-The complete EDC documentation can be found in the official open source [EDC GitHub Repository](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector). - ----- - -**Please note** -
-If you have already used the Fraunhofer Dataspace Connector, you are probably familiar with the IDS Domain Model. Don't confuse the IDS Domain Model with the EDC Domain Model. -The terms that are used in both models are pretty similar, but often don't represent the same thing. In the context of the EDC documentation it’s always safe to assume that the EDC Domain Model is in place, as the IDS model is only used when two Eclipse Dataspace COnnectors are exchanging messages. - ----- - -## Contract Offer Exchange - -In the EDC it’s not possible to create a _ContractOffer_ directly. The _ContractOffer_ is generated on the fly when -another connector asks about the _ContractOfferCatalog_. A _ContractOffer_ will only ge persistent when it becomes part of a -_ContractNegotiation_. - -A _ContractDefinition_ defines how many _ContractOffers_ should be generated and how the _Policy_ should -look like. - -The _ContractDefinition_ consists of a - -- _ContractPolicy_, that describes in EDC ODRL terms how the policy for a _ContractOffer_ should look like. -- _AccessPolicy_, that describes in EDC ODRL terms who is able to see a _ContractOffer_. But the content of this policy - will not be part of the _ContractOffer_. -- _AssetSelector_, that defines for which _Assets_ a _ContractOffer_ should be generated. An _Asset_ describes the data - itself that may be offered/transferred and is comparable to the IDS triplet of IDS-Resource, IDS-Representation, IDS - Artifact. - -So the ContractDefinition looks somewhat like this: - - - -![test](http://www.plantuml.com/plantuml/png/PSvD2a9130FWVKyn-tU99-fUU2SOEbKAOqUQ28fuTnL_DYxpGK9ci2RFnowYlG9bFO9PbHlRUpXzHBd9j30z3iMRJBlHNQ-bgXhm3Z_KJ_dBAy2uM3VboEtbb0Qy5l57SXUHsQ8zhpm0) - -When another connector asks the EDC about its _ContractOffers_ it: - -- Checks the connector identity -- Finds all the _ContractDefinitions_ that have a passing AccessPolicy -- Finds for each passing _ContractDefinition_ the corresponding _Assets_ -- Generates a new _ContractOffer_ for each _Asset_. The policy of the _ContractOffer_ is described in the ContractPolicy of the _ContractDefinition_. -- Maps the content of the _ContractOffer_ into the IDS domain and sends an IDS-ContractOffers to the other connector. -- The other connector then maps the IDS-ContractOffers back into its EDC domain and can then processes the _ContractOffer_. - -# Data Management API - -The documentation of the Data Management API can be found in the official open -source [EDC GitHub Repository](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector). - -The complete Eclipse Dataspace Connector API is described in the [EDC Open API Specification](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/blob/main/resources/openapi/openapi.yaml). Please be aware that this specification contains all APIs, that are implemented in the open source repository. The extensions, that implement those APIs, might not be part of the Control- and/or Data-Plane applications in this repository. Additionally, depending on the extension configuration, the documented paths might be only reachable using the configured ports. - -## Contract Offer Exchange - ----- - -**Please note**
-This chapter showcases the contract offer exchange between two connectors. It should function as starting point when working the Eclipse Dataspace Connector API. For a more detailed explanation of the various topics, that are touched in this section, please consolidate the official documentation in the [EDC GitHub Repository](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector). - ----- - -As described in the chapter about the EDC domain, the following resources must be created at the data provider: - -- **Asset** (& DataAddress), describing the data and how it can be transferred -- **Policy**, as Contract- and/or AccessPolicy of the _ContractDefinition_ -- **ContractDefinition**, for the contract offer generation - -### 0. Calling the Data Management API - -The Data Management API is secured with an API key. The value of this key can be configured in `edc.api.auth.key` and -should then be passed in the header as `X-API-Key: `. -Additionally, most or all of the API methods accept only JSON content, therefore adding `Content-Type: application/json` -to the header for most of the calls is recommended. - -### 1. Create Asset using Data Mgmt API - -#### Bash Script - -```bash -# Variables (please update before running the script) -__connectorUrl=http://localhost:8181 -__dataMgmtPath=data-mgmt -__apiKey=X-Api-Key -__apiKeyValue=pwd -__assetId=1 -__assetDescription="Demo Asset" -__assetDataEndpoint="https://jsonplaceholder.typicode.com/todos/3" - -__asset="{ - \"asset\": { - \"properties\": { - \"asset:prop:id\": \"$__assetId\", - \"asset:prop:description\": \"$__assetDescription\" - } - }, - \"dataAddress\": { - \"properties\": { - \"type\": \"HttpProxy\", - \"endpoint\": \"$__assetDataEndpoint\" - } - } - }" - -# Call Data Management API -curl -X POST "$__connectorUrl/$__dataMgmtPath/assets" --header "$__apiKey: $__apiKeyValue" --header "Content-Type: application/json" --data "$__asset" -``` - -#### Bash Parameters - -| Name | Description | -| -------------------- | ----------------------------------------------------------------------------------------- | -| $__connectorUrl | URL of the Connector with the Data Management API port configured in `web.http.data.port` | -| $__dataMgmtPath | Path of the Data Management API as configured in `web.http.data.path` | -| $__apiKey | Should always be _X-Api-Key_ for the Data Management API | -| $__apiKeyValue | The API Key Value as configured in `edc.api.auth.key` | -| $__assetId | Unique identifier of the asset | -| $__assetDescription | Description of the asset | -| $__assetDataEndpoint | Endpoint that might be used when data is transferred. Irrelevant in this context / sample | - -#### Control Call - -Get Asset - -```bash -curl -X GET "$__connectorUrl/$__dataMgmtPath/assets/$__assetId" --header "$__apiKey: $__apiKeyValue" --header "Content-Type: application/json" | jq -``` - -### 2. Create Policy - -**Please be aware that the following policy make the data offer public for everyone and should be used with caution outside of this showcase!** - -Create a policy that can be used by the __ContractDefinition__. As the same policy may be used as contract- and access policy of the ContractDefinition, creating only one policy for both cases is totally fine for this demo. - -#### Bash Script - -```bash -# Variables -__connectorUrl=http://localhost:8181 -__dataMgmtPath=data-mgmt -__apiKey=X-Api-Key -__apiKeyValue=pwd -__policyId=1 - -__publicPolicy=" -{ - \"uid\": \"$__policyId\", - \"prohibitions\": [], - \"obligations\": [], - \"permissions\": [ - { - \"edctype\": \"dataspaceconnector:permission\", - \"action\": { - \"type\": \"USE\" - }, - } - ] -}" - -# Call Data Mgmt API -curl -X POST "$__connectorUrl/$__dataMgmtPath/policies" --header "$__apiKey: $__apiKeyValue" --header "Content-Type: application/json" --data "$__publicPolicy" -``` - -#### Bash Parameters - -| Name | Description | -| --------------- | ----------------------------------------------------------------------------------------- | -| $__connectorUrl | URL of the Connector with the Data Management API port configured in `web.http.data.port` | -| $__dataMgmtPath | Path of the Data Management API as configured in `web.http.data.path` | -| $__apiKey | Should always be _X-Api-Key_ for the Data Management API | -| $__apiKeyValue | The API Key Value as configured in `edc.api.auth.key` | -| $__policyId | Unique identifier of the policy. | - -#### Control Call - -Get Policy - -```bash -curl -X GET "$__connectorUrl/$__dataMgmtPath/policies/$__policyId" --header "$__apiKey: $__apiKeyValue" --header "Content-Type: application/json" | jq -``` - -### 3. Create Contract Definition - -The following uses the previously created public policy make the data offer available for everyone. - -#### Bash Script - -```bash -# Variables -__connectorUrl=http://localhost:8181 -__dataMgmtPath=data-mgmt -__apiKey=X-Api-Key -__apiKeyValue=pwd -__contractDefinitionId=1 -__policyId=1 -__assetId=1 - -__publicContractDefinition=" - { - \"id\": \"$__contractDefinitionId\", - \"accessPolicyId\": \"$__policyId\", - \"contractPolicyId\": \"$__policyId\", - \"criteria\": [ - { - \"left\": \"asset:prop:id\", - \"op\": \"=\", - \"right\": \"$__assetId\" - } - ] - }" - -# Call Data Mgmt API -curl -X POST "$__connectorUrl/$__dataMgmtPath/contractdefinitions" --header "$__apiKey: $__apiKeyValue" --header "Content-Type: application/json" --data "$__publicContractDefinition" -``` - -#### Bash Parameters - -| Name | Description | -| ----------------------- | ----------------------------------------------------------------------------------------- | -| $__connectorUrl | URL of the Connector with the Data Management API port configured in `web.http.data.port` | -| $__dataMgmtPath | Path of the Data Management API as configured in `web.http.data.path` | -| $__apiKey | Should always be _X-Api-Key_ for the Data Management API | -| $__apiKeyValue | The API Key Value as configured in `edc.api.auth.key` | -| $__contractDefinitionId | Unique identifier of the contract definition. | -| $__policyId | Unique identifier of the policy. Must be the same ID as in step 2. | -| $__assetId | Unique identifier of the asset. Must be the same ID as in step 1. | - -#### Control Call - -Get Contract Definition - -```bash -curl -X GET "$__connectorUrl/$__dataMgmtPath/contractdefinitions/$__contractDefinitionId" --header "$__apiKey: $__apiKeyValue" --header "Content-Type: application/json" | jq -``` - -# Secure your connector - -## API Security - The only API that is protected by some kind of security mechanism is the Data Management API. At the time of writing this is done by a simple API key. The key value must be configured in `edc.api.auth.key`. All requests to the Data Management API must have `X-Api-Key` header with the key value. @@ -483,7 +17,7 @@ curl -X GET --header "X-Api-Key: " # Known Control Plane Issues -Please have look at all the open issues in the open source repository. The list below might not be maintained well and +Please have a look at the open issues in the open source repository. The list below might not be maintained well and only contains the most important issues. EDC Github Repository https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues @@ -494,23 +28,28 @@ EDC commit the Product-EDC uses. --- -## Contract negotiation not working when `web.http.ids.path` is configured/changed +**Configuration** +- Contract negotiation not working when `web.http.ids.path` is configured/changed ([issue](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1249)) + - **Workaround:** Don't configure `web.http.ids.path`, so that the default path is used. -https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1249 +**Data Management API** +- Contract negotiation not working when initiated with policy id ([issue](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1251)) + - **Workaround:** The DataManagement API can also initiate a contract negotiation using the actual policy object. -**Workaround:** -Don't configure `web.http.ids.path`, so that the default path is used. +- Contract-Offer-Receiving-Connectors must also pass the ContractPolicy of the ContractDefinition before receiving offers([issue](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1331)) -## Contract negotiation not working when initiated with policy id +- Deletion of Asset becomes impossible when Contract Negotiation exists([issue](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1403)) + - **Workaround:** Delete Asset from DB manually. Be aware that deleting Assets, that are part of a ContractNegotiation or ContractAgreement, may corrupt the connector instance! -https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1251 +- Deletion of Policy becomes impossible when Contract Definition exists([issue](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1410)) + - **Workaround:** Delete Policy from DB manually. Be aware that deleting Policies, that are part of a ContractDefinition, ContractNegotiation or ContractAgreement, may corrupt the connector instance! -**Workaround:** -The DataManagement API can also initiate a contract negotiation using the actual policy object. +**Other** +- Non-IDS-Transformable-ContractDefinition causes connector to be unable to send out self-descriptions/catalogs([issue](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1265)) + - **Workaround:** Delete non-transformable ContractDefinition or Policy. -## Non-IDS-Transformable-ContractDefinition causes connector to be unable to send out self-descriptions/catalogs +**Security** +- DataAddress is passed unencrypted from DataProvider to DataConsumer ([issue](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1504)) + - **Workaround:** Use only test data! -https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1265 -**Solution** -Delete non-transformable ContractDefinition or Policy. From 4e0cc67d96479c19000193e22b353486431137c5 Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Fri, 8 Jul 2022 09:46:24 +0200 Subject: [PATCH 160/433] Add documentation to deploy EDC settings as k8 secrets (#253) Signed-off-by: Dominik Pinsel Co-authored-by: Florian Rusch (ZF Friedrichshafen AG) --- deployment/helm/README.md | 4 + edc-controlplane/README.md | 15 ++- edc-dataplane/README.md | 15 +++ .../deployment/helm/all-in-one/Chart.yaml | 22 ++--- .../helm/all-in-one/templates/_helpers.tpl | 62 +++++++++++++ .../helm/all-in-one/templates/secret.yaml | 91 +++++++++++++++++++ .../deployment/helm/all-in-one/values.yaml | 66 ++++---------- 7 files changed, 215 insertions(+), 60 deletions(-) create mode 100644 edc-dataplane/README.md create mode 100644 edc-tests/src/main/resources/deployment/helm/all-in-one/templates/_helpers.tpl create mode 100644 edc-tests/src/main/resources/deployment/helm/all-in-one/templates/secret.yaml diff --git a/deployment/helm/README.md b/deployment/helm/README.md index 111df59d1..1f453a962 100644 --- a/deployment/helm/README.md +++ b/deployment/helm/README.md @@ -11,3 +11,7 @@ To generate chart README.md files from its respective values.yaml file we use th ```shell docker run --rm --volume "$(pwd):/helm-docs" -u $(id -u) jnorwood/helm-docs:v1.10.0 ``` + +# Confidential EDC Settings + +Some EDC settings should better not be part of the actual deployment (like credentials to the database or the vault). Therefore, it is possible to deploy a secret with these confidential settings beforehand, and make it known to the deployment by setting the secret name in the `envSecretName` field of the deployment. \ No newline at end of file diff --git a/edc-controlplane/README.md b/edc-controlplane/README.md index 7c0fe39b5..280d90230 100644 --- a/edc-controlplane/README.md +++ b/edc-controlplane/README.md @@ -15,6 +15,19 @@ Example: curl -X GET --header "X-Api-Key: " ``` +## Security + +### Confidential Settings + +Please be aware that there are several confidential settings, that should not be part of the actual EDC configuration file. + +Some of these confidential settings are +- Vault credentials +- Data Management API key +- Database credentials + +As it is possible to configure EDC settings via environment variables, one way to do it would be via Kubernetes Secrets. For other deployment scenarios than Kubernetes equivalent measures should be taken. + # Known Control Plane Issues Please have a look at the open issues in the open source repository. The list below might not be maintained well and @@ -51,5 +64,3 @@ EDC commit the Product-EDC uses. **Security** - DataAddress is passed unencrypted from DataProvider to DataConsumer ([issue](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1504)) - **Workaround:** Use only test data! - - diff --git a/edc-dataplane/README.md b/edc-dataplane/README.md new file mode 100644 index 000000000..6efe7366f --- /dev/null +++ b/edc-dataplane/README.md @@ -0,0 +1,15 @@ +# Data Plane + +The Eclipse Dataspace Connector consists of a **Control Plan** and a **Data Plane** Application. +While the **Control Plane** managing several data transfers, the **Data Plane** is responsible for doing the actual transfer. Like this data is never routed through the control plane itself und must always pass the data plane. + +## Security + +### Confidential Settings + +Please be aware that there are several confidential settings, that should not be part of the actual EDC configuration file (e.g. the Vault credentials). + +As it is possible to configure EDC settings via environment variables, one way to do it would be via Kubernetes Secrets. For other deployment scenarios than Kubernetes equivalent measures should be taken. + +# Known Data Plane Issues +Please have a look at the open issues in the open source repository: [EDC Github Repository](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues) diff --git a/edc-tests/src/main/resources/deployment/helm/all-in-one/Chart.yaml b/edc-tests/src/main/resources/deployment/helm/all-in-one/Chart.yaml index 9deeb5792..49495a47e 100644 --- a/edc-tests/src/main/resources/deployment/helm/all-in-one/Chart.yaml +++ b/edc-tests/src/main/resources/deployment/helm/all-in-one/Chart.yaml @@ -29,48 +29,48 @@ dependencies: - name: ids-daps version: 0.0.1 repository: "file://../omejdn" - alias: ids-daps + alias: idsdaps # PLATO CONNECTOR - name: edc-controlplane version: ">=0.0.1" repository: "file://../../../../../../../deployment/helm/edc-controlplane" - alias: plato-edc-controlplane + alias: platoedccontrolplane - name: edc-dataplane version: ">=0.0.1" repository: "file://../../../../../../../deployment/helm/edc-dataplane" - alias: plato-edc-dataplane + alias: platoedcdataplane - name: backend-application version: ">=0.0.1" repository: "file://../backend-application" - alias: plato-backend-application + alias: platobackendapplication - name: vault version: 0.20.0 repository: https://helm.releases.hashicorp.com - alias: plato-vault + alias: platovault - name: postgresql version: 11.2.4 repository: https://charts.bitnami.com/bitnami - alias: plato-postgresql + alias: platopostgresql # SOKRATES CONNECTOR - name: edc-controlplane version: ">=0.0.1" repository: "file://../../../../../../../deployment/helm/edc-controlplane" - alias: sokrates-edc-controlplane + alias: sokratesedccontrolplane - name: edc-dataplane version: ">=0.0.1" repository: "file://../../../../../../../deployment/helm/edc-dataplane" - alias: sokrates-edc-dataplane + alias: sokratesedcdataplane - name: backend-application version: ">=0.0.1" repository: "file://../backend-application" - alias: sokrates-backend-application + alias: sokratesbackendapplication - name: vault version: 0.20.0 repository: https://helm.releases.hashicorp.com - alias: sokrates-vault + alias: sokratesvault - name: postgresql version: 11.2.4 repository: https://charts.bitnami.com/bitnami - alias: sokrates-postgresql + alias: sokratespostgresql diff --git a/edc-tests/src/main/resources/deployment/helm/all-in-one/templates/_helpers.tpl b/edc-tests/src/main/resources/deployment/helm/all-in-one/templates/_helpers.tpl new file mode 100644 index 000000000..782767de0 --- /dev/null +++ b/edc-tests/src/main/resources/deployment/helm/all-in-one/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "aio.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "aio.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "aio.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "aio.labels" -}} +helm.sh/chart: {{ include "aio.chart" . }} +{{ include "aio.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "aio.selectorLabels" -}} +app.kubernetes.io/name: {{ include "aio.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "aio.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "aio.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/edc-tests/src/main/resources/deployment/helm/all-in-one/templates/secret.yaml b/edc-tests/src/main/resources/deployment/helm/all-in-one/templates/secret.yaml new file mode 100644 index 000000000..8e27ac060 --- /dev/null +++ b/edc-tests/src/main/resources/deployment/helm/all-in-one/templates/secret.yaml @@ -0,0 +1,91 @@ +--- + +# When deploying an EDC, there are various configuration parameters that should not be part of the configuration file. +# To not serve a bad example, this demo will set some settings using secrets as well. In a productive environment this secrets would probably be deployed independently. + +{{- $plato_psql_password := .Values.platopostgresql.auth.password -}} +{{- $plato_api_auth_key := "password" -}} +{{- $plato_vault_token := .Values.platovault.server.dev.devRootToken -}} +{{- $sokrates_psql_password := .Values.sokratespostgresql.auth.password -}} +{{- $sokrates_api_auth_key := "password" -}} +{{- $sokrates_vault_token := .Values.sokratesvault.server.dev.devRootToken -}} + +--- + +apiVersion: v1 +kind: Secret +metadata: + name: aio-plato-control-secret + namespace: {{ .Release.Namespace | default "default" | quote }} + labels: + {{- include "aio.labels" . | nindent 4 }} +type: Opaque +stringData: + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/api/auth-tokenbased + EDC_API_AUTH_KEY: {{ $plato_api_auth_key | toString | quote }} + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/asset-index-sql + EDC_DATASOURCE_ASSET_PASSWORD: {{ $plato_psql_password | toString | quote }} + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/contract-definition-store-sql + EDC_DATASOURCE_CONTRACTDEFINITION_PASSWORD: {{ $plato_psql_password | toString | quote }} + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/contract-negotiation-store-sql + EDC_DATASOURCE_CONTRACTNEGOTIATION_PASSWORD: {{ $plato_psql_password | toString | quote }} + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/policy-store-sql + EDC_DATASOURCE_POLICY_PASSWORD: {{ $plato_psql_password | toString | quote }} + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/transfer-process-store-sql + EDC_DATASOURCE_TRANSFERPROCESS_PASSWORD: {{ $plato_psql_password | toString | quote }} + # see extension https://github.com/catenax-ng/product-edc/tree/develop/edc-extensions/hashicorp-vault + EDC_VAULT_HASHICORP_TOKEN: {{ $plato_vault_token | toString | quote }} + +--- + +apiVersion: v1 +kind: Secret +metadata: + name: aio-plato-data-secret + namespace: {{ .Release.Namespace | default "default" | quote }} + labels: + {{- include "aio.labels" . | nindent 4 }} +type: Opaque +stringData: + # see extension https://github.com/catenax-ng/product-edc/tree/develop/edc-extensions/hashicorp-vault + EDC_VAULT_HASHICORP_TOKEN: {{ $plato_vault_token | toString | quote }} + +--- + +apiVersion: v1 +kind: Secret +metadata: + name: aio-sokrates-control-secret + namespace: {{ .Release.Namespace | default "default" | quote }} + labels: + {{- include "aio.labels" . | nindent 4 }} +type: Opaque +stringData: + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/api/auth-tokenbased + EDC_API_AUTH_KEY: {{ $sokrates_api_auth_key | toString | quote }} + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/asset-index-sql + EDC_DATASOURCE_ASSET_PASSWORD: {{ $sokrates_psql_password | toString | quote }} + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/contract-definition-store-sql + EDC_DATASOURCE_CONTRACTDEFINITION_PASSWORD: {{ $sokrates_psql_password | toString | quote }} + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/contract-negotiation-store-sql + EDC_DATASOURCE_CONTRACTNEGOTIATION_PASSWORD: {{ $sokrates_psql_password | toString | quote }} + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/policy-store-sql + EDC_DATASOURCE_POLICY_PASSWORD: {{ $sokrates_psql_password | toString | quote }} + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/transfer-process-store-sql + EDC_DATASOURCE_TRANSFERPROCESS_PASSWORD: {{ $sokrates_psql_password | toString | quote }} + # see extension https://github.com/catenax-ng/product-edc/tree/develop/edc-extensions/hashicorp-vault + EDC_VAULT_HASHICORP_TOKEN: {{ $sokrates_vault_token | toString | quote }} + +--- + +apiVersion: v1 +kind: Secret +metadata: + name: aio-sokrates-data-secret + namespace: {{ .Release.Namespace | default "default" | quote }} + labels: + {{- include "aio.labels" . | nindent 4 }} +type: Opaque +stringData: + # see extension https://github.com/catenax-ng/product-edc/tree/develop/edc-extensions/hashicorp-vault + EDC_VAULT_HASHICORP_TOKEN: {{ $sokrates_vault_token | toString | quote }} diff --git a/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml b/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml index d7b76163b..d1b092df3 100644 --- a/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml +++ b/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml @@ -1,9 +1,10 @@ --- + # Default values for all-in-one deployment. # This is a YAML-formatted file. # Declare variables to be passed into your templates. -ids-daps: +idsdaps: fullnameOverride: "ids-daps" connectors: - id: &sokratesDapsClientId E7:07:2D:74:56:66:31:F0:7B:10:EA:B6:03:06:4C:23:7F:ED:A6:65:keyid:E7:07:2D:74:56:66:31:F0:7B:10:EA:B6:03:06:4C:23:7F:ED:A6:65 @@ -66,26 +67,26 @@ ids-daps: ######### # PLATO # ######### -plato-backend-application: +platobackendapplication: fullnameOverride: "plato-backend-application" service: port: 80 -plato-postgresql: +platopostgresql: fullnameOverride: "plato-postgresql" auth: - username: &psqlUsername "postgresql_sandbox_user" password: &psqlPassword "psql_password" + username: &psqlUsername "postgresql_sandbox_user" database: &psqlDatabase "edc" persistence: enabled: false -plato-vault: +platovault: fullnameOverride: "plato-vault" injector: enabled: false server: dev: enabled: true - devRootToken: &platoVaultToken "root" + devRootToken: "root" # Must be the same certificate that is configured in section 'ids-daps' postStart: @@ -159,11 +160,12 @@ plato-vault: enabled: true externalPort: 8200 targetPort: 8200 -plato-edc-dataplane: +platoedcdataplane: fullnameOverride: "plato-edc-dataplane" image: repository: &edcDataPlaneImage ghcr.io/catenax-ng/product-edc/edc-dataplane-hashicorp-vault tag: &edcDataPlaneImageTag develop + envSecretName: "aio-plato-data-secret" edc: endpoints: public: @@ -189,8 +191,7 @@ plato-edc-dataplane: # see extension https://github.com/catenax-ng/product-edc/tree/develop/edc-extensions/hashicorp-vault EDC_VAULT_HASHICORP_URL: http://plato-vault - EDC_VAULT_HASHICORP_TOKEN: *platoVaultToken -plato-edc-controlplane: +platoedccontrolplane: fullnameOverride: "plato-edc-controlplane" service: type: NodePort @@ -201,6 +202,7 @@ plato-edc-controlplane: properties: |- otel.javaagent.enabled=false otel.javaagent.debug=false + envSecretName: "aio-plato-control-secret" env: JAVA_TOOL_OPTIONS: "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=1044" @@ -227,47 +229,32 @@ plato-edc-controlplane: EDC_IDS_ENDPOINT: http://plato-edc-controlplane:8282/api/v1/ids EDC_IDS_DESCRIPTION: "Plato Control Plane" - ######################### - ## DATA MANAGEMENT API ## - ######################### - - # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/api/auth-tokenbased - EDC_API_AUTH_KEY: &edcControlPlaneAuthKey "password" - ################ ## POSTGRESQL ## ################ - # the psql is configured in section 'plato-postgresql' - # password and username are taken from there - # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/asset-index-sql EDC_DATASOURCE_ASSET_NAME: asset - EDC_DATASOURCE_ASSET_PASSWORD: *psqlPassword EDC_DATASOURCE_ASSET_USER: *psqlUsername EDC_DATASOURCE_ASSET_URL: &platoPsqlConStr "jdbc:postgresql://plato-postgresql:5432/edc" # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/contract-definition-store-sql EDC_DATASOURCE_CONTRACTDEFINITION_NAME: contractdefinition - EDC_DATASOURCE_CONTRACTDEFINITION_PASSWORD: *psqlPassword EDC_DATASOURCE_CONTRACTDEFINITION_USER: *psqlUsername EDC_DATASOURCE_CONTRACTDEFINITION_URL: *platoPsqlConStr # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/contract-negotiation-store-sql EDC_DATASOURCE_CONTRACTNEGOTIATION_NAME: contractnegotiation - EDC_DATASOURCE_CONTRACTNEGOTIATION_PASSWORD: *psqlPassword EDC_DATASOURCE_CONTRACTNEGOTIATION_USER: *psqlUsername EDC_DATASOURCE_CONTRACTNEGOTIATION_URL: *platoPsqlConStr # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/policy-store-sql EDC_DATASOURCE_POLICY_NAME: policy - EDC_DATASOURCE_POLICY_PASSWORD: *psqlPassword EDC_DATASOURCE_POLICY_USER: *psqlUsername EDC_DATASOURCE_POLICY_URL: *platoPsqlConStr # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/transfer-process-store-sql EDC_DATASOURCE_TRANSFERPROCESS_NAME: transferprocess - EDC_DATASOURCE_TRANSFERPROCESS_PASSWORD: *psqlPassword EDC_DATASOURCE_TRANSFERPROCESS_USER: *psqlUsername EDC_DATASOURCE_TRANSFERPROCESS_URL: *platoPsqlConStr @@ -289,22 +276,21 @@ plato-edc-controlplane: # see extension https://github.com/catenax-ng/product-edc/tree/develop/edc-extensions/hashicorp-vault EDC_VAULT_HASHICORP_URL: http://plato-vault:8200 - EDC_VAULT_HASHICORP_TOKEN: *platoVaultToken ############ # SOKRATES # ############ -sokrates-backend-application: +sokratesbackendapplication: fullnameOverride: "sokrates-backend-application" service: port: 80 -sokrates-postgresql: +sokratespostgresql: fullnameOverride: "sokrates-postgresql" auth: - username: *psqlUsername password: *psqlPassword + username: *psqlUsername database: *psqlDatabase -sokrates-vault: +sokratesvault: fullnameOverride: "sokrates-vault" injector: enabled: false @@ -385,11 +371,12 @@ sokrates-vault: enabled: true externalPort: 8200 targetPort: 8200 -sokrates-edc-dataplane: +sokratesedcdataplane: fullnameOverride: "sokrates-edc-dataplane" image: repository: *edcDataPlaneImage tag: *edcDataPlaneImageTag + envSecretName: "aio-plato-data-secret" edc: endpoints: public: @@ -416,10 +403,11 @@ sokrates-edc-dataplane: # see extension https://github.com/catenax-ng/product-edc/tree/develop/edc-extensions/hashicorp-vault EDC_VAULT_HASHICORP_URL: http://sokrates-vault-0 EDC_VAULT_HASHICORP_TOKEN: *sokratesVaultToken -sokrates-edc-controlplane: +sokratesedccontrolplane: fullnameOverride: "sokrates-edc-controlplane" service: type: NodePort + envSecretName: "aio-sokrates-control-secret" image: repository: *edcControlPlaneImage tag: *edcControlPlaneImageTag @@ -449,47 +437,32 @@ sokrates-edc-controlplane: EDC_IDS_ENDPOINT: http://sokrates-edc-controlplane:8282/api/v1/ids EDC_IDS_DESCRIPTION: "Sokrates Control Plane" - ######################### - ## DATA MANAGEMENT API ## - ######################### - - # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/api/auth-tokenbased - EDC_API_AUTH_KEY: *edcControlPlaneAuthKey - ################ ## POSTGRESQL ## ################ - # the psql is configured in section 'sokrates-postgresql' - # password and username are taken from there - # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/asset-index-sql EDC_DATASOURCE_ASSET_NAME: asset - EDC_DATASOURCE_ASSET_PASSWORD: *psqlPassword EDC_DATASOURCE_ASSET_USER: *psqlUsername EDC_DATASOURCE_ASSET_URL: &SokratesPsqlConStr "jdbc:postgresql://sokrates-postgresql:5432/edc" # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/contract-definition-store-sql EDC_DATASOURCE_CONTRACTDEFINITION_NAME: contractdefinition - EDC_DATASOURCE_CONTRACTDEFINITION_PASSWORD: *psqlPassword EDC_DATASOURCE_CONTRACTDEFINITION_USER: *psqlUsername EDC_DATASOURCE_CONTRACTDEFINITION_URL: *SokratesPsqlConStr # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/contract-negotiation-store-sql EDC_DATASOURCE_CONTRACTNEGOTIATION_NAME: contractnegotiation - EDC_DATASOURCE_CONTRACTNEGOTIATION_PASSWORD: *psqlPassword EDC_DATASOURCE_CONTRACTNEGOTIATION_USER: *psqlUsername EDC_DATASOURCE_CONTRACTNEGOTIATION_URL: *SokratesPsqlConStr # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/policy-store-sql EDC_DATASOURCE_POLICY_NAME: policy - EDC_DATASOURCE_POLICY_PASSWORD: *psqlPassword EDC_DATASOURCE_POLICY_USER: *psqlUsername EDC_DATASOURCE_POLICY_URL: *SokratesPsqlConStr # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/transfer-process-store-sql EDC_DATASOURCE_TRANSFERPROCESS_NAME: transferprocess - EDC_DATASOURCE_TRANSFERPROCESS_PASSWORD: *psqlPassword EDC_DATASOURCE_TRANSFERPROCESS_USER: *psqlUsername EDC_DATASOURCE_TRANSFERPROCESS_URL: *SokratesPsqlConStr @@ -511,4 +484,3 @@ sokrates-edc-controlplane: # see extension https://github.com/catenax-ng/product-edc/tree/develop/edc-extensions/hashicorp-vault EDC_VAULT_HASHICORP_URL: http://sokrates-vault:8200 - EDC_VAULT_HASHICORP_TOKEN: *sokratesVaultToken From 42e8870102010282d8037ab9561ab0b9549e3e14 Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Fri, 8 Jul 2022 09:48:31 +0200 Subject: [PATCH 161/433] update config key in helm deployment (#256) Signed-off-by: Dominik Pinsel --- deployment/helm/edc-controlplane/README.md | 4 ++-- deployment/helm/edc-controlplane/values.yaml | 2 +- deployment/helm/edc-dataplane/README.md | 2 +- deployment/helm/edc-dataplane/values.yaml | 3 --- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/deployment/helm/edc-controlplane/README.md b/deployment/helm/edc-controlplane/README.md index 47982434b..e1bfc33e5 100644 --- a/deployment/helm/edc-controlplane/README.md +++ b/deployment/helm/edc-controlplane/README.md @@ -23,7 +23,7 @@ $ helm install my-release catenax-ng-product-edc/edc-controlplane --version 0.0. | autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | | autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | | autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | -| configuration.properties | string | `"# edc.api.auth.key=\n# edc.atomikos.checkpoint.interval=\n# edc.atomikos.directory=\n# edc.atomikos.logging=\n# edc.atomikos.threaded2pc=\n# edc.atomikos.timeout=\n# edc.aws.access.key=\n# edc.aws.provision.retry.retries.max=\n# edc.aws.provision.role.duration.session.max=\n# edc.aws.secret.access.key=\n# edc.blobstore.endpoint=\n# edc.controlplane.validation-endpoint=\n# edc.core.retry.backoff.max=\n# edc.core.retry.backoff.min=\n# edc.core.retry.retries.max=\n# edc.core.system.health.check.liveness-period=\n# edc.core.system.health.check.readiness-period=\n# edc.core.system.health.check.startup-period=\n# edc.core.system.health.check.threadpool-size=\n# edc.dataplane.queue.capacity=\n# edc.dataplane.wait=\n# edc.dataplane.workers=\n# edc.datasource.asset.name=\"default\"\n# edc.datasource.contractdefinition.name=\"default\"\n# edc.datasource.contractnegotiation.name=\"default\"\n# edc.datasource.policy.name=\"default\"\n# edc.datasource.transferprocess.name=\"default\"\n# edc.datasource.default.pool.maxIdleConnections=\n# edc.datasource.default.pool.maxTotalConnections=\n# edc.datasource.default.pool.minIdleConnections=\n# edc.datasource.default.pool.testConnectionOnBorrow=\n# edc.datasource.default.pool.testConnectionOnCreate=\n# edc.datasource.default.pool.testConnectionOnReturn=\n# edc.datasource.default.pool.testConnectionWhileIdle=\n# edc.datasource.default.pool.testQuery=\n# edc.datasource.default.url=\n# edc.datasource.default.user=\n# edc.datasource.default.password=\n# edc.dpf.selector.url=\n# edc.events.topic.endpoint=\n# edc.events.topic.name=\n# edc.fs.config=\n# edc.hostname=\n# edc.identity.did.url=\n# edc.ids.catalog.id=\n# edc.ids.curator=\n# edc.ids.description=\n# edc.ids.endpoint=\n# edc.ids.id=\n# edc.ids.maintainer=\n# edc.ids.security.profile=\n# edc.ids.title=\n# edc.ids.validation.referringconnector=\n# edc.ion.crawler.did-type=\n# edc.ion.crawler.interval-minutes=\n# edc.ion.crawler.ion.url=\n# edc.metrics.enabled=\n# edc.metrics.executor.enabled=\n# edc.metrics.jersey.enabled=\n# edc.metrics.jetty.enabled=\n# edc.metrics.okhttp.enabled=\n# edc.metrics.system.enabled=\n# edc.negotiation.consumer.state-machine.batch-size=\n# edc.negotiation.provider.state-machine.batch-size=\n# edc.oauth.client.id=\n# edc.oauth.private.key.alias=\n# edc.oauth.provider.audience=\n# edc.oauth.provider.jwks.refresh=\n# edc.oauth.provider.jwks.url=\n# edc.oauth.public.key.alias=\n# edc.oauth.token.url=\n# edc.oauth.validation.nbf.leeway=\n# edc.receiver.http.auth-code=\n# edc.receiver.http.auth-key=\n# edc.receiver.http.endpoint=\n# edc.transfer.proxy.endpoint=\n# edc.transfer.dataplane.sync.token.validity=\n# edc.transfer.proxy.token.signer.privatekey.alias=\n# edc.transfer.functions.check.endpoint=\n# edc.transfer.functions.enabled.protocols=\n# edc.transfer.functions.transfer.endpoint=\n# edc.transfer-process-store.database.name=\n# edc.transfer.state-machine.batch-size=\n# edc.vault=\n# edc.vault.certificate=\n# edc.vault.clientid=\n# edc.vault.clientsecret=\n# edc.vault.name=\n# edc.vault.tenantid=\n# edc.vault.hashicorp.url=\n# edc.vault.hashicorp.token=\n# edc.vault.hashicorp.timeout.seconds=\n# edc.webdid.doh.url=\n# edc.web.rest.cors.enabled=\n# edc.web.rest.cors.headers=\n# edc.web.rest.cors.methods=\n# edc.web.rest.cors.origins=\n# ids.webhook.address="` | EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) | +| configuration.properties | string | `"# edc.api.auth.key=\n# edc.atomikos.checkpoint.interval=\n# edc.atomikos.directory=\n# edc.atomikos.logging=\n# edc.atomikos.threaded2pc=\n# edc.atomikos.timeout=\n# edc.aws.access.key=\n# edc.aws.provision.retry.retries.max=\n# edc.aws.provision.role.duration.session.max=\n# edc.aws.secret.access.key=\n# edc.blobstore.endpoint=\n# edc.controlplane.validation-endpoint=\n# edc.core.retry.backoff.max=\n# edc.core.retry.backoff.min=\n# edc.core.retry.retries.max=\n# edc.core.system.health.check.liveness-period=\n# edc.core.system.health.check.readiness-period=\n# edc.core.system.health.check.startup-period=\n# edc.core.system.health.check.threadpool-size=\n# edc.dataplane.queue.capacity=\n# edc.dataplane.wait=\n# edc.dataplane.workers=\n# edc.datasource.asset.name=\"default\"\n# edc.datasource.contractdefinition.name=\"default\"\n# edc.datasource.contractnegotiation.name=\"default\"\n# edc.datasource.policy.name=\"default\"\n# edc.datasource.transferprocess.name=\"default\"\n# edc.datasource.default.pool.maxIdleConnections=\n# edc.datasource.default.pool.maxTotalConnections=\n# edc.datasource.default.pool.minIdleConnections=\n# edc.datasource.default.pool.testConnectionOnBorrow=\n# edc.datasource.default.pool.testConnectionOnCreate=\n# edc.datasource.default.pool.testConnectionOnReturn=\n# edc.datasource.default.pool.testConnectionWhileIdle=\n# edc.datasource.default.pool.testQuery=\n# edc.datasource.default.url=\n# edc.datasource.default.user=\n# edc.datasource.default.password=\n# edc.dpf.selector.url=\n# edc.events.topic.endpoint=\n# edc.events.topic.name=\n# edc.fs.config=\n# edc.hostname=\n# edc.identity.did.url=\n# edc.ids.catalog.id=\n# edc.ids.curator=\n# edc.ids.description=\n# edc.ids.endpoint=\n# edc.ids.id=\n# edc.ids.maintainer=\n# edc.ids.security.profile=\n# edc.ids.title=\n# edc.ids.validation.referringconnector=\n# edc.ion.crawler.did-type=\n# edc.ion.crawler.interval-minutes=\n# edc.ion.crawler.ion.url=\n# edc.metrics.enabled=\n# edc.metrics.executor.enabled=\n# edc.metrics.jersey.enabled=\n# edc.metrics.jetty.enabled=\n# edc.metrics.okhttp.enabled=\n# edc.metrics.system.enabled=\n# edc.negotiation.consumer.state-machine.batch-size=\n# edc.negotiation.provider.state-machine.batch-size=\n# edc.oauth.client.id=\n# edc.oauth.private.key.alias=\n# edc.oauth.provider.audience=\n# edc.oauth.provider.jwks.refresh=\n# edc.oauth.provider.jwks.url=\n# edc.oauth.public.key.alias=\n# edc.oauth.token.url=\n# edc.oauth.validation.nbf.leeway=\n# edc.receiver.http.auth-code=\n# edc.receiver.http.auth-key=\n# edc.receiver.http.endpoint=\n# edc.transfer.proxy.endpoint=\n# edc.transfer.proxy.token.validity.seconds=\n# edc.transfer.proxy.token.signer.privatekey.alias=\n# edc.transfer.functions.check.endpoint=\n# edc.transfer.functions.enabled.protocols=\n# edc.transfer.functions.transfer.endpoint=\n# edc.transfer-process-store.database.name=\n# edc.transfer.state-machine.batch-size=\n# edc.vault=\n# edc.vault.certificate=\n# edc.vault.clientid=\n# edc.vault.clientsecret=\n# edc.vault.name=\n# edc.vault.tenantid=\n# edc.vault.hashicorp.url=\n# edc.vault.hashicorp.token=\n# edc.vault.hashicorp.timeout.seconds=\n# edc.webdid.doh.url=\n# edc.web.rest.cors.enabled=\n# edc.web.rest.cors.headers=\n# edc.web.rest.cors.methods=\n# edc.web.rest.cors.origins=\n# ids.webhook.address="` | EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) | | edc.endpoints.control.path | string | `"/api/controlplane/control"` | The path mapping the "control" api is going to be exposed at | | edc.endpoints.control.port | string | `"9999"` | The network port, which the "control" api is going to be exposed by the container, pod and service | | edc.endpoints.data.path | string | `"/data"` | The path mapping the "data" management api is going to be exposed at | @@ -37,7 +37,7 @@ $ helm install my-release catenax-ng-product-edc/edc-controlplane --version 0.0. | edc.endpoints.validation.path | string | `"/validation"` | The path mapping the "validation" api is going to be exposed at | | edc.endpoints.validation.port | string | `"8182"` | The network port, which the "validation" api is going to be exposed by the container, pod and service | | env | object | `{}` | Container environment variables e.g. for configuring [JAVA_TOOL_OPTIONS](https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/envvars002.html) Ex.: JAVA_TOOL_OPTIONS: > -Dhttp.proxyHost=proxy -Dhttp.proxyPort=80 -Dhttp.nonProxyHosts="localhost|127.*|[::1]" -Dhttps.proxyHost=proxy -Dhttps.proxyPort=443 | -| envSecretName | string | `nil` | Secret name to load environment variables from | +| envSecretName | string | `nil` | [Kubernetes Secret Resource](https://kubernetes.io/docs/concepts/configuration/secret/) name to load environment variables from | | fullnameOverride | string | `""` | Overrides the releases full name | | image.pullPolicy | string | `"IfNotPresent"` | [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use | | image.repository | string | `"ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql-hashicorp-vault"` | Which derivate of the edc control-plane to use. One of: [ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql-hashicorp-vault, ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql, ghcr.io/catenax-ng/product-edc/edc-controlplane-memory] | diff --git a/deployment/helm/edc-controlplane/values.yaml b/deployment/helm/edc-controlplane/values.yaml index 7da20cfbe..7dd99759b 100644 --- a/deployment/helm/edc-controlplane/values.yaml +++ b/deployment/helm/edc-controlplane/values.yaml @@ -316,7 +316,7 @@ configuration: # edc.receiver.http.auth-key= # edc.receiver.http.endpoint= # edc.transfer.proxy.endpoint= - # edc.transfer.dataplane.sync.token.validity= + # edc.transfer.proxy.token.validity.seconds= # edc.transfer.proxy.token.signer.privatekey.alias= # edc.transfer.functions.check.endpoint= # edc.transfer.functions.enabled.protocols= diff --git a/deployment/helm/edc-dataplane/README.md b/deployment/helm/edc-dataplane/README.md index 3f087eece..135323946 100644 --- a/deployment/helm/edc-dataplane/README.md +++ b/deployment/helm/edc-dataplane/README.md @@ -23,7 +23,7 @@ $ helm install my-release catenax-ng-product-edc/edc-dataplane --version 0.0.4 | autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | | autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | | autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | -| configuration.properties | string | `"# edc.atomikos.checkpoint.interval=\n# edc.atomikos.directory=\n# edc.atomikos.logging=\n# edc.atomikos.threaded2pc=\n# edc.atomikos.timeout=\n# edc.aws.access.key=\n# edc.aws.provision.retry.retries.max=\n# edc.aws.provision.role.duration.session.max=\n# edc.aws.secret.access.key=\n# edc.blobstore.endpoint=\n# edc.controlplane.validation-endpoint=\n# edc.core.retry.backoff.max=\n# edc.core.retry.backoff.min=\n# edc.core.retry.retries.max=\n# edc.core.system.health.check.liveness-period=\n# edc.core.system.health.check.readiness-period=\n# edc.core.system.health.check.startup-period=\n# edc.core.system.health.check.threadpool-size=\n# edc.dataplane.queue.capacity=\n# edc.dataplane.wait=\n# edc.dataplane.workers=\n# edc.datasource.asset.name=\"default\"\n# edc.datasource.contractdefinition.name=\"default\"\n# edc.datasource.contractnegotiation.name=\"default\"\n# edc.datasource.policy.name=\"default\"\n# edc.datasource.transferprocess.name=\"default\"\n# edc.datasource.default.pool.maxIdleConnections=\n# edc.datasource.default.pool.maxTotalConnections=\n# edc.datasource.default.pool.minIdleConnections=\n# edc.datasource.default.pool.testConnectionOnBorrow=\n# edc.datasource.default.pool.testConnectionOnCreate=\n# edc.datasource.default.pool.testConnectionOnReturn=\n# edc.datasource.default.pool.testConnectionWhileIdle=\n# edc.datasource.default.pool.testQuery=\n# edc.datasource.default.url=\n# edc.datasource.default.user=\n# edc.datasource.default.password=\n# edc.dpf.selector.url=\n# edc.events.topic.endpoint=\n# edc.events.topic.name=\n# edc.fs.config=\n# edc.hostname=\n# edc.identity.did.url=\n# edc.ids.catalog.id=\n# edc.ids.curator=\n# edc.ids.description=\n# edc.ids.endpoint=\n# edc.ids.id=\n# edc.ids.maintainer=\n# edc.ids.security.profile=\n# edc.ids.title=\n# edc.ids.validation.referringconnector=\n# edc.ion.crawler.did-type=\n# edc.ion.crawler.interval-minutes=\n# edc.ion.crawler.ion.url=\n# edc.metrics.enabled=\n# edc.metrics.executor.enabled=\n# edc.metrics.jersey.enabled=\n# edc.metrics.jetty.enabled=\n# edc.metrics.okhttp.enabled=\n# edc.metrics.system.enabled=\n# edc.negotiation.consumer.state-machine.batch-size=\n# edc.negotiation.provider.state-machine.batch-size=\n# edc.oauth.client.id=\n# edc.oauth.private.key.alias=\n# edc.oauth.provider.audience=\n# edc.oauth.provider.jwks.refresh=\n# edc.oauth.provider.jwks.url=\n# edc.oauth.public.key.alias=\n# edc.oauth.token.url=\n# edc.oauth.validation.nbf.leeway=\n# edc.receiver.http.auth-code=\n# edc.receiver.http.auth-key=\n# edc.receiver.http.endpoint=\n# edc.transfer.proxy.endpoint=\n# edc.transfer.dataplane.sync.token.validity=\n# edc.transfer.proxy.token.signer.privatekey.alias=\n# edc.transfer.functions.check.endpoint=\n# edc.transfer.functions.enabled.protocols=\n# edc.transfer.functions.transfer.endpoint=\n# edc.transfer-process-store.database.name=\n# edc.transfer.state-machine.batch-size=\n# edc.vault=\n# edc.vault.certificate=\n# edc.vault.clientid=\n# edc.vault.clientsecret=\n# edc.vault.name=\n# edc.vault.tenantid=\n# edc.vault.hashicorp.url=\n# edc.vault.hashicorp.token=\n# edc.vault.hashicorp.timeout.seconds=\n# edc.webdid.doh.url=\n# edc.web.rest.cors.enabled=\n# edc.web.rest.cors.headers=\n# edc.web.rest.cors.methods=\n# edc.web.rest.cors.origins="` | EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) | +| configuration.properties | string | `"# edc.atomikos.checkpoint.interval=\n# edc.atomikos.directory=\n# edc.atomikos.logging=\n# edc.atomikos.threaded2pc=\n# edc.atomikos.timeout=\n# edc.aws.access.key=\n# edc.aws.provision.retry.retries.max=\n# edc.aws.provision.role.duration.session.max=\n# edc.aws.secret.access.key=\n# edc.blobstore.endpoint=\n# edc.controlplane.validation-endpoint=\n# edc.core.retry.backoff.max=\n# edc.core.retry.backoff.min=\n# edc.core.retry.retries.max=\n# edc.core.system.health.check.liveness-period=\n# edc.core.system.health.check.readiness-period=\n# edc.core.system.health.check.startup-period=\n# edc.core.system.health.check.threadpool-size=\n# edc.dataplane.queue.capacity=\n# edc.dataplane.wait=\n# edc.dataplane.workers=\n# edc.datasource.asset.name=\"default\"\n# edc.datasource.contractdefinition.name=\"default\"\n# edc.datasource.contractnegotiation.name=\"default\"\n# edc.datasource.policy.name=\"default\"\n# edc.datasource.transferprocess.name=\"default\"\n# edc.datasource.default.pool.maxIdleConnections=\n# edc.datasource.default.pool.maxTotalConnections=\n# edc.datasource.default.pool.minIdleConnections=\n# edc.datasource.default.pool.testConnectionOnBorrow=\n# edc.datasource.default.pool.testConnectionOnCreate=\n# edc.datasource.default.pool.testConnectionOnReturn=\n# edc.datasource.default.pool.testConnectionWhileIdle=\n# edc.datasource.default.pool.testQuery=\n# edc.datasource.default.url=\n# edc.datasource.default.user=\n# edc.datasource.default.password=\n# edc.dpf.selector.url=\n# edc.events.topic.endpoint=\n# edc.events.topic.name=\n# edc.fs.config=\n# edc.hostname=\n# edc.identity.did.url=\n# edc.ids.catalog.id=\n# edc.ids.curator=\n# edc.ids.description=\n# edc.ids.endpoint=\n# edc.ids.id=\n# edc.ids.maintainer=\n# edc.ids.security.profile=\n# edc.ids.title=\n# edc.ids.validation.referringconnector=\n# edc.ion.crawler.did-type=\n# edc.ion.crawler.interval-minutes=\n# edc.ion.crawler.ion.url=\n# edc.metrics.enabled=\n# edc.metrics.executor.enabled=\n# edc.metrics.jersey.enabled=\n# edc.metrics.jetty.enabled=\n# edc.metrics.okhttp.enabled=\n# edc.metrics.system.enabled=\n# edc.negotiation.consumer.state-machine.batch-size=\n# edc.negotiation.provider.state-machine.batch-size=\n# edc.oauth.client.id=\n# edc.oauth.private.key.alias=\n# edc.oauth.provider.audience=\n# edc.oauth.provider.jwks.refresh=\n# edc.oauth.provider.jwks.url=\n# edc.oauth.public.key.alias=\n# edc.oauth.token.url=\n# edc.oauth.validation.nbf.leeway=\n# edc.receiver.http.auth-code=\n# edc.receiver.http.auth-key=\n# edc.receiver.http.endpoint=\n# edc.transfer.functions.check.endpoint=\n# edc.transfer.functions.enabled.protocols=\n# edc.transfer.functions.transfer.endpoint=\n# edc.transfer-process-store.database.name=\n# edc.transfer.state-machine.batch-size=\n# edc.vault=\n# edc.vault.certificate=\n# edc.vault.clientid=\n# edc.vault.clientsecret=\n# edc.vault.name=\n# edc.vault.tenantid=\n# edc.vault.hashicorp.url=\n# edc.vault.hashicorp.token=\n# edc.vault.hashicorp.timeout.seconds=\n# edc.webdid.doh.url=\n# edc.web.rest.cors.enabled=\n# edc.web.rest.cors.headers=\n# edc.web.rest.cors.methods=\n# edc.web.rest.cors.origins="` | EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) | | edc.endpoints.control.path | string | `"/api/dataplane/control"` | The path mapping the "control" api is going to be exposed by | | edc.endpoints.control.port | string | `"9999"` | The network port, which the "control" api is going to be exposed by the container, pod and service | | edc.endpoints.default.path | string | `"/api"` | The path mapping the "default" api is going to be exposed by | diff --git a/deployment/helm/edc-dataplane/values.yaml b/deployment/helm/edc-dataplane/values.yaml index c82c0aa53..3d3ee7c68 100644 --- a/deployment/helm/edc-dataplane/values.yaml +++ b/deployment/helm/edc-dataplane/values.yaml @@ -281,9 +281,6 @@ configuration: # edc.receiver.http.auth-code= # edc.receiver.http.auth-key= # edc.receiver.http.endpoint= - # edc.transfer.proxy.endpoint= - # edc.transfer.dataplane.sync.token.validity= - # edc.transfer.proxy.token.signer.privatekey.alias= # edc.transfer.functions.check.endpoint= # edc.transfer.functions.enabled.protocols= # edc.transfer.functions.transfer.endpoint= From 727625d1099987d86c1ea848790d2a738a959ed8 Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Fri, 8 Jul 2022 14:43:08 +0200 Subject: [PATCH 162/433] remove duplicated transfer md (#257) --- docs/Transfer Data.md | 160 ------------------------------------------ 1 file changed, 160 deletions(-) delete mode 100644 docs/Transfer Data.md diff --git a/docs/Transfer Data.md b/docs/Transfer Data.md deleted file mode 100644 index 78f848bf2..000000000 --- a/docs/Transfer Data.md +++ /dev/null @@ -1,160 +0,0 @@ -# Transfer Data - -This document will showcase a data transfer between two connectors. - ---- - -Before running the commands setup the all-in-one deployment from the. This is documented in it's [README.md](../edc-tests/src/main/resources/deployment/helm/all-in-one/README.md#Setup). - -Please install [Bash jq](https://linuxhint.com/bash_jq_command/), as it is used in the bash calls of this document. - ---- - -For this transfer connector **Plato** will act as data provider, and connector **Sokrates** will act as data -consumer. But the roles could be inverse as well. - -**Contents** - -0. Before running the demo - 1. Ensure all pods are running - 2. Set environment variables -1. Setup Data Offer -2. Request Contract Offers -3. Negotiate Contract -4. Transfer Data -5. Verify Data Transfer - -## 0. Before Running the demo - -### 0.1 Wait until all pods are running - -Get all the pods and wait until all pods are in a `Running` state before executing the next steps. -Please ignore that the EDC applications will crash 2-3 times during the start-up phase. This is normal. - -**Run** - -```bash -minikube kubectl -- -n edc-all-in-one get pods -``` - -### 0.2 Set environment variables used in subsequent calls - -Initialize the following environment variables, that are used in the upcoming API calls. - -**Run** - -```bash -export PLATO_DATAMGMT_URL=$(minikube service plato-edc-controlplane -n edc-all-in-one --url | sed -n 3p) -export PLATO_IDS_URL=$(minikube service plato-edc-controlplane -n edc-all-in-one --url | sed -n 5p) -export SOKRATES_DATAMGMT_URL=$(minikube service sokrates-edc-controlplane -n edc-all-in-one --url | sed -n 3p) -``` - -## 1. Setup Data Offer - -Set up a data offer in **Plato**, so that **Sokrates** has something to consume. - -In case you are unfamiliar with the EDC terms `Asset`, `Policy` or `ContractDefinition` please have a look the official open -source documentation ([link](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/blob/main/docs/architecture/domain-model.md)). - -![Sequence 1](./diagrams/transfer_sequence_1.png) - -**Run** - -The following commands will create an Asset, a Policy and a Contract Definition. -For simplicity `https://jsonplaceholder.typicode.com/todos/1` is used as data source of the asset, but could be any -other API, that is reachable from the Data Plane. - -```bash -curl -X POST "$PLATO_DATAMGMT_URL/data/assets" --header "X-Api-Key: password" --header "Content-Type: application/json" --data "{ \"asset\": { \"properties\": { \"asset:prop:id\": \"1\", \"asset:prop:description\": \"Product EDC Demo Asset\" } }, \"dataAddress\": { \"properties\": { \"type\": \"HttpData\", \"endpoint\": \"https://jsonplaceholder.typicode.com/todos/1\" } } }" -s -o /dev/null -w 'Response Code: %{http_code}\n' -``` - -```bash -curl -X POST "$PLATO_DATAMGMT_URL/data/policies" --header "X-Api-Key: password" --header "Content-Type: application/json" --data "{ \"uid\": \"1\", \"prohibitions\": [], \"obligations\": [], \"permissions\": [ { \"edctype\": \"dataspaceconnector:permission\", \"action\": { \"type\": \"USE\" }, \"constraints\": [] } ] }" -s -o /dev/null -w 'Response Code: %{http_code}\n' -``` - -```bash -curl -X POST "$PLATO_DATAMGMT_URL/data/contractdefinitions" --header "X-Api-Key: password" --header "Content-Type: application/json" --data "{ \"id\": \"1\", \"criteria\": [ { \"left\": \"asset:prop:id\", \"op\": \"=\", \"right\": \"1\" } ], \"accessPolicyId\": \"1\", \"contractPolicyId\": \"1\" }" -s -o /dev/null -w 'Response Code: %{http_code}\n' -``` - -## 2. Request Contract Offer Catalog - -In this step Sokrates gets told to request contract offers from another connector (in this case Plato). Sokrates will -then request the catalog over IDS messaging. - -For IDS messaging connectors will identify each other using the configured IDS DAPS. Therefore, it is important that -connectors, that intent to send messages to each other, have the same DAPS instance configured. - -![Sequence 1](./diagrams/transfer_sequence_2.png) - -**Run** - -```bash -curl -G -X GET "$SOKRATES_DATAMGMT_URL/data/catalog" --data-urlencode "providerUrl=$PLATO_IDS_URL/api/v1/ids/data" --header "X-Api-Key: password" --header "Content-Type: application/json" -s | jq -``` - -## 3. Negotiate Contract - -Initiate a contract negotiation for the asset (from step 1). Part of the negotiation payload is the contract -offer (received in step 2). - -In the diagram the IDS contract negotiation is marked as simplified, because the EDC is exchanging multiple messages -during contract negotiation. But the inter-controlplane communication is not in the scope of this document. - -After the negotiation is initiated ensure that is has concluded. This is done by requesting the negotiation from the API -and checking whether the `contractAgreementId` is set. This might take a few seconds. - -![Sequence 1](./diagrams/transfer_sequence_3.png) - -**Run** - -```bash -export NEGOTIATION_ID=$(curl -X POST "$SOKRATES_DATAMGMT_URL/data/contractnegotiations" --header "X-Api-Key: password" --header "Content-Type: application/json" --data "{ \"connectorId\": \"foo\", \"connectorAddress\": \"$PLATO_IDS_URL/api/v1/ids/data\", \"offer\": { \"offerId\": \"1:foo\", \"assetId\": \"1\", \"policy\": { \"uid\": \"1\", \"prohibitions\": [], \"obligations\": [], \"permissions\": [ { \"edctype\": \"dataspaceconnector:permission\", \"action\": { \"type\": \"USE\" }, \"target\": \"1\", \"constraints\": [] } ] } } }" -s | jq -r '.id') -``` - -```bash -curl -X GET "$SOKRATES_DATAMGMT_URL/data/contractnegotiations/$NEGOTIATION_ID" --header "X-Api-Key: password" --header "Content-Type: application/json" -s | jq -``` - -## 4. Transfer Data - -Initiate a data transfer using the contract agreement from the negotiation (from step 3). Then wait until the state of -the -transfer process is `COMPLETED`. - -![Sequence 1](./diagrams/transfer_sequence_4.png) - -**Run** - -```bash -export CONTRACT_AGREEMENT_ID=$(curl -X GET "$SOKRATES_DATAMGMT_URL/data/contractnegotiations/$NEGOTIATION_ID" --header "X-Api-Key: password" --header "Content-Type: application/json" -s | jq -r '.contractAgreementId') -``` - -```bash -export TRANSFER_PROCESS_ID=$(tr -dc '[:alnum:]' < /dev/urandom | head -c20) -export TRANSFER_ID=$(curl -X POST "$SOKRATES_DATAMGMT_URL/data/transferprocess" --header "X-Api-Key: password" --header "Content-Type: application/json" --data "{ \"id\": \"${TRANSFER_PROCESS_ID}\", \"connectorId\": \"foo\", \"connectorAddress\": \"${PLATO_IDS_URL}/api/v1/ids/data\", \"contractId\": \"${CONTRACT_AGREEMENT_ID}\", \"assetId\": \"1\", \"managedResources\": \"false\", \"dataDestination\": { \"type\": \"HttpProxy\" } }" -s | jq -r '.id') -``` - -```bash -curl -X GET "$SOKRATES_DATAMGMT_URL/data/transferprocess/$TRANSFER_ID" --header "X-Api-Key: password" --header "Content-Type: application/json" -s | jq -``` - -## 5. Verify Data Transfer - -After the transfer is complete the Backend Application has downloaded the data. The Backend Application stores the data -locally. In this demo the transfer can be verified by executing a simple `cat` call in the Pod. - -![Sequence 1](./diagrams/transfer_sequence_5.png) - -```bash -echo $(kubectl exec -n edc-all-in-one --stdin --tty `kubectl get pod -n edc-all-in-one -l app.kubernetes.io/name=sokrates-backend-application --template "{{ with index .items ${POD_INDEX:-0} }}{{ .metadata.name }}{{ end }}"` -- /usr/bin/cat /tmp/data/${TRANSFER_PROCESS_ID}) | jq -``` - -# Delete All Data - -```bash -minikube kubectl -- delete pvc -n edc-all-in-one --all -``` - -```bash -minikube kubectl -- delete pv -n edc-all-in-one --all -``` \ No newline at end of file From db1725f83fbc9afcbf796f70546a386a91009552 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Jul 2022 10:33:50 +0200 Subject: [PATCH 163/433] Bump actions/setup-java from 3.4.0 to 3.4.1 (#259) --- .github/workflows/build.yaml | 12 ++++++------ .github/workflows/draft-new-release.yaml | 2 +- .github/workflows/publish-new-release.yml | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index c25a0b621..55c8c8752 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -43,7 +43,7 @@ jobs: fetch-depth: 0 - name: Set up JDK 11 - uses: actions/setup-java@v3.4.0 + uses: actions/setup-java@v3.4.1 with: java-version: '11' distribution: 'adopt' @@ -76,7 +76,7 @@ jobs: password: ${{ secrets.CXNG_GHCR_PAT }} - name: Set up JDK 11 - uses: actions/setup-java@v3.4.0 + uses: actions/setup-java@v3.4.1 with: java-version: '11' distribution: 'adopt' @@ -161,7 +161,7 @@ jobs: password: ${{ secrets.CXNG_GHCR_PAT }} - name: Set up JDK 11 - uses: actions/setup-java@v3.4.0 + uses: actions/setup-java@v3.4.1 with: java-version: '11' distribution: 'adopt' @@ -246,7 +246,7 @@ jobs: password: ${{ secrets.CXNG_GHCR_PAT }} - name: Set up JDK 11 - uses: actions/setup-java@v3.4.0 + uses: actions/setup-java@v3.4.1 with: java-version: '11' distribution: 'adopt' @@ -331,7 +331,7 @@ jobs: password: ${{ secrets.CXNG_GHCR_PAT }} - name: Set up JDK 11 - uses: actions/setup-java@v3.4.0 + uses: actions/setup-java@v3.4.1 with: java-version: '11' distribution: 'adopt' @@ -416,7 +416,7 @@ jobs: password: ${{ secrets.CXNG_GHCR_PAT }} - name: Set up JDK 11 - uses: actions/setup-java@v3.4.0 + uses: actions/setup-java@v3.4.1 with: java-version: '11' distribution: 'adopt' diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index 7acc3be1c..c0b509766 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -29,7 +29,7 @@ jobs: git config user.email noreply@github.com - name: Set up JDK 11 - uses: actions/setup-java@v3.4.0 + uses: actions/setup-java@v3.4.1 with: java-version: '11' distribution: 'adopt' diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index 0c96c390d..ac2fd5c33 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -71,7 +71,7 @@ jobs: submodules: recursive - name: Set up JDK 11 - uses: actions/setup-java@v3.4.0 + uses: actions/setup-java@v3.4.1 with: java-version: '11' distribution: 'adopt' @@ -180,7 +180,7 @@ jobs: prerelease: false - name: Set up JDK 11 - uses: actions/setup-java@v3.4.0 + uses: actions/setup-java@v3.4.1 with: java-version: '11' distribution: 'adopt' From 4d02c24a3c82cb59e7b8427edcd713d643fd7191 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Jul 2022 09:21:13 +0200 Subject: [PATCH 164/433] Bump exec-maven-plugin from 3.0.0 to 3.1.0 (#268) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 74fb2f252..67a2f95c9 100644 --- a/pom.xml +++ b/pom.xml @@ -45,7 +45,7 @@ 3.3.0 3.2.2 2.23.0 - 3.0.0 + 3.1.0 3.4.0 2.0.0 2.22.2 From dff0e8abaa1f78be9bc5d7413e33b7bc21547d9e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Jul 2022 11:50:19 +0200 Subject: [PATCH 165/433] Bump azure/setup-helm from 3.0 to 3.1 (#261) --- .github/workflows/helm-lint.yaml | 2 +- .github/workflows/publish-new-release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/helm-lint.yaml b/.github/workflows/helm-lint.yaml index b5cafca05..cdc8d9edd 100644 --- a/.github/workflows/helm-lint.yaml +++ b/.github/workflows/helm-lint.yaml @@ -32,7 +32,7 @@ jobs: fetch-depth: 0 - name: helm (setup) - uses: azure/setup-helm@v3.0 + uses: azure/setup-helm@v3.1 with: version: v3.8.1 - diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index ac2fd5c33..eb999ec77 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -109,7 +109,7 @@ jobs: fetch-depth: 0 - name: Install Helm - uses: azure/setup-helm@v3.0 + uses: azure/setup-helm@v3.1 with: version: v3.8.1 - From 4d204c9f62d4b41299073a8bda9a5be778018092 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Jul 2022 14:29:01 +0200 Subject: [PATCH 166/433] Bump mikefarah/yq from 4.25.3 to 4.26.1 (#265) --- .github/workflows/draft-new-release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index c0b509766..24ee4cb64 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -43,7 +43,7 @@ jobs: GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} - name: Bump version in deployment/helm - uses: mikefarah/yq@v4.25.3 + uses: mikefarah/yq@v4.26.1 with: cmd: |- find deployment/helm -name Chart.yaml | xargs -n1 yq -i '.appVersion = "${{ github.event.inputs.version }}" | .version = "${{ github.event.inputs.version }}"' From 7350ec085658dc912ea751ba0769b3ee05b23a86 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Jul 2022 09:00:04 +0200 Subject: [PATCH 167/433] Bump alpine in /edc-controlplane/edc-controlplane-memory/src/main/docker (#273) --- .../edc-controlplane-memory/src/main/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile index d4aa94d6b..43eca93dc 100644 --- a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile @@ -10,7 +10,7 @@ # Contributors: # Mercedes-Benz Tech Innovation GmbH - Initial Dockerfile # -FROM alpine:3.16.0 as otel +FROM alpine:3.16.1 as otel ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" From 5625872198addbede1142d9811cfc465d88e2d08 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Jul 2022 09:00:15 +0200 Subject: [PATCH 168/433] Bump alpine (#276) --- .../src/main/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile index d4aa94d6b..43eca93dc 100644 --- a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile @@ -10,7 +10,7 @@ # Contributors: # Mercedes-Benz Tech Innovation GmbH - Initial Dockerfile # -FROM alpine:3.16.0 as otel +FROM alpine:3.16.1 as otel ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" From ae2cc008d79184f0599ac7c3ab0e8a335121862f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Jul 2022 09:00:25 +0200 Subject: [PATCH 169/433] Bump alpine in /edc-dataplane/edc-dataplane-azure-vault/src/main/docker (#275) --- .../edc-dataplane-azure-vault/src/main/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile b/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile index 0af3e0b55..132135fcf 100644 --- a/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile +++ b/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile @@ -10,7 +10,7 @@ # Contributors: # Mercedes-Benz Tech Innovation GmbH - Initial Dockerfile # -FROM alpine:3.16.0 as otel +FROM alpine:3.16.1 as otel ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" From bb2d3901ac082025fcbc08b04b49d80621c009b2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Jul 2022 09:00:35 +0200 Subject: [PATCH 170/433] Bump alpine (#274) --- .../edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile b/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile index 0af3e0b55..132135fcf 100644 --- a/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile +++ b/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile @@ -10,7 +10,7 @@ # Contributors: # Mercedes-Benz Tech Innovation GmbH - Initial Dockerfile # -FROM alpine:3.16.0 as otel +FROM alpine:3.16.1 as otel ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" From 6b8b87bc287c22efcf464954e263c200f350cedf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Jul 2022 09:00:47 +0200 Subject: [PATCH 171/433] Bump alpine (#271) --- .../edc-controlplane-postgresql/src/main/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile index d4aa94d6b..43eca93dc 100644 --- a/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile @@ -10,7 +10,7 @@ # Contributors: # Mercedes-Benz Tech Innovation GmbH - Initial Dockerfile # -FROM alpine:3.16.0 as otel +FROM alpine:3.16.1 as otel ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" From 39f54c0707d202f290ceab0e375db66003ad065c Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Wed, 20 Jul 2022 10:48:37 +0200 Subject: [PATCH 172/433] update AIO DAPS version to 1.7.1 (#270) * update AIO DAPS version to 1.7.1 Signed-off-by: Dominik Pinsel * Update CHANGELOG.md * Update values.yaml --- CHANGELOG.md | 1 + .../deployment/helm/all-in-one/values.yaml | 6 +- .../helm/omejdn/templates/configmap.yaml | 69 ++++++++----------- .../helm/omejdn/templates/deployment.yaml | 27 ++++++-- .../deployment/helm/omejdn/values.yaml | 4 +- 5 files changed, 57 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bed3014b..2e6386a5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - BusinessPartnerNumber constraint supports List structure +- DAPS of All-In-One-Deployment now supports BusinessPartnerNumbers ## [0.0.4] - 2022-06-27 diff --git a/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml b/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml index d1b092df3..9bebbbe8d 100644 --- a/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml +++ b/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml @@ -9,6 +9,8 @@ idsdaps: connectors: - id: &sokratesDapsClientId E7:07:2D:74:56:66:31:F0:7B:10:EA:B6:03:06:4C:23:7F:ED:A6:65:keyid:E7:07:2D:74:56:66:31:F0:7B:10:EA:B6:03:06:4C:23:7F:ED:A6:65 name: sokrates + attributes: + issuerConnector: http://sokrates-edc-controlplane/BPNSOKRATES # Must be the same certificate that is stores in section 'sokrates-vault' certificate: |- -----BEGIN CERTIFICATE----- @@ -37,6 +39,8 @@ idsdaps: -----END CERTIFICATE----- - id: &platoDapsClientId 99:83:A7:17:86:FF:98:93:CE:A0:DD:A1:F1:36:FA:F6:0F:75:0A:23:keyid:99:83:A7:17:86:FF:98:93:CE:A0:DD:A1:F1:36:FA:F6:0F:75:0A:23 name: plato + attributes: + issuerConnector: http://plato-edc-controlplane/BPNPLATO # Must be the same certificate that is stores in section 'plato-vault' certificate: |- -----BEGIN CERTIFICATE----- @@ -212,7 +216,7 @@ platoedccontrolplane: # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/iam/oauth2/oauth2-core EDC_OAUTH_CLIENT_ID: *platoDapsClientId - EDC_OAUTH_PROVIDER_JWKS_URL: &edcControlPlaneOauthJwksUrl "http://ids-daps:4567/.well-known/jwks.json" + EDC_OAUTH_PROVIDER_JWKS_URL: &edcControlPlaneOauthJwksUrl "http://ids-daps:4567/jwks.json" EDC_OAUTH_TOKEN_URL: &edcControlPlaneOauthTokenUrl "http://ids-daps:4567/token" EDC_OAUTH_PRIVATE_KEY_ALIAS: my-plato-daps-key EDC_OAUTH_PUBLIC_KEY_ALIAS: my-plato-daps-crt diff --git a/edc-tests/src/main/resources/deployment/helm/omejdn/templates/configmap.yaml b/edc-tests/src/main/resources/deployment/helm/omejdn/templates/configmap.yaml index c6321c849..0974cbe9b 100644 --- a/edc-tests/src/main/resources/deployment/helm/omejdn/templates/configmap.yaml +++ b/edc-tests/src/main/resources/deployment/helm/omejdn/templates/configmap.yaml @@ -7,66 +7,51 @@ metadata: data: omejdn.yml: |- --- - host: http://localhost:4567 + host: http://ids-daps:4567/ path_prefix: '' bind_to: 0.0.0.0 allow_origin: "*" app_env: debug - openid: true - token: + openid: false + user_backend: + - yaml + user_backend_default: yaml + accept_audience: idsc:IDS_CONNECTORS_ALL + issuer: http://ids-daps:4567/ + environment: development + default_audience: + - idsc:IDS_CONNECTORS_ALL + access_token: expiration: 3600 - signing_key: omejdn_priv.pem - jwks_additions: - - omejdn_priv.pem.cert algorithm: RS256 - audience: idsc:IDS_CONNECTORS_ALL - issuer: http://localhost:4567 id_token: expiration: 3600 - signing_key: omejdn_priv.pem - jwks_additions: - - omejdn_priv.pem.cert algorithm: RS256 - issuer: http://localhost:4567 - user_backend: - - yaml - user_backend_default: yaml - user_selfservice: - enabled: true - allow_deletion: true - allow_password_change: true - editable_attributes: - - name - - family_name - - given_name - - middle_name - - nickname - - preferred_username - - profile - - picture - - website - - gender - - birthdate - - zoneinfo - - locale - - updated_at - - email - - email_verified - - address - - phone_number - - phone_number_verified - accept_audience: idsc:IDS_CONNECTORS_ALL + + plugins.yml: |- + --- + plugins: + token_user_attributes: clients.yml: |- --- {{- range $i, $val := .Values.connectors }} - client_id: {{ quote $val.id }} name: {{ quote $val.name }} - allowed_scopes: + token_endpoint_auth_method: private_key_jwt + grant_types: + - client_credentials + scope: - idsc:IDS_CONNECTOR_ATTRIBUTES_ALL attributes: + - key: idsc + value: IDS_CONNECTOR_ATTRIBUTES_ALL - key: securityProfile - value: idsc:BASE_SECURITY_PROFILE + value: idsc:BASE_SECURITY_PROFILE + {{- range $j, $attribute := $val.attributes }} + - key: {{ $j }} + value: {{ $attribute }} + {{- end }} redirect_uri: http://localhost:4200 {{ end -}} diff --git a/edc-tests/src/main/resources/deployment/helm/omejdn/templates/deployment.yaml b/edc-tests/src/main/resources/deployment/helm/omejdn/templates/deployment.yaml index 9244ddd6c..fda1b5066 100644 --- a/edc-tests/src/main/resources/deployment/helm/omejdn/templates/deployment.yaml +++ b/edc-tests/src/main/resources/deployment/helm/omejdn/templates/deployment.yaml @@ -55,6 +55,7 @@ spec: - | cp /opt/config/omejdn.yml /etc/daps/omejdn.yml cp /opt/config/clients.yml /etc/daps/clients.yml + cp /opt/config/plugins.yml /etc/daps/plugins.yml volumeMounts: - name: config-dir mountPath: /etc/daps @@ -64,6 +65,9 @@ spec: - mountPath: /opt/config/clients.yml name: clients-config subPath: clients.yml + - mountPath: /opt/config/plugins.yml + name: plugins-config + subPath: plugins.yml containers: - name: {{ .Chart.Name }} securityContext: @@ -77,7 +81,10 @@ spec: - mountPath: /opt/config/clients.yml name: config-dir subPath: clients.yml - - mountPath: /opt/keys/ + - mountPath: /opt/config/plugins.yml + name: config-dir + subPath: plugins.yml + - mountPath: /opt/keys/clients/ name: client-certificates ports: - name: http @@ -85,17 +92,19 @@ spec: protocol: TCP livenessProbe: httpGet: - path: /.well-known/jwks.json + path: /jwks.json port: http readinessProbe: httpGet: - path: /.well-known/jwks.json + path: /jwks.json port: http resources: {{- toYaml .Values.resources | nindent 12 }} env: - name: OMEJDN_JWT_AUD_OVERRIDE value: "idsc:IDS_CONNECTORS_ALL" + - name: OMEJDN_PLUGINS + value: "config/plugins.yml" volumes: - name: config-dir emptyDir: {} @@ -103,19 +112,25 @@ spec: configMap: name: {{ include "omejdn.fullname" . }} items: - - key: omejdn.yml - path: omejdn.yml + - key: omejdn.yml + path: omejdn.yml - name: clients-config configMap: name: {{ include "omejdn.fullname" . }} items: - key: clients.yml path: clients.yml + - name: plugins-config + configMap: + name: {{ include "omejdn.fullname" . }} + items: + - key: plugins.yml + path: plugins.yml - name: client-certificates configMap: name: {{ include "omejdn.fullname" . }} items: {{- range $i, $val := .Values.connectors }} - key: {{ $val.name }} - path: {{ $val.id | b64enc }}.cert + path: {{ $val.id }}.cert {{- end }} diff --git a/edc-tests/src/main/resources/deployment/helm/omejdn/values.yaml b/edc-tests/src/main/resources/deployment/helm/omejdn/values.yaml index 09c1d9592..ae82cd1d6 100644 --- a/edc-tests/src/main/resources/deployment/helm/omejdn/values.yaml +++ b/edc-tests/src/main/resources/deployment/helm/omejdn/values.yaml @@ -13,7 +13,7 @@ image: # -- [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use pullPolicy: IfNotPresent # -- Overrides the image tag whose default is the chart appVersion - tag: "1.4.2" + tag: "1.7.1" imagePullSecret: # -- Image pull secret to create to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) @@ -82,6 +82,8 @@ affinity: {} # Example Connector: # - id: grMsEz3EcsS3ENYJufNgUIeg4QsaL49M0gWxSexPdC4pon96Nvju90D8RlvAJB21 # name: my-connector +# attributes: +# issuerConnector: http://localhost:8080/ # certificate: |- # -----BEGIN CERTIFICATE----- # foo From a93db0f8b886c2ea655680d243468870d656a3d1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Jul 2022 22:37:09 +0200 Subject: [PATCH 173/433] Bump maven-deploy-plugin from 3.0.0-M2 to 3.0.0 (#279) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 67a2f95c9..738b621c8 100644 --- a/pom.xml +++ b/pom.xml @@ -49,7 +49,7 @@ 3.4.0 2.0.0 2.22.2 - 3.0.0-M2 + 3.0.0 3.2.1 3.0.0-M7 3.10.1 From 8060c0314244345733c26faf30bfc97518cff0f6 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Thu, 21 Jul 2022 14:26:23 +0200 Subject: [PATCH 174/433] Extend CI to utilize sonar (#213) --- .github/workflows/build.yaml | 366 ++++++++++-------- .github/workflows/publish-new-release.yml | 2 +- .../edc-controlplane-base/pom.xml | 4 + .../edc-controlplane-memory/pom.xml | 4 + .../pom.xml | 4 + .../edc-controlplane-postgresql/pom.xml | 4 + edc-controlplane/pom.xml | 4 + .../edc-dataplane-azure-vault/pom.xml | 4 + edc-dataplane/edc-dataplane-base/pom.xml | 4 + .../edc-dataplane-hashicorp-vault/pom.xml | 4 + edc-dataplane/pom.xml | 4 + .../business-partner-validation/pom.xml | 4 + edc-extensions/hashicorp-vault/pom.xml | 21 +- edc-extensions/pom.xml | 4 + edc-extensions/postgresql-migration/pom.xml | 4 + edc-tests/pom.xml | 106 +++++ pom.xml | 93 ++++- 17 files changed, 454 insertions(+), 182 deletions(-) create mode 100644 edc-tests/pom.xml diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 55c8c8752..7659941e4 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -24,15 +24,18 @@ jobs: CXNG_GHCR_PAT: ${{ steps.secret-presence.outputs.CXNG_GHCR_PAT }} ORG_VERACODE_API_ID: ${{ steps.secret-presence.outputs.ORG_VERACODE_API_ID }} ORG_VERACODE_API_KEY: ${{ steps.secret-presence.outputs.ORG_VERACODE_API_KEY }} + SONAR_TOKEN: ${{ steps.secret-presence.outputs.SONAR_TOKEN }} steps: - - name: Check whether secrets exist + - + name: Check whether secrets exist id: secret-presence run: | [ ! -z "${{ secrets.CXNG_GHCR_PAT }}" ] && echo "::set-output name=CXNG_GHCR_PAT::true" [ ! -z "${{ secrets.ORG_VERACODE_API_ID }}" ] && echo "::set-output name=ORG_VERACODE_API_ID::true" [ ! -z "${{ secrets.ORG_VERACODE_API_KEY }}" ] && echo "::set-output name=ORG_VERACODE_API_KEY::true" - + [ ! -z "${{ secrets.SONAR_TOKEN }}" ] && echo "::set-output name=SONAR_TOKEN::true" exit 0 + verify-formatting: runs-on: ubuntu-latest steps: @@ -50,7 +53,52 @@ jobs: cache: 'maven' - name: Verify proper formatting - run: ./mvnw spotless:check + run: ./mvnw -s settings.xml -B spotless:check + + sonar: + needs: [ secret-presence, verify-formatting ] + if: | + needs.secret-presence.outputs.SONAR_TOKEN + runs-on: ubuntu-latest + steps: + # Set-Up + - + name: Checkout + uses: actions/checkout@v3 + with: + submodules: recursive + - + name: Set up JDK 11 + uses: actions/setup-java@v3.3.0 + with: + java-version: '11' + distribution: 'adopt' + cache: 'maven' + - + name: Build edc with Gradle to get latest snapshots + run: ./gradlew publishToMavenLocal + working-directory: edc + - + name: Cache SonarCloud packages + uses: actions/cache@v3 + with: + path: ~/.sonar/cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar + # Analyse + - + name: Build with Maven and analyze with Sonar + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + run: |- + ./mvnw -s settings.xml -B clean verify sonar:sonar \ + -Pcoverage,failsafe \ + -Dsonar.projectKey=${GITHUB_REPOSITORY_OWNER}_product-edc \ + -Dsonar.organization=${GITHUB_REPOSITORY_OWNER} \ + -Dsonar.host.url=https://sonarcloud.io \ + -Dsonar.coverage.jacoco.xmlReportPaths=${GITHUB_WORKSPACE}/edc-tests/target/site/jacoco-aggregate/jacoco.xml \ + -Dsonar.verbose=true ################################# ### edc-dataplane-azure-vault ### @@ -174,7 +222,7 @@ jobs: - name: Build edc-dataplane-hashicorp-vault run: |- - ./mvnw -Pfailsafe -s settings.xml -B -pl .,edc-dataplane/edc-dataplane-hashicorp-vault -am verify + ./mvnw -s settings.xml -B -pl .,edc-dataplane/edc-dataplane-hashicorp-vault -am package env: GITHUB_PACKAGE_USERNAME: ${{ github.actor }} GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} @@ -229,83 +277,83 @@ jobs: needs: [ secret-presence, verify-formatting ] runs-on: ubuntu-latest steps: - # Set-Up - - - name: Checkout - uses: actions/checkout@v3 - with: - submodules: recursive - - - name: Login to GitHub Container Registry - if: | - needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.CXNG_GHCR_PAT }} - - - name: Set up JDK 11 - uses: actions/setup-java@v3.4.1 - with: - java-version: '11' - distribution: 'adopt' - cache: 'maven' - - - name: Build edc with Gradle to get latest snapshots - run: ./gradlew publishToMavenLocal - working-directory: edc - # Build - - - name: Build edc-controlplane-memory - run: |- - ./mvnw -Pfailsafe -s settings.xml -B -pl .,edc-controlplane/edc-controlplane-memory -am verify - env: - GITHUB_PACKAGE_USERNAME: ${{ github.actor }} - GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} - - - name: edc-controlplane-memory Docker Metadata - id: edc_controlplane_memory_meta - uses: docker/metadata-action@v4 - with: - images: | - ghcr.io/${{ github.repository }}/edc-controlplane-memory - tags: | - type=ref,event=branch - type=ref,event=pr - type=semver,pattern={{raw}} - type=match,pattern=\d.\d.\d - type=sha - - - name: Build edc-controlplane-memory Docker Image - uses: docker/build-push-action@v3 - with: - context: . - file: edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile - build-args: | - JAR=edc-controlplane/edc-controlplane-memory/target/edc-controlplane-memory.jar - LIB=edc-controlplane/edc-controlplane-memory/target/lib - push: | - ${{ (needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' && 'true') || 'false' }} - tags: ${{ steps.edc_controlplane_memory_meta.outputs.tags }} - labels: ${{ steps.edc_controlplane_memory_meta.outputs.labels }} - - - name: Veracode Upload And Scan - uses: veracode/veracode-uploadandscan-action@v1.0 - if: | - needs.secret-presence.outputs.ORG_VERACODE_API_ID && needs.secret-presence.outputs.ORG_VERACODE_API_KEY && contains(' - refs/heads/develop - refs/heads/release/ - refs/tags/ - refs/heads/main', github.ref) - continue-on-error: true - with: - appname: 'product-edc/edc-controlplane-memory' - createprofile: true - version: ${{ github.ref }}-${{ github.sha }} - filepath: edc-controlplane/edc-controlplane-memory/target/edc-controlplane-memory.jar - vid: ${{ secrets.ORG_VERACODE_API_ID }} - vkey: ${{ secrets.ORG_VERACODE_API_KEY }} + # Set-Up + - + name: Checkout + uses: actions/checkout@v3 + with: + submodules: recursive + - + name: Login to GitHub Container Registry + if: | + needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.CXNG_GHCR_PAT }} + - + name: Set up JDK 11 + uses: actions/setup-java@v3.4.0 + with: + java-version: '11' + distribution: 'adopt' + cache: 'maven' + - + name: Build edc with Gradle to get latest snapshots + run: ./gradlew publishToMavenLocal + working-directory: edc + # Build + - + name: Build edc-controlplane-memory + run: |- + ./mvnw -s settings.xml -B -pl .,edc-controlplane/edc-controlplane-memory -am package + env: + GITHUB_PACKAGE_USERNAME: ${{ github.actor }} + GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} + - + name: edc-controlplane-memory Docker Metadata + id: edc_controlplane_memory_meta + uses: docker/metadata-action@v4 + with: + images: | + ghcr.io/${{ github.repository }}/edc-controlplane-memory + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{raw}} + type=match,pattern=\d.\d.\d + type=sha + - + name: Build edc-controlplane-memory Docker Image + uses: docker/build-push-action@v3 + with: + context: . + file: edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile + build-args: | + JAR=edc-controlplane/edc-controlplane-memory/target/edc-controlplane-memory.jar + LIB=edc-controlplane/edc-controlplane-memory/target/lib + push: | + ${{ (needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' && 'true') || 'false' }} + tags: ${{ steps.edc_controlplane_memory_meta.outputs.tags }} + labels: ${{ steps.edc_controlplane_memory_meta.outputs.labels }} + - + name: Veracode Upload And Scan + uses: veracode/veracode-uploadandscan-action@v1.0 + if: | + needs.secret-presence.outputs.ORG_VERACODE_API_ID && needs.secret-presence.outputs.ORG_VERACODE_API_KEY && contains(' + refs/heads/develop + refs/heads/release/ + refs/tags/ + refs/heads/main', github.ref) + continue-on-error: true + with: + appname: 'product-edc/edc-controlplane-memory' + createprofile: true + version: ${{ github.ref }}-${{ github.sha }} + filepath: edc-controlplane/edc-controlplane-memory/target/edc-controlplane-memory.jar + vid: ${{ secrets.ORG_VERACODE_API_ID }} + vkey: ${{ secrets.ORG_VERACODE_API_KEY }} ################################### ### edc-controlplane-postgresql ### @@ -314,83 +362,83 @@ jobs: needs: [ secret-presence, verify-formatting ] runs-on: ubuntu-latest steps: - # Set-Up - - - name: Checkout - uses: actions/checkout@v3 - with: - submodules: recursive - - - name: Login to Github Packages - if: | - needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.CXNG_GHCR_PAT }} - - - name: Set up JDK 11 - uses: actions/setup-java@v3.4.1 - with: - java-version: '11' - distribution: 'adopt' - cache: 'maven' - - - name: Build edc with Gradle to get latest snapshots - run: ./gradlew publishToMavenLocal - working-directory: edc - # Build - - - name: Build edc-controlplane-postgresql - run: |- - ./mvnw -Pfailsafe -s settings.xml -B -pl .,edc-controlplane/edc-controlplane-postgresql -am verify - env: - GITHUB_PACKAGE_USERNAME: ${{ github.actor }} - GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} - - - name: edc-controlplane-postgresql Docker Metadata - id: edc_controlplane_postgresql_meta - uses: docker/metadata-action@v4 - with: - images: | - ghcr.io/${{ github.repository }}/edc-controlplane-postgresql - tags: | - type=ref,event=branch - type=ref,event=pr - type=semver,pattern={{raw}} - type=match,pattern=\d.\d.\d - type=sha - - - name: Build edc-controlplane-postgresql Docker Image - uses: docker/build-push-action@v3 - with: - context: . - file: edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile - build-args: | - JAR=edc-controlplane/edc-controlplane-postgresql/target/edc-controlplane-postgresql.jar - LIB=edc-controlplane/edc-controlplane-postgresql/target/lib - push: | - ${{ (needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' && 'true') || 'false' }} - tags: ${{ steps.edc_controlplane_postgresql_meta.outputs.tags }} - labels: ${{ steps.edc_controlplane_postgresql_meta.outputs.labels }} - - - name: Veracode Upload And Scan - uses: veracode/veracode-uploadandscan-action@v1.0 - if: | - needs.secret-presence.outputs.ORG_VERACODE_API_ID && needs.secret-presence.outputs.ORG_VERACODE_API_KEY && contains(' - refs/heads/develop - refs/heads/release/ - refs/tags/ - refs/heads/main', github.ref) - continue-on-error: true - with: - appname: 'product-edc/edc-controlplane-postgresql' - createprofile: true - filepath: edc-controlplane/edc-controlplane-postgresql/target/edc-controlplane-postgresql.jar - version: ${{ github.ref_name }}-${{ github.sha }} - vid: ${{ secrets.ORG_VERACODE_API_ID }} - vkey: ${{ secrets.ORG_VERACODE_API_KEY }} + # Set-Up + - + name: Checkout + uses: actions/checkout@v3 + with: + submodules: recursive + - + name: Login to Github Packages + if: | + needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.CXNG_GHCR_PAT }} + - + name: Set up JDK 11 + uses: actions/setup-java@v3.4.0 + with: + java-version: '11' + distribution: 'adopt' + cache: 'maven' + - + name: Build edc with Gradle to get latest snapshots + run: ./gradlew publishToMavenLocal + working-directory: edc + # Build + - + name: Build edc-controlplane-postgresql + run: |- + ./mvnw -s settings.xml -B -pl .,edc-controlplane/edc-controlplane-postgresql -am package + env: + GITHUB_PACKAGE_USERNAME: ${{ github.actor }} + GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} + - + name: edc-controlplane-postgresql Docker Metadata + id: edc_controlplane_postgresql_meta + uses: docker/metadata-action@v4 + with: + images: | + ghcr.io/${{ github.repository }}/edc-controlplane-postgresql + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{raw}} + type=match,pattern=\d.\d.\d + type=sha + - + name: Build edc-controlplane-postgresql Docker Image + uses: docker/build-push-action@v3 + with: + context: . + file: edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile + build-args: | + JAR=edc-controlplane/edc-controlplane-postgresql/target/edc-controlplane-postgresql.jar + LIB=edc-controlplane/edc-controlplane-postgresql/target/lib + push: | + ${{ (needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' && 'true') || 'false' }} + tags: ${{ steps.edc_controlplane_postgresql_meta.outputs.tags }} + labels: ${{ steps.edc_controlplane_postgresql_meta.outputs.labels }} + - + name: Veracode Upload And Scan + uses: veracode/veracode-uploadandscan-action@v1.0 + if: | + needs.secret-presence.outputs.ORG_VERACODE_API_ID && needs.secret-presence.outputs.ORG_VERACODE_API_KEY && contains(' + refs/heads/develop + refs/heads/release/ + refs/tags/ + refs/heads/main', github.ref) + continue-on-error: true + with: + appname: 'product-edc/edc-controlplane-postgresql' + createprofile: true + filepath: edc-controlplane/edc-controlplane-postgresql/target/edc-controlplane-postgresql.jar + version: ${{ github.ref_name }}-${{ github.sha }} + vid: ${{ secrets.ORG_VERACODE_API_ID }} + vkey: ${{ secrets.ORG_VERACODE_API_KEY }} ################################################### ### edc-controlplane-postgresql-hashicorp-vault ### @@ -429,7 +477,7 @@ jobs: - name: Build edc-controlplane-postgresql-hashicorp-vault run: |- - ./mvnw -Pfailsafe -s settings.xml -B -pl .,edc-controlplane/edc-controlplane-postgresql-hashicorp-vault -am verify + ./mvnw -s settings.xml -B -pl .,edc-controlplane/edc-controlplane-postgresql-hashicorp-vault -am package env: GITHUB_PACKAGE_USERNAME: ${{ github.actor }} GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index eb999ec77..c7d51f5c4 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -84,7 +84,7 @@ jobs: name: Deploy run: |- ./mvnw -s settings.xml \ - -Pdelombok -pl '!edc-controlplane,!edc-controlplane/edc-controlplane-memory,!edc-controlplane/edc-controlplane-postgresql,!edc-controlplane/edc-controlplane-postgresql-hashicorp-vault,!edc-dataplane/edc-dataplane-azure-vault,!edc-dataplane/edc-dataplane-hashicorp-vault' \ + -Pdelombok -pl '!edc-controlplane,!edc-controlplane/edc-controlplane-memory,!edc-controlplane/edc-controlplane-postgresql,!edc-controlplane/edc-controlplane-postgresql-hashicorp-vault,!edc-dataplane/edc-dataplane-azure-vault,!edc-dataplane/edc-dataplane-hashicorp-vault,!edc-tests' \ -DaltReleaseDeploymentRepository=github::https://maven.pkg.github.com/${{ github.repository }} \ -Dmaven.test.skip=true -B package deploy:deploy env: diff --git a/edc-controlplane/edc-controlplane-base/pom.xml b/edc-controlplane/edc-controlplane-base/pom.xml index b7e9dc70f..4ab06496b 100644 --- a/edc-controlplane/edc-controlplane-base/pom.xml +++ b/edc-controlplane/edc-controlplane-base/pom.xml @@ -12,6 +12,10 @@ edc-controlplane-base jar + + ${project.groupId}_${project.artifactId} + + ${project.artifactId} diff --git a/edc-controlplane/edc-controlplane-memory/pom.xml b/edc-controlplane/edc-controlplane-memory/pom.xml index 4b887828b..f29fed5ce 100644 --- a/edc-controlplane/edc-controlplane-memory/pom.xml +++ b/edc-controlplane/edc-controlplane-memory/pom.xml @@ -23,6 +23,10 @@ edc-controlplane-memory jar + + ${project.groupId}_${project.artifactId} + + ${project.artifactId} diff --git a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/pom.xml b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/pom.xml index d4e71a28d..93ac10ad6 100644 --- a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/pom.xml +++ b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/pom.xml @@ -24,6 +24,10 @@ edc-controlplane-postgresql-hashicorp-vault jar + + ${project.groupId}_${project.artifactId} + + ${project.artifactId} diff --git a/edc-controlplane/edc-controlplane-postgresql/pom.xml b/edc-controlplane/edc-controlplane-postgresql/pom.xml index 83e41a1e5..8163586e6 100644 --- a/edc-controlplane/edc-controlplane-postgresql/pom.xml +++ b/edc-controlplane/edc-controlplane-postgresql/pom.xml @@ -24,6 +24,10 @@ edc-controlplane-postgresql jar + + ${project.groupId}_${project.artifactId} + + ${project.artifactId} diff --git a/edc-controlplane/pom.xml b/edc-controlplane/pom.xml index 54bef7329..d4311d34c 100644 --- a/edc-controlplane/pom.xml +++ b/edc-controlplane/pom.xml @@ -24,6 +24,10 @@ edc-controlplane pom + + ${project.groupId}_${project.artifactId} + + edc-controlplane-base diff --git a/edc-dataplane/edc-dataplane-azure-vault/pom.xml b/edc-dataplane/edc-dataplane-azure-vault/pom.xml index 52d31aaa6..227a886cc 100644 --- a/edc-dataplane/edc-dataplane-azure-vault/pom.xml +++ b/edc-dataplane/edc-dataplane-azure-vault/pom.xml @@ -24,6 +24,10 @@ edc-dataplane-azure-vault jar + + ${project.groupId}_${project.artifactId} + + ${project.artifactId} diff --git a/edc-dataplane/edc-dataplane-base/pom.xml b/edc-dataplane/edc-dataplane-base/pom.xml index 9d9d80a71..7a1daf861 100644 --- a/edc-dataplane/edc-dataplane-base/pom.xml +++ b/edc-dataplane/edc-dataplane-base/pom.xml @@ -12,6 +12,10 @@ edc-dataplane-base jar + + ${project.groupId}_${project.artifactId} + + ${project.artifactId} diff --git a/edc-dataplane/edc-dataplane-hashicorp-vault/pom.xml b/edc-dataplane/edc-dataplane-hashicorp-vault/pom.xml index 535612d0f..00d8a6732 100644 --- a/edc-dataplane/edc-dataplane-hashicorp-vault/pom.xml +++ b/edc-dataplane/edc-dataplane-hashicorp-vault/pom.xml @@ -24,6 +24,10 @@ edc-dataplane-hashicorp-vault jar + + ${project.groupId}_${project.artifactId} + + ${project.artifactId} diff --git a/edc-dataplane/pom.xml b/edc-dataplane/pom.xml index 84b91e84e..870b708ff 100644 --- a/edc-dataplane/pom.xml +++ b/edc-dataplane/pom.xml @@ -24,6 +24,10 @@ edc-dataplane pom + + ${project.groupId}_${project.artifactId} + + edc-dataplane-base diff --git a/edc-extensions/business-partner-validation/pom.xml b/edc-extensions/business-partner-validation/pom.xml index c4815bda1..ed8ca3e38 100644 --- a/edc-extensions/business-partner-validation/pom.xml +++ b/edc-extensions/business-partner-validation/pom.xml @@ -24,6 +24,10 @@ business-partner-validation jar + + ${project.groupId}_${project.artifactId} + + diff --git a/edc-extensions/hashicorp-vault/pom.xml b/edc-extensions/hashicorp-vault/pom.xml index ceffaafcd..72aaa2b8c 100644 --- a/edc-extensions/hashicorp-vault/pom.xml +++ b/edc-extensions/hashicorp-vault/pom.xml @@ -28,6 +28,7 @@ ${project.basedir}/src/main/java ${originalSourceDirectory} ${project.build.directory}/delombok + ${project.groupId}_${project.artifactId} @@ -194,25 +195,5 @@ ${delombokSourceDirectory} - - - failsafe - - - - org.apache.maven.plugins - maven-failsafe-plugin - - - - integration-test - verify - - - - - - - diff --git a/edc-extensions/pom.xml b/edc-extensions/pom.xml index b59e3b97f..6ab78aa72 100644 --- a/edc-extensions/pom.xml +++ b/edc-extensions/pom.xml @@ -25,6 +25,10 @@ edc-extensions pom + + ${project.groupId}_${project.artifactId} + + business-partner-validation postgresql-migration diff --git a/edc-extensions/postgresql-migration/pom.xml b/edc-extensions/postgresql-migration/pom.xml index 81bc969b5..ea0e098bf 100644 --- a/edc-extensions/postgresql-migration/pom.xml +++ b/edc-extensions/postgresql-migration/pom.xml @@ -24,6 +24,10 @@ postgresql-migration jar + + ${project.groupId}_${project.artifactId} + + diff --git a/edc-tests/pom.xml b/edc-tests/pom.xml new file mode 100644 index 000000000..0672e001c --- /dev/null +++ b/edc-tests/pom.xml @@ -0,0 +1,106 @@ + + + + 4.0.0 + + + net.catenax.edc + product-edc-parent + 0.0.5-SNAPSHOT + + + net.catenax.edc.tests + edc-tests + + + ${project.groupId}_${project.artifactId} + + + + + + + + net.catenax.edc.extensions + business-partner-validation + + + net.catenax.edc.extensions + hashicorp-vault + + + net.catenax.edc.extensions + postgresql-migration + + + + + net.catenax.edc + edc-controlplane-base + + + net.catenax.edc + edc-controlplane-memory + + + net.catenax.edc + edc-controlplane-postgresql + + + net.catenax.edc + edc-controlplane-postgresql-hashicorp-vault + + + + + net.catenax.edc + edc-dataplane-base + + + net.catenax.edc + edc-dataplane-azure-vault + + + net.catenax.edc + edc-dataplane-hashicorp-vault + + + + + + coverage + + + + org.jacoco + jacoco-maven-plugin + + + report + + report-aggregate + + verify + + + + + + + + diff --git a/pom.xml b/pom.xml index 738b621c8..501127bfc 100644 --- a/pom.xml +++ b/pom.xml @@ -25,6 +25,7 @@ edc-extensions edc-controlplane edc-dataplane + edc-tests 2022 @@ -55,7 +56,9 @@ 3.10.1 3.2.0 1.18.20.0 + 0.8.8 1.1.0 + 3.9.1.2184 0.0.1-SNAPSHOT @@ -73,6 +76,11 @@ 1.17.3 2.0.0-alpha1 1.2.11 + + + catenax-ng + catenax-ng_product-edc + ${project.groupId}_${project.artifactId} @@ -180,6 +188,9 @@ org.apache.maven.plugins maven-surefire-plugin ${org.apache.maven.plugins.surefire.version} + + all + org.codehaus.mojo @@ -250,6 +261,16 @@ + + org.jacoco + jacoco-maven-plugin + ${org.jacoco.maven.plugin.version} + + + org.sonarsource.scanner.maven + sonar-maven-plugin + ${org.sonarsource.scanner.maven.plugin.version} + @@ -286,6 +307,18 @@ edc-controlplane-base ${project.version} + + net.catenax.edc + edc-dataplane-base + ${project.version} + + + + + net.catenax.edc + edc-controlplane-memory + ${project.version} + net.catenax.edc edc-controlplane-postgresql @@ -293,7 +326,19 @@ net.catenax.edc - edc-dataplane-base + edc-controlplane-postgresql-hashicorp-vault + ${project.version} + + + + + net.catenax.edc + edc-dataplane-azure-vault + ${project.version} + + + net.catenax.edc + edc-dataplane-hashicorp-vault ${project.version} @@ -1010,7 +1055,31 @@ + + + failsafe + + + + org.apache.maven.plugins + maven-failsafe-plugin + + all + + + + + integration-test + verify + + + + + + + + generate-notice @@ -1048,5 +1117,25 @@ + + + coverage + + + + org.jacoco + jacoco-maven-plugin + + + prepare-agent + + prepare-agent + + + + + + + - \ No newline at end of file + From 31caf811f94481fa4b194a4841d5368e80283907 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Thu, 21 Jul 2022 14:50:42 +0200 Subject: [PATCH 175/433] Disable shallow clone for sonar ci build (#280) --- .github/workflows/build.yaml | 1 + pom.xml | 2 ++ 2 files changed, 3 insertions(+) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 7659941e4..c19f46ae9 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -67,6 +67,7 @@ jobs: uses: actions/checkout@v3 with: submodules: recursive + fetch-depth: 0 - name: Set up JDK 11 uses: actions/setup-java@v3.3.0 diff --git a/pom.xml b/pom.xml index 501127bfc..25d567081 100644 --- a/pom.xml +++ b/pom.xml @@ -21,6 +21,8 @@ 0.0.5-SNAPSHOT pom + product-edc + edc-extensions edc-controlplane From 3fb9ed7687b1090b83e06c2a3ab803c038dca08e Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Mon, 25 Jul 2022 16:22:06 +0200 Subject: [PATCH 176/433] fix documentation (#287) --- docs/data-transfer/Transfer Data.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/data-transfer/Transfer Data.md b/docs/data-transfer/Transfer Data.md index 70449ecfa..855a1b5f3 100644 --- a/docs/data-transfer/Transfer Data.md +++ b/docs/data-transfer/Transfer Data.md @@ -146,7 +146,7 @@ locally. In this demo the transfer can be verified by executing a simple `cat` c ![Sequence 1](diagrams/transfer_sequence_5.png) ```bash -echo $(kubectl exec -n edc-all-in-one --stdin --tty `kubectl get pod -n edc-all-in-one -l app.kubernetes.io/name=sokrates-backend-application --template "{{ with index .items ${POD_INDEX:-0} }}{{ .metadata.name }}{{ end }}"` -- /usr/bin/cat /tmp/data/${TRANSFER_PROCESS_ID}) | jq +echo $(kubectl exec -n edc-all-in-one --stdin --tty `kubectl get pod -n edc-all-in-one -l app.kubernetes.io/name=sokratesbackendapplication --template "{{ with index .items ${POD_INDEX:-0} }}{{ .metadata.name }}{{ end }}"` -- /usr/bin/cat /tmp/data/${TRANSFER_PROCESS_ID}) | jq ``` # Delete All Data From 02dbb49df4772ab2ea845e5e0233a1ae4ee783c9 Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Mon, 25 Jul 2022 16:23:56 +0200 Subject: [PATCH 177/433] update CHANGELOG.md (#277) --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e6386a5b..61d56a355 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - BusinessPartnerNumber constraint supports List structure -- DAPS of All-In-One-Deployment now supports BusinessPartnerNumbers +- Confidential EDC settings can be set using K8 secrets +- DAPS of All-In-One-Deployment supports BusinessPartnerNumbers ## [0.0.4] - 2022-06-27 From 7d26e737c12099125ec13eef25823a5ad8c92dad Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Tue, 26 Jul 2022 15:51:46 +0200 Subject: [PATCH 178/433] Add Business-Tests and integrate into CI (#248) --- .github/workflows/build.yaml | 2 +- .github/workflows/business-tests.yaml | 259 ++++++++++++ docs/data-transfer/Transfer Data.md | 2 +- edc-tests/README.md | 13 + edc-tests/pom.xml | 134 ++++-- .../deployment/helm/all-in-one/Chart.yaml | 11 + .../deployment/helm/all-in-one/values.yaml | 11 + .../helm/backend-application/Chart.yaml | 3 +- .../helm/backend-application/README.md | 54 ++- .../helm/backend-application/README.md.gotmpl | 19 + .../templates/configmap.yaml | 1 + .../templates/deployment.yaml | 11 + .../backend-application/templates/hpa.yaml | 1 + .../backend-application/templates/pvc.yaml | 26 ++ .../templates/service.yaml | 1 + .../templates/serviceaccount.yaml | 1 + .../helm/backend-application/values.yaml | 10 + .../helm/omejdn/templates/deployment.yaml | 23 +- .../net/catenax/edc/tests/AssetStepDefs.java | 56 +++ .../catenax/edc/tests/CatalogStepDefs.java | 58 +++ .../java/net/catenax/edc/tests/Connector.java | 34 ++ .../catenax/edc/tests/ConnectorFactory.java | 37 ++ .../java/net/catenax/edc/tests/Constants.java | 29 ++ .../edc/tests/ContractDefinitionStepDefs.java | 62 +++ .../catenax/edc/tests/DataManagementAPI.java | 382 ++++++++++++++++++ .../net/catenax/edc/tests/Environment.java | 53 +++ .../net/catenax/edc/tests/PolicyStepDefs.java | 60 +++ .../net/catenax/edc/tests/data/Asset.java | 24 ++ .../edc/tests/data/ContractDefinition.java | 29 ++ .../catenax/edc/tests/data/ContractOffer.java | 24 ++ .../catenax/edc/tests/data/Permission.java | 23 ++ .../net/catenax/edc/tests/data/Policy.java | 25 ++ .../edc/tests/features/ParameterTypes.java | 13 + .../edc/tests/features/RunCucumberTest.java | 22 + .../test/resources/junit-platform.properties | 3 + edc-tests/src/test/resources/logback-test.xml | 27 ++ .../edc/tests/features/ContractOffers.feature | 37 ++ pom.xml | 20 + 38 files changed, 1557 insertions(+), 43 deletions(-) create mode 100644 .github/workflows/business-tests.yaml create mode 100644 edc-tests/README.md create mode 100644 edc-tests/src/main/resources/deployment/helm/backend-application/README.md.gotmpl create mode 100644 edc-tests/src/main/resources/deployment/helm/backend-application/templates/pvc.yaml create mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/AssetStepDefs.java create mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/CatalogStepDefs.java create mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/Connector.java create mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/ConnectorFactory.java create mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/Constants.java create mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/ContractDefinitionStepDefs.java create mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/DataManagementAPI.java create mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/Environment.java create mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/PolicyStepDefs.java create mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/data/Asset.java create mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/data/ContractDefinition.java create mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/data/ContractOffer.java create mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/data/Permission.java create mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/data/Policy.java create mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/features/ParameterTypes.java create mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/features/RunCucumberTest.java create mode 100644 edc-tests/src/test/resources/junit-platform.properties create mode 100644 edc-tests/src/test/resources/logback-test.xml create mode 100644 edc-tests/src/test/resources/net/catenax/edc/tests/features/ContractOffers.feature diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index c19f46ae9..2a1e4669a 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -138,7 +138,7 @@ jobs: - name: Build edc-dataplane-azure-vault run: |- - ./mvnw -Pfailsafe -s settings.xml -B -pl .,edc-dataplane/edc-dataplane-azure-vault -am verify + ./mvnw -s settings.xml -B -pl .,edc-dataplane/edc-dataplane-azure-vault -am package env: GITHUB_PACKAGE_USERNAME: ${{ github.actor }} GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} diff --git a/.github/workflows/business-tests.yaml b/.github/workflows/business-tests.yaml new file mode 100644 index 000000000..b85dafb39 --- /dev/null +++ b/.github/workflows/business-tests.yaml @@ -0,0 +1,259 @@ +--- +name: "Business Tests" + +on: + pull_request: + paths-ignore: + - docs/** + branches: + - develop/** + - release/** + - main + workflow_dispatch: + +jobs: + business-test: + runs-on: ubuntu-latest + steps: + ############## + ### Set-Up ### + ############## + - + name: Checkout + uses: actions/checkout@v3 + with: + submodules: recursive + - + name: Set-Up JDK 11 + uses: actions/setup-java@v3.4.0 + with: + java-version: '11' + distribution: 'adopt' + cache: 'maven' + - + name: Cache ContainerD Image Layers + uses: actions/cache@v3 + with: + path: /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs + key: ${{ runner.os }}-io.containerd.snapshotter.v1.overlayfs + - + name: Set-Up Kubectl + uses: azure/setup-kubectl@v2.0 + - + name: Helm Set-Up + uses: azure/setup-helm@v2.1 + with: + version: v3.8.1 + - + name: Create k8s Kind Cluster configuration (kind.config.yaml) + run: |- + export MAVEN_REPOSITORY=$(./mvnw help:evaluate -Dexpression=settings.localRepository -q -DforceStdout) + cat << EOF > kind.config.yaml + --- + kind: Cluster + apiVersion: kind.x-k8s.io/v1alpha4 + nodes: + - role: control-plane + extraMounts: + - hostPath: ${PWD} + containerPath: /srv/product-edc + - hostPath: ${MAVEN_REPOSITORY} + containerPath: /srv/m2-repository + - hostPath: /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs + containerPath: /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs + EOF + - + name: Create k8s Kind Cluster + uses: helm/kind-action@v1.3.0 + with: + config: kind.config.yaml + + ############################################## + ### Build and load recent images into KinD ### + ############################################## + - + name: Build edc with Gradle to get latest snapshots + run: ./gradlew publishToMavenLocal + working-directory: edc + - + name: Build edc-controlplane-postgresql-hashicorp-vault + run: |- + ./mvnw -s settings.xml -B -pl .,edc-controlplane/edc-controlplane-postgresql-hashicorp-vault -am package -Dmaven.test.skip=true -Pwith-docker-image + env: + GITHUB_PACKAGE_USERNAME: ${{ github.actor }} + GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + - + name: Build edc-dataplane-hashicorp-vault + run: |- + ./mvnw -s settings.xml -B -pl .,edc-dataplane/edc-dataplane-hashicorp-vault -am package -Dmaven.test.skip=true -Pwith-docker-image + env: + GITHUB_PACKAGE_USERNAME: ${{ github.actor }} + GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} + - + name: Load images into KinD + run: |- + kind get clusters | xargs -n1 kind load docker-image edc-controlplane-postgresql-hashicorp-vault:latest edc-dataplane-hashicorp-vault:latest --name + + ############################################ + ### Prepare And Install Test Environment ### + ############################################ + - + name: Define test environment variables + run: |- + # Define endpoints + echo "SOKRATES_DATA_MANAGEMENT_URL=http://sokrates-edc-controlplane:8181/data" | tee -a ${GITHUB_ENV} + echo "SOKRATES_IDS_URL=http://sokrates-edc-controlplane:8282/api/v1/ids" | tee -a ${GITHUB_ENV} + echo "SOKRATES_DATA_PLANE_URL=http://sokrates-edc-dataplane:8185/api/public" | tee -a ${GITHUB_ENV} + echo "PLATO_DATA_MANAGEMENT_URL=http://plato-edc-controlplane:8181/data" | tee -a ${GITHUB_ENV} + echo "PLATO_IDS_URL=http://plato-edc-controlplane:8282/api/v1/ids" | tee -a ${GITHUB_ENV} + echo "PLATO_DATA_PLANE_URL=http://plato-edc-dataplane:8185/api/public" | tee -a ${GITHUB_ENV} + - + name: Install test environment via Helm + run: |- + # Update helm dependencies + helm dependency update edc-tests/src/main/resources/deployment/helm/all-in-one + + # Install the all-in-one supporting infrastructure environment (daps, vault, pgsql) + helm install test-environment edc-tests/src/main/resources/deployment/helm/all-in-one \ + --set platoedccontrolplane.image.tag=latest \ + --set sokratesedccontrolplane.image.tag=latest \ + --set platoedcdataplane.image.tag=latest \ + --set sokratesedcdataplane.image.tag=latest \ + --set idsdaps.enabled=true \ + --set platovault.enabled=true \ + --set platopostgresql.enabled=true \ + --set sokratesvault.enabled=true \ + --set sokratespostgresql.enabled=true \ + --set platoedccontrolplane.enabled=false \ + --set platoedcdataplane.enabled=false \ + --set platobackendapplication.enabled=false \ + --set sokratesedccontrolplane.enabled=false \ + --set sokratesedcdataplane.enabled=false \ + --set sokratesbackendapplication.enabled=false \ + --set sokrates-backend-application.persistence.enabled=false \ + --set plato-backend-application.persistence.enabled=false \ + --wait-for-jobs --timeout=120s + + # GH pipelines constrained by cpu, so give helm some time to register all resources \w k8s + sleep 5s + + # Wait for supporting infrastructure to become ready (control-/data-plane, backend service) + kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=idsdaps --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=idsdaps && exit 1 ) + kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=sokratesvault --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=sokratesvault && exit 1 ) + kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=platovault --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=platovault && exit 1 ) + kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=sokratespostgresql --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=sokratespostgresql && exit 1 ) + kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=platopostgresql --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=platopostgresql && exit 1 ) + + # Install the all-in-one Control-/DataPlanes and backend-services + helm upgrade --install test-environment edc-tests/src/main/resources/deployment/helm/all-in-one \ + --set platoedccontrolplane.image.tag=latest \ + --set sokratesedccontrolplane.image.tag=latest \ + --set platoedcdataplane.image.tag=latest \ + --set sokratesedcdataplane.image.tag=latest \ + --set idsdaps.enabled=true \ + --set platovault.enabled=true \ + --set platopostgresql.enabled=true \ + --set sokratesvault.enabled=true \ + --set sokratespostgresql.enabled=true \ + --set platoedccontrolplane.enabled=true \ + --set platoedcdataplane.enabled=true \ + --set platobackendapplication.enabled=true \ + --set sokratesedccontrolplane.enabled=true \ + --set sokratesedcdataplane.enabled=true \ + --set sokratesbackendapplication.enabled=true \ + --set sokrates-backend-application.persistence.enabled=true \ + --set plato-backend-application.persistence.enabled=true \ + --wait-for-jobs --timeout=120s + + # GH pipelines constrained by cpu, so give helm some time to register all resources \w k8s + sleep 5s + + # Wait for Control-/DataPlane and backend-service to become ready + kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=sokratesbackendapplication --timeout=120s || ( kubectl logs -since=0 -l app.kubernetes.io/name=sokratesbackendapplication && exit 1 ) + kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=platobackendapplication --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=platobackendapplication && exit 1 ) + kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=sokratesedcdataplane --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=sokratesedcdataplane && exit 1 ) + kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=platoedcdataplane --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=platoedcdataplane && exit 1 ) + kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=sokratesedccontrolplane --timeout=600s || ( kubectl logs -l app.kubernetes.io/name=sokratesedccontrolplane && exit 1 ) + kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=platoedccontrolplane --timeout=600s || ( kubectl logs -l app.kubernetes.io/name=platoedccontrolplane && exit 1 ) + + ############################################## + ### Run Business Tests inside kind cluster ### + ############################################## + - + name: Run Business Tests + run: |- + cat << EOF >> pod.json + { + "apiVersion": "v1", + "kind": "Pod", + "spec": { + "containers": [ + { + "args": [ + "-c", + "cd /product-edc && ./mvnw -s settings.xml -B -Pbusiness-tests -pl edc-tests test -Dtest=net.catenax.edc.tests.features.RunCucumberTest" + ], + "command": [ + "/bin/sh" + ], + EOF + + # Ugly hack to get env vars passed into the k8s-run - if '--overrides' defined '--env' is ignored :( + cat << EOF >> pod.json + "env": [ + {"name": "SOKRATES_DATA_MANAGEMENT_API_AUTH_KEY", "value": "${SOKRATES_DATA_MANAGEMENT_API_AUTH_KEY}"}, + {"name": "PLATO_DATA_MANAGEMENT_API_AUTH_KEY", "value": "${PLATO_DATA_MANAGEMENT_API_AUTH_KEY}"}, + {"name": "SOKRATES_DATA_MANAGEMENT_URL", "value": "${SOKRATES_DATA_MANAGEMENT_URL}"}, + {"name": "SOKRATES_IDS_URL", "value": "${SOKRATES_IDS_URL}"}, + {"name": "SOKRATES_DATA_PLANE_URL", "value": "${SOKRATES_DATA_PLANE_URL}"}, + {"name": "PLATO_DATA_MANAGEMENT_URL", "value": "${PLATO_DATA_MANAGEMENT_URL}"}, + {"name": "PLATO_IDS_URL", "value": "${PLATO_IDS_URL}"}, + {"name": "PLATO_DATA_PLANE_URL", "value": "${PLATO_DATA_PLANE_URL}"} + ], + EOF + + cat << EOF >> pod.json + "image": "openjdk:11-jdk-slim", + "name": "edc-tests", + "volumeMounts": [ + { + "mountPath": "/product-edc", + "name": "product-edc" + }, + { + "mountPath": "/root/.m2/repository", + "name": "m2-repository" + } + ] + } + ], + "dnsPolicy": "ClusterFirst", + "restartPolicy": "Never", + "volumes": [ + { + "hostPath": { + "path": "/srv/product-edc" + }, + "name": "product-edc" + }, + { + "hostPath": { + "path": "/srv/m2-repository" + }, + "name": "m2-repository" + } + ] + } + } + EOF + + kubectl run -i --image=openjdk:11-jdk-slim --restart=Never --rm edc-tests --overrides="$(cat pod.json)" + + ################# + ### Tear Down ### + ################# + - + name: Destroy the kind cluster + if: always() + run: >- + kind get clusters | xargs -n1 kind delete cluster --name diff --git a/docs/data-transfer/Transfer Data.md b/docs/data-transfer/Transfer Data.md index 855a1b5f3..4da708597 100644 --- a/docs/data-transfer/Transfer Data.md +++ b/docs/data-transfer/Transfer Data.md @@ -157,4 +157,4 @@ minikube kubectl -- delete pvc -n edc-all-in-one --all ```bash minikube kubectl -- delete pv -n edc-all-in-one --all -``` \ No newline at end of file +``` diff --git a/edc-tests/README.md b/edc-tests/README.md new file mode 100644 index 000000000..1169a1998 --- /dev/null +++ b/edc-tests/README.md @@ -0,0 +1,13 @@ +# Invoke Business-Tests via Maven + +```shell +./mvnw -pl edc-tests -Pbusiness-tests -pl edc-tests test -Dtest=net.catenax.edc.tests.features.RunCucumberTest +``` + +# Test locally using Act Tool + +> "Think globally, [`act`](https://github.com/nektos/act) locally" + +```shell +act -j business-test +``` \ No newline at end of file diff --git a/edc-tests/pom.xml b/edc-tests/pom.xml index 0672e001c..4a5036fe0 100644 --- a/edc-tests/pom.xml +++ b/edc-tests/pom.xml @@ -24,66 +24,94 @@ net.catenax.edc.tests edc-tests + jar ${project.groupId}_${project.artifactId} + 2.8.8 + 4.5.13 + 1.2.11 + 1.7.36 - - - - net.catenax.edc.extensions - business-partner-validation + org.projectlombok + lombok + test - net.catenax.edc.extensions - hashicorp-vault + com.google.code.gson + gson + ${com.google.code.gson.version} + test - net.catenax.edc.extensions - postgresql-migration + org.apache.httpcomponents + httpclient + ${org.apache.httpcomponents.httpclient.version} + test - - - net.catenax.edc - edc-controlplane-base + org.junit.jupiter + junit-jupiter-api + test - net.catenax.edc - edc-controlplane-memory + org.junit.platform + junit-platform-suite + test - net.catenax.edc - edc-controlplane-postgresql + io.cucumber + cucumber-java + test - net.catenax.edc - edc-controlplane-postgresql-hashicorp-vault + io.cucumber + cucumber-junit-platform-engine + test - - - net.catenax.edc - edc-dataplane-base + io.rest-assured + rest-assured + test - net.catenax.edc - edc-dataplane-azure-vault + ch.qos.logback + logback-classic + ${ch.qos.logback.version} + test - net.catenax.edc - edc-dataplane-hashicorp-vault + org.slf4j + slf4j-api + ${org.slf4j.api.version} + test + + default + + true + + + true + + + + business-tests + + false + + coverage + + true + @@ -101,6 +129,54 @@ + + + + + net.catenax.edc.extensions + business-partner-validation + + + net.catenax.edc.extensions + hashicorp-vault + + + net.catenax.edc.extensions + postgresql-migration + + + + net.catenax.edc + edc-controlplane-base + + + net.catenax.edc + edc-controlplane-memory + + + net.catenax.edc + edc-controlplane-postgresql + + + net.catenax.edc + edc-controlplane-postgresql-hashicorp-vault + + + + net.catenax.edc + edc-dataplane-base + + + net.catenax.edc + edc-dataplane-azure-vault + + + net.catenax.edc + edc-dataplane-hashicorp-vault + + diff --git a/edc-tests/src/main/resources/deployment/helm/all-in-one/Chart.yaml b/edc-tests/src/main/resources/deployment/helm/all-in-one/Chart.yaml index 49495a47e..176e5014a 100644 --- a/edc-tests/src/main/resources/deployment/helm/all-in-one/Chart.yaml +++ b/edc-tests/src/main/resources/deployment/helm/all-in-one/Chart.yaml @@ -30,47 +30,58 @@ dependencies: version: 0.0.1 repository: "file://../omejdn" alias: idsdaps + condition: idsdaps.enabled # PLATO CONNECTOR - name: edc-controlplane version: ">=0.0.1" repository: "file://../../../../../../../deployment/helm/edc-controlplane" alias: platoedccontrolplane + condition: platoedccontrolplane.enabled - name: edc-dataplane version: ">=0.0.1" repository: "file://../../../../../../../deployment/helm/edc-dataplane" alias: platoedcdataplane + condition: platoedcdataplane.enabled - name: backend-application version: ">=0.0.1" repository: "file://../backend-application" alias: platobackendapplication + condition: platobackendapplication.enabled - name: vault version: 0.20.0 repository: https://helm.releases.hashicorp.com alias: platovault + condition: platovault.enabled - name: postgresql version: 11.2.4 repository: https://charts.bitnami.com/bitnami alias: platopostgresql + condition: platopostgresql.enabled # SOKRATES CONNECTOR - name: edc-controlplane version: ">=0.0.1" repository: "file://../../../../../../../deployment/helm/edc-controlplane" alias: sokratesedccontrolplane + condition: sokratesedccontrolplane.enabled - name: edc-dataplane version: ">=0.0.1" repository: "file://../../../../../../../deployment/helm/edc-dataplane" alias: sokratesedcdataplane + condition: sokratesedcdataplane.enabled - name: backend-application version: ">=0.0.1" repository: "file://../backend-application" alias: sokratesbackendapplication + condition: sokratesbackendapplication.enabled - name: vault version: 0.20.0 repository: https://helm.releases.hashicorp.com alias: sokratesvault + condition: sokratesvault.enabled - name: postgresql version: 11.2.4 repository: https://charts.bitnami.com/bitnami alias: sokratespostgresql + condition: sokratespostgresql.enabled diff --git a/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml b/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml index 9bebbbe8d..0f7ed8715 100644 --- a/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml +++ b/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml @@ -5,6 +5,7 @@ # Declare variables to be passed into your templates. idsdaps: + enabled: true fullnameOverride: "ids-daps" connectors: - id: &sokratesDapsClientId E7:07:2D:74:56:66:31:F0:7B:10:EA:B6:03:06:4C:23:7F:ED:A6:65:keyid:E7:07:2D:74:56:66:31:F0:7B:10:EA:B6:03:06:4C:23:7F:ED:A6:65 @@ -72,10 +73,12 @@ idsdaps: # PLATO # ######### platobackendapplication: + enabled: true fullnameOverride: "plato-backend-application" service: port: 80 platopostgresql: + enabled: true fullnameOverride: "plato-postgresql" auth: password: &psqlPassword "psql_password" @@ -84,6 +87,7 @@ platopostgresql: persistence: enabled: false platovault: + enabled: true fullnameOverride: "plato-vault" injector: enabled: false @@ -165,6 +169,7 @@ platovault: externalPort: 8200 targetPort: 8200 platoedcdataplane: + enabled: true fullnameOverride: "plato-edc-dataplane" image: repository: &edcDataPlaneImage ghcr.io/catenax-ng/product-edc/edc-dataplane-hashicorp-vault @@ -196,6 +201,7 @@ platoedcdataplane: # see extension https://github.com/catenax-ng/product-edc/tree/develop/edc-extensions/hashicorp-vault EDC_VAULT_HASHICORP_URL: http://plato-vault platoedccontrolplane: + enabled: true fullnameOverride: "plato-edc-controlplane" service: type: NodePort @@ -285,16 +291,19 @@ platoedccontrolplane: # SOKRATES # ############ sokratesbackendapplication: + enabled: true fullnameOverride: "sokrates-backend-application" service: port: 80 sokratespostgresql: + enabled: true fullnameOverride: "sokrates-postgresql" auth: password: *psqlPassword username: *psqlUsername database: *psqlDatabase sokratesvault: + enabled: true fullnameOverride: "sokrates-vault" injector: enabled: false @@ -376,6 +385,7 @@ sokratesvault: externalPort: 8200 targetPort: 8200 sokratesedcdataplane: + enabled: true fullnameOverride: "sokrates-edc-dataplane" image: repository: *edcDataPlaneImage @@ -408,6 +418,7 @@ sokratesedcdataplane: EDC_VAULT_HASHICORP_URL: http://sokrates-vault-0 EDC_VAULT_HASHICORP_TOKEN: *sokratesVaultToken sokratesedccontrolplane: + enabled: true fullnameOverride: "sokrates-edc-controlplane" service: type: NodePort diff --git a/edc-tests/src/main/resources/deployment/helm/backend-application/Chart.yaml b/edc-tests/src/main/resources/deployment/helm/backend-application/Chart.yaml index cdfd13060..64fa517fb 100644 --- a/edc-tests/src/main/resources/deployment/helm/backend-application/Chart.yaml +++ b/edc-tests/src/main/resources/deployment/helm/backend-application/Chart.yaml @@ -1,7 +1,8 @@ --- apiVersion: v2 name: backend-application -description: A Helm chart for Kubernetes +description: >- + The Eclipse Dataspace Connector requires the Backend Application to transfer data using the HTTP-TransferMethod. # A chart can be either an 'application' or a 'library' chart. # diff --git a/edc-tests/src/main/resources/deployment/helm/backend-application/README.md b/edc-tests/src/main/resources/deployment/helm/backend-application/README.md index 45b9855b1..717ac040e 100644 --- a/edc-tests/src/main/resources/deployment/helm/backend-application/README.md +++ b/edc-tests/src/main/resources/deployment/helm/backend-application/README.md @@ -1,3 +1,55 @@ -# Backend Application +# backend-application + +![Version: 0.0.1](https://img.shields.io/badge/Version-0.0.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.0.1](https://img.shields.io/badge/AppVersion-0.0.1-informational?style=flat-square) The Eclipse Dataspace Connector requires the Backend Application to transfer data using the HTTP-TransferMethod. + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| affinity | object | `{}` | [Affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) constrains which nodes the Pod can be scheduled on based on node labels. | +| automountServiceAccountToken | bool | `false` | Whether to [automount kubernetes API credentials](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server) into the pod | +| autoscaling.enabled | bool | `false` | Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) | +| autoscaling.maxReplicas | int | `100` | Maximum replicas if resource consumption exceeds resource threshholds | +| autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | +| autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | +| autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | +| container.port | int | `8080` | | +| fullnameOverride | string | `""` | Overrides the releases full name | +| image.command[0] | string | `"/bin/bash"` | | +| image.command[1] | string | `"-c"` | | +| image.command[2] | string | `"apt-get update && apt-get install -y ucspi-tcp curl jq && rm -rf /var/lib/apt/lists/* && tcpserver -v 0.0.0.0 \"${TCP_SERVER_PORT}\" \"${TCP_SERVER_SCRIPT_PATH}\""` | | +| image.pullPolicy | string | `"IfNotPresent"` | [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use | +| image.repository | string | `"ubuntu"` | Which container image to use | +| image.tag | string | `"22.04"` | Overrides the image tag whose default is the chart appVersion | +| imagePullSecrets | list | `[]` | Image pull secret to create to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) | +| livenessProbe | object | `{"exec":{"command":["/bin/bash","-c","/usr/bin/ps -ef | grep tcpserver | grep -v grep"]},"initialDelaySeconds":10,"periodSeconds":10}` | [Liveness-Probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-a-liveness-command) to detect and remedy broken applications | +| livenessProbe.exec | object | `{"command":["/bin/bash","-c","/usr/bin/ps -ef | grep tcpserver | grep -v grep"]}` | exec command for liveness check | +| livenessProbe.initialDelaySeconds | int | `10` | initialDelaySeconds before performing the first probe | +| livenessProbe.periodSeconds | int | `10` | periodSeconds between each probe | +| nameOverride | string | `""` | Overrides the charts name | +| nodeSelector | object | `{}` | [Node-Selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) to constrain the Pod to nodes with specific labels. | +| persistence.accessMode | string | `nil` | [PersistentVolume Access Modes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes) Access mode to use. One of (ReadOnlyMany, ReadWriteOnce, ReadWriteMany, ReadWriteOncePod) | +| persistence.capacity | string | `"100M"` | Capacity given to the claimed [PersistentVolume](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) | +| persistence.enabled | bool | `false` | Whether to enable persistence via [PersistentVolumeClaim](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#reserving-a-persistentvolume) | +| persistence.storageClassName | string | `nil` | Storage class to use together with the claimed [PersistentVolume](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) | +| podAnnotations | object | `{}` | [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) added to deployed [pods](https://kubernetes.io/docs/concepts/workloads/pods/) | +| podSecurityContext | object | `{}` | | +| readinessProbe | object | `{"exec":{"command":["/bin/bash","-c","/usr/bin/ps -ef | grep tcpserver | grep -v grep"]},"initialDelaySeconds":10,"periodSeconds":10}` | [Readiness-Probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-readiness-probes) to detect ready applications to receive traffic | +| readinessProbe.exec | object | `{"command":["/bin/bash","-c","/usr/bin/ps -ef | grep tcpserver | grep -v grep"]}` | exec command for readiness check | +| readinessProbe.initialDelaySeconds | int | `10` | initialDelaySeconds before performing the first probe | +| readinessProbe.periodSeconds | int | `10` | periodSeconds between each probe | +| replicaCount | int | `1` | | +| resources | object | `{}` | [Resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) applied to the deployed pod | +| script | object | `{"content":"#!/bin/bash\n\nPAYLOAD=\"\"\nPAYLOAD_INCOMING=0\nexport TMOUT=3.5\nwhile IFS= read -r LINE || [ \"$LINE\" ]; do\n if [ $PAYLOAD_INCOMING -eq 1 ]; then\n PAYLOAD=\"${PAYLOAD}${LINE}\"\n break\n fi\n\n if [[ \"${#LINE}\" = \"1\" && \"$(printf \"%d\" \"'${LINE}\")\" = \"13\" ]]; then\n PAYLOAD_INCOMING=1\n fi\ndone\n\nif [ -z \"$PAYLOAD\" ]; then\n echo -ne \"HTTP/1.1 400 Bad Request\\r\\nContent-Length: 2\\r\\nContent-Type: application/json\\r\\nConnection: close\\r\\n\\r\\n{}\"\n exit 1\nfi\n\nENDPOINT=$(echo $PAYLOAD | jq -r '.endpoint')\nif [ -z \"$ENDPOINT\" ]; then\n echo -ne \"HTTP/1.1 400 Bad Request\\r\\nContent-Length: 2\\r\\nContent-Type: application/json\\r\\nConnection: close\\r\\n\\r\\n{}\"\n exit 1\nfi\n\nID=$(echo $PAYLOAD | jq -r '.id')\nAUTH_KEY=$(echo $PAYLOAD | jq -r '.authKey')\nAUTH_CODE=$(echo $PAYLOAD | jq -r '.authCode')\n\nmkdir -p /tmp/data/\necho \"${AUTH_KEY}: ${AUTH_CODE}\" >| header.txt\n\ncurl -L -H @header.txt -o \"/tmp/data/${ID}\" ${ENDPOINT}\nif [ ! $? -eq 0 ]; then\n echo \"calling endpoint ($ENDPOINT) failed ($?)\" 1>&2\n echo -ne \"HTTP/1.1 400 Bad Request\\r\\nContent-Length: 2\\r\\nContent-Type: application/json\\r\\nConnection: close\\r\\n\\r\\n{}\"\n exit 1\nfi\n\necho -ne \"HTTP/1.1 200 OK\\r\\nContent-Length: 2\\r\\nContent-Type: application/json\\r\\nConnection: close\\r\\n\\r\\n{}\"","path":"/opt/tcpserver/handler.sh"}` | script invoked on http calls | +| securityContext | object | `{}` | | +| service.port | int | `80` | [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service) to expose the running application on a set of Pods as a network service. | +| service.type | string | `"ClusterIP"` | [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. | +| serviceAccount.annotations | object | `{}` | [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) to add to the service account | +| serviceAccount.create | bool | `true` | Specifies whether a [service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) should be created per release | +| serviceAccount.name | string | `""` | The name of the service account to use. If not set and create is true, a name is generated using the release's fullname template | +| tolerations | list | `[]` | [Tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) are applied to Pods to schedule onto nodes with matching taints. | + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.10.0](https://github.com/norwoodj/helm-docs/releases/v1.10.0) diff --git a/edc-tests/src/main/resources/deployment/helm/backend-application/README.md.gotmpl b/edc-tests/src/main/resources/deployment/helm/backend-application/README.md.gotmpl new file mode 100644 index 000000000..6cfc6fa29 --- /dev/null +++ b/edc-tests/src/main/resources/deployment/helm/backend-application/README.md.gotmpl @@ -0,0 +1,19 @@ +{{ template "chart.header" . }} + +{{ template "chart.deprecationWarning" . }} + +{{ template "chart.badgesSection" . }} + +{{ template "chart.description" . }} + +{{ template "chart.homepageLine" . }} + +{{ template "chart.maintainersSection" . }} + +{{ template "chart.sourcesSection" . }} + +{{ template "chart.requirementsSection" . }} + +{{ template "chart.valuesSection" . }} + +{{ template "helm-docs.versionFooter" . }} diff --git a/edc-tests/src/main/resources/deployment/helm/backend-application/templates/configmap.yaml b/edc-tests/src/main/resources/deployment/helm/backend-application/templates/configmap.yaml index 4f9d006ed..a150eeb45 100644 --- a/edc-tests/src/main/resources/deployment/helm/backend-application/templates/configmap.yaml +++ b/edc-tests/src/main/resources/deployment/helm/backend-application/templates/configmap.yaml @@ -1,3 +1,4 @@ +--- apiVersion: v1 kind: ConfigMap metadata: diff --git a/edc-tests/src/main/resources/deployment/helm/backend-application/templates/deployment.yaml b/edc-tests/src/main/resources/deployment/helm/backend-application/templates/deployment.yaml index 1d8751e2d..603d7b296 100644 --- a/edc-tests/src/main/resources/deployment/helm/backend-application/templates/deployment.yaml +++ b/edc-tests/src/main/resources/deployment/helm/backend-application/templates/deployment.yaml @@ -1,3 +1,4 @@ +--- apiVersion: apps/v1 kind: Deployment metadata: @@ -50,6 +51,8 @@ spec: - name: configmap mountPath: {{ .Values.script.path }} subPath: handler.sh + - name: data + mountPath: /tmp/data env: - name: TCP_SERVER_SCRIPT_PATH value: {{ .Values.script.path | quote }} @@ -67,6 +70,14 @@ spec: - key: handler.sh path: handler.sh defaultMode: 0744 + - name: data + {{- if .Values.persistence.enabled }} + persistentVolumeClaim: + claimName: {{ include "backend-application.fullname" . }}-pvc + {{ else }} + emptyDir: + {} + {{- end }} {{- with .Values.nodeSelector }} nodeSelector: {{- toYaml . | nindent 8 }} diff --git a/edc-tests/src/main/resources/deployment/helm/backend-application/templates/hpa.yaml b/edc-tests/src/main/resources/deployment/helm/backend-application/templates/hpa.yaml index 7e01b6053..cda38c67c 100644 --- a/edc-tests/src/main/resources/deployment/helm/backend-application/templates/hpa.yaml +++ b/edc-tests/src/main/resources/deployment/helm/backend-application/templates/hpa.yaml @@ -1,4 +1,5 @@ {{- if .Values.autoscaling.enabled }} +--- apiVersion: autoscaling/v2beta1 kind: HorizontalPodAutoscaler metadata: diff --git a/edc-tests/src/main/resources/deployment/helm/backend-application/templates/pvc.yaml b/edc-tests/src/main/resources/deployment/helm/backend-application/templates/pvc.yaml new file mode 100644 index 000000000..c4c98cd3a --- /dev/null +++ b/edc-tests/src/main/resources/deployment/helm/backend-application/templates/pvc.yaml @@ -0,0 +1,26 @@ +{{ if .Values.persistence.enabled -}} +--- +apiVersion: "v1" +kind: PersistentVolumeClaim +metadata: + name: {{ include "backend-application.fullname" . }}-pvc + labels: + {{- include "backend-application.labels" . | nindent 4 }} +spec: + {{- if .Values.persistence.storageClassName }} + storageClassName: {{ .Values.persistence.storageClassName | quote }} + {{- end }} + accessModes: + {{- if .Values.persistence.accessMode }} + - {{ .Values.persistence.accessMode | quote }} + {{ else }} + {{- if .Values.autoscaling.enabled }} + - ReadWriteMany + {{ else }} + - ReadWriteOnce + {{- end }} + {{- end }} + resources: + requests: + storage: {{ .Values.persistence.capacity | quote }} +{{ end -}} diff --git a/edc-tests/src/main/resources/deployment/helm/backend-application/templates/service.yaml b/edc-tests/src/main/resources/deployment/helm/backend-application/templates/service.yaml index 9328b68c8..77473dcb5 100644 --- a/edc-tests/src/main/resources/deployment/helm/backend-application/templates/service.yaml +++ b/edc-tests/src/main/resources/deployment/helm/backend-application/templates/service.yaml @@ -1,3 +1,4 @@ +--- apiVersion: v1 kind: Service metadata: diff --git a/edc-tests/src/main/resources/deployment/helm/backend-application/templates/serviceaccount.yaml b/edc-tests/src/main/resources/deployment/helm/backend-application/templates/serviceaccount.yaml index f3f87ff4f..ca18045bb 100644 --- a/edc-tests/src/main/resources/deployment/helm/backend-application/templates/serviceaccount.yaml +++ b/edc-tests/src/main/resources/deployment/helm/backend-application/templates/serviceaccount.yaml @@ -1,4 +1,5 @@ {{- if .Values.serviceAccount.create -}} +--- apiVersion: v1 kind: ServiceAccount metadata: diff --git a/edc-tests/src/main/resources/deployment/helm/backend-application/values.yaml b/edc-tests/src/main/resources/deployment/helm/backend-application/values.yaml index 20e356fd4..9b14ed531 100644 --- a/edc-tests/src/main/resources/deployment/helm/backend-application/values.yaml +++ b/edc-tests/src/main/resources/deployment/helm/backend-application/values.yaml @@ -105,6 +105,16 @@ tolerations: [] # -- [Affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) constrains which nodes the Pod can be scheduled on based on node labels. affinity: {} +persistence: + # -- Whether to enable persistence via [PersistentVolumeClaim](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#reserving-a-persistentvolume) + enabled: false + # -- [PersistentVolume Access Modes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes) Access mode to use. One of (ReadOnlyMany, ReadWriteOnce, ReadWriteMany, ReadWriteOncePod) + accessMode: + # -- Storage class to use together with the claimed [PersistentVolume](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) + storageClassName: + # -- Capacity given to the claimed [PersistentVolume](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) + capacity: 100M + # -- script invoked on http calls script: path: /opt/tcpserver/handler.sh diff --git a/edc-tests/src/main/resources/deployment/helm/omejdn/templates/deployment.yaml b/edc-tests/src/main/resources/deployment/helm/omejdn/templates/deployment.yaml index fda1b5066..f31277d78 100644 --- a/edc-tests/src/main/resources/deployment/helm/omejdn/templates/deployment.yaml +++ b/edc-tests/src/main/resources/deployment/helm/omejdn/templates/deployment.yaml @@ -56,9 +56,14 @@ spec: cp /opt/config/omejdn.yml /etc/daps/omejdn.yml cp /opt/config/clients.yml /etc/daps/clients.yml cp /opt/config/plugins.yml /etc/daps/plugins.yml + apk add --update openssl + openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout /etc/keys/omejdn/omejdn.key \ + -subj "/C=DE/ST=Berlin/L=Berlin/O=Product-EDC-Test, Inc./OU=DE" volumeMounts: - - name: config-dir - mountPath: /etc/daps + - mountPath: /etc/daps + name: config-dir + - mountPath: /etc/keys/omejdn + name: omejdn-key-dir - mountPath: /opt/config/omejdn.yml name: omejdn-config subPath: omejdn.yml @@ -75,15 +80,11 @@ spec: image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy }} volumeMounts: - - mountPath: /opt/config/omejdn.yml - name: config-dir - subPath: omejdn.yml - - mountPath: /opt/config/clients.yml + - mountPath: /opt/config/ name: config-dir - subPath: clients.yml - - mountPath: /opt/config/plugins.yml - name: config-dir - subPath: plugins.yml + - mountPath: /opt/keys/omejdn/omejdn.key + name: omejdn-key-dir + subPath: omejdn.key - mountPath: /opt/keys/clients/ name: client-certificates ports: @@ -108,6 +109,8 @@ spec: volumes: - name: config-dir emptyDir: {} + - name: omejdn-key-dir + emptyDir: {} - name: omejdn-config configMap: name: {{ include "omejdn.fullname" . }} diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/AssetStepDefs.java b/edc-tests/src/test/java/net/catenax/edc/tests/AssetStepDefs.java new file mode 100644 index 000000000..89f88f095 --- /dev/null +++ b/edc-tests/src/test/java/net/catenax/edc/tests/AssetStepDefs.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.tests; + +import io.cucumber.datatable.DataTable; +import io.cucumber.java.en.Given; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; +import net.catenax.edc.tests.data.Asset; + +public class AssetStepDefs { + + @Given("'{connector}' has no assets") + public void hasNoAssets(Connector connector) throws Exception { + final DataManagementAPI api = connector.getDataManagementAPI(); + + Stream assets = api.getAllAssets(); + for (Asset asset : assets.toArray(Asset[]::new)) { + api.deleteAsset(asset.getId()); + } + } + + @Given("'{connector}' has the following assets") + public void hasAssets(Connector connector, DataTable table) throws Exception { + final DataManagementAPI api = connector.getDataManagementAPI(); + final List assets = parseDataTable(table); + + for (Asset asset : assets) api.createAsset(asset); + } + + private List parseDataTable(DataTable table) { + final List assets = new ArrayList<>(); + + for (Map map : table.asMaps()) { + String id = map.get("id"); + String description = map.get("description"); + assets.add(new Asset(id, description)); + } + + return assets; + } +} diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/CatalogStepDefs.java b/edc-tests/src/test/java/net/catenax/edc/tests/CatalogStepDefs.java new file mode 100644 index 000000000..4c48e0a13 --- /dev/null +++ b/edc-tests/src/test/java/net/catenax/edc/tests/CatalogStepDefs.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.tests; + +import io.cucumber.datatable.DataTable; +import io.cucumber.java.en.Then; +import io.cucumber.java.en.When; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import net.catenax.edc.tests.data.ContractOffer; +import org.junit.jupiter.api.Assertions; + +public class CatalogStepDefs { + + private List lastRequestedOffers; + + @When("'{connector}' requests the catalog from '{connector}'") + public void requestCatalog(Connector sender, Connector receiver) throws IOException { + + final DataManagementAPI dataManagementAPI = sender.getDataManagementAPI(); + final String receiverIdsUrl = receiver.getEnvironment().getIdsUrl() + "/data"; + + lastRequestedOffers = + dataManagementAPI.requestCatalogFrom(receiverIdsUrl).collect(Collectors.toList()); + } + + @Then("the catalog contains the following offers") + public void verifyCatalog(DataTable table) { + for (Map map : table.asMaps()) { + final String sourceContractDefinitionId = map.get("source definition"); + final String assetId = map.get("asset"); + + final boolean isInCatalog = + lastRequestedOffers.stream() + .anyMatch( + c -> + c.getAssetId().equals(assetId) + && c.getId().startsWith(sourceContractDefinitionId)); + + Assertions.assertTrue( + isInCatalog, "The catalog does not contain the offer for asset " + assetId); + } + } +} diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/Connector.java b/edc-tests/src/test/java/net/catenax/edc/tests/Connector.java new file mode 100644 index 000000000..e29d9d7ed --- /dev/null +++ b/edc-tests/src/test/java/net/catenax/edc/tests/Connector.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.tests; + +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class Connector { + + @NonNull @Getter private final String name; + + @Getter @NonNull private final Environment environment; + + @Getter(lazy = true) + private final DataManagementAPI dataManagementAPI = loadDataManagementAPI(); + + private DataManagementAPI loadDataManagementAPI() { + return new DataManagementAPI(environment.getDataManagementUrl()); + } +} diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/ConnectorFactory.java b/edc-tests/src/test/java/net/catenax/edc/tests/ConnectorFactory.java new file mode 100644 index 000000000..9a9d7e2ac --- /dev/null +++ b/edc-tests/src/test/java/net/catenax/edc/tests/ConnectorFactory.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.tests; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import lombok.NonNull; +import lombok.experimental.UtilityClass; + +@UtilityClass +public class ConnectorFactory { + private static final Map CONNECTOR_CACHE = new HashMap<>(); + + public static Connector byName(@NonNull final String name) { + return CONNECTOR_CACHE.computeIfAbsent( + name.toUpperCase(Locale.ROOT), k -> createConnector(name)); + } + + private static Connector createConnector(@NonNull final String name) { + final Environment environment = Environment.byName(name); + + return new Connector(name, environment); + } +} diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/Constants.java b/edc-tests/src/test/java/net/catenax/edc/tests/Constants.java new file mode 100644 index 000000000..eac6e9b3b --- /dev/null +++ b/edc-tests/src/test/java/net/catenax/edc/tests/Constants.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.tests; + +import lombok.experimental.UtilityClass; + +@UtilityClass +public final class Constants { + public static final String DATA_MANAGEMENT_URL = "DATA_MANAGEMENT_URL"; + /** refers to edc.api.auth.key resp. EDC_API_AUTH_KEY */ + public static final String DATA_MANAGEMENT_API_AUTH_KEY = "DATA_MANAGEMENT_API_AUTH_KEY"; + + public static final String IDS_URL = "IDS_URL"; + public static final String DATA_PLANE_URL = "DATA_PLANE_URL"; + public static final String PLATO = "PLATO"; + public static final String SOKRATES = "SOKRATES"; +} diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/ContractDefinitionStepDefs.java b/edc-tests/src/test/java/net/catenax/edc/tests/ContractDefinitionStepDefs.java new file mode 100644 index 000000000..7172f6df2 --- /dev/null +++ b/edc-tests/src/test/java/net/catenax/edc/tests/ContractDefinitionStepDefs.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.tests; + +import io.cucumber.datatable.DataTable; +import io.cucumber.java.en.Given; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; +import net.catenax.edc.tests.data.ContractDefinition; + +public class ContractDefinitionStepDefs { + + @Given("'{connector}' has no contract definitions") + public void hasNoContractDefinitions(Connector connector) throws Exception { + + final DataManagementAPI api = connector.getDataManagementAPI(); + + Stream contractDefinitions = api.getAllContractDefinitions(); + for (ContractDefinition contractDefinition : + contractDefinitions.toArray(ContractDefinition[]::new)) { + api.deleteContractDefinition(contractDefinition.getId()); + } + } + + @Given("'{connector}' has the following contract definitions") + public void hasPolicies(Connector connector, DataTable table) throws Exception { + final DataManagementAPI api = connector.getDataManagementAPI(); + final List contractDefinitions = parseDataTable(table); + + for (ContractDefinition contractDefinition : contractDefinitions) + api.createContractDefinition(contractDefinition); + } + + private List parseDataTable(DataTable table) { + final List contractDefinitions = new ArrayList<>(); + + for (Map map : table.asMaps()) { + String id = map.get("id"); + String accessPolicyId = map.get("access policy"); + String contractPolicyId = map.get("contract policy"); + String assetid = map.get("asset"); + contractDefinitions.add( + new ContractDefinition(id, contractPolicyId, accessPolicyId, List.of(assetid))); + } + + return contractDefinitions; + } +} diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/DataManagementAPI.java b/edc-tests/src/test/java/net/catenax/edc/tests/DataManagementAPI.java new file mode 100644 index 000000000..e3b3ecdb9 --- /dev/null +++ b/edc-tests/src/test/java/net/catenax/edc/tests/DataManagementAPI.java @@ -0,0 +1,382 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.tests; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import java.io.IOException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import net.catenax.edc.tests.data.Asset; +import net.catenax.edc.tests.data.ContractDefinition; +import net.catenax.edc.tests.data.ContractOffer; +import net.catenax.edc.tests.data.Permission; +import net.catenax.edc.tests.data.Policy; +import org.apache.http.HttpResponse; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.HttpClientBuilder; + +@Slf4j +public class DataManagementAPI { + + private static final String ASSET_PATH = "/assets"; + private static final String POLICY_PATH = "/policies"; + private static final String CONTRACT_DEFINITIONS_PATH = "/contractdefinitions"; + private static final String CATALOG_PATH = "/catalog"; + + private static final String PARAM_NO_LIMIT = "limit=" + Integer.MAX_VALUE; + + private final String dataMgmtUrl; + private final HttpClient httpClient; + + public DataManagementAPI(String dataManagementUrl) { + this.httpClient = HttpClientBuilder.create().build(); + this.dataMgmtUrl = dataManagementUrl; + } + + public Stream requestCatalogFrom(String receivingConnectorUrl) throws IOException { + final String encodedUrl = + URLEncoder.encode(receivingConnectorUrl, StandardCharsets.UTF_8.toString()); + final DataManagementApiContractOfferCatalog catalog = + get( + CATALOG_PATH, + "providerUrl=" + encodedUrl, + new TypeToken() {}); + + log.debug("Received " + catalog.contractOffers.size() + " offers"); + + return catalog.contractOffers.stream().map(this::mapOffer); + } + + public Asset getAsset(String id) throws IOException { + final DataManagementApiAsset asset = + get(ASSET_PATH + "/" + id, new TypeToken() {}); + return mapAsset(asset); + } + + public Policy getPolicy(String id) throws IOException { + final DataManagementApiPolicy policy = + get(POLICY_PATH + "/" + id, new TypeToken() {}); + return mapPolicy(policy); + } + + public ContractDefinition getContractDefinition(String id) + throws IOException, ClientProtocolException { + final DataManagementApiContractDefinition contractDefinition = + get( + CONTRACT_DEFINITIONS_PATH + "/" + id, + new TypeToken() {}); + return mapContractDefinition(contractDefinition); + } + + public void createAsset(Asset asset) throws IOException { + final DataManagementApiDataAddress dataAddress = new DataManagementApiDataAddress(); + dataAddress.properties = + Map.of( + DataManagementApiDataAddress.TYPE, + "HttpData", + "endpoint", + "https://jsonplaceholder.typicode.com/todos/1"); + + final DataManagementApiAssetCreate assetCreate = new DataManagementApiAssetCreate(); + assetCreate.asset = mapAsset(asset); + assetCreate.dataAddress = dataAddress; + + post(ASSET_PATH, assetCreate); + } + + public void createPolicy(Policy policy) throws ClientProtocolException, IOException { + post(POLICY_PATH, mapPolicy(policy)); + } + + public void createContractDefinition(ContractDefinition contractDefinition) throws IOException { + post(CONTRACT_DEFINITIONS_PATH, mapContractDefinition(contractDefinition)); + } + + public Stream getAllAssets() throws IOException, ClientProtocolException { + final List assets = + get(ASSET_PATH, PARAM_NO_LIMIT, new TypeToken>() {}); + return assets.stream().map(this::mapAsset); + } + + public Stream getAllPolicies() throws IOException { + final List policies = + get(POLICY_PATH, PARAM_NO_LIMIT, new TypeToken>() {}); + return policies.stream().map(this::mapPolicy); + } + + public Stream getAllContractDefinitions() throws IOException { + final List contractDefinitions = + get( + CONTRACT_DEFINITIONS_PATH, + PARAM_NO_LIMIT, + new TypeToken>() {}); + return contractDefinitions.stream().map(this::mapContractDefinition); + } + + public void deleteAsset(String id) throws IOException { + delete(ASSET_PATH + "/" + id); + } + + public void deletePolicy(String id) throws IOException { + delete(POLICY_PATH + "/" + id); + } + + public void deleteContractDefinition(String id) throws IOException { + delete(CONTRACT_DEFINITIONS_PATH + "/" + id); + } + + private T get(String path, String params, TypeToken typeToken) throws IOException { + return get(path + "?" + params, typeToken); + } + + private T get(String path, TypeToken typeToken) throws IOException { + + final HttpGet get = new HttpGet(dataMgmtUrl + path); + + final HttpResponse response = sendRequest(get); + final byte[] json = response.getEntity().getContent().readAllBytes(); + + return new Gson().fromJson(new String(json), typeToken.getType()); + } + + private void delete(String path) throws IOException { + final HttpDelete delete = new HttpDelete(dataMgmtUrl + path); + + sendRequest(delete); + } + + private void post(String path, Object object) throws IOException { + final String url = String.format("%s%s", dataMgmtUrl, path); + final HttpPost post = new HttpPost(url); + post.addHeader("Content-Type", "application/json"); + + var json = new Gson().toJson(object); + + log.debug("POST Payload: " + json); + + post.setEntity(new StringEntity(json)); + sendRequest(post); + } + + private HttpResponse sendRequest(HttpRequestBase request) throws IOException { + request.addHeader("X-Api-Key", "password"); + + log.debug(String.format("Send %-6s %s", request.getMethod(), request.getURI())); + + final HttpResponse response = httpClient.execute(request); + if (200 > response.getStatusLine().getStatusCode() + || response.getStatusLine().getStatusCode() >= 300) { + throw new RuntimeException( + String.format("Unexpected response: %s", response.getStatusLine())); + } + + return response; + } + + private Asset mapAsset(DataManagementApiAsset DataManagementApiAsset) { + final String id = (String) DataManagementApiAsset.properties.get(DataManagementApiAsset.ID); + final String description = + (String) DataManagementApiAsset.properties.get(DataManagementApiAsset.DESCRIPTION); + + return new Asset(id, description); + } + + private DataManagementApiAsset mapAsset(Asset asset) { + final Map properties = + Map.of( + DataManagementApiAsset.ID, asset.getId(), + DataManagementApiAsset.DESCRIPTION, asset.getDescription()); + + final DataManagementApiAsset apiObject = new DataManagementApiAsset(); + apiObject.setProperties(properties); + return apiObject; + } + + private Policy mapPolicy(DataManagementApiPolicy dataManagementApiPolicy) { + final String id = dataManagementApiPolicy.uid; + final List permissions = + dataManagementApiPolicy.permissions.stream() + .map(this::mapPermission) + .collect(Collectors.toList()); + + return new Policy(id, permissions); + } + + private DataManagementApiPolicy mapPolicy(Policy policy) { + final List permission = + policy.getPermission().stream().map(this::mapPermission).collect(Collectors.toList()); + + final DataManagementApiPolicy apiObject = new DataManagementApiPolicy(); + apiObject.uid = policy.getId(); + apiObject.permissions = permission; + return apiObject; + } + + private Permission mapPermission(DataManagementApiPermission dataManagementApiPermission) { + final String target = dataManagementApiPermission.target; + final String action = dataManagementApiPermission.action.type; + + return new Permission(action, target); + } + + private DataManagementApiPermission mapPermission(Permission permission) { + final String target = permission.getTarget(); + final String action = permission.getAction(); + + final DataManagementApiRuleAction apiAction = new DataManagementApiRuleAction(); + apiAction.type = action; + + final DataManagementApiPermission apiObject = new DataManagementApiPermission(); + apiObject.target = target; + apiObject.action = apiAction; + return apiObject; + } + + private ContractOffer mapOffer(DataManagementApiContractOffer dataManagementApiContractOffer) { + final String id = dataManagementApiContractOffer.id; + final String assetId = + dataManagementApiContractOffer.assetId != null + ? dataManagementApiContractOffer.assetId + : (String) + dataManagementApiContractOffer.asset.getProperties().get(DataManagementApiAsset.ID); + + final Policy policy = mapPolicy(dataManagementApiContractOffer.getPolicy()); + + return new ContractOffer(id, policy, assetId); + } + + private ContractDefinition mapContractDefinition( + DataManagementApiContractDefinition dataManagementContractDefinition) { + final String id = dataManagementContractDefinition.id; + final String accessPolicy = dataManagementContractDefinition.accessPolicyId; + final String contractPolicy = dataManagementContractDefinition.contractPolicyId; + + final List assetIds; + if (dataManagementContractDefinition == null + || dataManagementContractDefinition.getCriteria() == null) assetIds = new ArrayList<>(); + else + assetIds = + dataManagementContractDefinition.getCriteria().stream() + .filter(c -> c.left.equals(DataManagementApiAsset.ID)) + .filter(c -> c.op.equals("=")) + .map(c -> c.getRight()) + .map(c -> (String) c) + .collect(Collectors.toList()); + + return new ContractDefinition(id, contractPolicy, accessPolicy, assetIds); + } + + private DataManagementApiContractDefinition mapContractDefinition( + ContractDefinition contractDefinition) { + + final DataManagementApiContractDefinition apiObject = new DataManagementApiContractDefinition(); + apiObject.id = contractDefinition.getId(); + apiObject.accessPolicyId = contractDefinition.getAcccessPolicyId(); + apiObject.contractPolicyId = contractDefinition.getContractPolicyId(); + apiObject.criteria = new ArrayList<>(); + + for (final String assetId : contractDefinition.getAssetIds()) { + DataManagementApiCriterion criterion = new DataManagementApiCriterion(); + criterion.left = DataManagementApiAsset.ID; + criterion.op = "="; + criterion.right = assetId; + + apiObject.criteria.add(criterion); + } + + return apiObject; + } + + @Data + private class DataManagementApiAssetCreate { + private DataManagementApiAsset asset; + private DataManagementApiDataAddress dataAddress; + } + + @Data + private class DataManagementApiAsset { + public static final String ID = "asset:prop:id"; + public static final String DESCRIPTION = "asset:prop:description"; + + private Map properties; + } + + @Data + private class DataManagementApiDataAddress { + public static final String TYPE = "type"; + private Map properties; + } + + @Data + private class DataManagementApiPolicy { + private String uid; + private List permissions; + } + + @Data + private class DataManagementApiPermission { + private String edctype = "dataspaceconnector:permission"; + private String target; + private DataManagementApiRuleAction action; + } + + @Data + private class DataManagementApiRuleAction { + private String type; + } + + @Data + private class DataManagementApiContractDefinition { + private String id; + private String accessPolicyId; + private String contractPolicyId; + private List criteria; + } + + @Data + private class DataManagementApiCriterion { + private Object left; + private String op; + private Object right; + } + + @Data + private class DataManagementApiContractOffer { + private String id; + private DataManagementApiPolicy policy; + private DataManagementApiAsset asset; + private String assetId; + } + + @Data + private class DataManagementApiContractOfferCatalog { + private String id; + private List contractOffers; + } +} diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/Environment.java b/edc-tests/src/test/java/net/catenax/edc/tests/Environment.java new file mode 100644 index 000000000..c1a127580 --- /dev/null +++ b/edc-tests/src/test/java/net/catenax/edc/tests/Environment.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.tests; + +import static net.catenax.edc.tests.Constants.DATA_MANAGEMENT_URL; +import static net.catenax.edc.tests.Constants.DATA_PLANE_URL; +import static net.catenax.edc.tests.Constants.IDS_URL; +import static net.catenax.edc.tests.Constants.PLATO; +import static net.catenax.edc.tests.Constants.SOKRATES; + +import java.util.Locale; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NonNull; + +@Builder(access = AccessLevel.PRIVATE) +@Getter +class Environment { + @NonNull private final String dataManagementUrl; + @NonNull private final String idsUrl; + @NonNull private final String dataPlaneUrl; + + public static Environment plato() { + return byName(PLATO); + } + + public static Environment sokrates() { + return byName(SOKRATES); + } + + public static Environment byName(String name) { + name = name.toUpperCase(Locale.ROOT); + + return Environment.builder() + .dataManagementUrl(System.getenv(String.join("_", name, DATA_MANAGEMENT_URL))) + .idsUrl(System.getenv(String.join("_", name, IDS_URL))) + .dataPlaneUrl(System.getenv(String.join("_", name, DATA_PLANE_URL))) + .build(); + } +} diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/PolicyStepDefs.java b/edc-tests/src/test/java/net/catenax/edc/tests/PolicyStepDefs.java new file mode 100644 index 000000000..54dbd1423 --- /dev/null +++ b/edc-tests/src/test/java/net/catenax/edc/tests/PolicyStepDefs.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.tests; + +import io.cucumber.datatable.DataTable; +import io.cucumber.java.en.Given; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; +import net.catenax.edc.tests.data.Permission; +import net.catenax.edc.tests.data.Policy; + +public class PolicyStepDefs { + + @Given("'{connector}' has no policies") + public void hasNoPolicies(Connector connector) throws Exception { + + final DataManagementAPI api = connector.getDataManagementAPI(); + + Stream policies = api.getAllPolicies(); + for (Policy policy : policies.toArray(Policy[]::new)) { + api.deletePolicy(policy.getId()); + } + } + + @Given("'{connector}' has the following policies") + public void hasPolicies(Connector connector, DataTable table) throws Exception { + final DataManagementAPI api = connector.getDataManagementAPI(); + final List policies = parseDataTable(table); + + for (Policy policy : policies) api.createPolicy(policy); + } + + private List parseDataTable(DataTable table) { + final List policies = new ArrayList<>(); + + for (Map map : table.asMaps()) { + final String id = map.get("id"); + final String action = map.get("action"); + final List permission = List.of(new Permission(action, null)); + + policies.add(new Policy(id, permission)); + } + + return policies; + } +} diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/data/Asset.java b/edc-tests/src/test/java/net/catenax/edc/tests/data/Asset.java new file mode 100644 index 000000000..b402df750 --- /dev/null +++ b/edc-tests/src/test/java/net/catenax/edc/tests/data/Asset.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.tests.data; + +import lombok.NonNull; +import lombok.Value; + +@Value +public class Asset { + @NonNull String Id; + + @NonNull String description; +} diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/data/ContractDefinition.java b/edc-tests/src/test/java/net/catenax/edc/tests/data/ContractDefinition.java new file mode 100644 index 000000000..dc2054a97 --- /dev/null +++ b/edc-tests/src/test/java/net/catenax/edc/tests/data/ContractDefinition.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.tests.data; + +import java.util.List; +import lombok.NonNull; +import lombok.Value; + +@Value +public class ContractDefinition { + + @NonNull String id; + + @NonNull String contractPolicyId; + @NonNull String acccessPolicyId; + + List assetIds; +} diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/data/ContractOffer.java b/edc-tests/src/test/java/net/catenax/edc/tests/data/ContractOffer.java new file mode 100644 index 000000000..329639fed --- /dev/null +++ b/edc-tests/src/test/java/net/catenax/edc/tests/data/ContractOffer.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.tests.data; + +import lombok.NonNull; +import lombok.Value; + +@Value +public class ContractOffer { + @NonNull String id; + Policy policy; + String assetId; +} diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/data/Permission.java b/edc-tests/src/test/java/net/catenax/edc/tests/data/Permission.java new file mode 100644 index 000000000..421905f8b --- /dev/null +++ b/edc-tests/src/test/java/net/catenax/edc/tests/data/Permission.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.tests.data; + +import lombok.NonNull; +import lombok.Value; + +@Value +public class Permission { + @NonNull String action; + String target; +} diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/data/Policy.java b/edc-tests/src/test/java/net/catenax/edc/tests/data/Policy.java new file mode 100644 index 000000000..4370ce0e6 --- /dev/null +++ b/edc-tests/src/test/java/net/catenax/edc/tests/data/Policy.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.tests.data; + +import java.util.List; +import lombok.NonNull; +import lombok.Value; + +@Value +public class Policy { + @NonNull String id; + @NonNull List Permission; +} diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/features/ParameterTypes.java b/edc-tests/src/test/java/net/catenax/edc/tests/features/ParameterTypes.java new file mode 100644 index 000000000..29a3b8072 --- /dev/null +++ b/edc-tests/src/test/java/net/catenax/edc/tests/features/ParameterTypes.java @@ -0,0 +1,13 @@ +package net.catenax.edc.tests.features; + +import io.cucumber.java.ParameterType; +import net.catenax.edc.tests.Connector; +import net.catenax.edc.tests.ConnectorFactory; + +public class ParameterTypes { + + @ParameterType("Plato|Sokrates") + public Connector connector(String name) { + return ConnectorFactory.byName(name); + } +} diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/features/RunCucumberTest.java b/edc-tests/src/test/java/net/catenax/edc/tests/features/RunCucumberTest.java new file mode 100644 index 000000000..0c4c9379c --- /dev/null +++ b/edc-tests/src/test/java/net/catenax/edc/tests/features/RunCucumberTest.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.tests.features; + +import org.junit.platform.suite.api.SelectClasspathResource; +import org.junit.platform.suite.api.Suite; + +@Suite +@SelectClasspathResource("net/catenax/edc/tests/features") +public class RunCucumberTest {} diff --git a/edc-tests/src/test/resources/junit-platform.properties b/edc-tests/src/test/resources/junit-platform.properties new file mode 100644 index 000000000..215cb0967 --- /dev/null +++ b/edc-tests/src/test/resources/junit-platform.properties @@ -0,0 +1,3 @@ +cucumber.publish.quiet=true +cucumber.publish.enabled=false +cucumber.plugin=json:target/cucumber-reports/,pretty diff --git a/edc-tests/src/test/resources/logback-test.xml b/edc-tests/src/test/resources/logback-test.xml new file mode 100644 index 000000000..b175f4c6c --- /dev/null +++ b/edc-tests/src/test/resources/logback-test.xml @@ -0,0 +1,27 @@ + + + + + + + + %d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n + + + + + + + diff --git a/edc-tests/src/test/resources/net/catenax/edc/tests/features/ContractOffers.feature b/edc-tests/src/test/resources/net/catenax/edc/tests/features/ContractOffers.feature new file mode 100644 index 000000000..e8411bc42 --- /dev/null +++ b/edc-tests/src/test/resources/net/catenax/edc/tests/features/ContractOffers.feature @@ -0,0 +1,37 @@ +# +# Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +# +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# +# Contributors: +# Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation +# + +Feature: Contract Offers + + Background: The Connector State + Given 'Plato' has no contract definitions + Given 'Plato' has no policies + Given 'Plato' has no assets + + Scenario: Catalog Request + Given 'Plato' has the following assets + | id | description | + | asset-1 | Example Asset | + | asset-2 | Example Asset | + And 'Plato' has the following policies + | id | action | + | policy-1 | USE | + And 'Plato' has the following contract definitions + | id | access policy | contract policy | asset | + | contract-definition-1 | policy-1 | policy-1 | asset-1 | + | contract-definition-2 | policy-1 | policy-1 | asset-2 | + When 'Sokrates' requests the catalog from 'Plato' + Then the catalog contains the following offers + | source definition | asset | + | contract-definition-1 | asset-1 | + | contract-definition-2 | asset-2 | diff --git a/pom.xml b/pom.xml index 25d567081..24d25368e 100644 --- a/pom.xml +++ b/pom.xml @@ -71,6 +71,9 @@ 5.8.2 1.8.2 + 7.3.2 + 5.0.1 + 1.1.0 4.6.1 1.18.24 1.70 @@ -1054,6 +1057,23 @@ ${org.testcontainers.version} test + + io.cucumber + cucumber-java + ${cucumber.version} + + + io.cucumber + cucumber-junit-platform-engine + ${cucumber.version} + test + + + io.rest-assured + rest-assured + ${io.rest.assured.version} + test + From 4fa5fc86fca93a7e87929e32ff6ef5ec27d7487d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Jul 2022 16:05:08 +0200 Subject: [PATCH 179/433] Bump azure/setup-helm from 3.1 to 3.3 (#288) --- .github/workflows/business-tests.yaml | 2 +- .github/workflows/helm-lint.yaml | 2 +- .github/workflows/publish-new-release.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/business-tests.yaml b/.github/workflows/business-tests.yaml index b85dafb39..a28d8ed49 100644 --- a/.github/workflows/business-tests.yaml +++ b/.github/workflows/business-tests.yaml @@ -41,7 +41,7 @@ jobs: uses: azure/setup-kubectl@v2.0 - name: Helm Set-Up - uses: azure/setup-helm@v2.1 + uses: azure/setup-helm@v3.3 with: version: v3.8.1 - diff --git a/.github/workflows/helm-lint.yaml b/.github/workflows/helm-lint.yaml index cdc8d9edd..ed4c24513 100644 --- a/.github/workflows/helm-lint.yaml +++ b/.github/workflows/helm-lint.yaml @@ -32,7 +32,7 @@ jobs: fetch-depth: 0 - name: helm (setup) - uses: azure/setup-helm@v3.1 + uses: azure/setup-helm@v3.3 with: version: v3.8.1 - diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index c7d51f5c4..e4a080f0c 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -109,7 +109,7 @@ jobs: fetch-depth: 0 - name: Install Helm - uses: azure/setup-helm@v3.1 + uses: azure/setup-helm@v3.3 with: version: v3.8.1 - From d179391f1f6ba8108c01d2a9ea3d9782de1fffcd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Jul 2022 16:05:24 +0200 Subject: [PATCH 180/433] Bump gson from 2.8.8 to 2.8.9 in /edc-tests (#292) --- edc-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-tests/pom.xml b/edc-tests/pom.xml index 4a5036fe0..3010340a6 100644 --- a/edc-tests/pom.xml +++ b/edc-tests/pom.xml @@ -28,7 +28,7 @@ ${project.groupId}_${project.artifactId} - 2.8.8 + 2.8.9 4.5.13 1.2.11 1.7.36 From 70f9798966650072d33e400a3f64de564d7ce153 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Jul 2022 16:05:37 +0200 Subject: [PATCH 181/433] Bump maven-resources-plugin from 3.2.0 to 3.3.0 (#290) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 24d25368e..dfe8cb117 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 3.2.1 3.0.0-M7 3.10.1 - 3.2.0 + 3.3.0 1.18.20.0 0.8.8 1.1.0 From 316e701b14d58d7260f9f46889d66574a7e132b8 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Tue, 26 Jul 2022 17:08:03 +0200 Subject: [PATCH 182/433] Enable business tests on pull request target branch develop (#293) --- .github/workflows/business-tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/business-tests.yaml b/.github/workflows/business-tests.yaml index a28d8ed49..8ece26e9c 100644 --- a/.github/workflows/business-tests.yaml +++ b/.github/workflows/business-tests.yaml @@ -6,7 +6,7 @@ on: paths-ignore: - docs/** branches: - - develop/** + - develop - release/** - main workflow_dispatch: From 15b117cf55c6132c126a31983ca0c04d1adf611b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Jul 2022 17:33:18 +0200 Subject: [PATCH 183/433] Bump flyway-core from 8.5.13 to 9.0.2 (#291) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index dfe8cb117..7523fb00a 100644 --- a/pom.xml +++ b/pom.xml @@ -66,7 +66,7 @@ 0.0.1-SNAPSHOT 1.2.2 42.4.0 - 8.5.13 + 9.0.2 5.8.2 From 37e77a2947eadd055827ee3dc59af9b5048630ef Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Wed, 27 Jul 2022 14:21:52 +0200 Subject: [PATCH 184/433] Exclude all-in-one charts from helm-release (#296) --- .github/workflows/publish-new-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index e4a080f0c..570e8b18c 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -120,7 +120,7 @@ jobs: git config user.email noreply@github.com # Package all charts - find -name Chart.yaml | xargs -n1 dirname | xargs -n1 helm package -u -d helm-charts + find -name Chart.yaml -not -path "./edc-tests/*" -not -path "./edc/*" | xargs -n1 dirname | xargs -n1 helm package -u -d helm-charts git checkout gh-pages || git checkout -b gh-pages git pull --rebase origin gh-pages From 51af2965d34f84519fcf0b780449a3e23262719a Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Wed, 27 Jul 2022 15:07:36 +0200 Subject: [PATCH 185/433] hashicorp vault path is now configurable (#254) --- edc-extensions/hashicorp-vault/README.md | 68 +++++++++- .../hashicorpvault/HashicorpVaultClient.java | 95 +++++++++----- .../HashicorpVaultClientConfig.java | 11 +- .../HashicorpVaultExtension.java | 72 +++++++++-- .../HashicorpVaultHealthCheck.java | 94 ++++++++++++++ .../HashicorpVaultHealthResponse.java | 59 +++++++++ .../HashicorpVaultHealthResponsePayload.java | 61 +++++++++ .../catenax/edc/hashicorpvault/PathUtil.java | 29 +++++ .../hashicorpvault/AbstractHashicorpIT.java | 90 ++++++++++++-- .../HashicorpCertificateResolverTest.java | 14 +++ .../HashicorpVaultClientTest.java | 116 +++++++++++++++++- .../HashicorpVaultExtensionTest.java | 113 +++++++++++++++++ .../HashicorpVaultHealthCheckTest.java | 66 ++++++++++ .../hashicorpvault/HashicorpVaultTest.java | 61 ++++++++- .../edc/hashicorpvault/PathUtilTest.java | 40 ++++++ lombok.config | 2 + 16 files changed, 923 insertions(+), 68 deletions(-) create mode 100644 edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthCheck.java create mode 100644 edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthResponse.java create mode 100644 edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthResponsePayload.java create mode 100644 edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/PathUtil.java create mode 100644 edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultExtensionTest.java create mode 100644 edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthCheckTest.java create mode 100644 edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/PathUtilTest.java create mode 100644 lombok.config diff --git a/edc-extensions/hashicorp-vault/README.md b/edc-extensions/hashicorp-vault/README.md index 5eea7a23c..7e31dde54 100644 --- a/edc-extensions/hashicorp-vault/README.md +++ b/edc-extensions/hashicorp-vault/README.md @@ -1,9 +1,67 @@ # [HashiCorp Vault](https://www.vaultproject.io/) Extension +--- + +**Please note:**
+In the HashiCorp vault it is possible to define multiple data entries per secret. Other vaults might allow only one entry per secret (e.g. Azure Key Vault). + +Therefore, the HashiCorp vault extension **only** checks the '**content**' data entry! Please use this knowledge when creating secrets the EDC should consume. + +--- + ## Configuration -| Key | Description | Mandatory | -|:---|:---|---| -| edc.vault.hashicorp.url | URL to connect to the HashiCorp Vault | X | -| edc.vault.hashicorp.token | Value for [Token Authentication](https://www.vaultproject.io/docs/auth/token) with the vault | X | -| edc.vault.hashicorp.timeout.seconds | Request timeout in seconds when contacting the vault (default: 30) | | +| Key | Description | Mandatory | Default | +|:--------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------|-----------|---------| +| edc.vault.hashicorp.url | URL to connect to the HashiCorp Vault | X || +| edc.vault.hashicorp.token | Value for [Token Authentication](https://www.vaultproject.io/docs/auth/token) with the vault | X || +| edc.vault.hashicorp.timeout.seconds | Request timeout in seconds when contacting the vault | | `30` | +| edc.vault.hashicorp.health.check.enabled | Enable health checks to ensure vault is initialized, unsealed and active (default: _true_) | | `true` | +| edc.vault.hashicorp.health.check.standby.ok | Specifies if a vault in standby is healthy. This is useful when Vault is behind a non-configurable load balancer. (default: _false_) | | `false` | +| edc.vault.hashicorp.api.secret.path | Path to the [secret api](https://www.vaultproject.io/api-docs/secret/kv/kv-v1) (default: _/v1/secret_) | | `/v1/secret` | +| edc.vault.hashicorp.api.health.check.path | Path to the [health api](https://www.vaultproject.io/api-docs/system/health) (default: _/sys/health_) | | `/sys/health` | + +## Example: Create & Configure DAPS Key + +1. Insert DAPS Key into HashiCorp Vault +```bash +cat << EOF | /bin/vault kv put secret/my-daps-key content=- + -----BEGIN PRIVATE KEY----- + MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCv+NUvK7ppJPiM + wZPaQQxE745T5pV38O/Mkay5m82nnd5BoMoCdhhRTy3Efy79FhvBfGruFBLLGzsQ + FOEUY53Albeumo2gmpZSKjJR/M2ifK4MTaRniVOWL5mEcZSKPhsItKpxdLaiYfB6 + 8uzqkqNICtmAQRSclYKzLBM9xHLEtxDWCbnzYFCHoOELGi+PTNIFsUnsT3QuKaJ/ + ejb47vdA/EZbwCQdtTyJ6i54jGhZUp0WMwq1Go2uhzJsygPmT2da/ZZZc7BNNEQE + sUSMZSpMH807TG/TunstotrzO4ShhpV4zbJ2FV/VlxH7yuCawmnR84F/KnXs9fUc + RSrQfuYBAgMBAAECggEAO+KjsjTgcG3bhBNQnMLsSP15Y0Yicbn18ZlVvaivGS7Z + d14fwSytY+ZdPfTGaey/L16HCVSdfK9cr0Fbw9OO2P5ajzobnp9dLsMbctlkpbpm + hNtbarzKTF8QkIkSsuUl0BWjt46vpJ1N+Jl5VO7oUFkY4dPEDvG2lAEY3zlekWDm + cQeOC/YgpoW4xfRwPPS6QE0w3Q+H5NfNjfz+mSHeItTlVfTKDRliWQLPWeRZFuXh + FlRFUQnTmEE/9wpIe3Hn7WXJ3fQqcYDzxU7/zwwY9I7bB15SgVHlR0ENDPAD5X8F + MVZ3EcLlqGBy+WvTWALp6pc8YfhW3fiTWyuamXtNrQKBgQDonsIzBKEOOKdKGW0e + uyw79ErmnmzkY5nuMrMxrmTA4WKCfJ/YRRA+4sxiltWsIJ3UkHe3OBCSSCdj79hb + ugb/+UzE70hOdgrct2NUQqbrj3gvsVvU8ZRQgTRMqKpmC0zY7KOMx6NU85z3IvS1 + z5fjszcUv4kLQlldYGSAuqPy+wKBgQDBqIkc8p/wcw7ygo1q/GerNeszfoxiIFp8 + h4RWLVhkwrcXFz30wBlUWuv5/kxU8tmJcmXxe72EmUstd6wvNOAnYwCiile6zQiJ + vsr1axavZnGOtNGUp6DUAsd2iviBl7IZ7kAcqCrQo4ivGhfHmahH3hmg8wuAMjYB + 8f+FSPgaMwKBgQC7W4tMrjDOFIFhJEOIWfcRvvxI7VcFSNelS76aiDzsQVwnfxr7 + hPzFucQmsBgfUBHvMADMWGK4f1cCnh5kGtwidXgIsjVJxLeQ+EAPkLOCzQZfW3l8 + dKshgD9QcxTzpaxal5ZPAEikVqaZQtVYToCmzCTUGETYBbOWitnH+Qut2wKBgQC6 + Y6DcSLUhc0xOotLDxv1sbu/aVxF8nFEbDD+Vxf0Otc4MnmUWPRHj+8KlkVkcZcR0 + IrP1kThd+EDAGS+TG9wmbIY+6tH3S8HM+eJUBWcHGJ1xUZ1p61DC3Y3nDWiTKlLT + 3Fi+fCkBOHSku4Npq/2odh7Kp0JJd4o9oxJg0VNhuwKBgQDSFn7dqFE0Xmwc40Vr + 0wJH8cPWXKGt7KJENpj894buk2DniLD4w2x874dzTjrOFi6fKxEzbBNA9Rq9UPo8 + u9gKvl/IyWmV0c4zFCNMjRwVdnkMEte/lXcJZ67T4FXZByqAZlhrr/v0FD442Z9B + AjWFbUiBCFOo+gpAFcQGrkOQHA== + -----END PRIVATE KEY----- + EOF +``` + +2. Configure Key in the EDC +```bash + EDC_OAUTH_PRIVATE_KEY_ALIAS: my-daps-key +``` +or +```bash + edc.oauth.private.key.alias=my-daps-key +``` \ No newline at end of file diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultClient.java b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultClient.java index 251633c5a..754ac5928 100644 --- a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultClient.java +++ b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultClient.java @@ -9,15 +9,16 @@ * * Contributors: * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation - * + * Mercedes-Benz Tech Innovation GmbH - Make secret data & metadata paths configurable + * Mercedes-Benz Tech Innovation GmbH - Add vault health check */ package net.catenax.edc.hashicorpvault; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; -import java.net.URI; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.Collections; @@ -25,6 +26,7 @@ import lombok.NonNull; import lombok.RequiredArgsConstructor; import okhttp3.Headers; +import okhttp3.HttpUrl; import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; @@ -38,29 +40,27 @@ class HashicorpVaultClient { static final String VAULT_DATA_ENTRY_NAME = "content"; private static final String VAULT_TOKEN_HEADER = "X-Vault-Token"; private static final String VAULT_REQUEST_HEADER = "X-Vault-Request"; - private static final MediaType MEDIA_TYPE_APPLICATION_JSON = MediaType.get("application/json"); - private static final String VAULT_API_VERSION = "v1"; - private static final String VAULT_SECRET_PATH = "secret"; private static final String VAULT_SECRET_DATA_PATH = "data"; private static final String VAULT_SECRET_METADATA_PATH = "metadata"; + private static final MediaType MEDIA_TYPE_APPLICATION_JSON = MediaType.get("application/json"); private static final String CALL_UNSUCCESSFUL_ERROR_TEMPLATE = "Call unsuccessful: %s"; + @NonNull private final HashicorpVaultClientConfig config; @NonNull private final OkHttpClient okHttpClient; @NonNull private final ObjectMapper objectMapper; Result getSecretValue(@NonNull String key) { - String requestURI = getSecretUrl(key, VAULT_SECRET_DATA_PATH); + HttpUrl requestURI = getSecretUrl(key, VAULT_SECRET_DATA_PATH); Headers headers = getHeaders(); Request request = new Request.Builder().url(requestURI).headers(headers).get().build(); try (Response response = okHttpClient.newCall(request).execute()) { - if (response.isSuccessful()) { - if (response.code() == 404) { - return Result.failure( - String.format(CALL_UNSUCCESSFUL_ERROR_TEMPLATE, "Secret not found")); - } + if (response.code() == 404) { + return Result.failure(String.format(CALL_UNSUCCESSFUL_ERROR_TEMPLATE, "Secret not found")); + } + if (response.isSuccessful()) { String responseBody = Objects.requireNonNull(response.body()).string(); HashicorpVaultGetEntryResponsePayload payload = objectMapper.readValue(responseBody, HashicorpVaultGetEntryResponsePayload.class); @@ -77,9 +77,34 @@ Result getSecretValue(@NonNull String key) { } } + public HashicorpVaultHealthResponse getHealth() throws IOException { + + HashicorpVaultHealthResponse.HashicorpVaultHealthResponseBuilder healthResponseBuilder = + HashicorpVaultHealthResponse.builder(); + + HttpUrl requestURI = getHealthUrl(); + Headers headers = getHeaders(); + Request request = new Request.Builder().url(requestURI).headers(headers).get().build(); + try (Response response = okHttpClient.newCall(request).execute()) { + final int code = response.code(); + healthResponseBuilder.code(code); + + try { + String responseBody = Objects.requireNonNull(response.body()).string(); + HashicorpVaultHealthResponsePayload responsePayload = + objectMapper.readValue(responseBody, HashicorpVaultHealthResponsePayload.class); + healthResponseBuilder.payload(responsePayload); + } catch (JsonMappingException e) { + // ignore. status code not checked, so it may be possible that no payload was provided + } + } + + return healthResponseBuilder.build(); + } + Result setSecret( @NonNull String key, @NonNull String value) { - String requestURI = getSecretUrl(key, VAULT_SECRET_DATA_PATH); + HttpUrl requestURI = getSecretUrl(key, VAULT_SECRET_DATA_PATH); Headers headers = getHeaders(); HashicorpVaultCreateEntryRequestPayload requestPayload = HashicorpVaultCreateEntryRequestPayload.builder() @@ -107,7 +132,7 @@ Result setSecret( } Result destroySecret(@NonNull String key) { - String requestURI = getSecretUrl(key, VAULT_SECRET_METADATA_PATH); + HttpUrl requestURI = getSecretUrl(key, VAULT_SECRET_METADATA_PATH); Headers headers = getHeaders(); Request request = new Request.Builder().url(requestURI).headers(headers).delete().build(); @@ -122,32 +147,38 @@ Result destroySecret(@NonNull String key) { @NotNull private Headers getHeaders() { - Headers.Builder headersBuilder = - new Headers.Builder().add(VAULT_REQUEST_HEADER, Boolean.toString(true)); - if (config.getVaultToken() != null) { - headersBuilder = headersBuilder.add(VAULT_TOKEN_HEADER, config.getVaultToken()); - } - return headersBuilder.build(); + return new Headers.Builder() + .add(VAULT_REQUEST_HEADER, Boolean.toString(true)) + .add(VAULT_TOKEN_HEADER, config.getVaultToken()) + .build(); } - private String getBaseUrl() { - String baseUrl = config.getVaultUrl(); + private HttpUrl getSecretUrl(String key, String entryType) { + key = URLEncoder.encode(key, StandardCharsets.UTF_8); - if (baseUrl.endsWith("/")) { - baseUrl = baseUrl.substring(0, baseUrl.length() - 1); - } + final String vaultApiPath = config.getVaultApiSecretPath(); - return baseUrl; + return Objects.requireNonNull(HttpUrl.parse(config.getVaultUrl())) + .newBuilder() + .addPathSegments(PathUtil.trimLeadingOrEndingSlash(vaultApiPath)) + .addPathSegment(entryType) + .addPathSegment(key) + .build(); } - private String getSecretUrl(String key, String entryType) { + private HttpUrl getHealthUrl() { + final String vaultHealthPath = config.getVaultApiHealthPath(); + final boolean isVaultHealthStandbyOk = config.isVaultApiHealthStandbyOk(); - key = URLEncoder.encode(key, StandardCharsets.UTF_8); - return URI.create( - String.format( - "%s/%s/%s/%s/%s", - getBaseUrl(), VAULT_API_VERSION, VAULT_SECRET_PATH, entryType, key)) - .toString(); + // by setting 'standbyok' and/or 'perfstandbyok' the vault will return an active status + // code instead of the standby status codes + + return Objects.requireNonNull(HttpUrl.parse(config.getVaultUrl())) + .newBuilder() + .addPathSegments(PathUtil.trimLeadingOrEndingSlash(vaultHealthPath)) + .addQueryParameter("standbyok", isVaultHealthStandbyOk ? "true" : "false") + .addQueryParameter("perfstandbyok", isVaultHealthStandbyOk ? "true" : "false") + .build(); } private RequestBody createRequestBody(Object requestPayload) { diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultClientConfig.java b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultClientConfig.java index 2b3c886f7..a9f0ee2a0 100644 --- a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultClientConfig.java +++ b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultClientConfig.java @@ -17,13 +17,18 @@ import java.time.Duration; import lombok.Builder; import lombok.Getter; +import lombok.NonNull; import lombok.RequiredArgsConstructor; @Builder @Getter @RequiredArgsConstructor class HashicorpVaultClientConfig { - private final String vaultUrl; - private final String vaultToken; - private final Duration timeout; + @NonNull private final String vaultUrl; + @NonNull private final String vaultToken; + @NonNull private final String vaultApiSecretPath; + @NonNull private final String vaultApiHealthPath; + @NonNull private final Duration timeout; + + private final boolean isVaultApiHealthStandbyOk; } diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultExtension.java b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultExtension.java index 6d886ac06..46344dcde 100644 --- a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultExtension.java +++ b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultExtension.java @@ -9,6 +9,8 @@ * * Contributors: * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Mercedes-Benz Tech Innovation GmbH - Make secret data & metadata paths configurable + * Mercedes-Benz Tech Innovation GmbH - Add vault health check * */ @@ -16,15 +18,17 @@ import java.time.Duration; import okhttp3.OkHttpClient; -import org.eclipse.dataspaceconnector.spi.EdcException; import org.eclipse.dataspaceconnector.spi.EdcSetting; import org.eclipse.dataspaceconnector.spi.security.CertificateResolver; import org.eclipse.dataspaceconnector.spi.security.PrivateKeyResolver; import org.eclipse.dataspaceconnector.spi.security.Vault; import org.eclipse.dataspaceconnector.spi.security.VaultPrivateKeyResolver; +import org.eclipse.dataspaceconnector.spi.system.Requires; import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; import org.eclipse.dataspaceconnector.spi.system.VaultExtension; +import org.eclipse.dataspaceconnector.spi.system.health.HealthCheckService; +@Requires(HealthCheckService.class) public class HashicorpVaultExtension implements VaultExtension { @EdcSetting(required = true) @@ -33,6 +37,27 @@ public class HashicorpVaultExtension implements VaultExtension { @EdcSetting(required = true) public static final String VAULT_TOKEN = "edc.vault.hashicorp.token"; + @EdcSetting + public static final String VAULT_API_SECRET_PATH = "edc.vault.hashicorp.api.secret.path"; + + public static final String VAULT_API_SECRET_PATH_DEFAULT = "/v1/secret"; + + @EdcSetting + public static final String VAULT_API_HEALTH_PATH = "edc.vault.hashicorp.api.health.check.path"; + + public static final String VAULT_API_HEALTH_PATH_DEFAULT = "/sys/health"; + + @EdcSetting + public static final String VAULT_HEALTH_CHECK = "edc.vault.hashicorp.health.check.enabled"; + + public static final boolean VAULT_HEALTH_CHECK_DEFAULT = true; + + @EdcSetting + public static final String VAULT_HEALTH_CHECK_STANDBY_OK = + "edc.vault.hashicorp.health.check.standby.ok"; + + public static final boolean VAULT_HEALTH_CHECK_STANDBY_OK_DEFAULT = false; + @EdcSetting private static final String VAULT_TIMEOUT_SECONDS = "edc.vault.hashicorp.timeout.seconds"; @@ -62,16 +87,19 @@ public CertificateResolver getCertificateResolver() { @Override public void initializeVault(ServiceExtensionContext context) { - HashicorpVaultClientConfig config = loadHashicorpVaultClientConfig(context); + final HashicorpVaultClientConfig config = loadHashicorpVaultClientConfig(context); + + final OkHttpClient okHttpClient = createOkHttpClient(config); - OkHttpClient okHttpClient = createOkHttpClient(config); - HashicorpVaultClient client = + final HashicorpVaultClient client = new HashicorpVaultClient(config, okHttpClient, context.getTypeManager().getMapper()); vault = new HashicorpVault(client, context.getMonitor()); certificateResolver = new HashicorpCertificateResolver(vault, context.getMonitor()); privateKeyResolver = new VaultPrivateKeyResolver(vault); + configureHealthCheck(client, context); + context.getMonitor().info("HashicorpVaultExtension: authentication/initialization complete."); } @@ -87,25 +115,51 @@ private OkHttpClient createOkHttpClient(HashicorpVaultClientConfig config) { private HashicorpVaultClientConfig loadHashicorpVaultClientConfig( ServiceExtensionContext context) { - String vaultUrl = context.getSetting(VAULT_URL, null); + final String vaultUrl = context.getSetting(VAULT_URL, null); if (vaultUrl == null) { throw new HashicorpVaultException(String.format("Vault URL (%s) must be defined", VAULT_URL)); } - int vaultTimeoutSeconds = Math.max(0, context.getSetting(VAULT_TIMEOUT_SECONDS, 30)); - Duration vaultTimeoutDuration = Duration.ofSeconds(vaultTimeoutSeconds); + final int vaultTimeoutSeconds = Math.max(0, context.getSetting(VAULT_TIMEOUT_SECONDS, 30)); + final Duration vaultTimeoutDuration = Duration.ofSeconds(vaultTimeoutSeconds); - String vaultToken = context.getSetting(VAULT_TOKEN, null); + final String vaultToken = context.getSetting(VAULT_TOKEN, null); if (vaultToken == null) { - throw new EdcException( + throw new HashicorpVaultException( String.format("For Vault authentication [%s] is required", VAULT_TOKEN)); } + final String apiSecretPath = + context.getSetting(VAULT_API_SECRET_PATH, VAULT_API_SECRET_PATH_DEFAULT); + + final String apiHealthPath = + context.getSetting(VAULT_API_HEALTH_PATH, VAULT_API_HEALTH_PATH_DEFAULT); + + final boolean isHealthStandbyOk = + context.getSetting(VAULT_HEALTH_CHECK_STANDBY_OK, VAULT_HEALTH_CHECK_STANDBY_OK_DEFAULT); + return HashicorpVaultClientConfig.builder() .vaultUrl(vaultUrl) .vaultToken(vaultToken) + .vaultApiSecretPath(apiSecretPath) + .vaultApiHealthPath(apiHealthPath) + .isVaultApiHealthStandbyOk(isHealthStandbyOk) .timeout(vaultTimeoutDuration) .build(); } + + private void configureHealthCheck(HashicorpVaultClient client, ServiceExtensionContext context) { + final boolean healthCheckEnabled = + context.getSetting(VAULT_HEALTH_CHECK, VAULT_HEALTH_CHECK_DEFAULT); + if (!healthCheckEnabled) return; + + final HashicorpVaultHealthCheck healthCheck = + new HashicorpVaultHealthCheck(client, context.getMonitor()); + + final HealthCheckService healthCheckService = context.getService(HealthCheckService.class); + healthCheckService.addLivenessProvider(healthCheck); + healthCheckService.addReadinessProvider(healthCheck); + healthCheckService.addStartupStatusProvider(healthCheck); + } } diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthCheck.java b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthCheck.java new file mode 100644 index 000000000..9b820953a --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthCheck.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Add vault health check + * + */ + +package net.catenax.edc.hashicorpvault; + +import java.io.IOException; +import lombok.RequiredArgsConstructor; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.system.health.HealthCheckResult; +import org.eclipse.dataspaceconnector.spi.system.health.LivenessProvider; +import org.eclipse.dataspaceconnector.spi.system.health.ReadinessProvider; +import org.eclipse.dataspaceconnector.spi.system.health.StartupStatusProvider; + +@RequiredArgsConstructor +public class HashicorpVaultHealthCheck + implements ReadinessProvider, LivenessProvider, StartupStatusProvider { + + private static final String HEALTH_CHECK_ERROR_TEMPLATE = + "HashiCorp Vault HealthCheck unsuccessful: %s %s"; + + private final HashicorpVaultClient client; + private final Monitor monitor; + + @Override + public HealthCheckResult get() { + + try { + final HashicorpVaultHealthResponse response = client.getHealth(); + + switch (response.getCodeAsEnum()) { + case INITIALIZED_UNSEALED_AND_ACTIVE: + monitor.debug("HashiCorp Vault HealthCheck successful. " + response.getPayload()); + return HealthCheckResult.success(); + case UNSEALED_AND_STANDBY: + final String standbyMsg = + String.format( + HEALTH_CHECK_ERROR_TEMPLATE, "Vault is in standby", response.getPayload()); + monitor.debug(standbyMsg); + return HealthCheckResult.failed(standbyMsg); + case DISASTER_RECOVERY_MODE_REPLICATION_SECONDARY_AND_ACTIVE: + final String recoveryModeMsg = + String.format( + HEALTH_CHECK_ERROR_TEMPLATE, "Vault is in recovery mode", response.getPayload()); + monitor.debug(recoveryModeMsg); + return HealthCheckResult.failed(recoveryModeMsg); + case PERFORMANCE_STANDBY: + final String performanceStandbyMsg = + String.format( + HEALTH_CHECK_ERROR_TEMPLATE, + "Vault is in performance standby", + response.getPayload()); + monitor.debug(performanceStandbyMsg); + return HealthCheckResult.failed(performanceStandbyMsg); + case NOT_INITIALIZED: + final String notInitializedMsg = + String.format( + HEALTH_CHECK_ERROR_TEMPLATE, "Vault is not initialized", response.getPayload()); + monitor.debug(notInitializedMsg); + return HealthCheckResult.failed(notInitializedMsg); + case SEALED: + final String sealedMsg = + String.format(HEALTH_CHECK_ERROR_TEMPLATE, "Vault is sealed", response.getPayload()); + monitor.debug(sealedMsg); + return HealthCheckResult.failed(sealedMsg); + case UNSPECIFIED: + default: + final String unspecifiedMsg = + String.format( + HEALTH_CHECK_ERROR_TEMPLATE, + "Unspecified response from vault. Code: " + response.getCode(), + response.getPayload()); + monitor.debug(unspecifiedMsg); + return HealthCheckResult.failed(unspecifiedMsg); + } + + } catch (IOException e) { + final String exceptionMsg = + String.format(HEALTH_CHECK_ERROR_TEMPLATE, "IOException: " + e.getMessage(), ""); + monitor.debug(exceptionMsg); + return HealthCheckResult.failed(exceptionMsg); + } + } +} diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthResponse.java b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthResponse.java new file mode 100644 index 000000000..4d3533198 --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthResponse.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Add vault health check + * + */ + +package net.catenax.edc.hashicorpvault; + +import lombok.Builder; +import lombok.Getter; +import org.jetbrains.annotations.Nullable; + +@Builder +@Getter +public class HashicorpVaultHealthResponse { + + @Nullable private HashicorpVaultHealthResponsePayload payload; + + private int code; + + public HashiCorpVaultHealthResponseCode getCodeAsEnum() { + switch (code) { + case 200: + return HashicorpVaultHealthResponse.HashiCorpVaultHealthResponseCode + .INITIALIZED_UNSEALED_AND_ACTIVE; + case 409: + return HashicorpVaultHealthResponse.HashiCorpVaultHealthResponseCode.UNSEALED_AND_STANDBY; + case 472: + return HashicorpVaultHealthResponse.HashiCorpVaultHealthResponseCode + .DISASTER_RECOVERY_MODE_REPLICATION_SECONDARY_AND_ACTIVE; + case 473: + return HashicorpVaultHealthResponse.HashiCorpVaultHealthResponseCode.PERFORMANCE_STANDBY; + case 501: + return HashicorpVaultHealthResponse.HashiCorpVaultHealthResponseCode.NOT_INITIALIZED; + case 503: + return HashicorpVaultHealthResponse.HashiCorpVaultHealthResponseCode.SEALED; + default: + return HashicorpVaultHealthResponse.HashiCorpVaultHealthResponseCode.UNSPECIFIED; + } + } + + public enum HashiCorpVaultHealthResponseCode { + UNSPECIFIED, // undefined status codes + INITIALIZED_UNSEALED_AND_ACTIVE, // status code 200 + UNSEALED_AND_STANDBY, // status code 429 + DISASTER_RECOVERY_MODE_REPLICATION_SECONDARY_AND_ACTIVE, // status code 472 + PERFORMANCE_STANDBY, // status code 473 + NOT_INITIALIZED, // status code 501 + SEALED // status code 503 + } +} diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthResponsePayload.java b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthResponsePayload.java new file mode 100644 index 000000000..d63b71408 --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthResponsePayload.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Add vault health check + * + */ + +package net.catenax.edc.hashicorpvault; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Data +@ToString +@JsonIgnoreProperties(ignoreUnknown = true) +public class HashicorpVaultHealthResponsePayload { + @JsonProperty("initialized") + private boolean isInitialized; + + @JsonProperty("sealed") + private boolean isSealed; + + @JsonProperty("standby") + private boolean isStandby; + + @JsonProperty("performance_standby") + private boolean isPerformanceStandby; + + @JsonProperty("replication_performance_mode") + private String replicationPerformanceMode; + + @JsonProperty("replication_dr_mode") + private String replicationDrMode; + + @JsonProperty("server_time_utc") + private long serverTimeUtc; + + @JsonProperty("version") + private String version; + + @JsonProperty("cluster_name") + private String clusterName; + + @JsonProperty("cluster_id") + private String clusterId; +} diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/PathUtil.java b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/PathUtil.java new file mode 100644 index 000000000..fe5bb69fa --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/PathUtil.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Make secret data & metadata paths configurable + * + */ + +package net.catenax.edc.hashicorpvault; + +final class PathUtil { + + private PathUtil() {} + + static String trimLeadingOrEndingSlash(String path) { + var fixedPath = path; + + if (fixedPath.startsWith("/")) fixedPath = fixedPath.substring(1); + if (fixedPath.endsWith("/")) fixedPath = fixedPath.substring(0, fixedPath.length() - 1); + + return fixedPath; + } +} diff --git a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/AbstractHashicorpIT.java b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/AbstractHashicorpIT.java index e7b4279e3..043f8e720 100644 --- a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/AbstractHashicorpIT.java +++ b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/AbstractHashicorpIT.java @@ -18,14 +18,24 @@ import static net.catenax.edc.hashicorpvault.HashicorpVaultExtension.VAULT_TOKEN; import static net.catenax.edc.hashicorpvault.HashicorpVaultExtension.VAULT_URL; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.UUID; +import java.util.stream.Collectors; import lombok.Getter; import org.eclipse.dataspaceconnector.junit.launcher.EdcExtension; import org.eclipse.dataspaceconnector.spi.security.CertificateResolver; import org.eclipse.dataspaceconnector.spi.security.Vault; import org.eclipse.dataspaceconnector.spi.system.ServiceExtension; import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; +import org.eclipse.dataspaceconnector.spi.system.health.HealthCheckResult; +import org.eclipse.dataspaceconnector.spi.system.health.HealthCheckService; +import org.eclipse.dataspaceconnector.spi.system.health.HealthStatus; +import org.eclipse.dataspaceconnector.spi.system.health.LivenessProvider; +import org.eclipse.dataspaceconnector.spi.system.health.ReadinessProvider; +import org.eclipse.dataspaceconnector.spi.system.health.StartupStatusProvider; import org.junit.ClassRule; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.ExtendWith; @@ -62,19 +72,23 @@ protected CertificateResolver getCertificateResolver() { @BeforeEach final void beforeEach(EdcExtension extension) { - extension.setConfiguration( - new HashMap<>() { - { - put( - VAULT_URL, - String.format( - "http://%s:%s", vaultContainer.getHost(), vaultContainer.getFirstMappedPort())); - put(VAULT_TOKEN, TOKEN); - } - }); + extension.setConfiguration(getConfig()); + extension.registerServiceMock(HealthCheckService.class, new MyHealthCheckService()); extension.registerSystemExtension(ServiceExtension.class, testExtension); } + protected Map getConfig() { + return new HashMap<>() { + { + put( + VAULT_URL, + String.format( + "http://%s:%s", vaultContainer.getHost(), vaultContainer.getFirstMappedPort())); + put(VAULT_TOKEN, TOKEN); + } + }; + } + @Getter private static class TestExtension implements ServiceExtension { private Vault vault; @@ -86,4 +100,60 @@ public void initialize(ServiceExtensionContext context) { certificateResolver = context.getService(CertificateResolver.class); } } + + private static class MyHealthCheckService implements HealthCheckService { + private final List livenessProviders = new ArrayList<>(); + private final List readinessProviders = new ArrayList<>(); + private final List startupStatusProviders = new ArrayList<>(); + + @Override + public void addLivenessProvider(LivenessProvider provider) { + livenessProviders.add(provider); + } + + @Override + public void addReadinessProvider(ReadinessProvider provider) { + readinessProviders.add(provider); + } + + @Override + public void addStartupStatusProvider(StartupStatusProvider provider) { + startupStatusProviders.add(provider); + } + + @Override + public HealthStatus isLive() { + return new HealthStatus( + livenessProviders.stream() + .map( + p -> + p.get().failed() ? HealthCheckResult.failed("") : HealthCheckResult.success()) + .collect(Collectors.toList())); + } + + @Override + public HealthStatus isReady() { + return new HealthStatus( + readinessProviders.stream() + .map( + p -> + p.get().failed() ? HealthCheckResult.failed("") : HealthCheckResult.success()) + .collect(Collectors.toList())); + } + + @Override + public HealthStatus getStartupStatus() { + return new HealthStatus( + startupStatusProviders.stream() + .map( + p -> + p.get().failed() ? HealthCheckResult.failed("") : HealthCheckResult.success()) + .collect(Collectors.toList())); + } + + @Override + public void refresh() { + // why? + } + } } diff --git a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpCertificateResolverTest.java b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpCertificateResolverTest.java index ca86d71fa..4a485cf4b 100644 --- a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpCertificateResolverTest.java +++ b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpCertificateResolverTest.java @@ -17,6 +17,7 @@ import java.security.cert.X509Certificate; import lombok.SneakyThrows; import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -49,4 +50,17 @@ void resolveCertificate() { // verify Mockito.verify(vault, Mockito.times(1)).resolveSecret(key); } + + @Test + @SneakyThrows + void nullIfVaultEmpty() { + // prepare + Mockito.when(vault.resolveSecret(key)).thenReturn(null); + + // invoke + final X509Certificate certificate = certificateResolver.resolveCertificate(key); + + // verify + Assertions.assertNull(certificate); + } } diff --git a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultClientTest.java b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultClientTest.java index ae0ca69c8..525881619 100644 --- a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultClientTest.java +++ b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultClientTest.java @@ -15,6 +15,7 @@ package net.catenax.edc.hashicorpvault; import com.fasterxml.jackson.databind.ObjectMapper; +import java.time.Duration; import java.util.UUID; import lombok.SneakyThrows; import okhttp3.Call; @@ -29,6 +30,9 @@ class HashicorpVaultClientTest { private static final String key = "key"; + private static final String customSecretPath = "v1/test/secret"; + private static final String healthPath = "sys/health"; + private static final Duration timeout = Duration.ofSeconds(30); private static final ObjectMapper objectMapper = new ObjectMapper(); @Test @@ -38,7 +42,14 @@ void getSecretValue() { String vaultUrl = "https://mock.url"; String vaultToken = UUID.randomUUID().toString(); HashicorpVaultClientConfig hashicorpVaultClientConfig = - HashicorpVaultClientConfig.builder().vaultUrl(vaultUrl).vaultToken(vaultToken).build(); + HashicorpVaultClientConfig.builder() + .vaultUrl(vaultUrl) + .vaultApiSecretPath(customSecretPath) + .vaultApiHealthPath(healthPath) + .isVaultApiHealthStandbyOk(false) + .vaultToken(vaultToken) + .timeout(timeout) + .build(); OkHttpClient okHttpClient = Mockito.mock(OkHttpClient.class); HashicorpVaultClient vaultClient = @@ -64,7 +75,7 @@ void getSecretValue() { Mockito.argThat( request -> request.method().equalsIgnoreCase("GET") - && request.url().encodedPath().contains("/v1/secret/data") + && request.url().encodedPath().contains(customSecretPath + "/data") && request.url().encodedPathSegments().contains(key))); } @@ -76,7 +87,14 @@ void setSecretValue() { String vaultToken = UUID.randomUUID().toString(); String secretValue = UUID.randomUUID().toString(); HashicorpVaultClientConfig hashicorpVaultClientConfig = - HashicorpVaultClientConfig.builder().vaultUrl(vaultUrl).vaultToken(vaultToken).build(); + HashicorpVaultClientConfig.builder() + .vaultUrl(vaultUrl) + .vaultApiSecretPath(customSecretPath) + .vaultApiHealthPath(healthPath) + .isVaultApiHealthStandbyOk(false) + .vaultToken(vaultToken) + .timeout(timeout) + .build(); OkHttpClient okHttpClient = Mockito.mock(OkHttpClient.class); HashicorpVaultClient vaultClient = @@ -105,10 +123,89 @@ void setSecretValue() { Mockito.argThat( request -> request.method().equalsIgnoreCase("POST") - && request.url().encodedPath().contains("/v1/secret/data") + && request.url().encodedPath().contains(customSecretPath + "/data") && request.url().encodedPathSegments().contains(key))); } + @Test + @SneakyThrows + void getHealth() { + // prepare + String vaultUrl = "https://mock.url"; + String vaultToken = UUID.randomUUID().toString(); + String secretValue = UUID.randomUUID().toString(); + HashicorpVaultClientConfig hashicorpVaultClientConfig = + HashicorpVaultClientConfig.builder() + .vaultUrl(vaultUrl) + .vaultApiSecretPath(customSecretPath) + .vaultApiHealthPath(healthPath) + .isVaultApiHealthStandbyOk(false) + .vaultToken(vaultToken) + .timeout(timeout) + .build(); + + OkHttpClient okHttpClient = Mockito.mock(OkHttpClient.class); + HashicorpVaultClient vaultClient = + new HashicorpVaultClient(hashicorpVaultClientConfig, okHttpClient, objectMapper); + HashicorpVaultHealthResponsePayload payload = new HashicorpVaultHealthResponsePayload(); + + Call call = Mockito.mock(Call.class); + Response response = Mockito.mock(Response.class); + ResponseBody body = Mockito.mock(ResponseBody.class); + + Mockito.when(okHttpClient.newCall(Mockito.any(Request.class))).thenReturn(call); + Mockito.when(call.execute()).thenReturn(response); + Mockito.when(response.code()).thenReturn(200); + Mockito.when(response.body()).thenReturn(body); + Mockito.when(body.string()) + .thenReturn( + "{ " + + "\"initialized\": true, " + + "\"sealed\": false," + + "\"standby\": false," + + "\"performance_standby\": false," + + "\"replication_performance_mode\": \"mode\"," + + "\"replication_dr_mode\": \"mode\"," + + "\"server_time_utc\": 100," + + "\"version\": \"1.0.0\"," + + "\"cluster_name\": \"name\"," + + "\"cluster_id\": \"id\" " + + " }"); + + // invoke + HashicorpVaultHealthResponse result = vaultClient.getHealth(); + + // verify + Assertions.assertNotNull(result); + Mockito.verify(okHttpClient, Mockito.times(1)) + .newCall( + Mockito.argThat( + request -> + request.method().equalsIgnoreCase("GET") + && request.url().encodedPath().contains(healthPath) + && request.url().queryParameter("standbyok").equals("false") + && request.url().queryParameter("perfstandbyok").equals("false"))); + Assertions.assertEquals(200, result.getCode()); + Assertions.assertEquals( + HashicorpVaultHealthResponse.HashiCorpVaultHealthResponseCode + .INITIALIZED_UNSEALED_AND_ACTIVE, + result.getCodeAsEnum()); + + HashicorpVaultHealthResponsePayload resultPayload = result.getPayload(); + + Assertions.assertNotNull(resultPayload); + Assertions.assertTrue(resultPayload.isInitialized()); + Assertions.assertFalse(resultPayload.isSealed()); + Assertions.assertFalse(resultPayload.isStandby()); + Assertions.assertFalse(resultPayload.isPerformanceStandby()); + Assertions.assertEquals("mode", resultPayload.getReplicationPerformanceMode()); + Assertions.assertEquals("mode", resultPayload.getReplicationDrMode()); + Assertions.assertEquals(100, resultPayload.getServerTimeUtc()); + Assertions.assertEquals("1.0.0", resultPayload.getVersion()); + Assertions.assertEquals("id", resultPayload.getClusterId()); + Assertions.assertEquals("name", resultPayload.getClusterName()); + } + @Test @SneakyThrows void destroySecretValue() { @@ -116,7 +213,14 @@ void destroySecretValue() { String vaultUrl = "https://mock.url"; String vaultToken = UUID.randomUUID().toString(); HashicorpVaultClientConfig hashicorpVaultClientConfig = - HashicorpVaultClientConfig.builder().vaultUrl(vaultUrl).vaultToken(vaultToken).build(); + HashicorpVaultClientConfig.builder() + .vaultUrl(vaultUrl) + .vaultApiSecretPath(customSecretPath) + .vaultApiHealthPath(healthPath) + .isVaultApiHealthStandbyOk(false) + .vaultToken(vaultToken) + .timeout(timeout) + .build(); OkHttpClient okHttpClient = Mockito.mock(OkHttpClient.class); HashicorpVaultClient vaultClient = @@ -140,7 +244,7 @@ void destroySecretValue() { Mockito.argThat( request -> request.method().equalsIgnoreCase("DELETE") - && request.url().encodedPath().contains("/v1/secret/metadata") + && request.url().encodedPath().contains(customSecretPath + "/metadata") && request.url().encodedPathSegments().contains(key))); } } diff --git a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultExtensionTest.java b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultExtensionTest.java new file mode 100644 index 000000000..ee5803379 --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultExtensionTest.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial Test + * + */ + +package net.catenax.edc.hashicorpvault; + +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; +import org.eclipse.dataspaceconnector.spi.system.health.HealthCheckService; +import org.eclipse.dataspaceconnector.spi.types.TypeManager; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class HashicorpVaultExtensionTest { + + private static final String VAULT_URL = "https://example.com"; + private static final String VAULT_TOKEN = "token"; + + private HashicorpVaultExtension extension; + + // mocks + private ServiceExtensionContext context; + private Monitor monitor; + private HealthCheckService healthCheckService; + + @BeforeEach + void setup() { + context = Mockito.mock(ServiceExtensionContext.class); + monitor = Mockito.mock(Monitor.class); + healthCheckService = Mockito.mock(HealthCheckService.class); + extension = new HashicorpVaultExtension(); + + Mockito.when(context.getService(HealthCheckService.class)).thenReturn(healthCheckService); + Mockito.when(context.getMonitor()).thenReturn(monitor); + Mockito.when(context.getTypeManager()).thenReturn(new TypeManager()); + Mockito.when(context.getSetting(HashicorpVaultExtension.VAULT_URL, null)).thenReturn(VAULT_URL); + Mockito.when(context.getSetting(HashicorpVaultExtension.VAULT_TOKEN, null)) + .thenReturn(VAULT_TOKEN); + + Mockito.when( + context.getSetting( + HashicorpVaultExtension.VAULT_API_SECRET_PATH, + HashicorpVaultExtension.VAULT_API_SECRET_PATH_DEFAULT)) + .thenReturn(HashicorpVaultExtension.VAULT_API_SECRET_PATH_DEFAULT); + Mockito.when( + context.getSetting( + HashicorpVaultExtension.VAULT_API_HEALTH_PATH, + HashicorpVaultExtension.VAULT_API_HEALTH_PATH_DEFAULT)) + .thenReturn(HashicorpVaultExtension.VAULT_API_HEALTH_PATH_DEFAULT); + Mockito.when( + context.getSetting( + HashicorpVaultExtension.VAULT_HEALTH_CHECK, + HashicorpVaultExtension.VAULT_HEALTH_CHECK_DEFAULT)) + .thenReturn(HashicorpVaultExtension.VAULT_HEALTH_CHECK_DEFAULT); + Mockito.when( + context.getSetting( + HashicorpVaultExtension.VAULT_HEALTH_CHECK_STANDBY_OK, + HashicorpVaultExtension.VAULT_HEALTH_CHECK_STANDBY_OK_DEFAULT)) + .thenReturn(HashicorpVaultExtension.VAULT_HEALTH_CHECK_STANDBY_OK_DEFAULT); + } + + @Test + void registersHealthCheckIfEnabled() { + Mockito.when(context.getSetting(HashicorpVaultExtension.VAULT_HEALTH_CHECK, true)) + .thenReturn(true); + + extension.initializeVault(context); + + Mockito.verify(healthCheckService, Mockito.times(1)).addReadinessProvider(Mockito.any()); + Mockito.verify(healthCheckService, Mockito.times(1)).addLivenessProvider(Mockito.any()); + Mockito.verify(healthCheckService, Mockito.times(1)).addStartupStatusProvider(Mockito.any()); + } + + @Test + void registersNoHealthCheckIfDisabled() { + Mockito.when(context.getSetting(HashicorpVaultExtension.VAULT_HEALTH_CHECK, true)) + .thenReturn(false); + + extension.initializeVault(context); + + Mockito.verify(healthCheckService, Mockito.times(0)).addReadinessProvider(Mockito.any()); + Mockito.verify(healthCheckService, Mockito.times(0)).addLivenessProvider(Mockito.any()); + Mockito.verify(healthCheckService, Mockito.times(0)).addStartupStatusProvider(Mockito.any()); + } + + @Test + void throwsHashicorpVaultExceptionOnVaultUrlUndefined() { + Mockito.when(context.getSetting(HashicorpVaultExtension.VAULT_URL, null)).thenReturn(null); + + Assertions.assertThrows( + HashicorpVaultException.class, () -> extension.initializeVault(context)); + } + + @Test + void throwsHashicorpVaultExceptionOnVaultTokenUndefined() { + Mockito.when(context.getSetting(HashicorpVaultExtension.VAULT_TOKEN, null)).thenReturn(null); + + Assertions.assertThrows( + HashicorpVaultException.class, () -> extension.initializeVault(context)); + } +} diff --git a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthCheckTest.java b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthCheckTest.java new file mode 100644 index 000000000..a124c51e1 --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthCheckTest.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial Test + * + */ + +package net.catenax.edc.hashicorpvault; + +import java.io.IOException; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.system.health.HealthCheckResult; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.Mockito; + +class HashicorpVaultHealthCheckTest { + + private HashicorpVaultHealthCheck healthCheck; + + // mocks + private Monitor monitor; + private HashicorpVaultClient client; + + @BeforeEach + void setup() { + monitor = Mockito.mock(Monitor.class); + client = Mockito.mock(HashicorpVaultClient.class); + + healthCheck = new HashicorpVaultHealthCheck(client, monitor); + } + + @ParameterizedTest + @ValueSource(ints = {200, 409, 472, 473, 501, 503, 999}) + void testResponseFromCode(int code) throws IOException { + + Mockito.when(client.getHealth()) + .thenReturn( + new HashicorpVaultHealthResponse(new HashicorpVaultHealthResponsePayload(), code)); + + final HealthCheckResult result = healthCheck.get(); + + if (code == 200) Assertions.assertTrue(result.succeeded()); + else Assertions.assertTrue(result.failed()); + + Mockito.verify(monitor, Mockito.times(1)).debug(Mockito.anyString()); + } + + @Test + void testResponseFromException() throws IOException { + Mockito.when(client.getHealth()).thenThrow(new IOException()); + + final HealthCheckResult result = healthCheck.get(); + Assertions.assertFalse(result.succeeded()); + } +} diff --git a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultTest.java b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultTest.java index c082b76e9..5b98c4f6e 100644 --- a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultTest.java +++ b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultTest.java @@ -39,13 +39,14 @@ void setup() { @Test @SneakyThrows - void getSecret() { + void getSecretSuccess() { // prepare String value = UUID.randomUUID().toString(); Result result = Mockito.mock(Result.class); Mockito.when(vaultClient.getSecretValue(key)).thenReturn(result); Mockito.when(result.getContent()).thenReturn(value); Mockito.when(result.succeeded()).thenReturn(true); + Mockito.when(result.failed()).thenReturn(false); // invoke String returnValue = vault.resolveSecret(key); @@ -57,12 +58,30 @@ void getSecret() { @Test @SneakyThrows - void setSecret() { + void getSecretFailure() { + // prepare + Result result = Mockito.mock(Result.class); + Mockito.when(vaultClient.getSecretValue(key)).thenReturn(result); + Mockito.when(result.succeeded()).thenReturn(false); + Mockito.when(result.failed()).thenReturn(true); + + // invoke + String returnValue = vault.resolveSecret(key); + + // verify + Mockito.verify(vaultClient, Mockito.times(1)).getSecretValue(key); + Assertions.assertNull(returnValue); + } + + @Test + @SneakyThrows + void setSecretSuccess() { // prepare String value = UUID.randomUUID().toString(); Result result = Mockito.mock(Result.class); Mockito.when(vaultClient.setSecret(key, value)).thenReturn(result); Mockito.when(result.succeeded()).thenReturn(true); + Mockito.when(result.failed()).thenReturn(false); // invoke Result returnValue = vault.storeSecret(key, value); @@ -74,11 +93,30 @@ void setSecret() { @Test @SneakyThrows - void destroySecret() { + void setSecretFailure() { + // prepare + String value = UUID.randomUUID().toString(); + Result result = Mockito.mock(Result.class); + Mockito.when(vaultClient.setSecret(key, value)).thenReturn(result); + Mockito.when(result.succeeded()).thenReturn(false); + Mockito.when(result.failed()).thenReturn(true); + + // invoke + Result returnValue = vault.storeSecret(key, value); + + // verify + Mockito.verify(vaultClient, Mockito.times(1)).setSecret(key, value); + Assertions.assertTrue(returnValue.failed()); + } + + @Test + @SneakyThrows + void destroySecretSuccess() { // prepare Result result = Mockito.mock(Result.class); Mockito.when(vaultClient.destroySecret(key)).thenReturn(result); Mockito.when(result.succeeded()).thenReturn(true); + Mockito.when(result.failed()).thenReturn(false); // invoke Result returnValue = vault.deleteSecret(key); @@ -87,4 +125,21 @@ void destroySecret() { Mockito.verify(vaultClient, Mockito.times(1)).destroySecret(key); Assertions.assertTrue(returnValue.succeeded()); } + + @Test + @SneakyThrows + void destroySecretFailure() { + // prepare + Result result = Mockito.mock(Result.class); + Mockito.when(vaultClient.destroySecret(key)).thenReturn(result); + Mockito.when(result.succeeded()).thenReturn(false); + Mockito.when(result.failed()).thenReturn(true); + + // invoke + Result returnValue = vault.deleteSecret(key); + + // verify + Mockito.verify(vaultClient, Mockito.times(1)).destroySecret(key); + Assertions.assertTrue(returnValue.failed()); + } } diff --git a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/PathUtilTest.java b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/PathUtilTest.java new file mode 100644 index 000000000..01fb0f40a --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/PathUtilTest.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.hashicorpvault; + +import java.util.stream.Stream; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class PathUtilTest { + + @ParameterizedTest + @MethodSource("provideStringsForTrimsPathsCorrect") + void trimsPathsCorrect(String path, String expected) { + final String result = PathUtil.trimLeadingOrEndingSlash(path); + + Assertions.assertEquals(expected, result); + } + + private static Stream provideStringsForTrimsPathsCorrect() { + return Stream.of( + Arguments.of("v1/secret/data", "v1/secret/data"), + Arguments.of("/v1/secret/data", "v1/secret/data"), + Arguments.of("/v1/secret/data/", "v1/secret/data"), + Arguments.of("v1/secret/data/", "v1/secret/data")); + } +} diff --git a/lombok.config b/lombok.config new file mode 100644 index 000000000..df71bb6a0 --- /dev/null +++ b/lombok.config @@ -0,0 +1,2 @@ +config.stopBubbling = true +lombok.addLombokGeneratedAnnotation = true From 54170f335537d1a8bd0bc727404e25dac5e7b7a8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 Jul 2022 16:11:57 +0200 Subject: [PATCH 186/433] Bump gson from 2.8.9 to 2.9.0 (#300) --- edc-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-tests/pom.xml b/edc-tests/pom.xml index 3010340a6..86971a89a 100644 --- a/edc-tests/pom.xml +++ b/edc-tests/pom.xml @@ -28,7 +28,7 @@ ${project.groupId}_${project.artifactId} - 2.8.9 + 2.9.0 4.5.13 1.2.11 1.7.36 From 7ad70e3224ca993921737609dae846e37e008197 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 Jul 2022 16:12:10 +0200 Subject: [PATCH 187/433] Bump junit-bom from 5.8.2 to 5.9.0 (#299) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7523fb00a..f4ab0749d 100644 --- a/pom.xml +++ b/pom.xml @@ -69,7 +69,7 @@ 9.0.2 - 5.8.2 + 5.9.0 1.8.2 7.3.2 5.0.1 From 36ae904aea81dfc838efb3255555eead65f2a7a0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 Jul 2022 16:12:18 +0200 Subject: [PATCH 188/433] Bump azure/setup-kubectl from 2.0 to 3.0 (#297) Bumps [azure/setup-kubectl](https://github.com/azure/setup-kubectl) from 2.0 to 3.0. - [Release notes](https://github.com/azure/setup-kubectl/releases) - [Commits](https://github.com/azure/setup-kubectl/compare/v2.0...v3.0) --- updated-dependencies: - dependency-name: azure/setup-kubectl dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/business-tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/business-tests.yaml b/.github/workflows/business-tests.yaml index 8ece26e9c..95be6f7ba 100644 --- a/.github/workflows/business-tests.yaml +++ b/.github/workflows/business-tests.yaml @@ -38,7 +38,7 @@ jobs: key: ${{ runner.os }}-io.containerd.snapshotter.v1.overlayfs - name: Set-Up Kubectl - uses: azure/setup-kubectl@v2.0 + uses: azure/setup-kubectl@v3.0 - name: Helm Set-Up uses: azure/setup-helm@v3.3 From e238dca9412983f0310b8a775fb644e440e626ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 Jul 2022 16:12:30 +0200 Subject: [PATCH 189/433] Bump cucumber.version from 7.3.2 to 7.4.1 (#298) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f4ab0749d..edfb2ab34 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ 5.9.0 1.8.2 - 7.3.2 + 7.4.1 5.0.1 1.1.0 4.6.1 From 5c35c6180b3c2a3208527d336aec21985920eaa9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 Jul 2022 16:50:21 +0200 Subject: [PATCH 190/433] Bump rest-assured from 5.0.1 to 5.1.1 (#301) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index edfb2ab34..193f28baa 100644 --- a/pom.xml +++ b/pom.xml @@ -72,7 +72,7 @@ 5.9.0 1.8.2 7.4.1 - 5.0.1 + 5.1.1 1.1.0 4.6.1 1.18.24 From 0e75b80be7914f66a2dc9cc16caaaa22defefeb5 Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Thu, 28 Jul 2022 08:54:12 +0200 Subject: [PATCH 191/433] several small improvements (#303) --- edc-extensions/hashicorp-vault/README.md | 14 +- edc-extensions/hashicorp-vault/pom.xml | 7 +- .../AbstractHashiCorpVaultExtension.java | 98 +++++++++++ .../HashicorpVaultExtension.java | 165 ------------------ .../HashicorpVaultHealthCheck.java | 16 +- .../HashicorpVaultHealthExtension.java | 65 +++++++ .../HashicorpVaultVaultExtension.java | 69 ++++++++ ...spaceconnector.spi.system.ServiceExtension | 13 ++ ...taspaceconnector.spi.system.VaultExtension | 2 +- .../hashicorpvault/AbstractHashicorpIT.java | 8 +- .../HashicorpVaultExtensionTest.java | 61 ++----- ...ashicorpVaultHealthCheckExtensionTest.java | 113 ++++++++++++ .../HashicorpVaultHealthCheckTest.java | 11 +- .../deployment/helm/all-in-one/values.yaml | 4 +- pom.xml | 17 ++ 15 files changed, 426 insertions(+), 237 deletions(-) create mode 100644 edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/AbstractHashiCorpVaultExtension.java delete mode 100644 edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultExtension.java create mode 100644 edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthExtension.java create mode 100644 edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultVaultExtension.java create mode 100644 edc-extensions/hashicorp-vault/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension create mode 100644 edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthCheckExtensionTest.java diff --git a/edc-extensions/hashicorp-vault/README.md b/edc-extensions/hashicorp-vault/README.md index 7e31dde54..caa6ff4db 100644 --- a/edc-extensions/hashicorp-vault/README.md +++ b/edc-extensions/hashicorp-vault/README.md @@ -11,15 +11,15 @@ Therefore, the HashiCorp vault extension **only** checks the '**content**' data ## Configuration -| Key | Description | Mandatory | Default | -|:--------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------|-----------|---------| +| Key | Description | Mandatory | Default | +|:--------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------|-----------|------------------| | edc.vault.hashicorp.url | URL to connect to the HashiCorp Vault | X || | edc.vault.hashicorp.token | Value for [Token Authentication](https://www.vaultproject.io/docs/auth/token) with the vault | X || -| edc.vault.hashicorp.timeout.seconds | Request timeout in seconds when contacting the vault | | `30` | -| edc.vault.hashicorp.health.check.enabled | Enable health checks to ensure vault is initialized, unsealed and active (default: _true_) | | `true` | -| edc.vault.hashicorp.health.check.standby.ok | Specifies if a vault in standby is healthy. This is useful when Vault is behind a non-configurable load balancer. (default: _false_) | | `false` | -| edc.vault.hashicorp.api.secret.path | Path to the [secret api](https://www.vaultproject.io/api-docs/secret/kv/kv-v1) (default: _/v1/secret_) | | `/v1/secret` | -| edc.vault.hashicorp.api.health.check.path | Path to the [health api](https://www.vaultproject.io/api-docs/system/health) (default: _/sys/health_) | | `/sys/health` | +| edc.vault.hashicorp.timeout.seconds | Request timeout in seconds when contacting the vault | | `30` | +| edc.vault.hashicorp.health.check.enabled | Enable health checks to ensure vault is initialized, unsealed and active (default: _true_) | | `true` | +| edc.vault.hashicorp.health.check.standby.ok | Specifies if a vault in standby is healthy. This is useful when Vault is behind a non-configurable load balancer. (default: _false_) | | `false` | +| edc.vault.hashicorp.api.secret.path | Path to the [secret api](https://www.vaultproject.io/api-docs/secret/kv/kv-v1) (default: _/v1/secret_) | | `/v1/secret` | +| edc.vault.hashicorp.api.health.check.path | Path to the [health api](https://www.vaultproject.io/api-docs/system/health) (default: _/sys/health_) | | `/v1/sys/health` | ## Example: Create & Configure DAPS Key diff --git a/edc-extensions/hashicorp-vault/pom.xml b/edc-extensions/hashicorp-vault/pom.xml index 72aaa2b8c..07ad5e7ac 100644 --- a/edc-extensions/hashicorp-vault/pom.xml +++ b/edc-extensions/hashicorp-vault/pom.xml @@ -183,9 +183,14 @@ org.hamcrest hamcrest - 2.2 test + + net.jodah + failsafe + test + + diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/AbstractHashiCorpVaultExtension.java b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/AbstractHashiCorpVaultExtension.java new file mode 100644 index 000000000..eae94c39e --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/AbstractHashiCorpVaultExtension.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Add vault health check + * + */ + +package net.catenax.edc.hashicorpvault; + +import java.time.Duration; +import okhttp3.OkHttpClient; +import org.eclipse.dataspaceconnector.spi.EdcSetting; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; + +/** + * Temporary solution as long as the Vault components needs to be loaded as dedicated vault + * extension. Will be changed from EDC milestone 5. + */ +public class AbstractHashiCorpVaultExtension { + + @EdcSetting(required = true) + public static final String VAULT_URL = "edc.vault.hashicorp.url"; + + @EdcSetting(required = true) + public static final String VAULT_TOKEN = "edc.vault.hashicorp.token"; + + @EdcSetting + public static final String VAULT_API_SECRET_PATH = "edc.vault.hashicorp.api.secret.path"; + + public static final String VAULT_API_SECRET_PATH_DEFAULT = "/v1/secret"; + + @EdcSetting + public static final String VAULT_API_HEALTH_PATH = "edc.vault.hashicorp.api.health.check.path"; + + public static final String VAULT_API_HEALTH_PATH_DEFAULT = "/v1/sys/health"; + + @EdcSetting + public static final String VAULT_HEALTH_CHECK_STANDBY_OK = + "edc.vault.hashicorp.health.check.standby.ok"; + + public static final boolean VAULT_HEALTH_CHECK_STANDBY_OK_DEFAULT = false; + + @EdcSetting + private static final String VAULT_TIMEOUT_SECONDS = "edc.vault.hashicorp.timeout.seconds"; + + protected OkHttpClient createOkHttpClient(HashicorpVaultClientConfig config) { + OkHttpClient.Builder builder = + new OkHttpClient.Builder() + .callTimeout(config.getTimeout()) + .readTimeout(config.getTimeout()); + + return builder.build(); + } + + protected HashicorpVaultClientConfig loadHashicorpVaultClientConfig( + ServiceExtensionContext context) { + + final String vaultUrl = context.getSetting(VAULT_URL, null); + if (vaultUrl == null) { + throw new HashicorpVaultException(String.format("Vault URL (%s) must be defined", VAULT_URL)); + } + + final int vaultTimeoutSeconds = Math.max(0, context.getSetting(VAULT_TIMEOUT_SECONDS, 30)); + final Duration vaultTimeoutDuration = Duration.ofSeconds(vaultTimeoutSeconds); + + final String vaultToken = context.getSetting(VAULT_TOKEN, null); + + if (vaultToken == null) { + throw new HashicorpVaultException( + String.format("For Vault authentication [%s] is required", VAULT_TOKEN)); + } + + final String apiSecretPath = + context.getSetting(VAULT_API_SECRET_PATH, VAULT_API_SECRET_PATH_DEFAULT); + + final String apiHealthPath = + context.getSetting(VAULT_API_HEALTH_PATH, VAULT_API_HEALTH_PATH_DEFAULT); + + final boolean isHealthStandbyOk = + context.getSetting(VAULT_HEALTH_CHECK_STANDBY_OK, VAULT_HEALTH_CHECK_STANDBY_OK_DEFAULT); + + return HashicorpVaultClientConfig.builder() + .vaultUrl(vaultUrl) + .vaultToken(vaultToken) + .vaultApiSecretPath(apiSecretPath) + .vaultApiHealthPath(apiHealthPath) + .isVaultApiHealthStandbyOk(isHealthStandbyOk) + .timeout(vaultTimeoutDuration) + .build(); + } +} diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultExtension.java b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultExtension.java deleted file mode 100644 index 46344dcde..000000000 --- a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultExtension.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 - * - * SPDX-License-Identifier: Apache-2.0 - * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation - * Mercedes-Benz Tech Innovation GmbH - Make secret data & metadata paths configurable - * Mercedes-Benz Tech Innovation GmbH - Add vault health check - * - */ - -package net.catenax.edc.hashicorpvault; - -import java.time.Duration; -import okhttp3.OkHttpClient; -import org.eclipse.dataspaceconnector.spi.EdcSetting; -import org.eclipse.dataspaceconnector.spi.security.CertificateResolver; -import org.eclipse.dataspaceconnector.spi.security.PrivateKeyResolver; -import org.eclipse.dataspaceconnector.spi.security.Vault; -import org.eclipse.dataspaceconnector.spi.security.VaultPrivateKeyResolver; -import org.eclipse.dataspaceconnector.spi.system.Requires; -import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; -import org.eclipse.dataspaceconnector.spi.system.VaultExtension; -import org.eclipse.dataspaceconnector.spi.system.health.HealthCheckService; - -@Requires(HealthCheckService.class) -public class HashicorpVaultExtension implements VaultExtension { - - @EdcSetting(required = true) - public static final String VAULT_URL = "edc.vault.hashicorp.url"; - - @EdcSetting(required = true) - public static final String VAULT_TOKEN = "edc.vault.hashicorp.token"; - - @EdcSetting - public static final String VAULT_API_SECRET_PATH = "edc.vault.hashicorp.api.secret.path"; - - public static final String VAULT_API_SECRET_PATH_DEFAULT = "/v1/secret"; - - @EdcSetting - public static final String VAULT_API_HEALTH_PATH = "edc.vault.hashicorp.api.health.check.path"; - - public static final String VAULT_API_HEALTH_PATH_DEFAULT = "/sys/health"; - - @EdcSetting - public static final String VAULT_HEALTH_CHECK = "edc.vault.hashicorp.health.check.enabled"; - - public static final boolean VAULT_HEALTH_CHECK_DEFAULT = true; - - @EdcSetting - public static final String VAULT_HEALTH_CHECK_STANDBY_OK = - "edc.vault.hashicorp.health.check.standby.ok"; - - public static final boolean VAULT_HEALTH_CHECK_STANDBY_OK_DEFAULT = false; - - @EdcSetting - private static final String VAULT_TIMEOUT_SECONDS = "edc.vault.hashicorp.timeout.seconds"; - - private Vault vault; - private CertificateResolver certificateResolver; - private PrivateKeyResolver privateKeyResolver; - - @Override - public String name() { - return "Hashicorp Vault"; - } - - @Override - public Vault getVault() { - return vault; - } - - @Override - public PrivateKeyResolver getPrivateKeyResolver() { - return privateKeyResolver; - } - - @Override - public CertificateResolver getCertificateResolver() { - return certificateResolver; - } - - @Override - public void initializeVault(ServiceExtensionContext context) { - final HashicorpVaultClientConfig config = loadHashicorpVaultClientConfig(context); - - final OkHttpClient okHttpClient = createOkHttpClient(config); - - final HashicorpVaultClient client = - new HashicorpVaultClient(config, okHttpClient, context.getTypeManager().getMapper()); - - vault = new HashicorpVault(client, context.getMonitor()); - certificateResolver = new HashicorpCertificateResolver(vault, context.getMonitor()); - privateKeyResolver = new VaultPrivateKeyResolver(vault); - - configureHealthCheck(client, context); - - context.getMonitor().info("HashicorpVaultExtension: authentication/initialization complete."); - } - - private OkHttpClient createOkHttpClient(HashicorpVaultClientConfig config) { - OkHttpClient.Builder builder = - new OkHttpClient.Builder() - .callTimeout(config.getTimeout()) - .readTimeout(config.getTimeout()); - - return builder.build(); - } - - private HashicorpVaultClientConfig loadHashicorpVaultClientConfig( - ServiceExtensionContext context) { - - final String vaultUrl = context.getSetting(VAULT_URL, null); - if (vaultUrl == null) { - throw new HashicorpVaultException(String.format("Vault URL (%s) must be defined", VAULT_URL)); - } - - final int vaultTimeoutSeconds = Math.max(0, context.getSetting(VAULT_TIMEOUT_SECONDS, 30)); - final Duration vaultTimeoutDuration = Duration.ofSeconds(vaultTimeoutSeconds); - - final String vaultToken = context.getSetting(VAULT_TOKEN, null); - - if (vaultToken == null) { - throw new HashicorpVaultException( - String.format("For Vault authentication [%s] is required", VAULT_TOKEN)); - } - - final String apiSecretPath = - context.getSetting(VAULT_API_SECRET_PATH, VAULT_API_SECRET_PATH_DEFAULT); - - final String apiHealthPath = - context.getSetting(VAULT_API_HEALTH_PATH, VAULT_API_HEALTH_PATH_DEFAULT); - - final boolean isHealthStandbyOk = - context.getSetting(VAULT_HEALTH_CHECK_STANDBY_OK, VAULT_HEALTH_CHECK_STANDBY_OK_DEFAULT); - - return HashicorpVaultClientConfig.builder() - .vaultUrl(vaultUrl) - .vaultToken(vaultToken) - .vaultApiSecretPath(apiSecretPath) - .vaultApiHealthPath(apiHealthPath) - .isVaultApiHealthStandbyOk(isHealthStandbyOk) - .timeout(vaultTimeoutDuration) - .build(); - } - - private void configureHealthCheck(HashicorpVaultClient client, ServiceExtensionContext context) { - final boolean healthCheckEnabled = - context.getSetting(VAULT_HEALTH_CHECK, VAULT_HEALTH_CHECK_DEFAULT); - if (!healthCheckEnabled) return; - - final HashicorpVaultHealthCheck healthCheck = - new HashicorpVaultHealthCheck(client, context.getMonitor()); - - final HealthCheckService healthCheckService = context.getService(HealthCheckService.class); - healthCheckService.addLivenessProvider(healthCheck); - healthCheckService.addReadinessProvider(healthCheck); - healthCheckService.addStartupStatusProvider(healthCheck); - } -} diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthCheck.java b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthCheck.java index 9b820953a..e2177d157 100644 --- a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthCheck.java +++ b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthCheck.java @@ -27,7 +27,7 @@ public class HashicorpVaultHealthCheck implements ReadinessProvider, LivenessProvider, StartupStatusProvider { private static final String HEALTH_CHECK_ERROR_TEMPLATE = - "HashiCorp Vault HealthCheck unsuccessful: %s %s"; + "HashiCorp Vault HealthCheck unsuccessful. %s %s"; private final HashicorpVaultClient client; private final Monitor monitor; @@ -46,13 +46,13 @@ public HealthCheckResult get() { final String standbyMsg = String.format( HEALTH_CHECK_ERROR_TEMPLATE, "Vault is in standby", response.getPayload()); - monitor.debug(standbyMsg); + monitor.warning(standbyMsg); return HealthCheckResult.failed(standbyMsg); case DISASTER_RECOVERY_MODE_REPLICATION_SECONDARY_AND_ACTIVE: final String recoveryModeMsg = String.format( HEALTH_CHECK_ERROR_TEMPLATE, "Vault is in recovery mode", response.getPayload()); - monitor.debug(recoveryModeMsg); + monitor.warning(recoveryModeMsg); return HealthCheckResult.failed(recoveryModeMsg); case PERFORMANCE_STANDBY: final String performanceStandbyMsg = @@ -60,18 +60,18 @@ public HealthCheckResult get() { HEALTH_CHECK_ERROR_TEMPLATE, "Vault is in performance standby", response.getPayload()); - monitor.debug(performanceStandbyMsg); + monitor.warning(performanceStandbyMsg); return HealthCheckResult.failed(performanceStandbyMsg); case NOT_INITIALIZED: final String notInitializedMsg = String.format( HEALTH_CHECK_ERROR_TEMPLATE, "Vault is not initialized", response.getPayload()); - monitor.debug(notInitializedMsg); + monitor.warning(notInitializedMsg); return HealthCheckResult.failed(notInitializedMsg); case SEALED: final String sealedMsg = String.format(HEALTH_CHECK_ERROR_TEMPLATE, "Vault is sealed", response.getPayload()); - monitor.debug(sealedMsg); + monitor.warning(sealedMsg); return HealthCheckResult.failed(sealedMsg); case UNSPECIFIED: default: @@ -80,14 +80,14 @@ public HealthCheckResult get() { HEALTH_CHECK_ERROR_TEMPLATE, "Unspecified response from vault. Code: " + response.getCode(), response.getPayload()); - monitor.debug(unspecifiedMsg); + monitor.warning(unspecifiedMsg); return HealthCheckResult.failed(unspecifiedMsg); } } catch (IOException e) { final String exceptionMsg = String.format(HEALTH_CHECK_ERROR_TEMPLATE, "IOException: " + e.getMessage(), ""); - monitor.debug(exceptionMsg); + monitor.severe(exceptionMsg); return HealthCheckResult.failed(exceptionMsg); } } diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthExtension.java b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthExtension.java new file mode 100644 index 000000000..017d0a75c --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthExtension.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Add vault health check + * + */ + +package net.catenax.edc.hashicorpvault; + +import okhttp3.OkHttpClient; +import org.eclipse.dataspaceconnector.spi.EdcSetting; +import org.eclipse.dataspaceconnector.spi.system.Requires; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtension; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; +import org.eclipse.dataspaceconnector.spi.system.health.HealthCheckService; + +@Requires(HealthCheckService.class) +public class HashicorpVaultHealthExtension extends AbstractHashiCorpVaultExtension + implements ServiceExtension { + + @EdcSetting + public static final String VAULT_HEALTH_CHECK = "edc.vault.hashicorp.health.check.enabled"; + + public static final boolean VAULT_HEALTH_CHECK_DEFAULT = true; + + @Override + public String name() { + return "Hashicorp Vault Health Check"; + } + + @Override + public void initialize(ServiceExtensionContext context) { + final HashicorpVaultClientConfig config = loadHashicorpVaultClientConfig(context); + + final OkHttpClient okHttpClient = createOkHttpClient(config); + + final HashicorpVaultClient client = + new HashicorpVaultClient(config, okHttpClient, context.getTypeManager().getMapper()); + + configureHealthCheck(client, context); + + context.getMonitor().info("HashicorpVaultExtension: health check initialization complete."); + } + + private void configureHealthCheck(HashicorpVaultClient client, ServiceExtensionContext context) { + final boolean healthCheckEnabled = + context.getSetting(VAULT_HEALTH_CHECK, VAULT_HEALTH_CHECK_DEFAULT); + if (!healthCheckEnabled) return; + + final HashicorpVaultHealthCheck healthCheck = + new HashicorpVaultHealthCheck(client, context.getMonitor()); + + final HealthCheckService healthCheckService = context.getService(HealthCheckService.class); + healthCheckService.addLivenessProvider(healthCheck); + healthCheckService.addReadinessProvider(healthCheck); + healthCheckService.addStartupStatusProvider(healthCheck); + } +} diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultVaultExtension.java b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultVaultExtension.java new file mode 100644 index 000000000..3f6728187 --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultVaultExtension.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Mercedes-Benz Tech Innovation GmbH - Make secret data & metadata paths configurable + * Mercedes-Benz Tech Innovation GmbH - Add vault health check + * + */ + +package net.catenax.edc.hashicorpvault; + +import okhttp3.OkHttpClient; +import org.eclipse.dataspaceconnector.spi.security.CertificateResolver; +import org.eclipse.dataspaceconnector.spi.security.PrivateKeyResolver; +import org.eclipse.dataspaceconnector.spi.security.Vault; +import org.eclipse.dataspaceconnector.spi.security.VaultPrivateKeyResolver; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; +import org.eclipse.dataspaceconnector.spi.system.VaultExtension; + +public class HashicorpVaultVaultExtension extends AbstractHashiCorpVaultExtension + implements VaultExtension { + + private Vault vault; + private CertificateResolver certificateResolver; + private PrivateKeyResolver privateKeyResolver; + + @Override + public String name() { + return "Hashicorp Vault"; + } + + @Override + public Vault getVault() { + return vault; + } + + @Override + public PrivateKeyResolver getPrivateKeyResolver() { + return privateKeyResolver; + } + + @Override + public CertificateResolver getCertificateResolver() { + return certificateResolver; + } + + @Override + public void initializeVault(ServiceExtensionContext context) { + final HashicorpVaultClientConfig config = loadHashicorpVaultClientConfig(context); + + final OkHttpClient okHttpClient = createOkHttpClient(config); + + final HashicorpVaultClient client = + new HashicorpVaultClient(config, okHttpClient, context.getTypeManager().getMapper()); + + vault = new HashicorpVault(client, context.getMonitor()); + certificateResolver = new HashicorpCertificateResolver(vault, context.getMonitor()); + privateKeyResolver = new VaultPrivateKeyResolver(vault); + + context.getMonitor().info("HashicorpVaultExtension: authentication/initialization complete."); + } +} diff --git a/edc-extensions/hashicorp-vault/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension b/edc-extensions/hashicorp-vault/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension new file mode 100644 index 000000000..486504f3f --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension @@ -0,0 +1,13 @@ +# +# Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +# +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# +# Contributors: +# Mercedes-Benz Tech Innovation GmbH - Initial ServiceExtension file +# +net.catenax.edc.hashicorpvault.HashicorpVaultHealthExtension diff --git a/edc-extensions/hashicorp-vault/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.VaultExtension b/edc-extensions/hashicorp-vault/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.VaultExtension index 4c244ca16..5e8489869 100644 --- a/edc-extensions/hashicorp-vault/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.VaultExtension +++ b/edc-extensions/hashicorp-vault/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.VaultExtension @@ -10,4 +10,4 @@ # Contributors: # Mercedes-Benz Tech Innovation GmbH - Initial ServiceExtension file # -net.catenax.edc.hashicorpvault.HashicorpVaultExtension +net.catenax.edc.hashicorpvault.HashicorpVaultVaultExtension diff --git a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/AbstractHashicorpIT.java b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/AbstractHashicorpIT.java index 043f8e720..b8772763a 100644 --- a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/AbstractHashicorpIT.java +++ b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/AbstractHashicorpIT.java @@ -15,8 +15,8 @@ package net.catenax.edc.hashicorpvault; import static net.catenax.edc.hashicorpvault.HashicorpVaultClient.VAULT_DATA_ENTRY_NAME; -import static net.catenax.edc.hashicorpvault.HashicorpVaultExtension.VAULT_TOKEN; -import static net.catenax.edc.hashicorpvault.HashicorpVaultExtension.VAULT_URL; +import static net.catenax.edc.hashicorpvault.HashicorpVaultVaultExtension.VAULT_TOKEN; +import static net.catenax.edc.hashicorpvault.HashicorpVaultVaultExtension.VAULT_URL; import java.util.ArrayList; import java.util.HashMap; @@ -152,8 +152,6 @@ public HealthStatus getStartupStatus() { } @Override - public void refresh() { - // why? - } + public void refresh() {} } } diff --git a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultExtensionTest.java b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultExtensionTest.java index ee5803379..3ab18b188 100644 --- a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultExtensionTest.java +++ b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultExtensionTest.java @@ -28,7 +28,7 @@ class HashicorpVaultExtensionTest { private static final String VAULT_URL = "https://example.com"; private static final String VAULT_TOKEN = "token"; - private HashicorpVaultExtension extension; + private HashicorpVaultVaultExtension extension; // mocks private ServiceExtensionContext context; @@ -40,64 +40,36 @@ void setup() { context = Mockito.mock(ServiceExtensionContext.class); monitor = Mockito.mock(Monitor.class); healthCheckService = Mockito.mock(HealthCheckService.class); - extension = new HashicorpVaultExtension(); + extension = new HashicorpVaultVaultExtension(); Mockito.when(context.getService(HealthCheckService.class)).thenReturn(healthCheckService); Mockito.when(context.getMonitor()).thenReturn(monitor); Mockito.when(context.getTypeManager()).thenReturn(new TypeManager()); - Mockito.when(context.getSetting(HashicorpVaultExtension.VAULT_URL, null)).thenReturn(VAULT_URL); - Mockito.when(context.getSetting(HashicorpVaultExtension.VAULT_TOKEN, null)) + Mockito.when(context.getSetting(HashicorpVaultVaultExtension.VAULT_URL, null)) + .thenReturn(VAULT_URL); + Mockito.when(context.getSetting(HashicorpVaultVaultExtension.VAULT_TOKEN, null)) .thenReturn(VAULT_TOKEN); Mockito.when( context.getSetting( - HashicorpVaultExtension.VAULT_API_SECRET_PATH, - HashicorpVaultExtension.VAULT_API_SECRET_PATH_DEFAULT)) - .thenReturn(HashicorpVaultExtension.VAULT_API_SECRET_PATH_DEFAULT); + HashicorpVaultVaultExtension.VAULT_API_SECRET_PATH, + HashicorpVaultVaultExtension.VAULT_API_SECRET_PATH_DEFAULT)) + .thenReturn(HashicorpVaultVaultExtension.VAULT_API_SECRET_PATH_DEFAULT); Mockito.when( context.getSetting( - HashicorpVaultExtension.VAULT_API_HEALTH_PATH, - HashicorpVaultExtension.VAULT_API_HEALTH_PATH_DEFAULT)) - .thenReturn(HashicorpVaultExtension.VAULT_API_HEALTH_PATH_DEFAULT); + HashicorpVaultVaultExtension.VAULT_API_HEALTH_PATH, + HashicorpVaultVaultExtension.VAULT_API_HEALTH_PATH_DEFAULT)) + .thenReturn(HashicorpVaultVaultExtension.VAULT_API_HEALTH_PATH_DEFAULT); Mockito.when( context.getSetting( - HashicorpVaultExtension.VAULT_HEALTH_CHECK, - HashicorpVaultExtension.VAULT_HEALTH_CHECK_DEFAULT)) - .thenReturn(HashicorpVaultExtension.VAULT_HEALTH_CHECK_DEFAULT); - Mockito.when( - context.getSetting( - HashicorpVaultExtension.VAULT_HEALTH_CHECK_STANDBY_OK, - HashicorpVaultExtension.VAULT_HEALTH_CHECK_STANDBY_OK_DEFAULT)) - .thenReturn(HashicorpVaultExtension.VAULT_HEALTH_CHECK_STANDBY_OK_DEFAULT); - } - - @Test - void registersHealthCheckIfEnabled() { - Mockito.when(context.getSetting(HashicorpVaultExtension.VAULT_HEALTH_CHECK, true)) - .thenReturn(true); - - extension.initializeVault(context); - - Mockito.verify(healthCheckService, Mockito.times(1)).addReadinessProvider(Mockito.any()); - Mockito.verify(healthCheckService, Mockito.times(1)).addLivenessProvider(Mockito.any()); - Mockito.verify(healthCheckService, Mockito.times(1)).addStartupStatusProvider(Mockito.any()); - } - - @Test - void registersNoHealthCheckIfDisabled() { - Mockito.when(context.getSetting(HashicorpVaultExtension.VAULT_HEALTH_CHECK, true)) - .thenReturn(false); - - extension.initializeVault(context); - - Mockito.verify(healthCheckService, Mockito.times(0)).addReadinessProvider(Mockito.any()); - Mockito.verify(healthCheckService, Mockito.times(0)).addLivenessProvider(Mockito.any()); - Mockito.verify(healthCheckService, Mockito.times(0)).addStartupStatusProvider(Mockito.any()); + HashicorpVaultVaultExtension.VAULT_HEALTH_CHECK_STANDBY_OK, + HashicorpVaultVaultExtension.VAULT_HEALTH_CHECK_STANDBY_OK_DEFAULT)) + .thenReturn(HashicorpVaultVaultExtension.VAULT_HEALTH_CHECK_STANDBY_OK_DEFAULT); } @Test void throwsHashicorpVaultExceptionOnVaultUrlUndefined() { - Mockito.when(context.getSetting(HashicorpVaultExtension.VAULT_URL, null)).thenReturn(null); + Mockito.when(context.getSetting(HashicorpVaultVaultExtension.VAULT_URL, null)).thenReturn(null); Assertions.assertThrows( HashicorpVaultException.class, () -> extension.initializeVault(context)); @@ -105,7 +77,8 @@ void throwsHashicorpVaultExceptionOnVaultUrlUndefined() { @Test void throwsHashicorpVaultExceptionOnVaultTokenUndefined() { - Mockito.when(context.getSetting(HashicorpVaultExtension.VAULT_TOKEN, null)).thenReturn(null); + Mockito.when(context.getSetting(HashicorpVaultVaultExtension.VAULT_TOKEN, null)) + .thenReturn(null); Assertions.assertThrows( HashicorpVaultException.class, () -> extension.initializeVault(context)); diff --git a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthCheckExtensionTest.java b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthCheckExtensionTest.java new file mode 100644 index 000000000..71605bd3e --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthCheckExtensionTest.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial Test + * + */ + +package net.catenax.edc.hashicorpvault; + +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; +import org.eclipse.dataspaceconnector.spi.system.health.HealthCheckService; +import org.eclipse.dataspaceconnector.spi.types.TypeManager; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class HashicorpVaultHealthCheckExtensionTest { + + private static final String VAULT_URL = "https://example.com"; + private static final String VAULT_TOKEN = "token"; + + private HashicorpVaultHealthExtension extension; + + // mocks + private ServiceExtensionContext context; + private Monitor monitor; + private HealthCheckService healthCheckService; + + @BeforeEach + void setup() { + context = Mockito.mock(ServiceExtensionContext.class); + monitor = Mockito.mock(Monitor.class); + healthCheckService = Mockito.mock(HealthCheckService.class); + extension = new HashicorpVaultHealthExtension(); + + Mockito.when(context.getService(HealthCheckService.class)).thenReturn(healthCheckService); + Mockito.when(context.getMonitor()).thenReturn(monitor); + Mockito.when(context.getTypeManager()).thenReturn(new TypeManager()); + Mockito.when(context.getSetting(HashicorpVaultVaultExtension.VAULT_URL, null)) + .thenReturn(VAULT_URL); + Mockito.when(context.getSetting(HashicorpVaultVaultExtension.VAULT_TOKEN, null)) + .thenReturn(VAULT_TOKEN); + + Mockito.when( + context.getSetting( + HashicorpVaultHealthExtension.VAULT_API_SECRET_PATH, + HashicorpVaultHealthExtension.VAULT_API_SECRET_PATH_DEFAULT)) + .thenReturn(HashicorpVaultHealthExtension.VAULT_API_SECRET_PATH_DEFAULT); + Mockito.when( + context.getSetting( + HashicorpVaultHealthExtension.VAULT_API_HEALTH_PATH, + HashicorpVaultHealthExtension.VAULT_API_HEALTH_PATH_DEFAULT)) + .thenReturn(HashicorpVaultHealthExtension.VAULT_API_HEALTH_PATH_DEFAULT); + Mockito.when( + context.getSetting( + HashicorpVaultHealthExtension.VAULT_HEALTH_CHECK, + HashicorpVaultHealthExtension.VAULT_HEALTH_CHECK_DEFAULT)) + .thenReturn(HashicorpVaultHealthExtension.VAULT_HEALTH_CHECK_DEFAULT); + Mockito.when( + context.getSetting( + HashicorpVaultHealthExtension.VAULT_HEALTH_CHECK_STANDBY_OK, + HashicorpVaultHealthExtension.VAULT_HEALTH_CHECK_STANDBY_OK_DEFAULT)) + .thenReturn(HashicorpVaultHealthExtension.VAULT_HEALTH_CHECK_STANDBY_OK_DEFAULT); + } + + @Test + void registersHealthCheckIfEnabled() { + Mockito.when(context.getSetting(HashicorpVaultHealthExtension.VAULT_HEALTH_CHECK, true)) + .thenReturn(true); + + extension.initialize(context); + + Mockito.verify(healthCheckService, Mockito.times(1)).addReadinessProvider(Mockito.any()); + Mockito.verify(healthCheckService, Mockito.times(1)).addLivenessProvider(Mockito.any()); + Mockito.verify(healthCheckService, Mockito.times(1)).addStartupStatusProvider(Mockito.any()); + } + + @Test + void registersNoHealthCheckIfDisabled() { + Mockito.when(context.getSetting(HashicorpVaultHealthExtension.VAULT_HEALTH_CHECK, true)) + .thenReturn(false); + + extension.initialize(context); + + Mockito.verify(healthCheckService, Mockito.times(0)).addReadinessProvider(Mockito.any()); + Mockito.verify(healthCheckService, Mockito.times(0)).addLivenessProvider(Mockito.any()); + Mockito.verify(healthCheckService, Mockito.times(0)).addStartupStatusProvider(Mockito.any()); + } + + @Test + void throwsHashicorpVaultExceptionOnVaultUrlUndefined() { + Mockito.when(context.getSetting(HashicorpVaultVaultExtension.VAULT_URL, null)).thenReturn(null); + + Assertions.assertThrows(HashicorpVaultException.class, () -> extension.initialize(context)); + } + + @Test + void throwsHashicorpVaultExceptionOnVaultTokenUndefined() { + Mockito.when(context.getSetting(HashicorpVaultVaultExtension.VAULT_TOKEN, null)) + .thenReturn(null); + + Assertions.assertThrows(HashicorpVaultException.class, () -> extension.initialize(context)); + } +} diff --git a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthCheckTest.java b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthCheckTest.java index a124c51e1..da198ba84 100644 --- a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthCheckTest.java +++ b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthCheckTest.java @@ -50,10 +50,13 @@ void testResponseFromCode(int code) throws IOException { final HealthCheckResult result = healthCheck.get(); - if (code == 200) Assertions.assertTrue(result.succeeded()); - else Assertions.assertTrue(result.failed()); - - Mockito.verify(monitor, Mockito.times(1)).debug(Mockito.anyString()); + if (code == 200) { + Mockito.verify(monitor, Mockito.times(1)).debug(Mockito.anyString()); + Assertions.assertTrue(result.succeeded()); + } else { + Assertions.assertTrue(result.failed()); + Mockito.verify(monitor, Mockito.times(1)).warning(Mockito.anyString()); + } } @Test diff --git a/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml b/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml index 0f7ed8715..27bab4e09 100644 --- a/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml +++ b/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml @@ -199,7 +199,7 @@ platoedcdataplane: ############### # see extension https://github.com/catenax-ng/product-edc/tree/develop/edc-extensions/hashicorp-vault - EDC_VAULT_HASHICORP_URL: http://plato-vault + EDC_VAULT_HASHICORP_URL: http://plato-vault:8200 platoedccontrolplane: enabled: true fullnameOverride: "plato-edc-controlplane" @@ -415,7 +415,7 @@ sokratesedcdataplane: ############### # see extension https://github.com/catenax-ng/product-edc/tree/develop/edc-extensions/hashicorp-vault - EDC_VAULT_HASHICORP_URL: http://sokrates-vault-0 + EDC_VAULT_HASHICORP_URL: http://sokrates-vault:8200 EDC_VAULT_HASHICORP_TOKEN: *sokratesVaultToken sokratesedccontrolplane: enabled: true diff --git a/pom.xml b/pom.xml index 193f28baa..7902bc1ce 100644 --- a/pom.xml +++ b/pom.xml @@ -81,6 +81,8 @@ 1.17.3 2.0.0-alpha1 1.2.11 + 2.2 + 2.4.3 catenax-ng @@ -1074,6 +1076,21 @@ ${io.rest.assured.version} test + + + + org.hamcrest + hamcrest + ${org.hamcrest.hamcrest.version} + test + + + net.jodah + failsafe + ${net.jodah.failsafe.version} + test + + From ccc2b092f4c897555c97b68edda0c6131f0e7f44 Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Thu, 28 Jul 2022 09:44:14 +0200 Subject: [PATCH 192/433] rename HashiCorp to Hashicorp (#305) --- CHANGELOG.md | 7 +++++-- ...Extension.java => AbstractHashicorpVaultExtension.java} | 2 +- .../edc/hashicorpvault/HashicorpVaultHealthExtension.java | 2 +- .../edc/hashicorpvault/HashicorpVaultVaultExtension.java | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) rename edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/{AbstractHashiCorpVaultExtension.java => AbstractHashicorpVaultExtension.java} (98%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61d56a355..8b7b4d8d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,10 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- EDC Health Checks for HashiCorp Vault + ### Changed - BusinessPartnerNumber constraint supports List structure -- Confidential EDC settings can be set using K8 secrets -- DAPS of All-In-One-Deployment supports BusinessPartnerNumbers +- Helm: Confidential EDC settings can be set using k8s secrets +- HashiCorp Vault API path configurable ## [0.0.4] - 2022-06-27 diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/AbstractHashiCorpVaultExtension.java b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/AbstractHashicorpVaultExtension.java similarity index 98% rename from edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/AbstractHashiCorpVaultExtension.java rename to edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/AbstractHashicorpVaultExtension.java index eae94c39e..4512e8512 100644 --- a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/AbstractHashiCorpVaultExtension.java +++ b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/AbstractHashicorpVaultExtension.java @@ -23,7 +23,7 @@ * Temporary solution as long as the Vault components needs to be loaded as dedicated vault * extension. Will be changed from EDC milestone 5. */ -public class AbstractHashiCorpVaultExtension { +public class AbstractHashicorpVaultExtension { @EdcSetting(required = true) public static final String VAULT_URL = "edc.vault.hashicorp.url"; diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthExtension.java b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthExtension.java index 017d0a75c..9e904a2c7 100644 --- a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthExtension.java +++ b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthExtension.java @@ -22,7 +22,7 @@ import org.eclipse.dataspaceconnector.spi.system.health.HealthCheckService; @Requires(HealthCheckService.class) -public class HashicorpVaultHealthExtension extends AbstractHashiCorpVaultExtension +public class HashicorpVaultHealthExtension extends AbstractHashicorpVaultExtension implements ServiceExtension { @EdcSetting diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultVaultExtension.java b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultVaultExtension.java index 3f6728187..ebbb819c5 100644 --- a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultVaultExtension.java +++ b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultVaultExtension.java @@ -24,7 +24,7 @@ import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; import org.eclipse.dataspaceconnector.spi.system.VaultExtension; -public class HashicorpVaultVaultExtension extends AbstractHashiCorpVaultExtension +public class HashicorpVaultVaultExtension extends AbstractHashicorpVaultExtension implements VaultExtension { private Vault vault; From 09533f1dde1b4e793402a6724124feb31072d137 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Thu, 28 Jul 2022 11:58:30 +0200 Subject: [PATCH 193/433] Simplify ci build using matrix vars (#304) --- .github/workflows/build.yaml | 334 +++++------------------------------ 1 file changed, 43 insertions(+), 291 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 2a1e4669a..165a7d67e 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -101,182 +101,16 @@ jobs: -Dsonar.coverage.jacoco.xmlReportPaths=${GITHUB_WORKSPACE}/edc-tests/target/site/jacoco-aggregate/jacoco.xml \ -Dsonar.verbose=true - ################################# - ### edc-dataplane-azure-vault ### - ################################# - build-edc-dataplane-azure-vault: - needs: [ secret-presence, verify-formatting ] - runs-on: ubuntu-latest - steps: - # Set-Up - - - name: Checkout - uses: actions/checkout@v3 - with: - submodules: recursive - - - name: Login to GitHub Container Registry - if: | - needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.CXNG_GHCR_PAT }} - - - name: Set up JDK 11 - uses: actions/setup-java@v3.4.1 - with: - java-version: '11' - distribution: 'adopt' - cache: 'maven' - - - name: Build edc with Gradle to get latest snapshots - run: ./gradlew publishToMavenLocal - working-directory: edc - # Build - - - name: Build edc-dataplane-azure-vault - run: |- - ./mvnw -s settings.xml -B -pl .,edc-dataplane/edc-dataplane-azure-vault -am package - env: - GITHUB_PACKAGE_USERNAME: ${{ github.actor }} - GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} - - - name: edc-dataplane-azure-vault Docker Metadata - id: edc_dataplane_azure_vault_meta - uses: docker/metadata-action@v4 - with: - images: | - ghcr.io/${{ github.repository }}/edc-dataplane-azure-vault - tags: | - type=ref,event=branch - type=ref,event=pr - type=semver,pattern={{raw}} - type=match,pattern=\d.\d.\d - type=sha - - - name: Build edc-dataplane-azure-vault Docker Image - uses: docker/build-push-action@v3 - with: - context: . - file: edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile - build-args: | - JAR=edc-dataplane/edc-dataplane-azure-vault/target/edc-dataplane-azure-vault.jar - LIB=edc-dataplane/edc-dataplane-azure-vault/target/lib - push: | - ${{ (needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' && 'true') || 'false' }} - tags: ${{ steps.edc_dataplane_azure_vault_meta.outputs.tags }} - labels: ${{ steps.edc_dataplane_azure_vault_meta.outputs.labels }} - - - name: Veracode Upload And Scan - uses: veracode/veracode-uploadandscan-action@v1.0 - if: | - needs.secret-presence.outputs.ORG_VERACODE_API_ID && needs.secret-presence.outputs.ORG_VERACODE_API_KEY && contains(' - refs/heads/develop - refs/heads/release/ - refs/tags/ - refs/heads/main', github.ref) - continue-on-error: true - with: - appname: 'product-edc/edc-dataplane-azure-vault' - createprofile: true - version: ${{ github.ref }}-${{ github.sha }} - filepath: edc-dataplane/edc-dataplane-azure-vault/target/edc-dataplane-azure-vault.jar - vid: ${{ secrets.ORG_VERACODE_API_ID }} - vkey: ${{ secrets.ORG_VERACODE_API_KEY }} - - ##################################### - ### edc-dataplane-hashicorp-vault ### - ##################################### - build-edc-dataplane-hashicorp-vault: - needs: [ secret-presence, verify-formatting ] + build-controlplane: runs-on: ubuntu-latest - steps: - # Set-Up - - - name: Checkout - uses: actions/checkout@v3 - with: - submodules: recursive - - - name: Login to GitHub Container Registry - if: | - needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.CXNG_GHCR_PAT }} - - - name: Set up JDK 11 - uses: actions/setup-java@v3.4.1 - with: - java-version: '11' - distribution: 'adopt' - cache: 'maven' - - - name: Build edc with Gradle to get latest snapshots - run: ./gradlew publishToMavenLocal - working-directory: edc - # Build - - - name: Build edc-dataplane-hashicorp-vault - run: |- - ./mvnw -s settings.xml -B -pl .,edc-dataplane/edc-dataplane-hashicorp-vault -am package - env: - GITHUB_PACKAGE_USERNAME: ${{ github.actor }} - GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} - - - name: edc-dataplane-hashicorp-vault Docker Metadata - id: edc_dataplane_hashicorp_vault_metadata - uses: docker/metadata-action@v4 - with: - images: | - ghcr.io/${{ github.repository }}/edc-dataplane-hashicorp-vault - tags: | - type=ref,event=branch - type=ref,event=pr - type=semver,pattern={{raw}} - type=match,pattern=\d.\d.\d - type=sha - - - name: Build edc-dataplane-hashicorp-vault Docker Image - uses: docker/build-push-action@v3 - with: - context: . - file: edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile - build-args: | - JAR=edc-dataplane/edc-dataplane-hashicorp-vault/target/edc-dataplane-hashicorp-vault.jar - LIB=edc-dataplane/edc-dataplane-hashicorp-vault/target/lib - push: | - ${{ (needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' && 'true') || 'false' }} - tags: ${{ steps.edc_dataplane_hashicorp_vault_metadata.outputs.tags }} - labels: ${{ steps.edc_dataplane_hashicorp_vault_metadata.outputs.labels }} - - - name: Veracode Upload And Scan - uses: veracode/veracode-uploadandscan-action@v1.0 - if: | - needs.secret-presence.outputs.ORG_VERACODE_API_ID && needs.secret-presence.outputs.ORG_VERACODE_API_KEY && contains(' - refs/heads/develop - refs/heads/release/ - refs/tags/ - refs/heads/main', github.ref) - continue-on-error: true - with: - appname: 'product-edc/edc-dataplane-hashicorp-vault' - createprofile: true - version: ${{ github.ref }}-${{ github.sha }} - filepath: edc-dataplane/edc-dataplane-hashicorp-vault/target/edc-dataplane-hashicorp-vault.jar - vid: ${{ secrets.ORG_VERACODE_API_ID }} - vkey: ${{ secrets.ORG_VERACODE_API_KEY }} - - ############################### - ### edc-controlplane-memory ### - ############################### - build-edc-controlplane-memory: needs: [ secret-presence, verify-formatting ] - runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + name: + - edc-controlplane-memory + - edc-controlplane-postgresql + - edc-controlplane-postgresql-hashicorp-vault steps: # Set-Up - @@ -306,19 +140,19 @@ jobs: working-directory: edc # Build - - name: Build edc-controlplane-memory + name: Build Controlplane run: |- - ./mvnw -s settings.xml -B -pl .,edc-controlplane/edc-controlplane-memory -am package + ./mvnw -s settings.xml -B -pl .,edc-controlplane/${{ matrix.name }} -am package env: GITHUB_PACKAGE_USERNAME: ${{ github.actor }} GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} - - name: edc-controlplane-memory Docker Metadata - id: edc_controlplane_memory_meta + name: edc-controlplane Docker Metadata + id: edc_controlplane_meta uses: docker/metadata-action@v4 with: images: | - ghcr.io/${{ github.repository }}/edc-controlplane-memory + ghcr.io/${{ github.repository }}/${{ matrix.name }} tags: | type=ref,event=branch type=ref,event=pr @@ -326,18 +160,18 @@ jobs: type=match,pattern=\d.\d.\d type=sha - - name: Build edc-controlplane-memory Docker Image + name: Build Docker Image uses: docker/build-push-action@v3 with: context: . - file: edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile + file: edc-controlplane/${{ matrix.name }}/src/main/docker/Dockerfile build-args: | - JAR=edc-controlplane/edc-controlplane-memory/target/edc-controlplane-memory.jar - LIB=edc-controlplane/edc-controlplane-memory/target/lib + JAR=edc-controlplane/${{ matrix.name }}/target/${{ matrix.name }}.jar + LIB=edc-controlplane/${{ matrix.name }}/target/lib push: | ${{ (needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' && 'true') || 'false' }} - tags: ${{ steps.edc_controlplane_memory_meta.outputs.tags }} - labels: ${{ steps.edc_controlplane_memory_meta.outputs.labels }} + tags: ${{ steps.edc_controlplane_meta.outputs.tags }} + labels: ${{ steps.edc_controlplane_meta.outputs.labels }} - name: Veracode Upload And Scan uses: veracode/veracode-uploadandscan-action@v1.0 @@ -349,104 +183,22 @@ jobs: refs/heads/main', github.ref) continue-on-error: true with: - appname: 'product-edc/edc-controlplane-memory' + appname: product-edc/${{ matrix.name }} createprofile: true version: ${{ github.ref }}-${{ github.sha }} - filepath: edc-controlplane/edc-controlplane-memory/target/edc-controlplane-memory.jar + filepath: edc-controlplane/${{ matrix.name }}/target/${{ matrix.name }}.jar vid: ${{ secrets.ORG_VERACODE_API_ID }} vkey: ${{ secrets.ORG_VERACODE_API_KEY }} - ################################### - ### edc-controlplane-postgresql ### - ################################### - build-edc-controlplane-postgresql: - needs: [ secret-presence, verify-formatting ] + build-dataplane: runs-on: ubuntu-latest - steps: - # Set-Up - - - name: Checkout - uses: actions/checkout@v3 - with: - submodules: recursive - - - name: Login to Github Packages - if: | - needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.CXNG_GHCR_PAT }} - - - name: Set up JDK 11 - uses: actions/setup-java@v3.4.0 - with: - java-version: '11' - distribution: 'adopt' - cache: 'maven' - - - name: Build edc with Gradle to get latest snapshots - run: ./gradlew publishToMavenLocal - working-directory: edc - # Build - - - name: Build edc-controlplane-postgresql - run: |- - ./mvnw -s settings.xml -B -pl .,edc-controlplane/edc-controlplane-postgresql -am package - env: - GITHUB_PACKAGE_USERNAME: ${{ github.actor }} - GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} - - - name: edc-controlplane-postgresql Docker Metadata - id: edc_controlplane_postgresql_meta - uses: docker/metadata-action@v4 - with: - images: | - ghcr.io/${{ github.repository }}/edc-controlplane-postgresql - tags: | - type=ref,event=branch - type=ref,event=pr - type=semver,pattern={{raw}} - type=match,pattern=\d.\d.\d - type=sha - - - name: Build edc-controlplane-postgresql Docker Image - uses: docker/build-push-action@v3 - with: - context: . - file: edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile - build-args: | - JAR=edc-controlplane/edc-controlplane-postgresql/target/edc-controlplane-postgresql.jar - LIB=edc-controlplane/edc-controlplane-postgresql/target/lib - push: | - ${{ (needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' && 'true') || 'false' }} - tags: ${{ steps.edc_controlplane_postgresql_meta.outputs.tags }} - labels: ${{ steps.edc_controlplane_postgresql_meta.outputs.labels }} - - - name: Veracode Upload And Scan - uses: veracode/veracode-uploadandscan-action@v1.0 - if: | - needs.secret-presence.outputs.ORG_VERACODE_API_ID && needs.secret-presence.outputs.ORG_VERACODE_API_KEY && contains(' - refs/heads/develop - refs/heads/release/ - refs/tags/ - refs/heads/main', github.ref) - continue-on-error: true - with: - appname: 'product-edc/edc-controlplane-postgresql' - createprofile: true - filepath: edc-controlplane/edc-controlplane-postgresql/target/edc-controlplane-postgresql.jar - version: ${{ github.ref_name }}-${{ github.sha }} - vid: ${{ secrets.ORG_VERACODE_API_ID }} - vkey: ${{ secrets.ORG_VERACODE_API_KEY }} - - ################################################### - ### edc-controlplane-postgresql-hashicorp-vault ### - ################################################### - build-edc-controlplane-postgresql-hashicorp-vault: needs: [ secret-presence, verify-formatting ] - runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + name: + - edc-dataplane-azure-vault + - edc-dataplane-hashicorp-vault steps: # Set-Up - @@ -455,7 +207,7 @@ jobs: with: submodules: recursive - - name: Login to Github Packages + name: Login to GitHub Container Registry if: | needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' uses: docker/login-action@v2 @@ -476,19 +228,19 @@ jobs: working-directory: edc # Build - - name: Build edc-controlplane-postgresql-hashicorp-vault + name: Build Dataplane run: |- - ./mvnw -s settings.xml -B -pl .,edc-controlplane/edc-controlplane-postgresql-hashicorp-vault -am package + ./mvnw -s settings.xml -B -pl .,edc-dataplane/${{ matrix.name }} -am package env: GITHUB_PACKAGE_USERNAME: ${{ github.actor }} GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} - - name: edc-controlplane-postgresql-hashicorp-vault Docker Metadata - id: edc_controlplane_postgresql_hashicorp_vault_meta + name: edc-dataplane Docker Metadata + id: edc_dataplane_meta uses: docker/metadata-action@v4 with: images: | - ghcr.io/${{ github.repository }}/edc-controlplane-postgresql-hashicorp-vault + ghcr.io/${{ github.repository }}/${{ matrix.name }} tags: | type=ref,event=branch type=ref,event=pr @@ -496,18 +248,18 @@ jobs: type=match,pattern=\d.\d.\d type=sha - - name: Build edc-controlplane-postgresql-hashicorp-vault Docker Image + name: Build Docker Image uses: docker/build-push-action@v3 with: context: . - file: edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile + file: edc-dataplane/${{ matrix.name }}/src/main/docker/Dockerfile build-args: | - JAR=edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/target/edc-controlplane-postgresql-hashicorp-vault.jar - LIB=edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/target/lib + JAR=edc-dataplane/${{ matrix.name }}/target/${{ matrix.name }}.jar + LIB=edc-dataplane/${{ matrix.name }}/target/lib push: | ${{ (needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' && 'true') || 'false' }} - tags: ${{ steps.edc_controlplane_postgresql_hashicorp_vault_meta.outputs.tags }} - labels: ${{ steps.edc_controlplane_postgresql_hashicorp_vault_meta.outputs.labels }} + tags: ${{ steps.edc_dataplane_meta.outputs.tags }} + labels: ${{ steps.edc_dataplane_meta.outputs.labels }} - name: Veracode Upload And Scan uses: veracode/veracode-uploadandscan-action@v1.0 @@ -519,9 +271,9 @@ jobs: refs/heads/main', github.ref) continue-on-error: true with: - appname: 'product-edc/edc-controlplane-postgresql-hashicorp-vault' + appname: product-edc/${{ matrix.name }} createprofile: true - filepath: edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/target/edc-controlplane-postgresql-hashicorp-vault.jar - version: ${{ github.ref_name }}-${{ github.sha }} + version: ${{ github.ref }}-${{ github.sha }} + filepath: edc-dataplane/${{ matrix.name }}/target/${{ matrix.name }}.jar vid: ${{ secrets.ORG_VERACODE_API_ID }} vkey: ${{ secrets.ORG_VERACODE_API_KEY }} From 9e7aeb164915ac7a1fe2125c04c2c440c3cbe092 Mon Sep 17 00:00:00 2001 From: marcingajek-zf Date: Thu, 28 Jul 2022 10:34:46 +0200 Subject: [PATCH 194/433] EDC extension - retrieve data reference --- edc-extensions/control-plane-adapter/pom.xml | 69 +++++++++ .../edc/cp/adapter/ApiAdapterConfig.java | 18 +++ .../edc/cp/adapter/ApiAdapterExtension.java | 138 ++++++++++++++++++ .../edc/cp/adapter/HttpController.java | 56 +++++++ .../edc/cp/adapter/dto/ProcessData.java | 40 +++++ .../exception/ConfigurationException.java | 7 + .../exception/ContractOfferNotAvailable.java | 7 + .../DataReferenceAccessException.java | 7 + .../exception/ResourceNotFoundException.java | 7 + .../edc/cp/adapter/messaging/Channel.java | 8 + .../messaging/InMemoryMessageService.java | 47 ++++++ .../edc/cp/adapter/messaging/Listener.java | 5 + .../cp/adapter/messaging/ListenerService.java | 30 ++++ .../edc/cp/adapter/messaging/Message.java | 53 +++++++ .../cp/adapter/messaging/MessageService.java | 5 + .../messaging/PersistentMessageService.java | 25 ++++ .../ContractConfirmationHandler.java | 112 ++++++++++++++ .../contractconfirmation/DataStore.java | 17 +++ .../InMemoryDataStore.java | 40 +++++ .../ContractNegotiationHandler.java | 81 ++++++++++ .../datareference/DataReferenceHandler.java | 59 ++++++++ .../process/datareference/DataStore.java | 18 +++ .../datareference/InMemoryDataStore.java | 42 ++++++ .../edc/cp/adapter/service/ResultService.java | 52 +++++++ ...spaceconnector.spi.system.ServiceExtension | 14 ++ 25 files changed, 957 insertions(+) create mode 100644 edc-extensions/control-plane-adapter/pom.xml create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/ApiAdapterConfig.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/ApiAdapterExtension.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/HttpController.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/dto/ProcessData.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ConfigurationException.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ContractOfferNotAvailable.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/DataReferenceAccessException.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ResourceNotFoundException.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/Channel.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/InMemoryMessageService.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/Listener.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/ListenerService.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/Message.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/MessageService.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/PersistentMessageService.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/ContractConfirmationHandler.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/DataStore.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/InMemoryDataStore.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnegotiation/ContractNegotiationHandler.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataReferenceHandler.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataStore.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/InMemoryDataStore.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/service/ResultService.java create mode 100644 edc-extensions/control-plane-adapter/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension diff --git a/edc-extensions/control-plane-adapter/pom.xml b/edc-extensions/control-plane-adapter/pom.xml new file mode 100644 index 000000000..04dd64788 --- /dev/null +++ b/edc-extensions/control-plane-adapter/pom.xml @@ -0,0 +1,69 @@ + + + + edc-extensions + net.catenax.edc.extensions + 0.0.5-SNAPSHOT + + 4.0.0 + + control-plane-adapter + + + 18 + 18 + + + + + + org.eclipse.dataspaceconnector + core-spi + + + org.eclipse.dataspaceconnector + policy-spi + + + org.eclipse.dataspaceconnector + data-management-api + + + org.eclipse.dataspaceconnector + catalog-spi + + + org.eclipse.dataspaceconnector + transaction-spi + + + + + org.projectlombok + lombok + + + jakarta.ws.rs + jakarta.ws.rs-api + 3.1.0 + + + + + org.junit.jupiter + junit-jupiter + test + + + org.mockito + mockito-core + test + + + org.projectlombok + lombok + + + \ No newline at end of file diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/ApiAdapterConfig.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/ApiAdapterConfig.java new file mode 100644 index 000000000..3b2e91b1e --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/ApiAdapterConfig.java @@ -0,0 +1,18 @@ +package net.catenax.edc.cp.adapter; + +import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; + +public class ApiAdapterConfig { + private static final String DEFAULT_MESSAGE_RETRY_NUMBER = + "edc.cp.adapter.default.message.retry.number"; + + private final ServiceExtensionContext context; + + public ApiAdapterConfig(ServiceExtensionContext context) { + this.context = context; + } + + public String getDefaultMessageRetryNumber() { + return context.getSetting(DEFAULT_MESSAGE_RETRY_NUMBER, null); + } +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/ApiAdapterExtension.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/ApiAdapterExtension.java new file mode 100644 index 000000000..66e34c5cd --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/ApiAdapterExtension.java @@ -0,0 +1,138 @@ +package net.catenax.edc.cp.adapter; + +import static java.util.Objects.nonNull; +import static java.util.Optional.ofNullable; + +import net.catenax.edc.cp.adapter.messaging.Channel; +import net.catenax.edc.cp.adapter.messaging.InMemoryMessageService; +import net.catenax.edc.cp.adapter.messaging.ListenerService; +import net.catenax.edc.cp.adapter.process.contractconfirmation.ContractConfirmationHandler; +import net.catenax.edc.cp.adapter.process.contractconfirmation.InMemoryDataStore; +import net.catenax.edc.cp.adapter.process.contractnegotiation.ContractNegotiationHandler; +import net.catenax.edc.cp.adapter.process.datareference.DataReferenceHandler; +import net.catenax.edc.cp.adapter.service.ResultService; +import org.eclipse.dataspaceconnector.api.datamanagement.catalog.service.CatalogServiceImpl; +import org.eclipse.dataspaceconnector.api.datamanagement.contractnegotiation.service.ContractNegotiationService; +import org.eclipse.dataspaceconnector.api.datamanagement.contractnegotiation.service.ContractNegotiationServiceImpl; +import org.eclipse.dataspaceconnector.api.datamanagement.transferprocess.service.TransferProcessServiceImpl; +import org.eclipse.dataspaceconnector.spi.WebService; +import org.eclipse.dataspaceconnector.spi.contract.negotiation.ConsumerContractNegotiationManager; +import org.eclipse.dataspaceconnector.spi.contract.negotiation.observe.ContractNegotiationObservable; +import org.eclipse.dataspaceconnector.spi.contract.negotiation.store.ContractNegotiationStore; +import org.eclipse.dataspaceconnector.spi.message.RemoteMessageDispatcherRegistry; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.system.Inject; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtension; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; +import org.eclipse.dataspaceconnector.spi.transaction.NoopTransactionContext; +import org.eclipse.dataspaceconnector.spi.transaction.TransactionContext; +import org.eclipse.dataspaceconnector.spi.transfer.TransferProcessManager; +import org.eclipse.dataspaceconnector.spi.transfer.edr.EndpointDataReferenceReceiverRegistry; +import org.eclipse.dataspaceconnector.spi.transfer.store.TransferProcessStore; + +public class ApiAdapterExtension implements ServiceExtension { + @Inject private WebService webService; + @Inject private ContractNegotiationStore store; + @Inject private ConsumerContractNegotiationManager manager; + @Inject private TransactionContext transactionContext; + @Inject private RemoteMessageDispatcherRegistry dispatcher; + @Inject private TransferProcessStore transferProcessStore; + @Inject private TransferProcessManager transferProcessManager; + @Inject private EndpointDataReferenceReceiverRegistry receiverRegistry; + + @Override + public String name() { + return "Control Plane Adapter Extension"; + } + + @Override + public void initialize(ServiceExtensionContext context) { + /** external dependencies * */ + Monitor monitor = context.getMonitor(); + ContractNegotiationObservable negotiationObservable = + context.getService(ContractNegotiationObservable.class, false); + + /** internal dependencies * */ + ContractNegotiationService contractNegotiationService = + new ContractNegotiationServiceImpl(store, manager, getTransactionContext(monitor)); + ListenerService listenerService = new ListenerService(); + InMemoryMessageService messageService = new InMemoryMessageService(monitor, listenerService); + ResultService resultService = new ResultService(monitor); + listenerService.addListener(Channel.RESULT, resultService); + + initHttpController(monitor, messageService, resultService); + initContractNegotiationHandler( + monitor, contractNegotiationService, messageService, listenerService); + initContractConfirmationHandler( + monitor, + negotiationObservable, + contractNegotiationService, + messageService, + listenerService); + initDataReferenceHandler(monitor, messageService, listenerService); + } + + private void initHttpController( + Monitor monitor, InMemoryMessageService messageService, ResultService resultService) { + webService.registerResource(new HttpController(monitor, resultService, messageService)); + } + + private void initContractNegotiationHandler( + Monitor monitor, + ContractNegotiationService contractNegotiationService, + InMemoryMessageService messageService, + ListenerService listenerService) { + + listenerService.addListener( + Channel.INITIAL, + new ContractNegotiationHandler( + monitor, + messageService, + contractNegotiationService, + new CatalogServiceImpl(dispatcher))); + } + + private void initContractConfirmationHandler( + Monitor monitor, + ContractNegotiationObservable negotiationObservable, + ContractNegotiationService contractNegotiationService, + InMemoryMessageService messageService, + ListenerService listenerService) { + + ContractConfirmationHandler contractConfirmationHandler = + new ContractConfirmationHandler( + monitor, + messageService, + new InMemoryDataStore(), + contractNegotiationService, + new TransferProcessServiceImpl( + transferProcessStore, transferProcessManager, getTransactionContext(monitor))); + + listenerService.addListener(Channel.CONTRACT_CONFIRMATION, contractConfirmationHandler); + if (nonNull(negotiationObservable)) { + negotiationObservable.registerListener(contractConfirmationHandler); + } + } + + private void initDataReferenceHandler( + Monitor monitor, InMemoryMessageService messageService, ListenerService listenerService) { + + DataReferenceHandler dataReferenceHandler = + new DataReferenceHandler( + monitor, + messageService, + new net.catenax.edc.cp.adapter.process.datareference.InMemoryDataStore()); + listenerService.addListener(Channel.DATA_REFERENCE, dataReferenceHandler); + receiverRegistry.registerReceiver(dataReferenceHandler); + } + + private TransactionContext getTransactionContext(Monitor monitor) { + return ofNullable(transactionContext) + .orElseGet( + () -> { + monitor.warning( + "No TransactionContext registered, a no-op implementation will be used, not suitable for production environments"); + return new NoopTransactionContext(); + }); + } +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/HttpController.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/HttpController.java new file mode 100644 index 000000000..c77764960 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/HttpController.java @@ -0,0 +1,56 @@ +package net.catenax.edc.cp.adapter; + +import static java.util.Objects.isNull; + +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import lombok.RequiredArgsConstructor; +import net.catenax.edc.cp.adapter.dto.ProcessData; +import net.catenax.edc.cp.adapter.messaging.Channel; +import net.catenax.edc.cp.adapter.messaging.Message; +import net.catenax.edc.cp.adapter.messaging.MessageService; +import net.catenax.edc.cp.adapter.service.ResultService; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; + +@Consumes({MediaType.APPLICATION_JSON}) +@Produces({MediaType.APPLICATION_JSON}) +@Path("/adapter/asset") +@RequiredArgsConstructor +public class HttpController { + private final Monitor monitor; + private final ResultService resultService; + private final MessageService messageService; + + @GET + @Path("sync/{assetId}") + public Response getAssetSynchronous( + @PathParam("assetId") String assetId, @QueryParam("providerUrl") String providerUrl) { + + if (invalidParams(assetId, providerUrl)) { + return Response.status(Response.Status.BAD_REQUEST) + .entity("AssetId or providerUrl is empty!") + .build(); + } + + String traceId = initiateProcess(assetId, providerUrl); + + try { + return Response.status(Response.Status.OK).entity(resultService.poll(traceId)).build(); + } catch (InterruptedException e) { + monitor.severe("InterruptedException", e); + return Response.status(Response.Status.NOT_FOUND).build(); + } + } + + private boolean invalidParams(String assetId, String providerUrl) { + return isNull(assetId) || assetId.isBlank() || isNull(providerUrl) || providerUrl.isBlank(); + } + + private String initiateProcess(String assetId, String providerUrl) { + ProcessData processData = new ProcessData(assetId, providerUrl); + Message message = new Message(processData); + messageService.send(Channel.INITIAL, message); + return message.getTraceId(); + } +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/dto/ProcessData.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/dto/ProcessData.java new file mode 100644 index 000000000..0465e20a6 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/dto/ProcessData.java @@ -0,0 +1,40 @@ +package net.catenax.edc.cp.adapter.dto; + +import static java.lang.System.currentTimeMillis; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import org.eclipse.dataspaceconnector.spi.types.domain.edr.EndpointDataReference; + +@Getter +@ToString +public class ProcessData { + /** request data */ + private final String assetId; + + private final String provider; + private String contractOfferId; + + private final long timestamp = currentTimeMillis(); + + /** contract data * */ + @Setter private String contractNegotiationId; + + @Setter private String contractAgreementId; + @Setter private boolean isContractConfirmed = false; + + /** result/response data * */ + @Setter private EndpointDataReference endpointDataReference; + + public ProcessData(String assetId, String provider) { + this.assetId = assetId; + this.provider = provider; + } + + public ProcessData(String assetId, String provider, String contractOfferId) { + this.assetId = assetId; + this.provider = provider; + this.contractOfferId = contractOfferId; + } +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ConfigurationException.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ConfigurationException.java new file mode 100644 index 000000000..8a9ea2236 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ConfigurationException.java @@ -0,0 +1,7 @@ +package net.catenax.edc.cp.adapter.exception; + +public class ConfigurationException extends RuntimeException { + public ConfigurationException(String message) { + super(message); + } +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ContractOfferNotAvailable.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ContractOfferNotAvailable.java new file mode 100644 index 000000000..1f0cb89e9 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ContractOfferNotAvailable.java @@ -0,0 +1,7 @@ +package net.catenax.edc.cp.adapter.exception; + +public class ContractOfferNotAvailable extends RuntimeException { + public ContractOfferNotAvailable(Throwable cause) { + super(cause); + } +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/DataReferenceAccessException.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/DataReferenceAccessException.java new file mode 100644 index 000000000..06dcce70a --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/DataReferenceAccessException.java @@ -0,0 +1,7 @@ +package net.catenax.edc.cp.adapter.exception; + +public class DataReferenceAccessException extends RuntimeException { + public DataReferenceAccessException(final String id) { + super(String.format("Data reference initial request failed! AssetId: %s", id)); + } +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ResourceNotFoundException.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ResourceNotFoundException.java new file mode 100644 index 000000000..b9c419de0 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ResourceNotFoundException.java @@ -0,0 +1,7 @@ +package net.catenax.edc.cp.adapter.exception; + +public class ResourceNotFoundException extends RuntimeException { + public ResourceNotFoundException(final String id) { + super(String.format("Resource not found for id %s", id)); + } +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/Channel.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/Channel.java new file mode 100644 index 000000000..bcd56a5f0 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/Channel.java @@ -0,0 +1,8 @@ +package net.catenax.edc.cp.adapter.messaging; + +public enum Channel { + INITIAL, + CONTRACT_CONFIRMATION, + DATA_REFERENCE, + RESULT +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/InMemoryMessageService.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/InMemoryMessageService.java new file mode 100644 index 000000000..33e8f849b --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/InMemoryMessageService.java @@ -0,0 +1,47 @@ +package net.catenax.edc.cp.adapter.messaging; + +import static java.util.Objects.isNull; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import lombok.RequiredArgsConstructor; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; + +@RequiredArgsConstructor +public class InMemoryMessageService implements MessageService { + private static final int THREAD_POOL_SIZE = 10; + private final ScheduledExecutorService executorService = + Executors.newScheduledThreadPool(THREAD_POOL_SIZE); + private final Monitor monitor; + private final ListenerService listenerService; + + @Override + public void send(Channel name, Message message) { + if (isNull(message)) { + monitor.warning(String.format("Message is empty, channel: %s", name)); + } else { + monitor.info(String.format("[%s] Message sent to channel: %s", message.getTraceId(), name)); + executorService.submit(() -> run(name, message)); + } + } + + /** Returns 'false' if message processing should be retried. * */ + protected boolean run(Channel name, Message message) { + try { + listenerService.getListener(name).process(message); + message.succeeded(); + return true; + } catch (Exception e) { + monitor.warning(String.format("[%s] Message processing error.", message.getTraceId()), e); + if (!message.canRetry()) { + monitor.warning(String.format("[%s] Message reached retry limit!", message.getTraceId())); + // TODO move to DLQ + return true; + } + long delayTime = message.unsucceeded(); + executorService.schedule(() -> run(name, message), delayTime, TimeUnit.MILLISECONDS); + return false; + } + } +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/Listener.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/Listener.java new file mode 100644 index 000000000..948b40f9e --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/Listener.java @@ -0,0 +1,5 @@ +package net.catenax.edc.cp.adapter.messaging; + +public interface Listener { + void process(Message message); +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/ListenerService.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/ListenerService.java new file mode 100644 index 000000000..6bf0164eb --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/ListenerService.java @@ -0,0 +1,30 @@ +package net.catenax.edc.cp.adapter.messaging; + +import static java.util.Objects.isNull; + +import java.util.HashMap; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import net.catenax.edc.cp.adapter.exception.ConfigurationException; + +@RequiredArgsConstructor +public class ListenerService { + /** only single listener for a message at the moment * */ + private final Map listeners = new HashMap<>(); + + public void addListener(Channel name, Listener listener) { + listeners.put(name, listener); + } + + public void removeListener(Channel name) { + listeners.remove(name); + } + + Listener getListener(Channel name) { + Listener listener = listeners.get(name); + if (isNull(listener)) { + throw new ConfigurationException("No listener found for channel: " + name); + } + return listener; + } +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/Message.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/Message.java new file mode 100644 index 000000000..cc7a08fe8 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/Message.java @@ -0,0 +1,53 @@ +package net.catenax.edc.cp.adapter.messaging; + +import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; +import lombok.Getter; +import net.catenax.edc.cp.adapter.dto.ProcessData; + +public class Message { + @Getter private final String traceId; + @Getter private final ProcessData payload; + private final AtomicInteger errorNumber = new AtomicInteger(); + private int retryLimit = 3; // TODO configure + + public Message(String id, ProcessData payload, int retryLimit) { + this(id, payload); + this.retryLimit = retryLimit; + } + + public Message(String id, ProcessData payload) { + this.payload = payload; + this.traceId = id; + } + + public Message(ProcessData payload, int retryLimit) { + this(payload); + this.retryLimit = retryLimit; + } + + public Message(ProcessData payload) { + traceId = UUID.randomUUID().toString(); + this.payload = payload; + } + + protected long unsucceeded() { + errorNumber.incrementAndGet(); + return getDelayTime(); + } + + protected void succeeded() { + errorNumber.set(0); + } + + protected boolean canRetry() { + return errorNumber.get() < retryLimit; + } + + // TODO external configuration? extract to other class implements BackoffPolicy - strategy + private int getDelayTime() { + return errorNumber.get() < 5 + ? errorNumber.get() * 750 + : (int) Math.pow(errorNumber.get(), 2) * 150; + } +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/MessageService.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/MessageService.java new file mode 100644 index 000000000..31775e230 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/MessageService.java @@ -0,0 +1,5 @@ +package net.catenax.edc.cp.adapter.messaging; + +public interface MessageService { + void send(Channel name, Message message); +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/PersistentMessageService.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/PersistentMessageService.java new file mode 100644 index 000000000..1c7cce244 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/PersistentMessageService.java @@ -0,0 +1,25 @@ +package net.catenax.edc.cp.adapter.messaging; + +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; + +public class PersistentMessageService extends InMemoryMessageService { + public PersistentMessageService(Monitor monitor, ListenerService listenerService) { + super(monitor, listenerService); + // TODO init scheduler from DB (use send method?) + } + + @Override + public void send(Channel name, Message message) { + // TODO save to db + super.send(name, message); + } + + @Override + protected boolean run(Channel name, Message message) { + boolean isProcessed = super.run(name, message); + if (isProcessed) { + // TODO remove from DB + } + return isProcessed; + } +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/ContractConfirmationHandler.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/ContractConfirmationHandler.java new file mode 100644 index 000000000..fe7f35064 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/ContractConfirmationHandler.java @@ -0,0 +1,112 @@ +package net.catenax.edc.cp.adapter.process.contractconfirmation; + +import static java.util.Objects.isNull; + +import lombok.RequiredArgsConstructor; +import net.catenax.edc.cp.adapter.exception.DataReferenceAccessException; +import net.catenax.edc.cp.adapter.messaging.Channel; +import net.catenax.edc.cp.adapter.messaging.Listener; +import net.catenax.edc.cp.adapter.messaging.Message; +import net.catenax.edc.cp.adapter.messaging.MessageService; +import org.eclipse.dataspaceconnector.api.datamanagement.contractnegotiation.service.ContractNegotiationService; +import org.eclipse.dataspaceconnector.api.datamanagement.transferprocess.service.TransferProcessService; +import org.eclipse.dataspaceconnector.api.result.ServiceResult; +import org.eclipse.dataspaceconnector.spi.contract.negotiation.observe.ContractNegotiationListener; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.types.domain.DataAddress; +import org.eclipse.dataspaceconnector.spi.types.domain.contract.negotiation.ContractNegotiation; +import org.eclipse.dataspaceconnector.spi.types.domain.contract.negotiation.ContractNegotiationStates; +import org.eclipse.dataspaceconnector.spi.types.domain.transfer.DataRequest; +import org.eclipse.dataspaceconnector.spi.types.domain.transfer.TransferType; + +@RequiredArgsConstructor +public class ContractConfirmationHandler implements Listener, ContractNegotiationListener { + private final Monitor monitor; + private final MessageService messageService; + private final DataStore dataStore; + private final ContractNegotiationService contractNegotiationService; + private final TransferProcessService transferProcessService; + + @Override + public void process(Message message) { + monitor.info( + String.format("[%s] ContractConfirmationHandler: received message.", message.getTraceId())); + String contractNegotiationId = message.getPayload().getContractNegotiationId(); + + if (isContractConfirmed(contractNegotiationId)) { + initiateDataTransfer(message); // TODO - contractAgreementID may be missing + return; + } + + String confirmedContractAgreementId = dataStore.getConfirmedContract(contractNegotiationId); + if (isNull(confirmedContractAgreementId)) { + dataStore.storeMessage(message); + return; + } + + message.getPayload().setContractAgreementId(confirmedContractAgreementId); + initiateDataTransfer(message); + dataStore.removeConfirmedContract(contractNegotiationId); + } + + @Override + public void preConfirmed(ContractNegotiation negotiation) { + monitor.info("ContractConfirmationHandler: received 'ContractNegotiation' event"); + String contractNegotiationId = negotiation.getId(); + String contractAgreementId = negotiation.getContractAgreement().getId(); + Message message = dataStore.getMessage(contractNegotiationId); + if (isNull(message)) { + dataStore.storeConfirmedContract(contractNegotiationId, contractAgreementId); + return; + } + message.getPayload().setContractAgreementId(contractAgreementId); + initiateDataTransfer(message); + dataStore.removeMessage(contractNegotiationId); + } + + private boolean isContractConfirmed(String contractNegotiationId) { + return contractNegotiationService + .getState(contractNegotiationId) + .equals(ContractNegotiationStates.CONFIRMED.name()); + } + + private void initiateDataTransfer(Message message) { + sendInitiationRequest(message); + message.getPayload().setContractConfirmed(true); + messageService.send(Channel.DATA_REFERENCE, message); + } + + private void sendInitiationRequest(Message message) { + monitor.info( + String.format( + "[%s] ContractConfirmationHandler: transfer init - start.", message.getTraceId())); + DataAddress dataDestination = DataAddress.Builder.newInstance().type("HttpProxy").build(); + + TransferType transferType = + TransferType.Builder.transferType() + .contentType("application/octet-stream") + .isFinite(true) + .build(); + + DataRequest dataRequest = + DataRequest.Builder.newInstance() + .id(message.getTraceId()) + .assetId(message.getPayload().getAssetId()) + .contractId(message.getPayload().getContractAgreementId()) + .connectorId("provider") + .connectorAddress(message.getPayload().getProvider()) + .protocol("ids-multipart") + .dataDestination(dataDestination) + .managedResources(false) + .transferType(transferType) + .build(); + + ServiceResult result = transferProcessService.initiateTransfer(dataRequest); + monitor.info( + String.format( + "[%s] ContractConfirmationHandler: transfer init - end", message.getTraceId())); + if (result.failed()) { + throw new DataReferenceAccessException(message.getPayload().getAssetId()); + } + } +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/DataStore.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/DataStore.java new file mode 100644 index 000000000..3247a5464 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/DataStore.java @@ -0,0 +1,17 @@ +package net.catenax.edc.cp.adapter.process.contractconfirmation; + +import net.catenax.edc.cp.adapter.messaging.Message; + +public interface DataStore { + void storeConfirmedContract(String contractNegotiationId, String contractAgreementId); + + String getConfirmedContract(String contractNegotiationId); + + void removeConfirmedContract(String key); + + void storeMessage(Message message); + + Message getMessage(String key); + + void removeMessage(String key); +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/InMemoryDataStore.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/InMemoryDataStore.java new file mode 100644 index 000000000..de9c3746c --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/InMemoryDataStore.java @@ -0,0 +1,40 @@ +package net.catenax.edc.cp.adapter.process.contractconfirmation; + +import java.util.HashMap; +import java.util.Map; +import net.catenax.edc.cp.adapter.messaging.Message; + +public class InMemoryDataStore implements DataStore { + private final Map messages = new HashMap<>(); + private final Map confirmedContracts = new HashMap<>(); + + @Override + public void storeConfirmedContract(String contractNegotiationId, String contractAgreementId) { + confirmedContracts.put(contractNegotiationId, contractAgreementId); + } + + @Override + public String getConfirmedContract(String contractNegotiationId) { + return confirmedContracts.get(contractNegotiationId); + } + + @Override + public void removeConfirmedContract(String contractNegotiationId) { + confirmedContracts.remove(contractNegotiationId); + } + + @Override + public void storeMessage(Message message) { + messages.put(message.getPayload().getContractNegotiationId(), message); + } + + @Override + public Message getMessage(String contractNegotiationId) { + return messages.get(contractNegotiationId); + } + + @Override + public void removeMessage(String contractNegotiationId) { + messages.remove(contractNegotiationId); + } +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnegotiation/ContractNegotiationHandler.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnegotiation/ContractNegotiationHandler.java new file mode 100644 index 000000000..0014cf862 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnegotiation/ContractNegotiationHandler.java @@ -0,0 +1,81 @@ +package net.catenax.edc.cp.adapter.process.contractnegotiation; + +import java.util.Collections; +import java.util.Optional; +import java.util.concurrent.ExecutionException; +import lombok.RequiredArgsConstructor; +import net.catenax.edc.cp.adapter.dto.ProcessData; +import net.catenax.edc.cp.adapter.exception.ContractOfferNotAvailable; +import net.catenax.edc.cp.adapter.exception.ResourceNotFoundException; +import net.catenax.edc.cp.adapter.messaging.Channel; +import net.catenax.edc.cp.adapter.messaging.Listener; +import net.catenax.edc.cp.adapter.messaging.Message; +import net.catenax.edc.cp.adapter.messaging.MessageService; +import org.eclipse.dataspaceconnector.api.datamanagement.catalog.service.CatalogService; +import org.eclipse.dataspaceconnector.api.datamanagement.contractnegotiation.service.ContractNegotiationService; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.types.domain.catalog.Catalog; +import org.eclipse.dataspaceconnector.spi.types.domain.contract.negotiation.ContractNegotiation; +import org.eclipse.dataspaceconnector.spi.types.domain.contract.negotiation.ContractOfferRequest; +import org.eclipse.dataspaceconnector.spi.types.domain.contract.offer.ContractOffer; + +@RequiredArgsConstructor +public class ContractNegotiationHandler implements Listener { + private final Monitor monitor; + private final MessageService messageService; + private final ContractNegotiationService contractNegotiationService; + private final CatalogService catalogService; + + @Override + public void process(Message message) { + monitor.debug("RequestHandler: input request: " + message.getPayload()); + ProcessData processData = message.getPayload(); + ContractOffer contractOffer = + findContractOffer(processData.getAssetId(), processData.getProvider()); + + String contractNegotiationId = + initializeContractNegotiation( + contractOffer, message.getPayload().getProvider(), message.getTraceId()); + message.getPayload().setContractNegotiationId(contractNegotiationId); + + messageService.send(Channel.CONTRACT_CONFIRMATION, message); + } + + private ContractOffer findContractOffer(String assetId, String providerUrl) { + Catalog catalog = getCatalog(providerUrl); + return Optional.ofNullable(catalog.getContractOffers()).orElse(Collections.emptyList()).stream() + .filter(it -> it.getAsset().getId().equals(assetId)) + .findFirst() + .orElseThrow( + () -> + new ResourceNotFoundException("Could not find Contract Offer for given Asset Id")); + } + + private Catalog getCatalog(String providerUrl) { + try { + return catalogService.getByProviderUrl(providerUrl).get(); + } catch (InterruptedException | ExecutionException e) { + throw new ContractOfferNotAvailable(e); + } + } + + private String initializeContractNegotiation( + ContractOffer contractOffer, String providerUrl, String traceId) { + monitor.info(String.format("[%s] RequestHandler: initiateNegotiation - start", traceId)); + ContractOfferRequest contractOfferRequest = + ContractOfferRequest.Builder.newInstance() + .connectorAddress(providerUrl) + .contractOffer(contractOffer) + .type(ContractOfferRequest.Type.INITIAL) + .connectorId("provider") + .protocol("ids-multipart") + .correlationId(traceId) + .build(); + + ContractNegotiation contractNegotiation = + contractNegotiationService.initiateNegotiation(contractOfferRequest); + monitor.info(String.format("[%s] RequestHandler: initiateNegotiation - end", traceId)); + return Optional.ofNullable(contractNegotiation.getId()) + .orElseThrow(() -> new ResourceNotFoundException("Could not find Contract NegotiationId")); + } +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataReferenceHandler.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataReferenceHandler.java new file mode 100644 index 000000000..6e2c9687e --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataReferenceHandler.java @@ -0,0 +1,59 @@ +package net.catenax.edc.cp.adapter.process.datareference; + +import static java.util.Objects.isNull; + +import java.util.concurrent.CompletableFuture; +import lombok.RequiredArgsConstructor; +import net.catenax.edc.cp.adapter.messaging.Channel; +import net.catenax.edc.cp.adapter.messaging.Listener; +import net.catenax.edc.cp.adapter.messaging.Message; +import net.catenax.edc.cp.adapter.messaging.MessageService; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.result.Result; +import org.eclipse.dataspaceconnector.spi.transfer.edr.EndpointDataReferenceReceiver; +import org.eclipse.dataspaceconnector.spi.types.domain.edr.EndpointDataReference; +import org.jetbrains.annotations.NotNull; + +@RequiredArgsConstructor +public class DataReferenceHandler implements Listener, EndpointDataReferenceReceiver { + private final Monitor monitor; + private final MessageService messageService; + private final DataStore dataStore; + + @Override + public void process(Message message) { + String contractAgreementId = message.getPayload().getContractAgreementId(); + monitor.info( + String.format("[%s] DataReferenceHandler: message received.", message.getTraceId())); + + EndpointDataReference dataReference = dataStore.getDataReference(contractAgreementId); + if (isNull(dataReference)) { + dataStore.storeMessage(message); + return; + } + + message.getPayload().setEndpointDataReference(dataReference); + messageService.send(Channel.RESULT, message); + dataStore.removeDataReference(contractAgreementId); + monitor.info( + String.format("[%s] DataReferenceHandler: message processed.", message.getTraceId())); + } + + @Override + public CompletableFuture> send(@NotNull EndpointDataReference dataReference) { + String contractAgreementId = dataReference.getProperties().get("cid"); + monitor.info(String.format("DataReference received, contractAgr.: %s", contractAgreementId)); + + Message message = dataStore.getMessage(contractAgreementId); + if (isNull(message)) { + dataStore.storeDataReference(contractAgreementId, dataReference); + return CompletableFuture.completedFuture(Result.success()); + } + message.getPayload().setEndpointDataReference(dataReference); + messageService.send(Channel.RESULT, message); + dataStore.removeMessage(contractAgreementId); + + monitor.info(String.format("[%s] DataReference processed.", message.getTraceId())); + return CompletableFuture.completedFuture(Result.success()); + } +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataStore.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataStore.java new file mode 100644 index 000000000..4f4a61f6b --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataStore.java @@ -0,0 +1,18 @@ +package net.catenax.edc.cp.adapter.process.datareference; + +import net.catenax.edc.cp.adapter.messaging.Message; +import org.eclipse.dataspaceconnector.spi.types.domain.edr.EndpointDataReference; + +public interface DataStore { + void storeDataReference(String contractAgreementId, EndpointDataReference endpointDataReference); + + EndpointDataReference getDataReference(String contractAgreementId); + + void removeDataReference(String contractAgreementId); + + void storeMessage(Message message); + + Message getMessage(String key); + + void removeMessage(String key); +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/InMemoryDataStore.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/InMemoryDataStore.java new file mode 100644 index 000000000..8c2c75634 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/InMemoryDataStore.java @@ -0,0 +1,42 @@ +package net.catenax.edc.cp.adapter.process.datareference; + +import java.util.HashMap; +import java.util.Map; +import net.catenax.edc.cp.adapter.messaging.Message; +import org.eclipse.dataspaceconnector.spi.types.domain.edr.EndpointDataReference; + +public class InMemoryDataStore implements DataStore { + private final Map messages = new HashMap<>(); + private final Map dataReferences = new HashMap<>(); + + @Override + public void storeDataReference( + String contractAgreementId, EndpointDataReference endpointDataReference) { + dataReferences.put(contractAgreementId, endpointDataReference); + } + + @Override + public EndpointDataReference getDataReference(String contractAgreementId) { + return dataReferences.get(contractAgreementId); + } + + @Override + public void removeDataReference(String contractAgreementId) { + dataReferences.remove(contractAgreementId); + } + + @Override + public void storeMessage(Message message) { + messages.put(message.getPayload().getContractAgreementId(), message); + } + + @Override + public Message getMessage(String contractAgreementId) { + return messages.get(contractAgreementId); + } + + @Override + public void removeMessage(String contractAgreementId) { + messages.remove(contractAgreementId); + } +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/service/ResultService.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/service/ResultService.java new file mode 100644 index 000000000..08f865fdd --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/service/ResultService.java @@ -0,0 +1,52 @@ +package net.catenax.edc.cp.adapter.service; + +import static java.util.concurrent.TimeUnit.SECONDS; + +import java.util.Map; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import lombok.RequiredArgsConstructor; +import net.catenax.edc.cp.adapter.messaging.Listener; +import net.catenax.edc.cp.adapter.messaging.Message; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.types.domain.edr.EndpointDataReference; + +@RequiredArgsConstructor +public class ResultService implements Listener { + private final int CAPACITY = 1; + private final int DEFAULT_TIMEOUT = 15; // TODO move to config + private final Monitor monitor; + private final Map> dataReference = + new ConcurrentHashMap<>(); + + public EndpointDataReference poll(String id) throws InterruptedException { + return poll(id, DEFAULT_TIMEOUT, SECONDS); + } + + public EndpointDataReference poll(String id, long timeout, TimeUnit unit) + throws InterruptedException { + if (!dataReference.containsKey(id)) { + initiate(id); + } + EndpointDataReference result = dataReference.get(id).poll(timeout, unit); + dataReference.remove(id); + return result; + } + + @Override + public void process(Message message) { + add(message.getTraceId(), message.getPayload().getEndpointDataReference()); + } + + private void add(String id, EndpointDataReference dataReferenceDto) { + if (!dataReference.containsKey(id)) { + initiate(id); + } + dataReference.get(id).add(dataReferenceDto); + } + + private void initiate(String id) { + dataReference.put(id, new ArrayBlockingQueue<>(CAPACITY)); + } +} diff --git a/edc-extensions/control-plane-adapter/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension b/edc-extensions/control-plane-adapter/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension new file mode 100644 index 000000000..7e411103b --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension @@ -0,0 +1,14 @@ +# +# Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +# +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# +# Contributors: +# Mercedes-Benz Tech Innovation GmbH - Initial ServiceExtension file +# +# +net.catenax.edc.cp.adapter.ApiAdapterExtension From d9a831a6eadf5f43bf526af7ebe7d7e7e6c1f9ff Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Thu, 28 Jul 2022 16:16:39 +0200 Subject: [PATCH 195/433] Remove jodah failsafe (#308) --- edc-extensions/hashicorp-vault/pom.xml | 6 ------ pom.xml | 8 -------- 2 files changed, 14 deletions(-) diff --git a/edc-extensions/hashicorp-vault/pom.xml b/edc-extensions/hashicorp-vault/pom.xml index 26f0c83c4..9cdec910a 100644 --- a/edc-extensions/hashicorp-vault/pom.xml +++ b/edc-extensions/hashicorp-vault/pom.xml @@ -185,12 +185,6 @@ hamcrest test - - net.jodah - failsafe - test - - diff --git a/pom.xml b/pom.xml index a250a5595..9ed5dca6d 100644 --- a/pom.xml +++ b/pom.xml @@ -82,7 +82,6 @@ 2.0.0-alpha1 1.2.11 2.2 - 2.4.3 catenax-ng @@ -1084,13 +1083,6 @@ ${org.hamcrest.hamcrest.version} test - - net.jodah - failsafe - ${net.jodah.failsafe.version} - test - - From 0a5b3026b7f415048054fcdbc66e2715ffabd940 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Fri, 29 Jul 2022 11:28:22 +0200 Subject: [PATCH 196/433] CI: Change Business Tests to use most recent image (#314) --- .github/workflows/business-tests.yaml | 28 ++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/.github/workflows/business-tests.yaml b/.github/workflows/business-tests.yaml index 95be6f7ba..51ddba2a9 100644 --- a/.github/workflows/business-tests.yaml +++ b/.github/workflows/business-tests.yaml @@ -92,7 +92,9 @@ jobs: - name: Load images into KinD run: |- - kind get clusters | xargs -n1 kind load docker-image edc-controlplane-postgresql-hashicorp-vault:latest edc-dataplane-hashicorp-vault:latest --name + docker tag edc-controlplane-postgresql-hashicorp-vault:latest edc-controlplane-postgresql-hashicorp-vault:business-test + docker tag edc-dataplane-hashicorp-vault:latest edc-dataplane-hashicorp-vault:business-test + kind get clusters | xargs -n1 kind load docker-image edc-controlplane-postgresql-hashicorp-vault:business-test edc-dataplane-hashicorp-vault:business-test --name ############################################ ### Prepare And Install Test Environment ### @@ -115,10 +117,14 @@ jobs: # Install the all-in-one supporting infrastructure environment (daps, vault, pgsql) helm install test-environment edc-tests/src/main/resources/deployment/helm/all-in-one \ - --set platoedccontrolplane.image.tag=latest \ - --set sokratesedccontrolplane.image.tag=latest \ - --set platoedcdataplane.image.tag=latest \ - --set sokratesedcdataplane.image.tag=latest \ + --set platoedccontrolplane.image.repository=edc-controlplane-postgresql-hashicorp-vault \ + --set platoedccontrolplane.image.tag=business-test \ + --set sokratesedccontrolplane.image.repository=edc-controlplane-postgresql-hashicorp-vault \ + --set sokratesedccontrolplane.image.tag=business-test \ + --set platoedcdataplane.image.repository=edc-dataplane-hashicorp-vault \ + --set platoedcdataplane.image.tag=business-test \ + --set sokratesedcdataplane.image.repository=edc-dataplane-hashicorp-vault \ + --set sokratesedcdataplane.image.tag=business-test \ --set idsdaps.enabled=true \ --set platovault.enabled=true \ --set platopostgresql.enabled=true \ @@ -146,10 +152,14 @@ jobs: # Install the all-in-one Control-/DataPlanes and backend-services helm upgrade --install test-environment edc-tests/src/main/resources/deployment/helm/all-in-one \ - --set platoedccontrolplane.image.tag=latest \ - --set sokratesedccontrolplane.image.tag=latest \ - --set platoedcdataplane.image.tag=latest \ - --set sokratesedcdataplane.image.tag=latest \ + --set platoedccontrolplane.image.repository=edc-controlplane-postgresql-hashicorp-vault \ + --set platoedccontrolplane.image.tag=business-test \ + --set sokratesedccontrolplane.image.repository=edc-controlplane-postgresql-hashicorp-vault \ + --set sokratesedccontrolplane.image.tag=business-test \ + --set platoedcdataplane.image.repository=edc-dataplane-hashicorp-vault \ + --set platoedcdataplane.image.tag=business-test \ + --set sokratesedcdataplane.image.repository=edc-dataplane-hashicorp-vault \ + --set sokratesedcdataplane.image.tag=business-test \ --set idsdaps.enabled=true \ --set platovault.enabled=true \ --set platopostgresql.enabled=true \ From 8b6df471a02da236b57ba0a54a7845884c3c4a40 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 29 Jul 2022 14:11:27 +0200 Subject: [PATCH 197/433] Bump cucumber.version from 7.4.1 to 7.5.0 (#311) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9ed5dca6d..7f00f0b25 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ 5.9.0 1.8.2 - 7.4.1 + 7.5.0 5.1.1 1.1.0 4.6.1 From 9756b882327f81773dcee5ba4c14157e42ffe0f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 29 Jul 2022 14:38:32 +0200 Subject: [PATCH 198/433] Bump flyway-core from 9.0.2 to 9.0.4 (#315) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7f00f0b25..5711ba9d7 100644 --- a/pom.xml +++ b/pom.xml @@ -66,7 +66,7 @@ 0.0.1-SNAPSHOT 1.2.2 42.4.0 - 9.0.2 + 9.0.4 5.9.0 From 4b5c174ab7ae00b5ffef39856afe4362b70b703b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 29 Jul 2022 15:02:36 +0200 Subject: [PATCH 199/433] Bump spotless-maven-plugin from 2.23.0 to 2.24.0 (#310) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5711ba9d7..38888d3ef 100644 --- a/pom.xml +++ b/pom.xml @@ -47,7 +47,7 @@ 3.3.0 3.2.2 - 2.23.0 + 2.24.0 3.1.0 3.4.0 2.0.0 From 05eae1a767fa8cd10b3197958df3756cbab67eab Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Fri, 29 Jul 2022 15:28:43 +0200 Subject: [PATCH 200/433] Update Changelog (#316) --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05c56d823..b1ef2e54f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- Fixes [release 0.0.5](https://github.com/catenax-ng/product-edc/releases/tag/0.0.5), which introduced classpath issues due to usage of [net.jodah:failsafe:2.4.3](https://search.maven.org/artifact/net.jodah/failsafe/2.4.3/jar) library + + ## [0.0.5] - 2022-07-28 ### Added From 3a4f87c538d01691bff89f7bfb464fce05c06be6 Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Mon, 1 Aug 2022 13:52:50 +0200 Subject: [PATCH 201/433] new business test for BPN (#319) --- docs/data-transfer/Transfer Data.md | 2 +- .../BusinessPartnerValidationExtension.java | 2 +- .../AbstractBusinessPartnerValidation.java | 33 ++++---- .../deployment/helm/all-in-one/values.yaml | 4 +- .../helm/omejdn/templates/configmap.yaml | 11 ++- .../helm/omejdn/templates/deployment.yaml | 10 +++ .../catenax/edc/tests/CatalogStepDefs.java | 39 ++++++--- .../catenax/edc/tests/DataManagementAPI.java | 79 ++++++++++++++----- .../net/catenax/edc/tests/PolicyStepDefs.java | 10 ++- .../data/BusinessPartnerNumberConstraint.java | 10 +++ .../catenax/edc/tests/data/Constraint.java | 3 + .../catenax/edc/tests/data/Permission.java | 3 + .../edc/tests/features/ContractOffers.feature | 43 ++++++++++ 13 files changed, 194 insertions(+), 55 deletions(-) create mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/data/BusinessPartnerNumberConstraint.java create mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/data/Constraint.java diff --git a/docs/data-transfer/Transfer Data.md b/docs/data-transfer/Transfer Data.md index 4da708597..752b94117 100644 --- a/docs/data-transfer/Transfer Data.md +++ b/docs/data-transfer/Transfer Data.md @@ -70,7 +70,7 @@ curl -X POST "$PLATO_DATAMGMT_URL/data/assets" --header "X-Api-Key: password" -- ``` ```bash -curl -X POST "$PLATO_DATAMGMT_URL/data/policies" --header "X-Api-Key: password" --header "Content-Type: application/json" --data "{ \"uid\": \"1\", \"prohibitions\": [], \"obligations\": [], \"permissions\": [ { \"edctype\": \"dataspaceconnector:permission\", \"action\": { \"type\": \"USE\" }, \"constraints\": [] } ] }" -s -o /dev/null -w 'Response Code: %{http_code}\n' +curl -X POST "$PLATO_DATAMGMT_URL/data/policies" --header "X-Api-Key: password" --header "Content-Type: application/json" --data "{ \"uid\": \"1\", \"prohibitions\": [], \"obligations\": [], \"permissions\": [ { \"edctype\": \"dataspaceconnector:permission\", \"action\": { \"type\": \"USE\" }, \"constraints\": [ { \"edctype\": \"AtomicConstraint\", \"leftExpression\": { \"edctype\": \"dataspaceconnector:literalexpression\", \"value\": \"BusinessPartnerNumber\" }, \"rightExpression\": { \"edctype\": \"dataspaceconnector:literalexpression\", \"value\": \"BPNSOKRATES\" }, \"operator\": \"EQ\" } ] } ] }" -s -o /dev/null -w 'Response Code: %{http_code}\n' ``` ```bash diff --git a/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/BusinessPartnerValidationExtension.java b/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/BusinessPartnerValidationExtension.java index 16343530d..88caf2121 100644 --- a/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/BusinessPartnerValidationExtension.java +++ b/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/BusinessPartnerValidationExtension.java @@ -69,7 +69,7 @@ public void initialize(ServiceExtensionContext context) { new BusinessPartnerProhibitionFunction(monitor); ruleBindingRegistry.bind("USE", ALL_SCOPES); - ruleBindingRegistry.bind("BusinessPartnerNumber", ALL_SCOPES); + ruleBindingRegistry.bind(BUSINESS_PARTNER_CONSTRAINT_KEY, ALL_SCOPES); policyEngine.registerFunction( ALL_SCOPES, Duty.class, BUSINESS_PARTNER_CONSTRAINT_KEY, dutyFunction); diff --git a/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java b/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java index f2bc61640..c38101c7d 100644 --- a/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java +++ b/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java @@ -68,7 +68,7 @@ protected AbstractBusinessPartnerValidation(Monitor monitor) { protected boolean evaluate( final Operator operator, final Object rightValue, final PolicyContext policyContext) { - if (policyContext.hasProblems() && policyContext.getProblems().size() > 0) { + if (policyContext.hasProblems() && !policyContext.getProblems().isEmpty()) { String problems = String.join(", ", policyContext.getProblems()); String message = String.format( @@ -103,13 +103,13 @@ protected boolean evaluate( } /** - * @param referingConnectorClaim of the participant - * @param businessPartnerNumber object + * @param referringConnectorClaim of the participant + * @param businessPartnerNumbers object * @return true if object is an iterable and constains a string that is successfully evaluated * against the claim */ private boolean containsBusinessPartnerNumber( - String referingConnectorClaim, Object businessPartnerNumbers, PolicyContext policyContext) { + String referringConnectorClaim, Object businessPartnerNumbers, PolicyContext policyContext) { if (businessPartnerNumbers == null) { final String message = String.format(FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_ITERABLE, "null"); @@ -133,18 +133,15 @@ private boolean containsBusinessPartnerNumber( String.format(SKIP_EVALUATION_BECAUSE_ITERABLE_VALUE_NOT_STRING, "null"); monitor.warning(message); policyContext.reportProblem(message); - continue; - } - if (!(businessPartnerNumber instanceof String)) { + } else if (!(businessPartnerNumber instanceof String)) { final String message = String.format( SKIP_EVALUATION_BECAUSE_ITERABLE_VALUE_NOT_STRING, businessPartnerNumber.getClass().getName()); monitor.warning(message); policyContext.reportProblem(message); - continue; - } - if (isCorrectBusinessPartner(referingConnectorClaim, (String) businessPartnerNumber)) { + } else if (isCorrectBusinessPartner( + referringConnectorClaim, (String) businessPartnerNumber)) { return true; // iterable does contain at least one matching value } } @@ -153,12 +150,12 @@ private boolean containsBusinessPartnerNumber( } /** - * @param referingConnectorClaim of the participant + * @param referringConnectorClaim of the participant * @param businessPartnerNumber object * @return true if object is string and successfully evaluated against the claim */ private boolean isBusinessPartnerNumber( - String referingConnectorClaim, Object businessPartnerNumber, PolicyContext policyContext) { + String referringConnectorClaim, Object businessPartnerNumber, PolicyContext policyContext) { if (businessPartnerNumber == null) { final String message = String.format(FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_STRING, "null"); monitor.warning(message); @@ -175,22 +172,22 @@ private boolean isBusinessPartnerNumber( return false; } - return isCorrectBusinessPartner(referingConnectorClaim, (String) businessPartnerNumber); + return isCorrectBusinessPartner(referringConnectorClaim, (String) businessPartnerNumber); } /** * At the time of writing (11. April 2022) the business partner number is part of the - * 'referingConnector' claim, which contains a connector URL. As the CX projects are not further - * alligned about the URL formatting, the enforcement can only be done by checking whether the URL + * 'referringConnector' claim, which contains a connector URL. As the CX projects are not further + * aligned about the URL formatting, the enforcement can only be done by checking whether the URL * _contains_ the number. As this introduces some insecurities when validation business partner * numbers, this should be addresses in the long term. * - * @param referingConnectorClaim describing URL with business partner number + * @param referringConnectorClaim describing URL with business partner number * @param businessPartnerNumber of the constraint * @return true if claim contains the business partner number */ private static boolean isCorrectBusinessPartner( - String referingConnectorClaim, String businessPartnerNumber) { - return referingConnectorClaim.contains(businessPartnerNumber); + String referringConnectorClaim, String businessPartnerNumber) { + return referringConnectorClaim.contains(businessPartnerNumber); } } diff --git a/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml b/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml index 27bab4e09..56fd80f69 100644 --- a/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml +++ b/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml @@ -11,7 +11,7 @@ idsdaps: - id: &sokratesDapsClientId E7:07:2D:74:56:66:31:F0:7B:10:EA:B6:03:06:4C:23:7F:ED:A6:65:keyid:E7:07:2D:74:56:66:31:F0:7B:10:EA:B6:03:06:4C:23:7F:ED:A6:65 name: sokrates attributes: - issuerConnector: http://sokrates-edc-controlplane/BPNSOKRATES + referringConnector: http://sokrates-edc-controlplane/BPNSOKRATES # Must be the same certificate that is stores in section 'sokrates-vault' certificate: |- -----BEGIN CERTIFICATE----- @@ -41,7 +41,7 @@ idsdaps: - id: &platoDapsClientId 99:83:A7:17:86:FF:98:93:CE:A0:DD:A1:F1:36:FA:F6:0F:75:0A:23:keyid:99:83:A7:17:86:FF:98:93:CE:A0:DD:A1:F1:36:FA:F6:0F:75:0A:23 name: plato attributes: - issuerConnector: http://plato-edc-controlplane/BPNPLATO + referringConnector: http://plato-edc-controlplane/BPNPLATO # Must be the same certificate that is stores in section 'plato-vault' certificate: |- -----BEGIN CERTIFICATE----- diff --git a/edc-tests/src/main/resources/deployment/helm/omejdn/templates/configmap.yaml b/edc-tests/src/main/resources/deployment/helm/omejdn/templates/configmap.yaml index 0974cbe9b..597850bd9 100644 --- a/edc-tests/src/main/resources/deployment/helm/omejdn/templates/configmap.yaml +++ b/edc-tests/src/main/resources/deployment/helm/omejdn/templates/configmap.yaml @@ -5,6 +5,11 @@ metadata: labels: {{- include "omejdn.labels" . | nindent 4 }} data: + scope_mapping.yml: |- + --- + idsc:IDS_CONNECTOR_ATTRIBUTES_ALL: + - referringConnector + omejdn.yml: |- --- host: http://ids-daps:4567/ @@ -48,9 +53,9 @@ data: value: IDS_CONNECTOR_ATTRIBUTES_ALL - key: securityProfile value: idsc:BASE_SECURITY_PROFILE - {{- range $j, $attribute := $val.attributes }} - - key: {{ $j }} - value: {{ $attribute }} + {{- range $key, $value := $val.attributes }} + - key: {{ $key }} + value: {{ $value }} {{- end }} redirect_uri: http://localhost:4200 {{ end -}} diff --git a/edc-tests/src/main/resources/deployment/helm/omejdn/templates/deployment.yaml b/edc-tests/src/main/resources/deployment/helm/omejdn/templates/deployment.yaml index f31277d78..567f48a74 100644 --- a/edc-tests/src/main/resources/deployment/helm/omejdn/templates/deployment.yaml +++ b/edc-tests/src/main/resources/deployment/helm/omejdn/templates/deployment.yaml @@ -56,6 +56,7 @@ spec: cp /opt/config/omejdn.yml /etc/daps/omejdn.yml cp /opt/config/clients.yml /etc/daps/clients.yml cp /opt/config/plugins.yml /etc/daps/plugins.yml + cp /opt/config/scope_mapping.yml /etc/daps/scope_mapping.yml apk add --update openssl openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout /etc/keys/omejdn/omejdn.key \ -subj "/C=DE/ST=Berlin/L=Berlin/O=Product-EDC-Test, Inc./OU=DE" @@ -67,6 +68,9 @@ spec: - mountPath: /opt/config/omejdn.yml name: omejdn-config subPath: omejdn.yml + - mountPath: /opt/config/scope_mapping.yml + name: scope-mapping + subPath: scope_mapping.yml - mountPath: /opt/config/clients.yml name: clients-config subPath: clients.yml @@ -117,6 +121,12 @@ spec: items: - key: omejdn.yml path: omejdn.yml + - name: scope-mapping + configMap: + name: {{ include "omejdn.fullname" . }} + items: + - key: scope_mapping.yml + path: scope_mapping.yml - name: clients-config configMap: name: {{ include "omejdn.fullname" . }} diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/CatalogStepDefs.java b/edc-tests/src/test/java/net/catenax/edc/tests/CatalogStepDefs.java index 4c48e0a13..f4b2010e7 100644 --- a/edc-tests/src/test/java/net/catenax/edc/tests/CatalogStepDefs.java +++ b/edc-tests/src/test/java/net/catenax/edc/tests/CatalogStepDefs.java @@ -20,7 +20,6 @@ import java.io.IOException; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; import net.catenax.edc.tests.data.ContractOffer; import org.junit.jupiter.api.Assertions; @@ -34,25 +33,43 @@ public void requestCatalog(Connector sender, Connector receiver) throws IOExcept final DataManagementAPI dataManagementAPI = sender.getDataManagementAPI(); final String receiverIdsUrl = receiver.getEnvironment().getIdsUrl() + "/data"; - lastRequestedOffers = - dataManagementAPI.requestCatalogFrom(receiverIdsUrl).collect(Collectors.toList()); + lastRequestedOffers = dataManagementAPI.requestCatalogFrom(receiverIdsUrl); } @Then("the catalog contains the following offers") - public void verifyCatalog(DataTable table) { + public void verifyCatalogContains(DataTable table) { for (Map map : table.asMaps()) { final String sourceContractDefinitionId = map.get("source definition"); final String assetId = map.get("asset"); - final boolean isInCatalog = - lastRequestedOffers.stream() - .anyMatch( - c -> - c.getAssetId().equals(assetId) - && c.getId().startsWith(sourceContractDefinitionId)); + final boolean isInCatalog = isInCatalog(assetId, sourceContractDefinitionId); Assertions.assertTrue( - isInCatalog, "The catalog does not contain the offer for asset " + assetId); + isInCatalog, + String.format( + "Expected the catalog to contain offer for definition '%s' and asset '%s' ", + sourceContractDefinitionId, assetId)); } } + + @Then("the catalog does not contain the following offers") + public void verifyCatalogContainsNot(DataTable table) { + for (Map map : table.asMaps()) { + final String sourceContractDefinitionId = map.get("source definition"); + final String assetId = map.get("asset"); + + final boolean isInCatalog = isInCatalog(assetId, sourceContractDefinitionId); + + Assertions.assertFalse( + isInCatalog, + String.format( + "Expected the catalog to not contain offer for definition '%s' and asset '%s' ", + sourceContractDefinitionId, assetId)); + } + } + + private boolean isInCatalog(String assetId, String definitionId) { + return lastRequestedOffers.stream() + .anyMatch(c -> c.getAssetId().equals(assetId) && c.getId().startsWith(definitionId)); + } } diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/DataManagementAPI.java b/edc-tests/src/test/java/net/catenax/edc/tests/DataManagementAPI.java index e3b3ecdb9..f1a953568 100644 --- a/edc-tests/src/test/java/net/catenax/edc/tests/DataManagementAPI.java +++ b/edc-tests/src/test/java/net/catenax/edc/tests/DataManagementAPI.java @@ -27,6 +27,8 @@ import lombok.Data; import lombok.extern.slf4j.Slf4j; import net.catenax.edc.tests.data.Asset; +import net.catenax.edc.tests.data.BusinessPartnerNumberConstraint; +import net.catenax.edc.tests.data.Constraint; import net.catenax.edc.tests.data.ContractDefinition; import net.catenax.edc.tests.data.ContractOffer; import net.catenax.edc.tests.data.Permission; @@ -59,7 +61,7 @@ public DataManagementAPI(String dataManagementUrl) { this.dataMgmtUrl = dataManagementUrl; } - public Stream requestCatalogFrom(String receivingConnectorUrl) throws IOException { + public List requestCatalogFrom(String receivingConnectorUrl) throws IOException { final String encodedUrl = URLEncoder.encode(receivingConnectorUrl, StandardCharsets.UTF_8.toString()); final DataManagementApiContractOfferCatalog catalog = @@ -70,7 +72,7 @@ public Stream requestCatalogFrom(String receivingConnectorUrl) th log.debug("Received " + catalog.contractOffers.size() + " offers"); - return catalog.contractOffers.stream().map(this::mapOffer); + return catalog.contractOffers.stream().map(this::mapOffer).collect(Collectors.toList()); } public Asset getAsset(String id) throws IOException { @@ -158,11 +160,11 @@ private T get(String path, String params, TypeToken typeToken) throws IOE private T get(String path, TypeToken typeToken) throws IOException { final HttpGet get = new HttpGet(dataMgmtUrl + path); - final HttpResponse response = sendRequest(get); final byte[] json = response.getEntity().getContent().readAllBytes(); - return new Gson().fromJson(new String(json), typeToken.getType()); + log.debug("Received response: {}", new String(json, StandardCharsets.UTF_8)); + return new Gson().fromJson(new String(json, StandardCharsets.UTF_8), typeToken.getType()); } private void delete(String path) throws IOException { @@ -231,7 +233,6 @@ private Policy mapPolicy(DataManagementApiPolicy dataManagementApiPolicy) { private DataManagementApiPolicy mapPolicy(Policy policy) { final List permission = policy.getPermission().stream().map(this::mapPermission).collect(Collectors.toList()); - final DataManagementApiPolicy apiObject = new DataManagementApiPolicy(); apiObject.uid = policy.getId(); apiObject.permissions = permission; @@ -241,8 +242,7 @@ private DataManagementApiPolicy mapPolicy(Policy policy) { private Permission mapPermission(DataManagementApiPermission dataManagementApiPermission) { final String target = dataManagementApiPermission.target; final String action = dataManagementApiPermission.action.type; - - return new Permission(action, target); + return new Permission(action, target, new ArrayList<>()); } private DataManagementApiPermission mapPermission(Permission permission) { @@ -252,12 +252,41 @@ private DataManagementApiPermission mapPermission(Permission permission) { final DataManagementApiRuleAction apiAction = new DataManagementApiRuleAction(); apiAction.type = action; + final List constraints = + permission.getConstraints().stream().map(this::mapConstraint).collect(Collectors.toList()); + final DataManagementApiPermission apiObject = new DataManagementApiPermission(); apiObject.target = target; apiObject.action = apiAction; + apiObject.constraints = constraints; return apiObject; } + private DataManagementApiConstraint mapConstraint(Constraint constraint) { + if (!(constraint instanceof BusinessPartnerNumberConstraint)) + throw new UnsupportedOperationException( + "Unsupported constraint type: " + constraint.getClass().getName()); + + final BusinessPartnerNumberConstraint businessPartnerNumberConstraint = + (BusinessPartnerNumberConstraint) constraint; + + final DataManagementApiLiteralExpression leftExpression = + new DataManagementApiLiteralExpression(); + leftExpression.value = "BusinessPartnerNumber"; + + final DataManagementApiLiteralExpression rightExpression = + new DataManagementApiLiteralExpression(); + rightExpression.value = businessPartnerNumberConstraint.getBusinessPartnerNumber(); + + final DataManagementApiConstraint dataManagementApiConstraint = + new DataManagementApiConstraint(); + dataManagementApiConstraint.leftExpression = leftExpression; + dataManagementApiConstraint.rightExpression = rightExpression; + dataManagementApiConstraint.operator = "EQ"; + + return dataManagementApiConstraint; + } + private ContractOffer mapOffer(DataManagementApiContractOffer dataManagementApiContractOffer) { final String id = dataManagementApiContractOffer.id; final String assetId = @@ -278,16 +307,15 @@ private ContractDefinition mapContractDefinition( final String contractPolicy = dataManagementContractDefinition.contractPolicyId; final List assetIds; - if (dataManagementContractDefinition == null - || dataManagementContractDefinition.getCriteria() == null) assetIds = new ArrayList<>(); - else - assetIds = - dataManagementContractDefinition.getCriteria().stream() - .filter(c -> c.left.equals(DataManagementApiAsset.ID)) - .filter(c -> c.op.equals("=")) - .map(c -> c.getRight()) - .map(c -> (String) c) - .collect(Collectors.toList()); + assetIds = + dataManagementContractDefinition.getCriteria() == null + ? new ArrayList<>() + : dataManagementContractDefinition.getCriteria().stream() + .filter(c -> c.left.equals(DataManagementApiAsset.ID)) + .filter(c -> c.op.equals("=")) + .map(DataManagementApiCriterion::getRight) + .map(c -> (String) c) + .collect(Collectors.toList()); return new ContractDefinition(id, contractPolicy, accessPolicy, assetIds); } @@ -342,8 +370,23 @@ private class DataManagementApiPolicy { @Data private class DataManagementApiPermission { private String edctype = "dataspaceconnector:permission"; - private String target; private DataManagementApiRuleAction action; + private String target; + private List constraints; + } + + @Data + private class DataManagementApiConstraint { + private String edctype = "AtomicConstraint"; + private DataManagementApiLiteralExpression leftExpression; + private DataManagementApiLiteralExpression rightExpression; + private String operator; + } + + @Data + private class DataManagementApiLiteralExpression { + private String edctype = "dataspaceconnector:literalexpression"; + private String value; } @Data diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/PolicyStepDefs.java b/edc-tests/src/test/java/net/catenax/edc/tests/PolicyStepDefs.java index 54dbd1423..2d738317e 100644 --- a/edc-tests/src/test/java/net/catenax/edc/tests/PolicyStepDefs.java +++ b/edc-tests/src/test/java/net/catenax/edc/tests/PolicyStepDefs.java @@ -20,6 +20,8 @@ import java.util.List; import java.util.Map; import java.util.stream.Stream; +import net.catenax.edc.tests.data.BusinessPartnerNumberConstraint; +import net.catenax.edc.tests.data.Constraint; import net.catenax.edc.tests.data.Permission; import net.catenax.edc.tests.data.Policy; @@ -50,7 +52,13 @@ private List parseDataTable(DataTable table) { for (Map map : table.asMaps()) { final String id = map.get("id"); final String action = map.get("action"); - final List permission = List.of(new Permission(action, null)); + + List accessConstraints = new ArrayList<>(); + final String businessPartnerNumber = map.get("businessPartnerNumber"); + if (businessPartnerNumber != null && !businessPartnerNumber.isBlank()) + accessConstraints.add(new BusinessPartnerNumberConstraint(businessPartnerNumber)); + + final List permission = List.of(new Permission(action, null, accessConstraints)); policies.add(new Policy(id, permission)); } diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/data/BusinessPartnerNumberConstraint.java b/edc-tests/src/test/java/net/catenax/edc/tests/data/BusinessPartnerNumberConstraint.java new file mode 100644 index 000000000..d7cee2be9 --- /dev/null +++ b/edc-tests/src/test/java/net/catenax/edc/tests/data/BusinessPartnerNumberConstraint.java @@ -0,0 +1,10 @@ +package net.catenax.edc.tests.data; + +import lombok.NonNull; +import lombok.Value; + +@Value +public class BusinessPartnerNumberConstraint implements Constraint { + + @NonNull String businessPartnerNumber; +} diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/data/Constraint.java b/edc-tests/src/test/java/net/catenax/edc/tests/data/Constraint.java new file mode 100644 index 000000000..1fbd2fa59 --- /dev/null +++ b/edc-tests/src/test/java/net/catenax/edc/tests/data/Constraint.java @@ -0,0 +1,3 @@ +package net.catenax.edc.tests.data; + +public interface Constraint {} diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/data/Permission.java b/edc-tests/src/test/java/net/catenax/edc/tests/data/Permission.java index 421905f8b..2b89066b7 100644 --- a/edc-tests/src/test/java/net/catenax/edc/tests/data/Permission.java +++ b/edc-tests/src/test/java/net/catenax/edc/tests/data/Permission.java @@ -13,6 +13,7 @@ */ package net.catenax.edc.tests.data; +import java.util.List; import lombok.NonNull; import lombok.Value; @@ -20,4 +21,6 @@ public class Permission { @NonNull String action; String target; + + List constraints; } diff --git a/edc-tests/src/test/resources/net/catenax/edc/tests/features/ContractOffers.feature b/edc-tests/src/test/resources/net/catenax/edc/tests/features/ContractOffers.feature index e8411bc42..9f8a9f12e 100644 --- a/edc-tests/src/test/resources/net/catenax/edc/tests/features/ContractOffers.feature +++ b/edc-tests/src/test/resources/net/catenax/edc/tests/features/ContractOffers.feature @@ -35,3 +35,46 @@ Feature: Contract Offers | source definition | asset | | contract-definition-1 | asset-1 | | contract-definition-2 | asset-2 | + + Scenario: EQ Business Partner Constrain for Catalog + Given 'Plato' has the following assets + | id | description | + | asset-1 | Example Asset | + | asset-2 | Example Asset | + | asset-3 | Example Asset | + And 'Plato' has the following policies + | id | action | businessPartnerNumber | + | policy-1 | USE | | + | policy-2 | USE | BPNFOO | + | policy-3 | USE | BPNSOKRATES | + And 'Plato' has the following contract definitions + | id | access policy | contract policy | asset | + | contract-definition-1 | policy-1 | policy-1 | asset-1 | + | contract-definition-2 | policy-2 | policy-1 | asset-2 | + | contract-definition-3 | policy-3 | policy-1 | asset-3 | + When 'Sokrates' requests the catalog from 'Plato' + Then the catalog contains the following offers + | source definition | asset | + | contract-definition-1 | asset-1 | + | contract-definition-3 | asset-3 | + Then the catalog does not contain the following offers + | source definition | asset | + | contract-definition-2 | asset-2 | + + Scenario: Multiple Contract Offers for same Asset + Given 'Plato' has the following assets + | id | description | + | asset-1 | Example Asset | + And 'Plato' has the following policies + | id | action | businessPartnerNumber | + | policy-1 | USE | | + | policy-2 | USE | BPNSOKRATES | + And 'Plato' has the following contract definitions + | id | access policy | contract policy | asset | + | contract-definition-1 | policy-1 | policy-1 | asset-1 | + | contract-definition-2 | policy-2 | policy-1 | asset-1 | + When 'Sokrates' requests the catalog from 'Plato' + Then the catalog contains the following offers + | source definition | asset | + | contract-definition-2 | asset-1 | + #| contract-definition-1 | asset-1 | # Issue https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1764 From 506977fac3412c0d572eedbf0fdaed8604d4d7d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 16:51:46 +0200 Subject: [PATCH 202/433] Bump gson from 2.9.0 to 2.9.1 (#322) --- edc-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-tests/pom.xml b/edc-tests/pom.xml index 90a503d0c..573fcb982 100644 --- a/edc-tests/pom.xml +++ b/edc-tests/pom.xml @@ -28,7 +28,7 @@ ${project.groupId}_${project.artifactId} - 2.9.0 + 2.9.1 4.5.13 1.2.11 1.7.36 From 42677717b60c626905fedd103612f22665f1b972 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Aug 2022 09:24:06 +0200 Subject: [PATCH 203/433] Bump mikefarah/yq from 4.26.1 to 4.27.2 (#320) --- .github/workflows/draft-new-release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index 24ee4cb64..056c0a026 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -43,7 +43,7 @@ jobs: GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} - name: Bump version in deployment/helm - uses: mikefarah/yq@v4.26.1 + uses: mikefarah/yq@v4.27.2 with: cmd: |- find deployment/helm -name Chart.yaml | xargs -n1 yq -i '.appVersion = "${{ github.event.inputs.version }}" | .version = "${{ github.event.inputs.version }}"' From 609050169f661539f58b096eff516a7683512430 Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Tue, 2 Aug 2022 13:13:33 +0200 Subject: [PATCH 204/433] document hashicorp health check (#323) --- edc-extensions/hashicorp-vault/README.md | 44 ++++++++++++++++++------ 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/edc-extensions/hashicorp-vault/README.md b/edc-extensions/hashicorp-vault/README.md index caa6ff4db..0c2ebf424 100644 --- a/edc-extensions/hashicorp-vault/README.md +++ b/edc-extensions/hashicorp-vault/README.md @@ -3,27 +3,46 @@ --- **Please note:**
-In the HashiCorp vault it is possible to define multiple data entries per secret. Other vaults might allow only one entry per secret (e.g. Azure Key Vault). +Using the HashiCorp vault it is possible to define multiple data entries per secret. Other vaults might allow only one +entry per secret (e.g. Azure Key Vault). -Therefore, the HashiCorp vault extension **only** checks the '**content**' data entry! Please use this knowledge when creating secrets the EDC should consume. +Therefore, the HashiCorp vault extension **only** checks the '**content**' data entry! Please use this knowledge when +creating secrets the EDC should consume. --- ## Configuration -| Key | Description | Mandatory | Default | -|:--------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------|-----------|------------------| -| edc.vault.hashicorp.url | URL to connect to the HashiCorp Vault | X || -| edc.vault.hashicorp.token | Value for [Token Authentication](https://www.vaultproject.io/docs/auth/token) with the vault | X || -| edc.vault.hashicorp.timeout.seconds | Request timeout in seconds when contacting the vault | | `30` | -| edc.vault.hashicorp.health.check.enabled | Enable health checks to ensure vault is initialized, unsealed and active (default: _true_) | | `true` | -| edc.vault.hashicorp.health.check.standby.ok | Specifies if a vault in standby is healthy. This is useful when Vault is behind a non-configurable load balancer. (default: _false_) | | `false` | -| edc.vault.hashicorp.api.secret.path | Path to the [secret api](https://www.vaultproject.io/api-docs/secret/kv/kv-v1) (default: _/v1/secret_) | | `/v1/secret` | -| edc.vault.hashicorp.api.health.check.path | Path to the [health api](https://www.vaultproject.io/api-docs/system/health) (default: _/sys/health_) | | `/v1/sys/health` | +| Key | Description | Mandatory | Default | +|:--------------------------------------------|:-----------------------------------------------------------------------------------------------------------------|-----------|------------------| +| edc.vault.hashicorp.url | URL to connect to the HashiCorp Vault | X || | +| edc.vault.hashicorp.token | Value for [Token Authentication](https://www.vaultproject.io/docs/auth/token) with the vault | X || | +| edc.vault.hashicorp.timeout.seconds | Request timeout in seconds when contacting the vault | | `30` | +| edc.vault.hashicorp.health.check.enabled | Enable health checks to ensure vault is initialized, unsealed and active | | `true` | +| edc.vault.hashicorp.health.check.standby.ok | Specifies if a vault in standby is healthy. This is useful when Vault is behind a non-configurable load balancer | | `false` | +| edc.vault.hashicorp.api.secret.path | Path to the [secret api](https://www.vaultproject.io/api-docs/secret/kv/kv-v1) | | `/v1/secret` | +| edc.vault.hashicorp.api.health.check.path | Path to the [health api](https://www.vaultproject.io/api-docs/system/health) | | `/v1/sys/health` | + +## Health Check + +The HashiCorp Vault Extension is able to run health checks. A health check is successful when the vault is _initialized_, _active_ and _unsealed_. Successful health checks are logged with level _FINE_. Unsuccessful health checks will be logged +with level _WARNING_. + +--- +**Health Checks in Catena-X** + +If your project uses the Catena-X HashiCorp Vault please set `edc.vault.hashicorp.health.check.standby.ok` to _true_. Otherwise the health check would fail if the Vault is in standby. +```bash + # Logs of successful check with standby vault + [2022-08-01 14:48:37] [FINE ] HashiCorp Vault HealthCheck successful. HashicorpVaultHealthResponsePayload(isInitialized=true, isSealed=false, isStandby=true, isPerformanceStandby=false, replicationPerformanceMode=disabled,replicationDrMode=disabled, serverTimeUtc=1659365317, version=1.9.2, clusterName=vault-cluster-4b193c26, clusterId=83fabd45-685d-7f8d-9495-18fab6f50d5e) +``` + +--- ## Example: Create & Configure DAPS Key 1. Insert DAPS Key into HashiCorp Vault + ```bash cat << EOF | /bin/vault kv put secret/my-daps-key content=- -----BEGIN PRIVATE KEY----- @@ -58,10 +77,13 @@ cat << EOF | /bin/vault kv put secret/my-daps-key content=- ``` 2. Configure Key in the EDC + ```bash EDC_OAUTH_PRIVATE_KEY_ALIAS: my-daps-key ``` + or + ```bash edc.oauth.private.key.alias=my-daps-key ``` \ No newline at end of file From 5a83d7ed58de9ffa8eafa1aad5a98d615121406a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Aug 2022 16:12:05 +0200 Subject: [PATCH 205/433] Bump postgresql from 42.4.0 to 42.4.1 (#327) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5cf10443f..44920259a 100644 --- a/pom.xml +++ b/pom.xml @@ -65,7 +65,7 @@ 0.0.1-SNAPSHOT 1.2.2 - 42.4.0 + 42.4.1 9.0.4 From 753725195dac45e9558e140baa87fcb0cc8ef3a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Aug 2022 16:24:16 +0200 Subject: [PATCH 206/433] Bump flyway-core from 9.0.4 to 9.1.2 (#328) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 44920259a..a772548b2 100644 --- a/pom.xml +++ b/pom.xml @@ -66,7 +66,7 @@ 0.0.1-SNAPSHOT 1.2.2 42.4.1 - 9.0.4 + 9.1.2 5.9.0 From bfd09cb75cb3fedfb95b1f982b7f5b0a65ef95fe Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Thu, 4 Aug 2022 10:47:56 +0200 Subject: [PATCH 207/433] create business test for 1000 contract offers (#329) --- .../java/net/catenax/edc/tests/AssetStepDefs.java | 9 +++++++++ .../java/net/catenax/edc/tests/CatalogStepDefs.java | 11 +++++++++++ .../catenax/edc/tests/ContractDefinitionStepDefs.java | 6 ++++-- .../catenax/edc/tests/features/ContractOffers.feature | 11 +++++++++++ 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/AssetStepDefs.java b/edc-tests/src/test/java/net/catenax/edc/tests/AssetStepDefs.java index 89f88f095..2d024b889 100644 --- a/edc-tests/src/test/java/net/catenax/edc/tests/AssetStepDefs.java +++ b/edc-tests/src/test/java/net/catenax/edc/tests/AssetStepDefs.java @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.UUID; import java.util.stream.Stream; import net.catenax.edc.tests.data.Asset; @@ -42,6 +43,14 @@ public void hasAssets(Connector connector, DataTable table) throws Exception { for (Asset asset : assets) api.createAsset(asset); } + @Given("'{connector}' has '{int}' assets") + public void hasAssets(Connector connector, int assetCount) throws Exception { + final DataManagementAPI api = connector.getDataManagementAPI(); + + for (var i = 0; i < assetCount; i++) + api.createAsset(new Asset(UUID.randomUUID().toString(), i + 1 + " / " + assetCount)); + } + private List parseDataTable(DataTable table) { final List assets = new ArrayList<>(); diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/CatalogStepDefs.java b/edc-tests/src/test/java/net/catenax/edc/tests/CatalogStepDefs.java index f4b2010e7..aa4468dbc 100644 --- a/edc-tests/src/test/java/net/catenax/edc/tests/CatalogStepDefs.java +++ b/edc-tests/src/test/java/net/catenax/edc/tests/CatalogStepDefs.java @@ -68,6 +68,17 @@ public void verifyCatalogContainsNot(DataTable table) { } } + @Then("the catalog contains '{int}' offers") + public void verifyCatalogContainsXOffers(int offerCount) { + + Assertions.assertEquals( + offerCount, + lastRequestedOffers.size(), + String.format( + "Expected the catalog to contain '%s' offers, but got '%s'.", + offerCount, lastRequestedOffers.size())); + } + private boolean isInCatalog(String assetId, String definitionId) { return lastRequestedOffers.stream() .anyMatch(c -> c.getAssetId().equals(assetId) && c.getId().startsWith(definitionId)); diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/ContractDefinitionStepDefs.java b/edc-tests/src/test/java/net/catenax/edc/tests/ContractDefinitionStepDefs.java index 7172f6df2..5e571a049 100644 --- a/edc-tests/src/test/java/net/catenax/edc/tests/ContractDefinitionStepDefs.java +++ b/edc-tests/src/test/java/net/catenax/edc/tests/ContractDefinitionStepDefs.java @@ -52,9 +52,11 @@ private List parseDataTable(DataTable table) { String id = map.get("id"); String accessPolicyId = map.get("access policy"); String contractPolicyId = map.get("contract policy"); - String assetid = map.get("asset"); + String assetId = map.get("asset"); + List assetIds = assetId == null ? new ArrayList<>() : List.of(assetId); + contractDefinitions.add( - new ContractDefinition(id, contractPolicyId, accessPolicyId, List.of(assetid))); + new ContractDefinition(id, contractPolicyId, accessPolicyId, assetIds)); } return contractDefinitions; diff --git a/edc-tests/src/test/resources/net/catenax/edc/tests/features/ContractOffers.feature b/edc-tests/src/test/resources/net/catenax/edc/tests/features/ContractOffers.feature index 9f8a9f12e..acded42b2 100644 --- a/edc-tests/src/test/resources/net/catenax/edc/tests/features/ContractOffers.feature +++ b/edc-tests/src/test/resources/net/catenax/edc/tests/features/ContractOffers.feature @@ -78,3 +78,14 @@ Feature: Contract Offers | source definition | asset | | contract-definition-2 | asset-1 | #| contract-definition-1 | asset-1 | # Issue https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1764 + + Scenario: Catalog with 1000 Contract Offers + Given 'Plato' has '1000' assets + And 'Plato' has the following policies + | id | action | + | policy-1 | USE | + And 'Plato' has the following contract definitions + | id | access policy | contract policy | + | contract-definition-1 | policy-1 | policy-1 | + When 'Sokrates' requests the catalog from 'Plato' + Then the catalog contains '1000' offers From c59e5f979457af4ba3fe097ecf3a7e1f7757c1f3 Mon Sep 17 00:00:00 2001 From: Sebastian Scherer <59142915+the-tatanka@users.noreply.github.com> Date: Fri, 5 Aug 2022 15:58:14 +0200 Subject: [PATCH 208/433] Update and rename checkov.yaml to kics.yaml --- .github/workflows/checkov.yaml | 52 --------------------------------- .github/workflows/kics.yaml | 53 ++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 52 deletions(-) delete mode 100644 .github/workflows/checkov.yaml create mode 100644 .github/workflows/kics.yaml diff --git a/.github/workflows/checkov.yaml b/.github/workflows/checkov.yaml deleted file mode 100644 index f1f2004d6..000000000 --- a/.github/workflows/checkov.yaml +++ /dev/null @@ -1,52 +0,0 @@ ---- -name: "Checkov" - -on: - workflow_dispatch: - push: - branches: - - main - - develop - tags: - - '[0-9]+.[0-9]+.[0-9]+' - paths-ignore: - - '**' - - '!deployment/helm/**' - pull_request: - branches: - - '*' - paths-ignore: - - '**' - - '!deployment/helm/**' - -jobs: - analyze: - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - name: checkov-action - steps: - - - name: Checkout repo - uses: actions/checkout@master - - - name: Run Checkov action - id: checkov - uses: bridgecrewio/checkov-action@master - with: - directory: deployment/helm - quiet: true # optional: display only failed checks - soft_fail: false # optional: do not return an error code if there are failed checks - framework: helm # optional: run only on a specific infrastructure {cloudformation,terraform,kubernetes,all} - output_format: sarif # optional: the output format, one of: cli, json, junitxml, github_failed_only, or sarif. Default: sarif - download_external_modules: true # optional: download external terraform modules from public git repositories and terraform registry - log_level: DEBUG # optional: set log level. Default WARNING - config_file: checkov.yaml - - - name: Upload Checkov scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@v2 - if: always() - with: - sarif_file: "results.sarif" diff --git a/.github/workflows/kics.yaml b/.github/workflows/kics.yaml new file mode 100644 index 000000000..fb86acca5 --- /dev/null +++ b/.github/workflows/kics.yaml @@ -0,0 +1,53 @@ +name: "KICS" + +on: + push: + branches: [main, master, develop] + # pull_request: + # The branches below must be a subset of the branches above + # branches: [main, master] + # paths-ignore: + # - "**/*.md" + # - "**/*.txt" + schedule: + - cron: "28 15 * * 3" + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + steps: + - uses: actions/checkout@v2 + + - name: KICS scan + uses: checkmarx/kics-github-action@master + with: + # Scanning directory . + path: "." + # Fail on HIGH severity results + fail_on: high + # when provided with a directory on output_path + # it will generate the specified reports file named 'results.{extension}' + # in this example it will generate: + # - results-dir/results.json + # - results-dir/results.sarif + output_path: kicsResults/ + output_formats: "json,sarif" + # If you want KICS to ignore the results and return exit status code 0 unless a KICS engine error happens + # ignore_on_exit: results + # GITHUB_TOKEN enables this github action to access github API and post comments in a pull request + # token: ${{ secrets.GITHUB_TOKEN }} + # enable_comments: true + + # Upload findings to GitHub Advanced Security Dashboard + - name: Upload SARIF file for GitHub Advanced Security Dashboard + if: always() + uses: github/codeql-action/upload-sarif@v1 + with: + sarif_file: kicsResults/results.sarif + From 5fb6a85346c0ec0eb2e5a68d2605e8f231cfe9e5 Mon Sep 17 00:00:00 2001 From: Sebastian Scherer <59142915+the-tatanka@users.noreply.github.com> Date: Sat, 6 Aug 2022 00:17:25 +0200 Subject: [PATCH 209/433] Update kics.yaml --- .github/workflows/kics.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/kics.yaml b/.github/workflows/kics.yaml index fb86acca5..7bdd6a89f 100644 --- a/.github/workflows/kics.yaml +++ b/.github/workflows/kics.yaml @@ -10,7 +10,7 @@ on: # - "**/*.md" # - "**/*.txt" schedule: - - cron: "28 15 * * 3" + - cron: "0 0 * * *" jobs: analyze: From e9a9b2de60eb30cf06bb30328b0adeb746a23862 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Mon, 8 Aug 2022 13:10:26 +0200 Subject: [PATCH 210/433] Update product-edc to use EDC version v0.0.1-milestone-5 (#302) --- .github/dependabot.yml | 49 +-- .github/workflows/build.yaml | 17 - .github/workflows/business-tests.yaml | 14 +- .github/workflows/publish-new-release.yml | 8 +- CHANGELOG.md | 34 +- NOTICE.md | 2 +- README.md | 19 -- deployment/helm/edc-controlplane/README.md | 2 +- deployment/helm/edc-controlplane/values.yaml | 2 +- deployment/helm/edc-dataplane/README.md | 2 +- deployment/helm/edc-dataplane/values.yaml | 2 +- docs/README.md | 1 + docs/data-transfer/Transfer Data.md | 6 +- docs/migration/Version_0.0.x_0.1.x.md | 294 ++++++++++++++++++ edc | 1 - .../edc-controlplane-base/pom.xml | 16 + edc-controlplane/pom.xml | 2 - .../edc-dataplane-azure-vault/README.md | 4 +- .../edc-dataplane-hashicorp-vault/README.md | 4 +- .../business-partner-validation/pom.xml | 15 - .../AbstractBusinessPartnerValidation.java | 10 +- .../README.md | 17 + .../dataplane-selector-configuration/pom.xml | 74 +++++ ...SelectorConfigurationServiceExtension.java | 188 +++++++++++ ...paceconnector.spi.system.ServiceExtension} | 4 +- ...ationServiceExtensionEdcExtensionTest.java | 150 +++++++++ ...ctorConfigurationServiceExtensionTest.java | 218 +++++++++++++ edc-extensions/hashicorp-vault/pom.xml | 5 +- .../HashicorpVaultHealthResponse.java | 2 +- .../HashicorpVaultVaultExtension.java | 38 +-- ...spaceconnector.spi.system.ServiceExtension | 1 + .../hashicorpvault/AbstractHashicorpIT.java | 2 +- .../HashicorpVaultExtensionTest.java | 6 +- edc-extensions/pom.xml | 1 + edc-extensions/postgresql-migration/pom.xml | 15 - .../AssetPostgresqlMigrationExtension.java | 2 +- .../asset/V0_0_2__Milestone5_Update.sql | 16 + .../V0_0_3__Milestone5_Update.sql | 23 ++ .../policy/V0_0_2__Milestone5_Update.sql | 21 ++ .../V0_0_3__Alter_Rename_Id.sql | 16 + .../V0_0_4__Milestone5_Update.sql | 24 ++ edc-tests/pom.xml | 4 + .../deployment/helm/all-in-one/values.yaml | 31 +- .../helm/backend-application/values.yaml | 1 + .../catenax/edc/tests/DataManagementAPI.java | 73 +++-- .../net/catenax/edc/tests/data/Policy.java | 2 +- misc/license-mappings.xml | 2 +- pom.xml | 11 +- 48 files changed, 1228 insertions(+), 223 deletions(-) create mode 100644 docs/migration/Version_0.0.x_0.1.x.md delete mode 160000 edc create mode 100644 edc-extensions/dataplane-selector-configuration/README.md create mode 100644 edc-extensions/dataplane-selector-configuration/pom.xml create mode 100644 edc-extensions/dataplane-selector-configuration/src/main/java/net/catenax/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtension.java rename edc-extensions/{hashicorp-vault/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.VaultExtension => dataplane-selector-configuration/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension} (79%) create mode 100644 edc-extensions/dataplane-selector-configuration/src/test/java/net/catenax/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtensionEdcExtensionTest.java create mode 100644 edc-extensions/dataplane-selector-configuration/src/test/java/net/catenax/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtensionTest.java create mode 100644 edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/asset/V0_0_2__Milestone5_Update.sql create mode 100644 edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractnegotiation/V0_0_3__Milestone5_Update.sql create mode 100644 edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/policy/V0_0_2__Milestone5_Update.sql create mode 100644 edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/transferprocess/V0_0_3__Alter_Rename_Id.sql create mode 100644 edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/transferprocess/V0_0_4__Milestone5_Update.sql diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 74c476660..1c28f97d5 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,19 +1,10 @@ --- version: 2 updates: - - - package-ecosystem: "gitsubmodule" - target-branch: develop - directory: "/" - labels: - - "dependabot" - - "edc" - schedule: - interval: "daily" - package-ecosystem: "github-actions" target-branch: develop - directory: "/" + directory: / labels: - "dependabot" - "github-actions" @@ -22,43 +13,7 @@ updates: - package-ecosystem: "docker" target-branch: develop - directory: "edc-dataplane/edc-dataplane-azure-vault/src/main/docker" - labels: - - "dependabot" - - "docker" - schedule: - interval: "daily" - - - package-ecosystem: "docker" - target-branch: develop - directory: "edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker" - labels: - - "dependabot" - - "docker" - schedule: - interval: "daily" - - - package-ecosystem: "docker" - target-branch: develop - directory: "edc-controlplane/edc-controlplane-memory/src/main/docker" - labels: - - "dependabot" - - "docker" - schedule: - interval: "daily" - - - package-ecosystem: "docker" - target-branch: develop - directory: "edc-controlplane/edc-controlplane-postgresql/src/main/docker" - labels: - - "dependabot" - - "docker" - schedule: - interval: "daily" - - - package-ecosystem: "docker" - target-branch: develop - directory: "edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker" + directory: / labels: - "dependabot" - "docker" diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 165a7d67e..4d12dbe0c 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -66,7 +66,6 @@ jobs: name: Checkout uses: actions/checkout@v3 with: - submodules: recursive fetch-depth: 0 - name: Set up JDK 11 @@ -75,10 +74,6 @@ jobs: java-version: '11' distribution: 'adopt' cache: 'maven' - - - name: Build edc with Gradle to get latest snapshots - run: ./gradlew publishToMavenLocal - working-directory: edc - name: Cache SonarCloud packages uses: actions/cache@v3 @@ -116,8 +111,6 @@ jobs: - name: Checkout uses: actions/checkout@v3 - with: - submodules: recursive - name: Login to GitHub Container Registry if: | @@ -134,10 +127,6 @@ jobs: java-version: '11' distribution: 'adopt' cache: 'maven' - - - name: Build edc with Gradle to get latest snapshots - run: ./gradlew publishToMavenLocal - working-directory: edc # Build - name: Build Controlplane @@ -204,8 +193,6 @@ jobs: - name: Checkout uses: actions/checkout@v3 - with: - submodules: recursive - name: Login to GitHub Container Registry if: | @@ -222,10 +209,6 @@ jobs: java-version: '11' distribution: 'adopt' cache: 'maven' - - - name: Build edc with Gradle to get latest snapshots - run: ./gradlew publishToMavenLocal - working-directory: edc # Build - name: Build Dataplane diff --git a/.github/workflows/business-tests.yaml b/.github/workflows/business-tests.yaml index 51ddba2a9..d2dd6fbd0 100644 --- a/.github/workflows/business-tests.yaml +++ b/.github/workflows/business-tests.yaml @@ -21,8 +21,6 @@ jobs: - name: Checkout uses: actions/checkout@v3 - with: - submodules: recursive - name: Set-Up JDK 11 uses: actions/setup-java@v3.4.0 @@ -71,10 +69,6 @@ jobs: ############################################## ### Build and load recent images into KinD ### ############################################## - - - name: Build edc with Gradle to get latest snapshots - run: ./gradlew publishToMavenLocal - working-directory: edc - name: Build edc-controlplane-postgresql-hashicorp-vault run: |- @@ -119,12 +113,16 @@ jobs: helm install test-environment edc-tests/src/main/resources/deployment/helm/all-in-one \ --set platoedccontrolplane.image.repository=edc-controlplane-postgresql-hashicorp-vault \ --set platoedccontrolplane.image.tag=business-test \ + --set platoedccontrolplane.image.pullPolicy=Never \ --set sokratesedccontrolplane.image.repository=edc-controlplane-postgresql-hashicorp-vault \ --set sokratesedccontrolplane.image.tag=business-test \ + --set sokratesedccontrolplane.image.pullPolicy=Never \ --set platoedcdataplane.image.repository=edc-dataplane-hashicorp-vault \ --set platoedcdataplane.image.tag=business-test \ + --set platoedcdataplane.image.pullPolicy=Never \ --set sokratesedcdataplane.image.repository=edc-dataplane-hashicorp-vault \ --set sokratesedcdataplane.image.tag=business-test \ + --set sokratesedcdataplane.image.pullPolicy=Never \ --set idsdaps.enabled=true \ --set platovault.enabled=true \ --set platopostgresql.enabled=true \ @@ -154,12 +152,16 @@ jobs: helm upgrade --install test-environment edc-tests/src/main/resources/deployment/helm/all-in-one \ --set platoedccontrolplane.image.repository=edc-controlplane-postgresql-hashicorp-vault \ --set platoedccontrolplane.image.tag=business-test \ + --set platoedccontrolplane.image.pullPolicy=Never \ --set sokratesedccontrolplane.image.repository=edc-controlplane-postgresql-hashicorp-vault \ --set sokratesedccontrolplane.image.tag=business-test \ + --set sokratesedccontrolplane.image.pullPolicy=Never \ --set platoedcdataplane.image.repository=edc-dataplane-hashicorp-vault \ --set platoedcdataplane.image.tag=business-test \ + --set platoedcdataplane.image.pullPolicy=Never \ --set sokratesedcdataplane.image.repository=edc-dataplane-hashicorp-vault \ --set sokratesedcdataplane.image.tag=business-test \ + --set sokratesedcdataplane.image.pullPolicy=Never \ --set idsdaps.enabled=true \ --set platovault.enabled=true \ --set platopostgresql.enabled=true \ diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index 570e8b18c..1b52a43c7 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -67,8 +67,6 @@ jobs: - name: Checkout uses: actions/checkout@v3 - with: - submodules: recursive - name: Set up JDK 11 uses: actions/setup-java@v3.4.1 @@ -76,10 +74,6 @@ jobs: java-version: '11' distribution: 'adopt' cache: 'maven' - - - name: Build edc with Gradle to get latest snapshots - run: ./gradlew publishToMavenLocal - working-directory: edc - name: Deploy run: |- @@ -120,7 +114,7 @@ jobs: git config user.email noreply@github.com # Package all charts - find -name Chart.yaml -not -path "./edc-tests/*" -not -path "./edc/*" | xargs -n1 dirname | xargs -n1 helm package -u -d helm-charts + find deployment/helm -name Chart.yaml -not -path "./edc-tests/*" | xargs -n1 dirname | xargs -n1 helm package -u -d helm-charts git checkout gh-pages || git checkout -b gh-pages git pull --rebase origin gh-pages diff --git a/CHANGELOG.md b/CHANGELOG.md index 96b22659c..e78683a41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +**Important Note**: Version 0.1.0 introduces multiple breaking changes. Before updating **always** consolidate the +corresponding [documentation](/docs/migration/Version_0.0.x_0.1.x.md). + +### Added + +- control plane + extension ([data-plane-selector-client](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/v0.0.1-milestone-5/extensions/data-plane-selector/selector-client)) +- control plane extension([dataplane-selector-configuration](edc-extensions/dataplane-selector-configuration)) + +### Changed + +- update setting name (`edc.dataplane.token.validation.endpoint` -> `edc.dataplane.token.validation.endpoint`) + ## [0.0.6] - 2022-07-29 ### Fixed @@ -17,33 +30,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- EDC Health Checks for HashiCorp Vault +- EDC Health Checks for HashiCorp Vault ### Changed -- BusinessPartnerNumber constraint supports List structure -- Helm: Confidential EDC settings can be set using k8s secrets -- HashiCorp Vault API path configurable +- BusinessPartnerNumber constraint supports List structure +- Helm: Confidential EDC settings can be set using k8s secrets +- HashiCorp Vault API path configurable ## [0.0.4] - 2022-06-27 ### Added -- HashiCorp Vault Extension -- Control Plane with HashiCorp Vault and PostgreSQL support +- HashiCorp Vault Extension +- Control Plane with HashiCorp Vault and PostgreSQL support ### Changed -- Release Worklow now publishes Product EDC Extensions as Maven Artifacts +- Release Worklow now publishes Product EDC Extensions as Maven Artifacts ### Fixed -- [#1515](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1515) SQL: Connector sends out 50 contract offers max. +- [#1515](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1515) SQL: Connector sends out 50 + contract offers max. ### Removed -- CosmosDB Control Plane -- Control API Extension for all Control Planes +- CosmosDB Control Plane +- Control API Extension for all Control Planes ## [0.0.3] - 2022-05-23 diff --git a/NOTICE.md b/NOTICE.md index 25d95db3f..79c32c49b 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -167,7 +167,7 @@ The project maintains the following source code repositoriy: * JUnit Jupiter Params under Eclipse Public License v2.0 * JUnit Platform Commons under Eclipse Public License v2.0 * JUnit Platform Engine API under Eclipse Public License v2.0 -* junit-extension under Apache License, Version 2.0 +* junit under Apache License, Version 2.0 * LatencyUtils under Public Domain, per Creative Commons CC0 * lease-sql under Apache License, Version 2.0 * Logback Classic Module under Eclipse Public License - v 1.0 or GNU Lesser General Public License diff --git a/README.md b/README.md index 25fd1732c..c720d758f 100644 --- a/README.md +++ b/README.md @@ -28,27 +28,8 @@ Derivatives of the Data-Plane can be found here ## Prerequisites -#### EDC artifacts - -Since the [EDC](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) does -not yet publish artifacts to a maven repository, which this project relies on, it needs -to be built upfront to be used: - -```shell -git submodule update --init -cd edc && ./gradlew publishToMavenLocal -``` - ## Build ```shell ./mvnw package -Pwith-docker-image ``` - -## Releases - -### Milestone 3 - -The Catena-X milestone 3 release can be found in the `release/0.0.4` branch. - -https://github.com/catenax-ng/product-edc/releases/tag/0.0.4 diff --git a/deployment/helm/edc-controlplane/README.md b/deployment/helm/edc-controlplane/README.md index 25832092b..43f010d70 100644 --- a/deployment/helm/edc-controlplane/README.md +++ b/deployment/helm/edc-controlplane/README.md @@ -23,7 +23,7 @@ $ helm install my-release catenax-ng-product-edc/edc-controlplane --version 0.0. | autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | | autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | | autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | -| configuration.properties | string | `"# edc.api.auth.key=\n# edc.atomikos.checkpoint.interval=\n# edc.atomikos.directory=\n# edc.atomikos.logging=\n# edc.atomikos.threaded2pc=\n# edc.atomikos.timeout=\n# edc.aws.access.key=\n# edc.aws.provision.retry.retries.max=\n# edc.aws.provision.role.duration.session.max=\n# edc.aws.secret.access.key=\n# edc.blobstore.endpoint=\n# edc.controlplane.validation-endpoint=\n# edc.core.retry.backoff.max=\n# edc.core.retry.backoff.min=\n# edc.core.retry.retries.max=\n# edc.core.system.health.check.liveness-period=\n# edc.core.system.health.check.readiness-period=\n# edc.core.system.health.check.startup-period=\n# edc.core.system.health.check.threadpool-size=\n# edc.dataplane.queue.capacity=\n# edc.dataplane.wait=\n# edc.dataplane.workers=\n# edc.datasource.asset.name=\"default\"\n# edc.datasource.contractdefinition.name=\"default\"\n# edc.datasource.contractnegotiation.name=\"default\"\n# edc.datasource.policy.name=\"default\"\n# edc.datasource.transferprocess.name=\"default\"\n# edc.datasource.default.pool.maxIdleConnections=\n# edc.datasource.default.pool.maxTotalConnections=\n# edc.datasource.default.pool.minIdleConnections=\n# edc.datasource.default.pool.testConnectionOnBorrow=\n# edc.datasource.default.pool.testConnectionOnCreate=\n# edc.datasource.default.pool.testConnectionOnReturn=\n# edc.datasource.default.pool.testConnectionWhileIdle=\n# edc.datasource.default.pool.testQuery=\n# edc.datasource.default.url=\n# edc.datasource.default.user=\n# edc.datasource.default.password=\n# edc.dpf.selector.url=\n# edc.events.topic.endpoint=\n# edc.events.topic.name=\n# edc.fs.config=\n# edc.hostname=\n# edc.identity.did.url=\n# edc.ids.catalog.id=\n# edc.ids.curator=\n# edc.ids.description=\n# edc.ids.endpoint=\n# edc.ids.id=\n# edc.ids.maintainer=\n# edc.ids.security.profile=\n# edc.ids.title=\n# edc.ids.validation.referringconnector=\n# edc.ion.crawler.did-type=\n# edc.ion.crawler.interval-minutes=\n# edc.ion.crawler.ion.url=\n# edc.metrics.enabled=\n# edc.metrics.executor.enabled=\n# edc.metrics.jersey.enabled=\n# edc.metrics.jetty.enabled=\n# edc.metrics.okhttp.enabled=\n# edc.metrics.system.enabled=\n# edc.negotiation.consumer.state-machine.batch-size=\n# edc.negotiation.provider.state-machine.batch-size=\n# edc.oauth.client.id=\n# edc.oauth.private.key.alias=\n# edc.oauth.provider.audience=\n# edc.oauth.provider.jwks.refresh=\n# edc.oauth.provider.jwks.url=\n# edc.oauth.public.key.alias=\n# edc.oauth.token.url=\n# edc.oauth.validation.nbf.leeway=\n# edc.receiver.http.auth-code=\n# edc.receiver.http.auth-key=\n# edc.receiver.http.endpoint=\n# edc.transfer.proxy.endpoint=\n# edc.transfer.proxy.token.validity.seconds=\n# edc.transfer.proxy.token.signer.privatekey.alias=\n# edc.transfer.functions.check.endpoint=\n# edc.transfer.functions.enabled.protocols=\n# edc.transfer.functions.transfer.endpoint=\n# edc.transfer-process-store.database.name=\n# edc.transfer.state-machine.batch-size=\n# edc.vault=\n# edc.vault.certificate=\n# edc.vault.clientid=\n# edc.vault.clientsecret=\n# edc.vault.name=\n# edc.vault.tenantid=\n# edc.vault.hashicorp.url=\n# edc.vault.hashicorp.token=\n# edc.vault.hashicorp.timeout.seconds=\n# edc.webdid.doh.url=\n# edc.web.rest.cors.enabled=\n# edc.web.rest.cors.headers=\n# edc.web.rest.cors.methods=\n# edc.web.rest.cors.origins=\n# ids.webhook.address="` | EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) | +| configuration.properties | string | `"# edc.api.auth.key=\n# edc.atomikos.checkpoint.interval=\n# edc.atomikos.directory=\n# edc.atomikos.logging=\n# edc.atomikos.threaded2pc=\n# edc.atomikos.timeout=\n# edc.aws.access.key=\n# edc.aws.provision.retry.retries.max=\n# edc.aws.provision.role.duration.session.max=\n# edc.aws.secret.access.key=\n# edc.blobstore.endpoint=\n# edc.dataplane.token.validation.endpoint=\n# edc.core.retry.backoff.max=\n# edc.core.retry.backoff.min=\n# edc.core.retry.retries.max=\n# edc.core.system.health.check.liveness-period=\n# edc.core.system.health.check.readiness-period=\n# edc.core.system.health.check.startup-period=\n# edc.core.system.health.check.threadpool-size=\n# edc.dataplane.queue.capacity=\n# edc.dataplane.wait=\n# edc.dataplane.workers=\n# edc.datasource.asset.name=\"default\"\n# edc.datasource.contractdefinition.name=\"default\"\n# edc.datasource.contractnegotiation.name=\"default\"\n# edc.datasource.policy.name=\"default\"\n# edc.datasource.transferprocess.name=\"default\"\n# edc.datasource.default.pool.maxIdleConnections=\n# edc.datasource.default.pool.maxTotalConnections=\n# edc.datasource.default.pool.minIdleConnections=\n# edc.datasource.default.pool.testConnectionOnBorrow=\n# edc.datasource.default.pool.testConnectionOnCreate=\n# edc.datasource.default.pool.testConnectionOnReturn=\n# edc.datasource.default.pool.testConnectionWhileIdle=\n# edc.datasource.default.pool.testQuery=\n# edc.datasource.default.url=\n# edc.datasource.default.user=\n# edc.datasource.default.password=\n# edc.dpf.selector.url=\n# edc.events.topic.endpoint=\n# edc.events.topic.name=\n# edc.fs.config=\n# edc.hostname=\n# edc.identity.did.url=\n# edc.ids.catalog.id=\n# edc.ids.curator=\n# edc.ids.description=\n# edc.ids.endpoint=\n# edc.ids.id=\n# edc.ids.maintainer=\n# edc.ids.security.profile=\n# edc.ids.title=\n# edc.ids.validation.referringconnector=\n# edc.ion.crawler.did-type=\n# edc.ion.crawler.interval-minutes=\n# edc.ion.crawler.ion.url=\n# edc.metrics.enabled=\n# edc.metrics.executor.enabled=\n# edc.metrics.jersey.enabled=\n# edc.metrics.jetty.enabled=\n# edc.metrics.okhttp.enabled=\n# edc.metrics.system.enabled=\n# edc.negotiation.consumer.state-machine.batch-size=\n# edc.negotiation.provider.state-machine.batch-size=\n# edc.oauth.client.id=\n# edc.oauth.private.key.alias=\n# edc.oauth.provider.audience=\n# edc.oauth.provider.jwks.refresh=\n# edc.oauth.provider.jwks.url=\n# edc.oauth.public.key.alias=\n# edc.oauth.token.url=\n# edc.oauth.validation.nbf.leeway=\n# edc.receiver.http.auth-code=\n# edc.receiver.http.auth-key=\n# edc.receiver.http.endpoint=\n# edc.transfer.proxy.endpoint=\n# edc.transfer.proxy.token.validity.seconds=\n# edc.transfer.proxy.token.signer.privatekey.alias=\n# edc.transfer.functions.check.endpoint=\n# edc.transfer.functions.enabled.protocols=\n# edc.transfer.functions.transfer.endpoint=\n# edc.transfer-process-store.database.name=\n# edc.transfer.state-machine.batch-size=\n# edc.vault=\n# edc.vault.certificate=\n# edc.vault.clientid=\n# edc.vault.clientsecret=\n# edc.vault.name=\n# edc.vault.tenantid=\n# edc.vault.hashicorp.url=\n# edc.vault.hashicorp.token=\n# edc.vault.hashicorp.timeout.seconds=\n# edc.webdid.doh.url=\n# edc.web.rest.cors.enabled=\n# edc.web.rest.cors.headers=\n# edc.web.rest.cors.methods=\n# edc.web.rest.cors.origins=\n# ids.webhook.address="` | EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) | | edc.endpoints.control.path | string | `"/api/controlplane/control"` | The path mapping the "control" api is going to be exposed at | | edc.endpoints.control.port | string | `"9999"` | The network port, which the "control" api is going to be exposed by the container, pod and service | | edc.endpoints.data.path | string | `"/data"` | The path mapping the "data" management api is going to be exposed at | diff --git a/deployment/helm/edc-controlplane/values.yaml b/deployment/helm/edc-controlplane/values.yaml index 7dd99759b..b1605e6fd 100644 --- a/deployment/helm/edc-controlplane/values.yaml +++ b/deployment/helm/edc-controlplane/values.yaml @@ -251,7 +251,7 @@ configuration: # edc.aws.provision.role.duration.session.max= # edc.aws.secret.access.key= # edc.blobstore.endpoint= - # edc.controlplane.validation-endpoint= + # edc.dataplane.token.validation.endpoint= # edc.core.retry.backoff.max= # edc.core.retry.backoff.min= # edc.core.retry.retries.max= diff --git a/deployment/helm/edc-dataplane/README.md b/deployment/helm/edc-dataplane/README.md index 96108c59c..5c300960e 100644 --- a/deployment/helm/edc-dataplane/README.md +++ b/deployment/helm/edc-dataplane/README.md @@ -23,7 +23,7 @@ $ helm install my-release catenax-ng-product-edc/edc-dataplane --version 0.0.6 | autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | | autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | | autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | -| configuration.properties | string | `"# edc.atomikos.checkpoint.interval=\n# edc.atomikos.directory=\n# edc.atomikos.logging=\n# edc.atomikos.threaded2pc=\n# edc.atomikos.timeout=\n# edc.aws.access.key=\n# edc.aws.provision.retry.retries.max=\n# edc.aws.provision.role.duration.session.max=\n# edc.aws.secret.access.key=\n# edc.blobstore.endpoint=\n# edc.controlplane.validation-endpoint=\n# edc.core.retry.backoff.max=\n# edc.core.retry.backoff.min=\n# edc.core.retry.retries.max=\n# edc.core.system.health.check.liveness-period=\n# edc.core.system.health.check.readiness-period=\n# edc.core.system.health.check.startup-period=\n# edc.core.system.health.check.threadpool-size=\n# edc.dataplane.queue.capacity=\n# edc.dataplane.wait=\n# edc.dataplane.workers=\n# edc.datasource.asset.name=\"default\"\n# edc.datasource.contractdefinition.name=\"default\"\n# edc.datasource.contractnegotiation.name=\"default\"\n# edc.datasource.policy.name=\"default\"\n# edc.datasource.transferprocess.name=\"default\"\n# edc.datasource.default.pool.maxIdleConnections=\n# edc.datasource.default.pool.maxTotalConnections=\n# edc.datasource.default.pool.minIdleConnections=\n# edc.datasource.default.pool.testConnectionOnBorrow=\n# edc.datasource.default.pool.testConnectionOnCreate=\n# edc.datasource.default.pool.testConnectionOnReturn=\n# edc.datasource.default.pool.testConnectionWhileIdle=\n# edc.datasource.default.pool.testQuery=\n# edc.datasource.default.url=\n# edc.datasource.default.user=\n# edc.datasource.default.password=\n# edc.dpf.selector.url=\n# edc.events.topic.endpoint=\n# edc.events.topic.name=\n# edc.fs.config=\n# edc.hostname=\n# edc.identity.did.url=\n# edc.ids.catalog.id=\n# edc.ids.curator=\n# edc.ids.description=\n# edc.ids.endpoint=\n# edc.ids.id=\n# edc.ids.maintainer=\n# edc.ids.security.profile=\n# edc.ids.title=\n# edc.ids.validation.referringconnector=\n# edc.ion.crawler.did-type=\n# edc.ion.crawler.interval-minutes=\n# edc.ion.crawler.ion.url=\n# edc.metrics.enabled=\n# edc.metrics.executor.enabled=\n# edc.metrics.jersey.enabled=\n# edc.metrics.jetty.enabled=\n# edc.metrics.okhttp.enabled=\n# edc.metrics.system.enabled=\n# edc.negotiation.consumer.state-machine.batch-size=\n# edc.negotiation.provider.state-machine.batch-size=\n# edc.oauth.client.id=\n# edc.oauth.private.key.alias=\n# edc.oauth.provider.audience=\n# edc.oauth.provider.jwks.refresh=\n# edc.oauth.provider.jwks.url=\n# edc.oauth.public.key.alias=\n# edc.oauth.token.url=\n# edc.oauth.validation.nbf.leeway=\n# edc.receiver.http.auth-code=\n# edc.receiver.http.auth-key=\n# edc.receiver.http.endpoint=\n# edc.transfer.functions.check.endpoint=\n# edc.transfer.functions.enabled.protocols=\n# edc.transfer.functions.transfer.endpoint=\n# edc.transfer-process-store.database.name=\n# edc.transfer.state-machine.batch-size=\n# edc.vault=\n# edc.vault.certificate=\n# edc.vault.clientid=\n# edc.vault.clientsecret=\n# edc.vault.name=\n# edc.vault.tenantid=\n# edc.vault.hashicorp.url=\n# edc.vault.hashicorp.token=\n# edc.vault.hashicorp.timeout.seconds=\n# edc.webdid.doh.url=\n# edc.web.rest.cors.enabled=\n# edc.web.rest.cors.headers=\n# edc.web.rest.cors.methods=\n# edc.web.rest.cors.origins="` | EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) | +| configuration.properties | string | `"# edc.atomikos.checkpoint.interval=\n# edc.atomikos.directory=\n# edc.atomikos.logging=\n# edc.atomikos.threaded2pc=\n# edc.atomikos.timeout=\n# edc.aws.access.key=\n# edc.aws.provision.retry.retries.max=\n# edc.aws.provision.role.duration.session.max=\n# edc.aws.secret.access.key=\n# edc.blobstore.endpoint=\n# edc.dataplane.token.validation.endpoint=\n# edc.core.retry.backoff.max=\n# edc.core.retry.backoff.min=\n# edc.core.retry.retries.max=\n# edc.core.system.health.check.liveness-period=\n# edc.core.system.health.check.readiness-period=\n# edc.core.system.health.check.startup-period=\n# edc.core.system.health.check.threadpool-size=\n# edc.dataplane.queue.capacity=\n# edc.dataplane.wait=\n# edc.dataplane.workers=\n# edc.datasource.asset.name=\"default\"\n# edc.datasource.contractdefinition.name=\"default\"\n# edc.datasource.contractnegotiation.name=\"default\"\n# edc.datasource.policy.name=\"default\"\n# edc.datasource.transferprocess.name=\"default\"\n# edc.datasource.default.pool.maxIdleConnections=\n# edc.datasource.default.pool.maxTotalConnections=\n# edc.datasource.default.pool.minIdleConnections=\n# edc.datasource.default.pool.testConnectionOnBorrow=\n# edc.datasource.default.pool.testConnectionOnCreate=\n# edc.datasource.default.pool.testConnectionOnReturn=\n# edc.datasource.default.pool.testConnectionWhileIdle=\n# edc.datasource.default.pool.testQuery=\n# edc.datasource.default.url=\n# edc.datasource.default.user=\n# edc.datasource.default.password=\n# edc.dpf.selector.url=\n# edc.events.topic.endpoint=\n# edc.events.topic.name=\n# edc.fs.config=\n# edc.hostname=\n# edc.identity.did.url=\n# edc.ids.catalog.id=\n# edc.ids.curator=\n# edc.ids.description=\n# edc.ids.endpoint=\n# edc.ids.id=\n# edc.ids.maintainer=\n# edc.ids.security.profile=\n# edc.ids.title=\n# edc.ids.validation.referringconnector=\n# edc.ion.crawler.did-type=\n# edc.ion.crawler.interval-minutes=\n# edc.ion.crawler.ion.url=\n# edc.metrics.enabled=\n# edc.metrics.executor.enabled=\n# edc.metrics.jersey.enabled=\n# edc.metrics.jetty.enabled=\n# edc.metrics.okhttp.enabled=\n# edc.metrics.system.enabled=\n# edc.negotiation.consumer.state-machine.batch-size=\n# edc.negotiation.provider.state-machine.batch-size=\n# edc.oauth.client.id=\n# edc.oauth.private.key.alias=\n# edc.oauth.provider.audience=\n# edc.oauth.provider.jwks.refresh=\n# edc.oauth.provider.jwks.url=\n# edc.oauth.public.key.alias=\n# edc.oauth.token.url=\n# edc.oauth.validation.nbf.leeway=\n# edc.receiver.http.auth-code=\n# edc.receiver.http.auth-key=\n# edc.receiver.http.endpoint=\n# edc.transfer.functions.check.endpoint=\n# edc.transfer.functions.enabled.protocols=\n# edc.transfer.functions.transfer.endpoint=\n# edc.transfer-process-store.database.name=\n# edc.transfer.state-machine.batch-size=\n# edc.vault=\n# edc.vault.certificate=\n# edc.vault.clientid=\n# edc.vault.clientsecret=\n# edc.vault.name=\n# edc.vault.tenantid=\n# edc.vault.hashicorp.url=\n# edc.vault.hashicorp.token=\n# edc.vault.hashicorp.timeout.seconds=\n# edc.webdid.doh.url=\n# edc.web.rest.cors.enabled=\n# edc.web.rest.cors.headers=\n# edc.web.rest.cors.methods=\n# edc.web.rest.cors.origins="` | EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) | | edc.endpoints.control.path | string | `"/api/dataplane/control"` | The path mapping the "control" api is going to be exposed by | | edc.endpoints.control.port | string | `"9999"` | The network port, which the "control" api is going to be exposed by the container, pod and service | | edc.endpoints.default.path | string | `"/api"` | The path mapping the "default" api is going to be exposed by | diff --git a/deployment/helm/edc-dataplane/values.yaml b/deployment/helm/edc-dataplane/values.yaml index 3d3ee7c68..48d18fb96 100644 --- a/deployment/helm/edc-dataplane/values.yaml +++ b/deployment/helm/edc-dataplane/values.yaml @@ -217,7 +217,7 @@ configuration: # edc.aws.provision.role.duration.session.max= # edc.aws.secret.access.key= # edc.blobstore.endpoint= - # edc.controlplane.validation-endpoint= + # edc.dataplane.token.validation.endpoint= # edc.core.retry.backoff.max= # edc.core.retry.backoff.min= # edc.core.retry.retries.max= diff --git a/docs/README.md b/docs/README.md index f0544aaeb..aeb3e9d6d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -37,6 +37,7 @@ The three supported setups are. **This Repository** +- [Update EDC Version from 0.0.x - 0.1.x](migration/Version_0.0.x_0.1.x.md) - [Application: Control Plane](../edc-controlplane) - [Application: Data Plane](../edc-dataplane) - [Extension: Business Partner Numbers](../edc-extensions/business-partner-validation/README.md) diff --git a/docs/data-transfer/Transfer Data.md b/docs/data-transfer/Transfer Data.md index 752b94117..dd945fbf6 100644 --- a/docs/data-transfer/Transfer Data.md +++ b/docs/data-transfer/Transfer Data.md @@ -66,15 +66,15 @@ For simplicity `https://jsonplaceholder.typicode.com/todos/1` is used as data so other API, that is reachable from the Provider Data Plane. ```bash -curl -X POST "$PLATO_DATAMGMT_URL/data/assets" --header "X-Api-Key: password" --header "Content-Type: application/json" --data "{ \"asset\": { \"properties\": { \"asset:prop:id\": \"1\", \"asset:prop:description\": \"Product EDC Demo Asset\" } }, \"dataAddress\": { \"properties\": { \"type\": \"HttpData\", \"endpoint\": \"https://jsonplaceholder.typicode.com/todos/1\" } } }" -s -o /dev/null -w 'Response Code: %{http_code}\n' +curl -X POST "$PLATO_DATAMGMT_URL/data/assets" --header "X-Api-Key: password" --header "Content-Type: application/json" --data "{ \"asset\": { \"properties\": { \"asset:prop:id\": \"1\", \"asset:prop:description\": \"Product EDC Demo Asset\" } }, \"dataAddress\": { \"properties\": { \"type\": \"HttpData\", \"baseUrl\": \"https://jsonplaceholder.typicode.com/todos/1\" } } }" -s -o /dev/null -w 'Response Code: %{http_code}\n' ``` ```bash -curl -X POST "$PLATO_DATAMGMT_URL/data/policies" --header "X-Api-Key: password" --header "Content-Type: application/json" --data "{ \"uid\": \"1\", \"prohibitions\": [], \"obligations\": [], \"permissions\": [ { \"edctype\": \"dataspaceconnector:permission\", \"action\": { \"type\": \"USE\" }, \"constraints\": [ { \"edctype\": \"AtomicConstraint\", \"leftExpression\": { \"edctype\": \"dataspaceconnector:literalexpression\", \"value\": \"BusinessPartnerNumber\" }, \"rightExpression\": { \"edctype\": \"dataspaceconnector:literalexpression\", \"value\": \"BPNSOKRATES\" }, \"operator\": \"EQ\" } ] } ] }" -s -o /dev/null -w 'Response Code: %{http_code}\n' +curl -X POST "$PLATO_DATAMGMT_URL/data/policydefinitions" --header "X-Api-Key: password" --header "Content-Type: application/json" --data "{ \"uid\": \"1\", \"policy\": { \"prohibitions\": [], \"obligations\": [], \"permissions\": [ { \"edctype\": \"dataspaceconnector:permission\", \"action\": { \"type\": \"USE\" }, \"constraints\": [] } ] } }" -s -o /dev/null -w 'Response Code: %{http_code}\n' ``` ```bash -curl -X POST "$PLATO_DATAMGMT_URL/data/contractdefinitions" --header "X-Api-Key: password" --header "Content-Type: application/json" --data "{ \"id\": \"1\", \"criteria\": [ { \"left\": \"asset:prop:id\", \"op\": \"=\", \"right\": \"1\" } ], \"accessPolicyId\": \"1\", \"contractPolicyId\": \"1\" }" -s -o /dev/null -w 'Response Code: %{http_code}\n' +curl -X POST "$PLATO_DATAMGMT_URL/data/contractdefinitions" --header "X-Api-Key: password" --header "Content-Type: application/json" --data "{ \"id\": \"1\", \"criteria\": [ { \"operandLeft\": \"asset:prop:id\", \"operator\": \"=\", \"operandRight\": \"1\" } ], \"accessPolicyId\": \"1\", \"contractPolicyId\": \"1\" }" -s -o /dev/null -w 'Response Code: %{http_code}\n' ``` ## 2. Request Contract Offer Catalog diff --git a/docs/migration/Version_0.0.x_0.1.x.md b/docs/migration/Version_0.0.x_0.1.x.md new file mode 100644 index 000000000..245172067 --- /dev/null +++ b/docs/migration/Version_0.0.x_0.1.x.md @@ -0,0 +1,294 @@ +# Migration Version 0.0.x to 0.1.x + +This document contains a list of breaking changes that are introduced in version 0.1.x. + +## 0. Summary + +1. PostgreSQL Database + 1. Criteria in Policy & Contract Definitions Table + 2. Delete Contract Agreements +2. Data Management API + 1. Policy Path + 2. Policy Payload + 3. Criteria in Payload of Contract Definitions and Policies +3. Connector Configuration + 1. Token Validation Endpoint Setting + +## 1. PostgreSQL Database + +The Product EDC [PostgreSQL Migration Extension](../../edc-extensions/postgresql-migration/README.md) is able to run +normal migrations. But the extension will never cause a data loss automatically, therefore part of this migration must +be done by the user itself. + +### 1.1 Criteria in Policy & Contract Definitions Table + +Criteria in Policies and Contract Definitions are serialized as JSON and put into the database. The Criteria schema +changed and already existing database entries will cause _NullPointerExceptions_. + + + +

+ Example Exception + +#### Example Exception + +``` +[2022-08-02 09:32:37] [SEVERE ] Could not handle multipart request: null +org.eclipse.dataspaceconnector.spi.EdcException + at org.eclipse.dataspaceconnector.transaction.local.LocalTransactionContext.execute(LocalTransactionContext.java:70) + at org.eclipse.dataspaceconnector.sql.assetindex.SqlAssetIndex.queryAssets(SqlAssetIndex.java:141) + at org.eclipse.dataspaceconnector.sql.assetindex.SqlAssetIndex.queryAssets(SqlAssetIndex.java:134) + at org.eclipse.dataspaceconnector.contract.offer.ContractOfferServiceImpl.lambda$queryContractOffers$2(ContractOfferServiceImpl.java:61) + at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:271) + at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:177) + at java.base/java.util.LinkedList$LLSpliterator.forEachRemaining(LinkedList.java:1239) + at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484) + at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474) + at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913) + at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) + at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578) + at org.eclipse.dataspaceconnector.ids.core.service.CatalogServiceImpl.getDataCatalog(CatalogServiceImpl.java:55) + at org.eclipse.dataspaceconnector.ids.core.service.ConnectorServiceImpl.getConnector(ConnectorServiceImpl.java:51) + at org.eclipse.dataspaceconnector.ids.api.multipart.handler.description.ConnectorDescriptionRequestHandler.handle(ConnectorDescriptionRequestHandler.java:74) + at org.eclipse.dataspaceconnector.ids.api.multipart.handler.DescriptionHandler.handleRequestInternal(DescriptionHandler.java:117) + at org.eclipse.dataspaceconnector.ids.api.multipart.handler.DescriptionHandler.handleRequest(DescriptionHandler.java:82) + at org.eclipse.dataspaceconnector.ids.api.multipart.controller.MultipartController.request(MultipartController.java:146) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:566) + at org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory.lambda$static$0(ResourceMethodInvocationHandlerFactory.java:52) + at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:124) + at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:167) + at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$ResponseOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:176) + at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:79) + at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:475) + at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:397) + at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:81) + at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:255) + at org.glassfish.jersey.internal.Errors$1.call(Errors.java:248) + at org.glassfish.jersey.internal.Errors$1.call(Errors.java:244) + at org.glassfish.jersey.internal.Errors.process(Errors.java:292) + at org.glassfish.jersey.internal.Errors.process(Errors.java:274) + at org.glassfish.jersey.internal.Errors.process(Errors.java:244) + at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:265) + at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:234) + at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:684) + at org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:394) + at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:346) + at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:358) + at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:311) + at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:205) + at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:764) + at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:508) + at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:221) + at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1375) + at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:176) + at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:463) + at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:174) + at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1297) + at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:129) + at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:192) + at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122) + at org.eclipse.jetty.server.Server.handle(Server.java:562) + at org.eclipse.jetty.server.HttpChannel.lambda$handle$0(HttpChannel.java:505) + at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:762) + at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:497) + at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:282) + at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:319) + at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:100) + at org.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53) + at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.runTask(AdaptiveExecutionStrategy.java:412) + at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.consumeTask(AdaptiveExecutionStrategy.java:381) + at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.tryProduce(AdaptiveExecutionStrategy.java:268) + at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.produce(AdaptiveExecutionStrategy.java:190) + at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:894) + at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1038) + at java.base/java.lang.Thread.run(Thread.java:829) +Caused by: java.lang.NullPointerException + at org.eclipse.dataspaceconnector.sql.translation.SqlConditionExpression.isValidExpression(SqlConditionExpression.java:53) + at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195) + at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1655) + at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484) + at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474) + at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913) + at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) + at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578) + at org.eclipse.dataspaceconnector.sql.assetindex.schema.BaseSqlDialectStatements.createQuery(BaseSqlDialectStatements.java:108) + at org.eclipse.dataspaceconnector.sql.assetindex.SqlAssetIndex.lambda$queryAssets$2(SqlAssetIndex.java:144) + at org.eclipse.dataspaceconnector.transaction.local.LocalTransactionContext.execute(LocalTransactionContext.java:63) + ... 69 more +``` + +
+ +
+ + Solution 1: Update all Criteria manually + +#### Update all Criteria manually + +Root of this issue is that the operator, left- and right-operand Criteria field names changed. + +| Old | New | +|:----------|:-------------| +| left | operandLeft | +| right | operandRight | +| op | operator | + +It is possible to resolve this issue by updating the content of the column, that contain JSON serialized constraints, +from + +``` +{"criteria":[{"left":"asset:prop:id","op":"=","right":"asset-1"}]} +``` + +to + +``` +{"criteria":[{"operandLeft":"asset:prop:id","operator":"=","operandRight":"asset-1"}]} +``` + +
+ +
+ + Solution 2: Delete all rows containing Constraints + +#### Delete all rows containing Criteria + +Instead of updating each row in the database it's also possible to delete all Contract Definitions and Policies. +Additionally it's necessary to delete all Negotiations, as they might reference existing Contract Definitions and/or +Policies. + +Theoretically it's also necessary to delete Contract Agreements. As their deletion is already described in another +section, we can skip them here. + +**Required Queries** + +```sql +DELETE +FROM edc_contract_negotiation; +``` + +```sql +DELETE +FROM edc_contract_definitions; +``` + +```sql +DELETE +FROM edc_policydefinitins; +``` + +
+ +### 1.2 Delete Contract Agreements + +In the new version contract agreement rows contain a serialized policy at the time, the contract was concluded. +With the EDC update all existing Contract Agreements must be deleted. + +
+ Required Query + +```sql +DELETE +FROM edc_contract_agreement; +``` + +
+ +## 2. Data Management API + +It might be necessary to update applications and scripts that use the Data Management API. This section covers the most +important changes in endpoints and payloads. + +### 2.1 Policy Path + +The Data Management API Path for Policies changes from +`/policies` to `/policydefinitions`. + +
+ Example Call + +#### Get All Policies + +```bash +curl -X GET "${DATA_MGMT_ENDPOINT}/data/policydefinitions" --header "X-Api-Key: " --header "Content-Type: application/json" +``` + +
+ +### 2.2 Policy Payload + +The Policy Payload now wraps the policy details in an additional policy object. + +
+ +Payload Comparison + +**New Payload** + +```json +{ + "uid": "1", + "policy": { + "prohibitions": [], + "obligations": [], + "permissions": [] + } +} +``` + +**Old Payload** + +```json +{ + "uid": "1", + "prohibitions": [], + "obligations": [], + "permissions": [] +} +``` + +
+ +### 2.3 Criteria in Payload of Contract Definitions and Policies + +The payload of a Policy or a Contract Definition may contain one or more Criteria. The format of these serialized Criteria changed. +Please note that there is no input validation, that detects errors when the old Criteria format is used! + +
+ +Criterion Format Change + +**Old Criterion Format** +``` +{ "left": "asset:prop:id", "op": "=", "right": "1" } +``` + +**New Criterion Format** +``` +{ "operandLeft": "asset:prop:id", "operator": "=", "operandRight": "1" } +``` + +**Example Call** + +```bash +curl -X POST "${DATA_MGMT_ENDPOINT}/data/contractdefinitions" --header "X-Api-Key: " --header "Content-Type: application/json" --data "{ \"id\": \"1\", \"criteria\": [ { \"operandLeft\": \"asset:prop:id\", \"operator\": \"=\", \"operandRight\": \"1\" } ], \"accessPolicyId\": \"1\", \"contractPolicyId\": \"1\" }" +``` + +
+ +## 3. Connector Configuration + +### 3.1 Token Validation Endpoint Setting + +In the past the token validation endpoint was configured in `edc.controlplane.validation-endpoint`. This setting key +must be renamed to `edc.dataplane.token.validation.endpoint`. + + + +# TODO +- changed HttpData endpoint to baseUrl +- \ No newline at end of file diff --git a/edc b/edc deleted file mode 160000 index 1b3f3a86e..000000000 --- a/edc +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1b3f3a86e3cef781d325788a30bf32efb18af98b diff --git a/edc-controlplane/edc-controlplane-base/pom.xml b/edc-controlplane/edc-controlplane-base/pom.xml index bc2adc767..8d8ae0085 100644 --- a/edc-controlplane/edc-controlplane-base/pom.xml +++ b/edc-controlplane/edc-controlplane-base/pom.xml @@ -42,6 +42,10 @@ net.catenax.edc.extensions business-partner-validation
+ + net.catenax.edc.extensions + dataplane-selector-configuration + @@ -118,6 +122,18 @@ org.eclipse.dataspaceconnector data-plane-transfer-sync + + org.eclipse.dataspaceconnector + data-plane-selector-core + + + org.eclipse.dataspaceconnector + data-plane-selector-client + + + org.eclipse.dataspaceconnector + data-plane-selector-store + diff --git a/edc-controlplane/pom.xml b/edc-controlplane/pom.xml index 807015ec6..912f41cb6 100644 --- a/edc-controlplane/pom.xml +++ b/edc-controlplane/pom.xml @@ -30,9 +30,7 @@ edc-controlplane-base - edc-controlplane-memory - edc-controlplane-postgresql edc-controlplane-postgresql-hashicorp-vault diff --git a/edc-dataplane/edc-dataplane-azure-vault/README.md b/edc-dataplane/edc-dataplane-azure-vault/README.md index c3947ca8e..490bd0002 100644 --- a/edc-dataplane/edc-dataplane-azure-vault/README.md +++ b/edc-dataplane/edc-dataplane-azure-vault/README.md @@ -26,7 +26,7 @@ Details regarding each configuration property can be found at the [documentary s | edc.vault.tenantid | X | 55555555-6666-7777-8888-999999999999 | | | edc.vault.name | X | my-vault-name | | | edc.vault.clientsecret | X | 34-chars-secret | | -| edc.controlplane.validation-endpoint | X | http://controlplane:8182/validation/token | | +| edc.dataplane.token.validation.endpoint | X | http://controlplane:8182/validation/token | | #### Example configuration.properties @@ -45,7 +45,7 @@ web.http.control.port=9999 web.http.control.path=/api/dataplane/control # Validation endpoint of controlplane -edc.controlplane.validation-endpoint=http://controlplane:8182/validation/token +edc.dataplane.token.validation.endpoint=http://controlplane:8182/validation/token # EDC hostname edc.hostname=localhost diff --git a/edc-dataplane/edc-dataplane-hashicorp-vault/README.md b/edc-dataplane/edc-dataplane-hashicorp-vault/README.md index 22a8d4d60..45b196572 100644 --- a/edc-dataplane/edc-dataplane-hashicorp-vault/README.md +++ b/edc-dataplane/edc-dataplane-hashicorp-vault/README.md @@ -25,7 +25,7 @@ Details regarding each configuration property can be found at the [documentary s | edc.vault.hashicorp.url | X | http://vault | | | edc.vault.hashicorp.token | X | 55555555-6666-7777-8888-999999999999 | | | edc.vault.hashicorp.timeout.seconds | | 30 | | -| edc.controlplane.validation-endpoint | X | http://controlplane:8182/validation/token | | +| edc.dataplane.token.validation.endpoint | X | http://controlplane:8182/validation/token | | #### Example configuration.properties @@ -44,7 +44,7 @@ web.http.control.port=9999 web.http.control.path=/api/dataplane/control # Validation endpoint of controlplane -edc.controlplane.validation-endpoint=http://controlplane:8182/validation/token +edc.dataplane.token.validation.endpoint=http://controlplane:8182/validation/token # EDC hostname edc.hostname=localhost diff --git a/edc-extensions/business-partner-validation/pom.xml b/edc-extensions/business-partner-validation/pom.xml index ddcb0f2a8..8b89bf6bd 100644 --- a/edc-extensions/business-partner-validation/pom.xml +++ b/edc-extensions/business-partner-validation/pom.xml @@ -45,21 +45,6 @@ - - - - org.apache.maven.plugins - maven-source-plugin - - - attach-sources - - jar - - - - - diff --git a/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java b/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java index c38101c7d..47466b2d6 100644 --- a/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java +++ b/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java @@ -79,13 +79,19 @@ protected boolean evaluate( } final ParticipantAgent participantAgent = policyContext.getParticipantAgent(); - final Map claims = participantAgent.getClaims(); + final Map claims = participantAgent.getClaims(); if (!claims.containsKey(REFERRING_CONNECTOR_CLAIM)) { return false; } - String referringConnectorClaim = claims.get(REFERRING_CONNECTOR_CLAIM); + Object referringConnectorClaimObject = claims.get(REFERRING_CONNECTOR_CLAIM); + String referringConnectorClaim = null; + + if (referringConnectorClaimObject instanceof String) { + referringConnectorClaim = (String) referringConnectorClaimObject; + } + if (referringConnectorClaim == null || referringConnectorClaim.isEmpty()) { return false; } diff --git a/edc-extensions/dataplane-selector-configuration/README.md b/edc-extensions/dataplane-selector-configuration/README.md new file mode 100644 index 000000000..5cafbc8db --- /dev/null +++ b/edc-extensions/dataplane-selector-configuration/README.md @@ -0,0 +1,17 @@ +# Data Plane Selector Configuration Exception + +This control-plane extension makes it possible configure one or more data planes. After a data transfer is +triggered at the control plane will look for a data plane with matching capabilities. + +## Configuration + +Per data plane instance the following settings must be configured. As `` any unique string is valid. + +| Key | Description | Mandatory | Example | +|:--------------------------------------------------------|:--------------------------------------------------|-----------|-------------------------------------------------------------------| +| edc.dataplane.selector..url | URL to connect to the Data Plane Instance. | X | http://plato-edc-dataplane:9999/api/dataplane/control | +| edc.dataplane.selector..sourcetypes | Source Types in a comma separated List. | X | HttpData | +| edc.dataplane.selector..destinationtypes | Destination Types in a comma separated List. | X | HttpProxy | +| edc.dataplane.selector..properties | Additional properties of the Data Plane Instance. | (X) | { "publicApiUrl:": "http://plato-edc-dataplane:8185/api/public" } | + +The property `publicApiUrl` is mandatory for Data Plane Instances with destination type `HttpProxy`. \ No newline at end of file diff --git a/edc-extensions/dataplane-selector-configuration/pom.xml b/edc-extensions/dataplane-selector-configuration/pom.xml new file mode 100644 index 000000000..914bda0a6 --- /dev/null +++ b/edc-extensions/dataplane-selector-configuration/pom.xml @@ -0,0 +1,74 @@ + + + + + edc-extensions + net.catenax.edc.extensions + 0.0.7-SNAPSHOT + + 4.0.0 + + dataplane-selector-configuration + jar + + + ${project.groupId}_${project.artifactId} + + + + + + src/main/resources + + **/* + + + + ../../ + META-INF + + NOTICE.md + LICENSE + + + + + + + + org.eclipse.dataspaceconnector + core-spi + + + org.eclipse.dataspaceconnector + data-plane-selector-spi + + + + + org.eclipse.dataspaceconnector + junit + test + + + org.eclipse.dataspaceconnector + junit + test-fixtures + test + + + diff --git a/edc-extensions/dataplane-selector-configuration/src/main/java/net/catenax/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtension.java b/edc-extensions/dataplane-selector-configuration/src/main/java/net/catenax/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtension.java new file mode 100644 index 000000000..cd9a04271 --- /dev/null +++ b/edc-extensions/dataplane-selector-configuration/src/main/java/net/catenax/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtension.java @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial implementation + * + */ + +package net.catenax.edc.dataplane.selector.configuration; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import org.eclipse.dataspaceconnector.dataplane.selector.DataPlaneSelectorService; +import org.eclipse.dataspaceconnector.dataplane.selector.instance.DataPlaneInstanceImpl; +import org.eclipse.dataspaceconnector.spi.EdcException; +import org.eclipse.dataspaceconnector.spi.EdcSetting; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.system.Requires; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtension; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; +import org.eclipse.dataspaceconnector.spi.system.configuration.Config; + +/** + * This Extension provides functionality to read materialized DataPlane instances from the + * configuration file and add those to the DataPlaneSelectorService during + * configuration/initialization phase of the connector. + * + *

Following configuration keys are made available: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
KeyDescriptionMandatoryExample
edc.dataplane.selector..urlURL to connect to the Data Plane Instance.Xhttp://localhost:8181/control/transfer
edc.dataplane.selector..sourcetypesSource Types in a comma separated List.XHttpData
edc.dataplane.selector..destinationtypesDestination Types in a comma separated List.XHttpProxy
edc.dataplane.selector..propertiesAdditional properties of the Data Plane Instance.(X){ "publicApiUrl:": "http://localhost:8181/api/public" }
+ */ +@Requires({DataPlaneSelectorService.class}) +public class DataPlaneSelectorConfigurationServiceExtension implements ServiceExtension { + + @EdcSetting public static final String CONFIG_PREFIX = "edc.dataplane.selector"; + @EdcSetting public static final String URL_SUFFIX = "url"; + @EdcSetting public static final String DESTINATION_TYPES_SUFFIX = "destinationtypes"; + @EdcSetting public static final String SOURCE_TYPES_SUFFIX = "sourcetypes"; + @EdcSetting public static final String PROPERTIES_SUFFIX = "properties"; + @EdcSetting public static final String PUBLIC_API_URL_PROPERTY = "publicApiUrl"; + + private static final String NAME = "Data Plane Selector Configuration Extension"; + private static final String COMMA = ","; + private static final String LOG_MISSING_CONFIGURATION = + NAME + ": Missing configuration for " + CONFIG_PREFIX + ".%s.%s"; + private static final String LOG_SKIP_BC_MISSING_CONFIGURATION = + NAME + ": Configuration issues. Skip registering of Data Plane Instance '%s'"; + private static final String LOG_REGISTERED = + NAME + + ": Registered Data Plane Instance. (id=%s, url=%s, sourceTypes=%s, destinationTypes=%s, properties=)"; + + private Monitor monitor; + private DataPlaneSelectorService dataPlaneSelectorService; + + @Override + public String name() { + return NAME; + } + + @Override + public void initialize(final ServiceExtensionContext serviceExtensionContext) { + this.dataPlaneSelectorService = + serviceExtensionContext.getService(DataPlaneSelectorService.class); + this.monitor = serviceExtensionContext.getMonitor(); + + final Config config = serviceExtensionContext.getConfig(CONFIG_PREFIX); + + config.partition().forEach(this::configureDataPlaneInstance); + } + + private void configureDataPlaneInstance(final Config config) { + final String id = config.currentNode(); + + final String url = config.getString(URL_SUFFIX, ""); + final List sourceTypes = + Arrays.stream(config.getString(SOURCE_TYPES_SUFFIX, "").split(COMMA)) + .map(String::trim) + .filter(Predicate.not(String::isEmpty)) + .distinct() + .collect(Collectors.toList()); + final List destinationTypes = + Arrays.stream(config.getString(DESTINATION_TYPES_SUFFIX, "").split(COMMA)) + .map(String::trim) + .filter(Predicate.not(String::isEmpty)) + .distinct() + .collect(Collectors.toList()); + final String propertiesJson = config.getString(PROPERTIES_SUFFIX, "{}"); + + if (url.isEmpty()) { + monitor.warning(String.format(LOG_MISSING_CONFIGURATION, id, URL_SUFFIX)); + } + + if (sourceTypes.isEmpty()) { + monitor.warning(String.format(LOG_MISSING_CONFIGURATION, id, SOURCE_TYPES_SUFFIX)); + } + + if (destinationTypes.isEmpty()) { + monitor.warning(String.format(LOG_MISSING_CONFIGURATION, id, DESTINATION_TYPES_SUFFIX)); + } + + final Map properties; + try { + ObjectMapper mapper = new ObjectMapper(); + properties = mapper.readValue(propertiesJson, new TypeReference>() {}); + } catch (JsonProcessingException e) { + throw new EdcException(e); + } + + final boolean missingPublicApiProperty = !properties.containsKey(PUBLIC_API_URL_PROPERTY); + if (missingPublicApiProperty) { + monitor.warning( + String.format(LOG_MISSING_CONFIGURATION, id, PROPERTIES_SUFFIX) + + "." + + PUBLIC_API_URL_PROPERTY); + } + + final boolean invalidConfiguration = + url.isEmpty() || sourceTypes.isEmpty() || destinationTypes.isEmpty(); + if (invalidConfiguration || missingPublicApiProperty) { + monitor.warning(String.format(LOG_SKIP_BC_MISSING_CONFIGURATION, id)); + return; + } + + final DataPlaneInstanceImpl.Builder builder = + DataPlaneInstanceImpl.Builder.newInstance().id(id).url(url); + + sourceTypes.forEach(builder::allowedSourceType); + destinationTypes.forEach(builder::allowedDestType); + properties.forEach(builder::property); + + dataPlaneSelectorService.addInstance(builder.build()); + + monitor.debug( + String.format( + LOG_REGISTERED, + id, + url, + String.join(", ", sourceTypes), + String.join(", ", destinationTypes))); + } +} diff --git a/edc-extensions/hashicorp-vault/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.VaultExtension b/edc-extensions/dataplane-selector-configuration/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension similarity index 79% rename from edc-extensions/hashicorp-vault/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.VaultExtension rename to edc-extensions/dataplane-selector-configuration/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension index 5e8489869..f23f403dd 100644 --- a/edc-extensions/hashicorp-vault/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.VaultExtension +++ b/edc-extensions/dataplane-selector-configuration/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension @@ -10,4 +10,6 @@ # Contributors: # Mercedes-Benz Tech Innovation GmbH - Initial ServiceExtension file # -net.catenax.edc.hashicorpvault.HashicorpVaultVaultExtension +# + +net.catenax.edc.dataplane.selector.configuration.DataPlaneSelectorConfigurationServiceExtension diff --git a/edc-extensions/dataplane-selector-configuration/src/test/java/net/catenax/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtensionEdcExtensionTest.java b/edc-extensions/dataplane-selector-configuration/src/test/java/net/catenax/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtensionEdcExtensionTest.java new file mode 100644 index 000000000..8d162e0d3 --- /dev/null +++ b/edc-extensions/dataplane-selector-configuration/src/test/java/net/catenax/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtensionEdcExtensionTest.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Added Test + * + */ + +package net.catenax.edc.dataplane.selector.configuration; + +import java.util.HashMap; +import java.util.Map; +import org.eclipse.dataspaceconnector.dataplane.selector.DataPlaneSelectorService; +import org.eclipse.dataspaceconnector.junit.extensions.EdcExtension; +import org.eclipse.dataspaceconnector.spi.system.Provides; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtension; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; +import org.eclipse.dataspaceconnector.spi.types.domain.DataAddress; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; + +@ExtendWith(EdcExtension.class) +class DataPlaneSelectorConfigurationServiceExtensionEdcExtensionTest { + + private static final String S3_BUCKET = "s3-bucket"; + private static final String BLOB_STORAGE = "blob-storage"; + private static final String LOCAL_FILE_SYSTEM = "local-file-system"; + + private static final String DATA_PLANE_INSTANCE_ID = "test-plane"; + private static final String DATA_PLANE_INSTANCE_URL = "http://127.0.0.1:8080/test"; + private static final String DATA_PLANE_INSTANCE_SOURCE_TYPES = + String.format("%s, %s", S3_BUCKET, BLOB_STORAGE); + private static final String DATA_PLANE_INSTANCE_DESTINATION_TYPES = LOCAL_FILE_SYSTEM; + + // mocks + private DataPlaneSelectorService dataPlaneSelectorService; + + @BeforeEach + final void beforeEach(EdcExtension extension) { + dataPlaneSelectorService = Mockito.mock(DataPlaneSelectorService.class); + + extension.registerSystemExtension(ServiceExtension.class, new TestExtension()); + extension.setConfiguration(getConfig()); + } + + private Map getConfig() { + final String urlKey = + String.format( + "%s.%s.%s", + DataPlaneSelectorConfigurationServiceExtension.CONFIG_PREFIX, + DATA_PLANE_INSTANCE_ID, + DataPlaneSelectorConfigurationServiceExtension.URL_SUFFIX); + final String sourceTypesKey = + String.format( + "%s.%s.%s", + DataPlaneSelectorConfigurationServiceExtension.CONFIG_PREFIX, + DATA_PLANE_INSTANCE_ID, + DataPlaneSelectorConfigurationServiceExtension.SOURCE_TYPES_SUFFIX); + final String destinationTypesKey = + String.format( + "%s.%s.%s", + DataPlaneSelectorConfigurationServiceExtension.CONFIG_PREFIX, + DATA_PLANE_INSTANCE_ID, + DataPlaneSelectorConfigurationServiceExtension.DESTINATION_TYPES_SUFFIX); + final String propertiesKey = + String.format( + "%s.%s.%s", + DataPlaneSelectorConfigurationServiceExtension.CONFIG_PREFIX, + DATA_PLANE_INSTANCE_ID, + DataPlaneSelectorConfigurationServiceExtension.PROPERTIES_SUFFIX); + + return new HashMap<>() { + { + put(urlKey, DATA_PLANE_INSTANCE_URL); + put(sourceTypesKey, DATA_PLANE_INSTANCE_SOURCE_TYPES); + put(destinationTypesKey, DATA_PLANE_INSTANCE_DESTINATION_TYPES); + put( + propertiesKey, + String.format( + "{ \"%s\": \"%s\" }", + DataPlaneSelectorConfigurationServiceExtension.PUBLIC_API_URL_PROPERTY, + DATA_PLANE_INSTANCE_URL)); + } + }; + } + + @Test + void registersDataPlaneInstance() { + Mockito.verify(dataPlaneSelectorService, Mockito.times(1)) + .addInstance( + Mockito.argThat( + dataPlaneInstance -> { + final DataAddress s3Source = + DataAddress.Builder.newInstance().type(S3_BUCKET).build(); + final DataAddress blobSource = + DataAddress.Builder.newInstance().type(BLOB_STORAGE).build(); + final DataAddress fsSink = + DataAddress.Builder.newInstance().type(LOCAL_FILE_SYSTEM).build(); + + final boolean matchingId = + dataPlaneInstance.getId().equals(DATA_PLANE_INSTANCE_ID); + final boolean matchingUrl = + dataPlaneInstance.getUrl().toString().equals(DATA_PLANE_INSTANCE_URL); + final boolean matchingCanHandleS3ToFileSystem = + dataPlaneInstance.canHandle(s3Source, fsSink); + final boolean matchingCanHandleBlobToFileSystem = + dataPlaneInstance.canHandle(blobSource, fsSink); + + if (!matchingId) + System.err.printf( + "Expected ID %s, but got %s%n", + DATA_PLANE_INSTANCE_ID, dataPlaneInstance.getId()); + if (!matchingUrl) + System.err.printf( + "Expected URL %s, but got %s%n", + DATA_PLANE_INSTANCE_URL, dataPlaneInstance.getUrl()); + if (!matchingCanHandleS3ToFileSystem) + System.err.printf( + "Expected Instance to be handle source %s and sink %s%n", + S3_BUCKET, LOCAL_FILE_SYSTEM); + if (!matchingCanHandleBlobToFileSystem) + System.err.printf( + "Expected Instance to be handle source %s and sink %s%n", + BLOB_STORAGE, LOCAL_FILE_SYSTEM); + + return matchingId + && matchingUrl + && matchingCanHandleS3ToFileSystem + && matchingCanHandleBlobToFileSystem; + })); + } + + @Provides({DataPlaneSelectorService.class}) + private class TestExtension implements ServiceExtension { + + public void initialize(ServiceExtensionContext context) { + context.registerService( + org.eclipse.dataspaceconnector.dataplane.selector.DataPlaneSelectorService.class, + dataPlaneSelectorService); + } + } +} diff --git a/edc-extensions/dataplane-selector-configuration/src/test/java/net/catenax/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtensionTest.java b/edc-extensions/dataplane-selector-configuration/src/test/java/net/catenax/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtensionTest.java new file mode 100644 index 000000000..58a744fc5 --- /dev/null +++ b/edc-extensions/dataplane-selector-configuration/src/test/java/net/catenax/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtensionTest.java @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Added Test + * + */ + +package net.catenax.edc.dataplane.selector.configuration; + +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; +import org.eclipse.dataspaceconnector.dataplane.selector.DataPlaneSelectorService; +import org.eclipse.dataspaceconnector.spi.EdcException; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; +import org.eclipse.dataspaceconnector.spi.system.configuration.Config; +import org.eclipse.dataspaceconnector.spi.system.configuration.ConfigFactory; +import org.eclipse.dataspaceconnector.spi.types.domain.DataAddress; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; +import org.mockito.Mockito; + +class DataPlaneSelectorConfigurationServiceExtensionTest { + private static final String S3_BUCKET = "s3-bucket"; + private static final String BLOB_STORAGE = "blob-storage"; + private static final String LOCAL_FILE_SYSTEM = "local-file-system"; + + private static final String DATA_PLANE_INSTANCE_ID = "test-plane"; + private static final String DATA_PLANE_INSTANCE_URL = "http://127.0.0.1:8080/test"; + private static final String DATA_PLANE_INSTANCE_SOURCE_TYPES = + String.format("%s, %s", S3_BUCKET, BLOB_STORAGE); + private static final String DATA_PLANE_INSTANCE_DESTINATION_TYPES = LOCAL_FILE_SYSTEM; + + private static final String urlKey = + String.format( + "%s.%s", + DATA_PLANE_INSTANCE_ID, DataPlaneSelectorConfigurationServiceExtension.URL_SUFFIX); + private static final String sourceTypesKey = + String.format( + "%s.%s", + DATA_PLANE_INSTANCE_ID, + DataPlaneSelectorConfigurationServiceExtension.SOURCE_TYPES_SUFFIX); + private static final String destinationTypesKey = + String.format( + "%s.%s", + DATA_PLANE_INSTANCE_ID, + DataPlaneSelectorConfigurationServiceExtension.DESTINATION_TYPES_SUFFIX); + private static final String propertiesKey = + String.format( + "%s.%s", + DATA_PLANE_INSTANCE_ID, DataPlaneSelectorConfigurationServiceExtension.PROPERTIES_SUFFIX); + + private DataPlaneSelectorConfigurationServiceExtension extension; + + // mocks + private ServiceExtensionContext serviceExtensionContext; + private DataPlaneSelectorService dataPlaneSelectorService; + private Monitor monitor; + + @BeforeEach + void setup() { + extension = new DataPlaneSelectorConfigurationServiceExtension(); + + serviceExtensionContext = Mockito.mock(ServiceExtensionContext.class); + dataPlaneSelectorService = Mockito.mock(DataPlaneSelectorService.class); + monitor = Mockito.mock(Monitor.class); + + Mockito.when(serviceExtensionContext.getService(DataPlaneSelectorService.class)) + .thenReturn(dataPlaneSelectorService); + Mockito.when(serviceExtensionContext.getMonitor()).thenReturn(monitor); + } + + private Map getConfig() { + return new HashMap<>() { + { + put(urlKey, DATA_PLANE_INSTANCE_URL); + put(sourceTypesKey, DATA_PLANE_INSTANCE_SOURCE_TYPES); + put(destinationTypesKey, DATA_PLANE_INSTANCE_DESTINATION_TYPES); + put( + propertiesKey, + String.format( + "{ \"%s\": \"%s\" }", + DataPlaneSelectorConfigurationServiceExtension.PUBLIC_API_URL_PROPERTY, + DATA_PLANE_INSTANCE_URL)); + } + }; + } + + @Test + void testName() { + final DataPlaneSelectorConfigurationServiceExtension extension = + new DataPlaneSelectorConfigurationServiceExtension(); + + Assertions.assertNotNull(extension.name()); + Assertions.assertEquals("Data Plane Selector Configuration Extension", extension.name()); + } + + @Test + void testInitialize() { + + final Config config = ConfigFactory.fromMap(getConfig()); + + Mockito.when(serviceExtensionContext.getConfig("edc.dataplane.selector")).thenReturn(config); + + extension.initialize(serviceExtensionContext); + + Mockito.verify(serviceExtensionContext, Mockito.times(1)) + .getService(DataPlaneSelectorService.class); + Mockito.verify(serviceExtensionContext, Mockito.times(1)).getMonitor(); + Mockito.when( + serviceExtensionContext.getConfig( + DataPlaneSelectorConfigurationServiceExtension.CONFIG_PREFIX)) + .thenReturn(config); + + Mockito.verify(dataPlaneSelectorService, Mockito.times(1)) + .addInstance( + Mockito.argThat( + dataPlaneInstance -> { + final DataAddress s3Source = + DataAddress.Builder.newInstance().type(S3_BUCKET).build(); + final DataAddress blobSource = + DataAddress.Builder.newInstance().type(BLOB_STORAGE).build(); + final DataAddress fsSink = + DataAddress.Builder.newInstance().type(LOCAL_FILE_SYSTEM).build(); + + final boolean matchingId = + dataPlaneInstance.getId().equals(DATA_PLANE_INSTANCE_ID); + final boolean matchingUrl = + dataPlaneInstance.getUrl().toString().equals(DATA_PLANE_INSTANCE_URL); + final boolean matchingCanHandleS3ToFileSystem = + dataPlaneInstance.canHandle(s3Source, fsSink); + final boolean matchingCanHandleBlobToFileSystem = + dataPlaneInstance.canHandle(blobSource, fsSink); + + if (!matchingId) + System.err.printf( + "Expected ID %s, but got %s%n", + DATA_PLANE_INSTANCE_ID, dataPlaneInstance.getId()); + if (!matchingUrl) + System.err.printf( + "Expected URL %s, but got %s%n", + DATA_PLANE_INSTANCE_URL, dataPlaneInstance.getUrl()); + if (!matchingCanHandleS3ToFileSystem) + System.err.printf( + "Expected Instance to be handle source %s and sink %s%n", + S3_BUCKET, LOCAL_FILE_SYSTEM); + if (!matchingCanHandleBlobToFileSystem) + System.err.printf( + "Expected Instance to be handle source %s and sink %s%n", + BLOB_STORAGE, LOCAL_FILE_SYSTEM); + + return matchingId + && matchingUrl + && matchingCanHandleS3ToFileSystem + && matchingCanHandleBlobToFileSystem; + })); + } + + @ParameterizedTest + @ArgumentsSource(MissingConfigArgumentsProvider.class) + void testWarningOnPropertyMissing(String configKey, String configValue) { + Map configMap = getConfig(); + configMap.put(configKey, configValue); + + final Config config = ConfigFactory.fromMap(configMap); + + Mockito.when( + serviceExtensionContext.getConfig( + DataPlaneSelectorConfigurationServiceExtension.CONFIG_PREFIX)) + .thenReturn(config); + + extension.initialize(serviceExtensionContext); + + // one warning config missing, one warning data plane instance skipped + Mockito.verify(monitor, Mockito.times(2)).warning(Mockito.anyString()); + } + + @Test + void throwsExceptionOnPropertiesNoJson() { + Map configMap = getConfig(); + configMap.put(propertiesKey, "no json"); + + final Config config = ConfigFactory.fromMap(configMap); + + Mockito.when( + serviceExtensionContext.getConfig( + DataPlaneSelectorConfigurationServiceExtension.CONFIG_PREFIX)) + .thenReturn(config); + + Assertions.assertThrows( + EdcException.class, () -> extension.initialize(serviceExtensionContext)); + } + + private static class MissingConfigArgumentsProvider implements ArgumentsProvider { + @Override + public Stream provideArguments(ExtensionContext context) { + return Stream.of( + Arguments.of(urlKey, ""), + Arguments.of(sourceTypesKey, ""), + Arguments.of(destinationTypesKey, ""), + Arguments.of(propertiesKey, "{}")); + } + } +} diff --git a/edc-extensions/hashicorp-vault/pom.xml b/edc-extensions/hashicorp-vault/pom.xml index 74ad4315d..69d392b9b 100644 --- a/edc-extensions/hashicorp-vault/pom.xml +++ b/edc-extensions/hashicorp-vault/pom.xml @@ -114,12 +114,12 @@ org.eclipse.dataspaceconnector - junit-extension + junit test org.eclipse.dataspaceconnector - junit-extension + junit test-fixtures test @@ -185,6 +185,7 @@ hamcrest test + diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthResponse.java b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthResponse.java index 4d3533198..b987c7041 100644 --- a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthResponse.java +++ b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthResponse.java @@ -31,7 +31,7 @@ public HashiCorpVaultHealthResponseCode getCodeAsEnum() { case 200: return HashicorpVaultHealthResponse.HashiCorpVaultHealthResponseCode .INITIALIZED_UNSEALED_AND_ACTIVE; - case 409: + case 429: return HashicorpVaultHealthResponse.HashiCorpVaultHealthResponseCode.UNSEALED_AND_STANDBY; case 472: return HashicorpVaultHealthResponse.HashiCorpVaultHealthResponseCode diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultVaultExtension.java b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultVaultExtension.java index ebbb819c5..f6afafb07 100644 --- a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultVaultExtension.java +++ b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultVaultExtension.java @@ -21,15 +21,13 @@ import org.eclipse.dataspaceconnector.spi.security.PrivateKeyResolver; import org.eclipse.dataspaceconnector.spi.security.Vault; import org.eclipse.dataspaceconnector.spi.security.VaultPrivateKeyResolver; +import org.eclipse.dataspaceconnector.spi.system.Provides; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtension; import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; -import org.eclipse.dataspaceconnector.spi.system.VaultExtension; +@Provides({Vault.class, CertificateResolver.class, PrivateKeyResolver.class}) public class HashicorpVaultVaultExtension extends AbstractHashicorpVaultExtension - implements VaultExtension { - - private Vault vault; - private CertificateResolver certificateResolver; - private PrivateKeyResolver privateKeyResolver; + implements ServiceExtension { @Override public String name() { @@ -37,22 +35,7 @@ public String name() { } @Override - public Vault getVault() { - return vault; - } - - @Override - public PrivateKeyResolver getPrivateKeyResolver() { - return privateKeyResolver; - } - - @Override - public CertificateResolver getCertificateResolver() { - return certificateResolver; - } - - @Override - public void initializeVault(ServiceExtensionContext context) { + public void initialize(ServiceExtensionContext context) { final HashicorpVaultClientConfig config = loadHashicorpVaultClientConfig(context); final OkHttpClient okHttpClient = createOkHttpClient(config); @@ -60,9 +43,14 @@ public void initializeVault(ServiceExtensionContext context) { final HashicorpVaultClient client = new HashicorpVaultClient(config, okHttpClient, context.getTypeManager().getMapper()); - vault = new HashicorpVault(client, context.getMonitor()); - certificateResolver = new HashicorpCertificateResolver(vault, context.getMonitor()); - privateKeyResolver = new VaultPrivateKeyResolver(vault); + final HashicorpVault vault = new HashicorpVault(client, context.getMonitor()); + final CertificateResolver certificateResolver = + new HashicorpCertificateResolver(vault, context.getMonitor()); + final VaultPrivateKeyResolver privateKeyResolver = new VaultPrivateKeyResolver(vault); + + context.registerService(Vault.class, vault); + context.registerService(CertificateResolver.class, certificateResolver); + context.registerService(PrivateKeyResolver.class, privateKeyResolver); context.getMonitor().info("HashicorpVaultExtension: authentication/initialization complete."); } diff --git a/edc-extensions/hashicorp-vault/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension b/edc-extensions/hashicorp-vault/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension index 486504f3f..11f7b3617 100644 --- a/edc-extensions/hashicorp-vault/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension +++ b/edc-extensions/hashicorp-vault/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension @@ -11,3 +11,4 @@ # Mercedes-Benz Tech Innovation GmbH - Initial ServiceExtension file # net.catenax.edc.hashicorpvault.HashicorpVaultHealthExtension +net.catenax.edc.hashicorpvault.HashicorpVaultVaultExtension diff --git a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/AbstractHashicorpIT.java b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/AbstractHashicorpIT.java index b8772763a..57b2b584e 100644 --- a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/AbstractHashicorpIT.java +++ b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/AbstractHashicorpIT.java @@ -25,7 +25,7 @@ import java.util.UUID; import java.util.stream.Collectors; import lombok.Getter; -import org.eclipse.dataspaceconnector.junit.launcher.EdcExtension; +import org.eclipse.dataspaceconnector.junit.extensions.EdcExtension; import org.eclipse.dataspaceconnector.spi.security.CertificateResolver; import org.eclipse.dataspaceconnector.spi.security.Vault; import org.eclipse.dataspaceconnector.spi.system.ServiceExtension; diff --git a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultExtensionTest.java b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultExtensionTest.java index 3ab18b188..212477ced 100644 --- a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultExtensionTest.java +++ b/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultExtensionTest.java @@ -71,8 +71,7 @@ void setup() { void throwsHashicorpVaultExceptionOnVaultUrlUndefined() { Mockito.when(context.getSetting(HashicorpVaultVaultExtension.VAULT_URL, null)).thenReturn(null); - Assertions.assertThrows( - HashicorpVaultException.class, () -> extension.initializeVault(context)); + Assertions.assertThrows(HashicorpVaultException.class, () -> extension.initialize(context)); } @Test @@ -80,7 +79,6 @@ void throwsHashicorpVaultExceptionOnVaultTokenUndefined() { Mockito.when(context.getSetting(HashicorpVaultVaultExtension.VAULT_TOKEN, null)) .thenReturn(null); - Assertions.assertThrows( - HashicorpVaultException.class, () -> extension.initializeVault(context)); + Assertions.assertThrows(HashicorpVaultException.class, () -> extension.initialize(context)); } } diff --git a/edc-extensions/pom.xml b/edc-extensions/pom.xml index 3371f572f..c679c735c 100644 --- a/edc-extensions/pom.xml +++ b/edc-extensions/pom.xml @@ -33,6 +33,7 @@ business-partner-validation postgresql-migration hashicorp-vault + dataplane-selector-configuration diff --git a/edc-extensions/postgresql-migration/pom.xml b/edc-extensions/postgresql-migration/pom.xml index b2283e812..056736714 100644 --- a/edc-extensions/postgresql-migration/pom.xml +++ b/edc-extensions/postgresql-migration/pom.xml @@ -45,21 +45,6 @@ - - - - org.apache.maven.plugins - maven-source-plugin - - - attach-sources - - jar - - - - - diff --git a/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/AssetPostgresqlMigrationExtension.java b/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/AssetPostgresqlMigrationExtension.java index 44368a069..9876d17ea 100644 --- a/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/AssetPostgresqlMigrationExtension.java +++ b/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/AssetPostgresqlMigrationExtension.java @@ -14,7 +14,7 @@ package net.catenax.edc.postgresql.migration; -import org.eclipse.dataspaceconnector.sql.asset.index.ConfigurationKeys; +import org.eclipse.dataspaceconnector.sql.assetindex.ConfigurationKeys; public class AssetPostgresqlMigrationExtension extends AbstractPostgresqlMigrationExtension { private static final String NAME_SUBSYSTEM = "asset"; diff --git a/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/asset/V0_0_2__Milestone5_Update.sql b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/asset/V0_0_2__Milestone5_Update.sql new file mode 100644 index 000000000..ea193c434 --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/asset/V0_0_2__Milestone5_Update.sql @@ -0,0 +1,16 @@ +-- +-- Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +-- +-- This program and the accompanying materials are made available under the +-- terms of the Apache License, Version 2.0 which is available at +-- https://www.apache.org/licenses/LICENSE-2.0 +-- +-- SPDX-License-Identifier: Apache-2.0 +-- +-- Contributors: +-- Mercedes-Benz Tech Innovation GmbH - EDC Milestone 5 Update +-- + +-- rename asset id +ALTER TABLE edc_asset_dataaddress RENAME COLUMN asset_id TO asset_id_fk; +ALTER TABLE edc_asset_property RENAME COLUMN asset_id TO asset_id_fk; diff --git a/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractnegotiation/V0_0_3__Milestone5_Update.sql b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractnegotiation/V0_0_3__Milestone5_Update.sql new file mode 100644 index 000000000..01c03827f --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractnegotiation/V0_0_3__Milestone5_Update.sql @@ -0,0 +1,23 @@ +-- +-- Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +-- +-- This program and the accompanying materials are made available under the +-- terms of the Apache License, Version 2.0 which is available at +-- https://www.apache.org/licenses/LICENSE-2.0 +-- +-- SPDX-License-Identifier: Apache-2.0 +-- +-- Contributors: +-- Mercedes-Benz Tech Innovation GmbH - EDC Milestone 5 Update +-- + +-- rename id columns +ALTER TABLE edc_contract_negotiation RENAME COLUMN contract_agreement_id TO agreement_id; +ALTER TABLE edc_contract_agreement RENAME COLUMN agreement_id TO agr_id; + +-- convert types to JSON +ALTER TABLE edc_contract_negotiation ALTER COLUMN contract_offers TYPE JSON USING contract_offers::json; +ALTER TABLE edc_contract_negotiation ALTER COLUMN trace_context TYPE JSON USING trace_context::json; + +-- new policy column +ALTER TABLE edc_contract_agreement ADD COLUMN policy JSON; diff --git a/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/policy/V0_0_2__Milestone5_Update.sql b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/policy/V0_0_2__Milestone5_Update.sql new file mode 100644 index 000000000..6eec57b2b --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/policy/V0_0_2__Milestone5_Update.sql @@ -0,0 +1,21 @@ +-- +-- Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +-- +-- This program and the accompanying materials are made available under the +-- terms of the Apache License, Version 2.0 which is available at +-- https://www.apache.org/licenses/LICENSE-2.0 +-- +-- SPDX-License-Identifier: Apache-2.0 +-- +-- Contributors: +-- Mercedes-Benz Tech Innovation GmbH - EDC Milestone 5 Update +-- + +-- convert types to JSON +ALTER TABLE edc_policies ALTER COLUMN permissions TYPE JSON USING permissions::json; +ALTER TABLE edc_policies ALTER COLUMN prohibitions TYPE JSON USING prohibitions::json; +ALTER TABLE edc_policies ALTER COLUMN duties TYPE JSON USING duties::json; +ALTER TABLE edc_policies ALTER COLUMN extensible_properties TYPE JSON USING extensible_properties::json; + +-- rename policy table +ALTER TABLE edc_policies RENAME TO edc_policydefinitions; diff --git a/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/transferprocess/V0_0_3__Alter_Rename_Id.sql b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/transferprocess/V0_0_3__Alter_Rename_Id.sql new file mode 100644 index 000000000..2f3ca315a --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/transferprocess/V0_0_3__Alter_Rename_Id.sql @@ -0,0 +1,16 @@ +-- +-- Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +-- +-- This program and the accompanying materials are made available under the +-- terms of the Apache License, Version 2.0 which is available at +-- https://www.apache.org/licenses/LICENSE-2.0 +-- +-- SPDX-License-Identifier: Apache-2.0 +-- +-- Contributors: +-- Mercedes-Benz Tech Innovation GmbH - xxx +-- + +-- xxx +ALTER TABLE edc_transfer_process RENAME COLUMN id TO transferprocess_id; +ALTER TABLE edc_data_request RENAME COLUMN id TO datarequest_id; diff --git a/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/transferprocess/V0_0_4__Milestone5_Update.sql b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/transferprocess/V0_0_4__Milestone5_Update.sql new file mode 100644 index 000000000..97ffa75ea --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/transferprocess/V0_0_4__Milestone5_Update.sql @@ -0,0 +1,24 @@ +-- +-- Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +-- +-- This program and the accompanying materials are made available under the +-- terms of the Apache License, Version 2.0 which is available at +-- https://www.apache.org/licenses/LICENSE-2.0 +-- +-- SPDX-License-Identifier: Apache-2.0 +-- +-- Contributors: +-- Mercedes-Benz Tech Innovation GmbH - EDC Milestone 5 Update +-- + +-- add columns +ALTER TABLE edc_transfer_process ADD COLUMN created_time_stamp BIGINT; +ALTER TABLE edc_transfer_process ADD COLUMN deprovisioned_resources JSON; + +COMMENT ON COLUMN edc_transfer_process.deprovisioned_resources IS 'List of deprovisioned resources, serialized as JSON'; + +-- convert types to JSON +ALTER TABLE edc_transfer_process ALTER COLUMN provisioned_resource_set TYPE JSON USING provisioned_resource_set::json; +ALTER TABLE edc_transfer_process ALTER COLUMN trace_context TYPE JSON USING trace_context::json; +ALTER TABLE edc_transfer_process ALTER COLUMN resource_manifest TYPE JSON USING resource_manifest::json; +ALTER TABLE edc_transfer_process ALTER COLUMN content_data_address TYPE JSON USING content_data_address::json; diff --git a/edc-tests/pom.xml b/edc-tests/pom.xml index 573fcb982..75ecc8864 100644 --- a/edc-tests/pom.xml +++ b/edc-tests/pom.xml @@ -138,6 +138,10 @@ net.catenax.edc.extensions business-partner-validation + + net.catenax.edc.extensions + dataplane-selector-configuration + net.catenax.edc.extensions hashicorp-vault diff --git a/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml b/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml index 56fd80f69..38e2a4e4c 100644 --- a/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml +++ b/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml @@ -172,8 +172,9 @@ platoedcdataplane: enabled: true fullnameOverride: "plato-edc-dataplane" image: - repository: &edcDataPlaneImage ghcr.io/catenax-ng/product-edc/edc-dataplane-hashicorp-vault + repository: &edcDataPlaneImage ghcr.io/catenax-ng/product-edc/edc-dataplane-hashicorp-vault tag: &edcDataPlaneImageTag develop + pullPolicy: &pullPolicy Always envSecretName: "aio-plato-data-secret" edc: endpoints: @@ -192,7 +193,7 @@ platoedcdataplane: ############# # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/data-plane/data-plane-api - EDC_CONTROLPLANE_VALIDATION-ENDPOINT: http://plato-edc-controlplane:8182/validation/token + EDC_DATAPLANE_TOKEN_VALIDATION_ENDPOINT: http://plato-edc-controlplane:8182/validation/token ############### ## KEY VAULT ## @@ -206,8 +207,9 @@ platoedccontrolplane: service: type: NodePort image: - repository: &edcControlPlaneImage ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql-hashicorp-vault + repository: &edcControlPlaneImage ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql-hashicorp-vault tag: &edcControlPlaneImageTag develop + pullPolicy: *pullPolicy opentelemetry: properties: |- otel.javaagent.enabled=false @@ -272,6 +274,15 @@ platoedccontrolplane: ## DATA PLANE ## ################ + # see extension https://github.com/catenax-ng/product-edc/tree/develop/edc-extensions/dataplane-selector-configuration + EDC_DATAPLANE_SELECTOR_PLATOPLANE_URL: http://plato-edc-dataplane:9999/api/dataplane/control + EDC_DATAPLANE_SELECTOR_PLATOPLANE_SOURCETYPES : HttpData + EDC_DATAPLANE_SELECTOR_PLATOPLANE_DESTINATIONTYPES: HttpProxy + EDC_DATAPLANE_SELECTOR_PLATOPLANE_PROPERTIES: >- + { + "publicApiUrl": "http://plato-edc-dataplane:8185/api/public" + } + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/data-plane-transfer EDC_TRANSFER_PROXY_ENDPOINT: http://plato-edc-dataplane:8185/api/public EDC_TRANSFER_PROXY_TOKEN_SIGNER_PRIVATEKEY_ALIAS: my-plato-daps-key # for simplicity this example re-uses the DAPS keys. @@ -390,6 +401,7 @@ sokratesedcdataplane: image: repository: *edcDataPlaneImage tag: *edcDataPlaneImageTag + pullPolicy: *pullPolicy envSecretName: "aio-plato-data-secret" edc: endpoints: @@ -408,7 +420,7 @@ sokratesedcdataplane: ############# # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/data-plane/data-plane-api - EDC_CONTROLPLANE_VALIDATION-ENDPOINT: http://sokrates-edc-controlplane:8182/validation/token + EDC_DATAPLANE_TOKEN_VALIDATION_ENDPOINT: http://sokrates-edc-controlplane:8182/validation/token ############### ## KEY VAULT ## @@ -426,6 +438,7 @@ sokratesedccontrolplane: image: repository: *edcControlPlaneImage tag: *edcControlPlaneImageTag + pullPolicy: *pullPolicy opentelemetry: properties: |- otel.javaagent.enabled=false @@ -485,7 +498,17 @@ sokratesedccontrolplane: ## DATA PLANE ## ################ + # see extension https://github.com/catenax-ng/product-edc/tree/develop/edc-extensions/dataplane-selector-configuration + EDC_DATAPLANE_SELECTOR_SOKRATESPLANE_URL: http://sokrates-edc-dataplane:9999/api/dataplane/control + EDC_DATAPLANE_SELECTOR_SOKRATESPLANE_SOURCETYPES : HttpData + EDC_DATAPLANE_SELECTOR_SOKRATESPLANE_DESTINATIONTYPES: HttpProxy + EDC_DATAPLANE_SELECTOR_SOKRATESPLANE_PROPERTIES: >- + { + "publicApiUrl": "http://sokrates-edc-dataplane:8185/api/public" + } + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/data-plane-transfer + # TODO Can this be removed? EDC_TRANSFER_PROXY_ENDPOINT: http://sokrates-edc-dataplane:8185/api/public EDC_TRANSFER_PROXY_TOKEN_SIGNER_PRIVATEKEY_ALIAS: my-sokrates-daps-key # for simplicity this example re-uses the DAPS keys. EDC_TRANSFER_PROXY_TOKEN_VERIFIER_PUBLICKEY_ALIAS: my-sokrates-daps-crt # for simplicity this example re-uses the DAPS keys. diff --git a/edc-tests/src/main/resources/deployment/helm/backend-application/values.yaml b/edc-tests/src/main/resources/deployment/helm/backend-application/values.yaml index 9b14ed531..007168a8d 100644 --- a/edc-tests/src/main/resources/deployment/helm/backend-application/values.yaml +++ b/edc-tests/src/main/resources/deployment/helm/backend-application/values.yaml @@ -152,6 +152,7 @@ script: mkdir -p /tmp/data/ echo "${AUTH_KEY}: ${AUTH_CODE}" >| header.txt + echo "$PAYLOAD" > /tmp/data/${ID}.payload curl -L -H @header.txt -o "/tmp/data/${ID}" ${ENDPOINT} if [ ! $? -eq 0 ]; then diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/DataManagementAPI.java b/edc-tests/src/test/java/net/catenax/edc/tests/DataManagementAPI.java index f1a953568..447386007 100644 --- a/edc-tests/src/test/java/net/catenax/edc/tests/DataManagementAPI.java +++ b/edc-tests/src/test/java/net/catenax/edc/tests/DataManagementAPI.java @@ -47,7 +47,7 @@ public class DataManagementAPI { private static final String ASSET_PATH = "/assets"; - private static final String POLICY_PATH = "/policies"; + private static final String POLICY_PATH = "/policydefinitions"; private static final String CONTRACT_DEFINITIONS_PATH = "/contractdefinitions"; private static final String CATALOG_PATH = "/catalog"; @@ -82,9 +82,9 @@ public Asset getAsset(String id) throws IOException { } public Policy getPolicy(String id) throws IOException { - final DataManagementApiPolicy policy = - get(POLICY_PATH + "/" + id, new TypeToken() {}); - return mapPolicy(policy); + final DataManagementApiPolicyDefinition policyDefinition = + get(POLICY_PATH + "/" + id, new TypeToken() {}); + return mapPolicyDefinition(policyDefinition); } public ContractDefinition getContractDefinition(String id) @@ -102,7 +102,7 @@ public void createAsset(Asset asset) throws IOException { Map.of( DataManagementApiDataAddress.TYPE, "HttpData", - "endpoint", + "baseUrl", "https://jsonplaceholder.typicode.com/todos/1"); final DataManagementApiAssetCreate assetCreate = new DataManagementApiAssetCreate(); @@ -113,7 +113,7 @@ public void createAsset(Asset asset) throws IOException { } public void createPolicy(Policy policy) throws ClientProtocolException, IOException { - post(POLICY_PATH, mapPolicy(policy)); + post(POLICY_PATH, mapPolicyDefinition(policy)); } public void createContractDefinition(ContractDefinition contractDefinition) throws IOException { @@ -127,9 +127,12 @@ public Stream getAllAssets() throws IOException, ClientProtocolException } public Stream getAllPolicies() throws IOException { - final List policies = - get(POLICY_PATH, PARAM_NO_LIMIT, new TypeToken>() {}); - return policies.stream().map(this::mapPolicy); + final List policyDefinitions = + get( + POLICY_PATH, + PARAM_NO_LIMIT, + new TypeToken>() {}); + return policyDefinitions.stream().map(this::mapPolicyDefinition); } public Stream getAllContractDefinitions() throws IOException { @@ -230,12 +233,24 @@ private Policy mapPolicy(DataManagementApiPolicy dataManagementApiPolicy) { return new Policy(id, permissions); } - private DataManagementApiPolicy mapPolicy(Policy policy) { - final List permission = + private Policy mapPolicyDefinition(DataManagementApiPolicyDefinition policyDefinition) { + final DataManagementApiPolicy policy = policyDefinition.getPolicy(); + final List permission = + policy.getPermissions().stream().map(this::mapPermission).collect(Collectors.toList()); + + return new Policy(policyDefinition.uid, permission); + } + + private DataManagementApiPolicyDefinition mapPolicyDefinition(Policy policy) { + + final List permissions = policy.getPermission().stream().map(this::mapPermission).collect(Collectors.toList()); - final DataManagementApiPolicy apiObject = new DataManagementApiPolicy(); + final DataManagementApiPolicy p = new DataManagementApiPolicy(); + p.permissions = permissions; + + final DataManagementApiPolicyDefinition apiObject = new DataManagementApiPolicyDefinition(); apiObject.uid = policy.getId(); - apiObject.permissions = permission; + apiObject.policy = p; return apiObject; } @@ -311,9 +326,9 @@ private ContractDefinition mapContractDefinition( dataManagementContractDefinition.getCriteria() == null ? new ArrayList<>() : dataManagementContractDefinition.getCriteria().stream() - .filter(c -> c.left.equals(DataManagementApiAsset.ID)) - .filter(c -> c.op.equals("=")) - .map(DataManagementApiCriterion::getRight) + .filter(c -> c.operandLeft.equals(DataManagementApiAsset.ID)) + .filter(c -> c.operator.equals("=")) + .map(DataManagementApiCriterion::getOperandRight) .map(c -> (String) c) .collect(Collectors.toList()); @@ -331,9 +346,9 @@ private DataManagementApiContractDefinition mapContractDefinition( for (final String assetId : contractDefinition.getAssetIds()) { DataManagementApiCriterion criterion = new DataManagementApiCriterion(); - criterion.left = DataManagementApiAsset.ID; - criterion.op = "="; - criterion.right = assetId; + criterion.operandLeft = DataManagementApiAsset.ID; + criterion.operator = "="; + criterion.operandRight = assetId; apiObject.criteria.add(criterion); } @@ -361,10 +376,16 @@ private class DataManagementApiDataAddress { private Map properties; } + @Data + private class DataManagementApiPolicyDefinition { + private String uid; + private DataManagementApiPolicy policy; + } + @Data private class DataManagementApiPolicy { private String uid; - private List permissions; + private List permissions = new ArrayList<>(); } @Data @@ -372,7 +393,7 @@ private class DataManagementApiPermission { private String edctype = "dataspaceconnector:permission"; private DataManagementApiRuleAction action; private String target; - private List constraints; + private List constraints = new ArrayList<>(); } @Data @@ -399,14 +420,14 @@ private class DataManagementApiContractDefinition { private String id; private String accessPolicyId; private String contractPolicyId; - private List criteria; + private List criteria = new ArrayList<>(); } @Data private class DataManagementApiCriterion { - private Object left; - private String op; - private Object right; + private Object operandLeft; + private String operator; + private Object operandRight; } @Data @@ -420,6 +441,6 @@ private class DataManagementApiContractOffer { @Data private class DataManagementApiContractOfferCatalog { private String id; - private List contractOffers; + private List contractOffers = new ArrayList<>(); } } diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/data/Policy.java b/edc-tests/src/test/java/net/catenax/edc/tests/data/Policy.java index 4370ce0e6..2996fa032 100644 --- a/edc-tests/src/test/java/net/catenax/edc/tests/data/Policy.java +++ b/edc-tests/src/test/java/net/catenax/edc/tests/data/Policy.java @@ -20,6 +20,6 @@ @Value public class Policy { - @NonNull String id; + String id; @NonNull List Permission; } diff --git a/misc/license-mappings.xml b/misc/license-mappings.xml index e173088a7..cac36e74b 100644 --- a/misc/license-mappings.xml +++ b/misc/license-mappings.xml @@ -754,7 +754,7 @@ org.eclipse.dataspaceconnector - junit-extension + junit Apache License, Version 2.0 0.0.1-SNAPSHOT diff --git a/pom.xml b/pom.xml index a772548b2..e354164c8 100644 --- a/pom.xml +++ b/pom.xml @@ -63,7 +63,7 @@ 3.9.1.2184 - 0.0.1-SNAPSHOT + 0.0.1-milestone-5 1.2.2 42.4.1 9.1.2 @@ -306,6 +306,11 @@ hashicorp-vault ${project.version} + + net.catenax.edc.extensions + dataplane-selector-configuration + ${project.version} + @@ -1018,12 +1023,12 @@ org.eclipse.dataspaceconnector - junit-extension + junit ${org.eclipse.dataspaceconnector.version} org.eclipse.dataspaceconnector - junit-extension + junit ${org.eclipse.dataspaceconnector.version} test-fixtures From 5412b02e45287483120d41ec73600c230f761ae2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Aug 2022 08:49:32 +0200 Subject: [PATCH 211/433] Bump cucumber.version from 7.5.0 to 7.6.0 (#334) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e354164c8..c0d16ae4d 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ 5.9.0 1.8.2 - 7.5.0 + 7.6.0 5.1.1 1.1.0 4.6.1 From 20c0fb005e1b56d9577e377cc3b6d2b2a607df37 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Aug 2022 09:40:28 +0200 Subject: [PATCH 212/433] Bump org.slf4j.version from 2.0.0-alpha1 to 2.0.0-beta1 (#333) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c0d16ae4d..ae5665d87 100644 --- a/pom.xml +++ b/pom.xml @@ -79,7 +79,7 @@ 1.70 4.9.3 1.17.3 - 2.0.0-alpha1 + 2.0.0-beta1 1.2.11 2.2 From d4b187c39f15e3ef4271521247b3befbce73eed3 Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Wed, 10 Aug 2022 08:45:31 +0200 Subject: [PATCH 213/433] add s3 extension to data plane (#336) --- CHANGELOG.md | 7 +++++-- edc-dataplane/edc-dataplane-base/pom.xml | 14 ++++++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e78683a41..b9cf661e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,9 +12,12 @@ corresponding [documentation](/docs/migration/Version_0.0.x_0.1.x.md). ### Added -- control plane - extension ([data-plane-selector-client](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/v0.0.1-milestone-5/extensions/data-plane-selector/selector-client)) +- control plane extension ([data-plane-selector-client](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/v0.0.1-milestone-5/extensions/data-plane-selector/selector-client)) + - run the EDC with multiple data planes at once - control plane extension([dataplane-selector-configuration](edc-extensions/dataplane-selector-configuration)) + - add data plane instances to the control plane by configuration +- data plane extension ([s3-data-plane](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/aws/data-plane-s3)) + - transfer from and to AWS S3 buckets ### Changed diff --git a/edc-dataplane/edc-dataplane-base/pom.xml b/edc-dataplane/edc-dataplane-base/pom.xml index 6a80226f9..bdff3b0cf 100644 --- a/edc-dataplane/edc-dataplane-base/pom.xml +++ b/edc-dataplane/edc-dataplane-base/pom.xml @@ -43,15 +43,21 @@ filesystem-configuration - + org.eclipse.dataspaceconnector - data-plane-framework + data-plane-s3 org.eclipse.dataspaceconnector data-plane-http + + + + org.eclipse.dataspaceconnector + data-plane-framework + org.eclipse.dataspaceconnector data-plane-api @@ -70,6 +76,10 @@ org.eclipse.dataspaceconnector core-boot + + org.eclipse.dataspaceconnector + s3-core + From 4b18f7fb20e184432e5aac3e42f37eeda9decbee Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Wed, 10 Aug 2022 16:00:57 +0200 Subject: [PATCH 214/433] new business test for contract offer verification (#335) --- .github/workflows/business-tests.yaml | 38 ++- edc-tests/pom.xml | 12 + .../net/catenax/edc/tests/AssetStepDefs.java | 11 - .../java/net/catenax/edc/tests/Connector.java | 14 +- .../net/catenax/edc/tests/ConnectorSteps.java | 26 ++ .../java/net/catenax/edc/tests/Constants.java | 6 +- .../edc/tests/ContractDefinitionStepDefs.java | 13 - .../catenax/edc/tests/DataManagementAPI.java | 267 ++++++++++-------- .../net/catenax/edc/tests/Environment.java | 22 +- .../catenax/edc/tests/NegotiationSteps.java | 83 ++++++ .../net/catenax/edc/tests/PolicyStepDefs.java | 23 +- .../edc/tests/data/ContractNegotiation.java | 25 ++ .../tests/data/ContractNegotiationState.java | 23 ++ .../edc/tests/data/PayMeConstraint.java | 26 ++ .../catenax/edc/tests/data/Permission.java | 2 +- .../edc/tests/util/DatabaseCleaner.java | 45 +++ .../net/catenax/edc/tests/util/Timeouts.java | 23 ++ .../features/ContractNegotiation.feature | 34 +++ .../edc/tests/features/ContractOffers.feature | 5 +- pom.xml | 7 + 20 files changed, 514 insertions(+), 191 deletions(-) create mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/ConnectorSteps.java create mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/NegotiationSteps.java create mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/data/ContractNegotiation.java create mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/data/ContractNegotiationState.java create mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/data/PayMeConstraint.java create mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/util/DatabaseCleaner.java create mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/util/Timeouts.java create mode 100644 edc-tests/src/test/resources/net/catenax/edc/tests/features/ContractNegotiation.feature diff --git a/.github/workflows/business-tests.yaml b/.github/workflows/business-tests.yaml index d2dd6fbd0..f0ec379e5 100644 --- a/.github/workflows/business-tests.yaml +++ b/.github/workflows/business-tests.yaml @@ -97,12 +97,20 @@ jobs: name: Define test environment variables run: |- # Define endpoints + echo "SOKRATES_DATA_MANAGEMENT_API_AUTH_KEY=password" | tee -a ${GITHUB_ENV} echo "SOKRATES_DATA_MANAGEMENT_URL=http://sokrates-edc-controlplane:8181/data" | tee -a ${GITHUB_ENV} echo "SOKRATES_IDS_URL=http://sokrates-edc-controlplane:8282/api/v1/ids" | tee -a ${GITHUB_ENV} echo "SOKRATES_DATA_PLANE_URL=http://sokrates-edc-dataplane:8185/api/public" | tee -a ${GITHUB_ENV} + echo "SOKRATES_DATABASE_URL=jdbc:postgresql://plato-postgresql:5432/edc" | tee -a ${GITHUB_ENV} + echo "SOKRATES_DATABASE_USER=postgresql_sandbox_user" | tee -a ${GITHUB_ENV} + echo "SOKRATES_DATABASE_PASSWORD=psql_password" | tee -a ${GITHUB_ENV} + echo "PLATO_DATA_MANAGEMENT_API_AUTH_KEY=password" | tee -a ${GITHUB_ENV} echo "PLATO_DATA_MANAGEMENT_URL=http://plato-edc-controlplane:8181/data" | tee -a ${GITHUB_ENV} echo "PLATO_IDS_URL=http://plato-edc-controlplane:8282/api/v1/ids" | tee -a ${GITHUB_ENV} echo "PLATO_DATA_PLANE_URL=http://plato-edc-dataplane:8185/api/public" | tee -a ${GITHUB_ENV} + echo "PLATO_DATABASE_URL=jdbc:postgresql://plato-postgresql:5432/edc" | tee -a ${GITHUB_ENV} + echo "PLATO_DATABASE_USER=postgresql_sandbox_user" | tee -a ${GITHUB_ENV} + echo "PLATO_DATABASE_PASSWORD=psql_password" | tee -a ${GITHUB_ENV} - name: Install test environment via Helm run: |- @@ -142,11 +150,11 @@ jobs: sleep 5s # Wait for supporting infrastructure to become ready (control-/data-plane, backend service) - kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=idsdaps --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=idsdaps && exit 1 ) - kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=sokratesvault --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=sokratesvault && exit 1 ) - kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=platovault --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=platovault && exit 1 ) - kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=sokratespostgresql --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=sokratespostgresql && exit 1 ) - kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=platopostgresql --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=platopostgresql && exit 1 ) + kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=idsdaps --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=idsdaps --tail 500 && exit 1 ) + kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=sokratesvault --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=sokratesvault --tail 500 && exit 1 ) + kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=platovault --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=platovault --tail 500 && exit 1 ) + kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=sokratespostgresql --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=sokratespostgresql --tail 500 && exit 1 ) + kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=platopostgresql --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=platopostgresql --tail 500 && exit 1 ) # Install the all-in-one Control-/DataPlanes and backend-services helm upgrade --install test-environment edc-tests/src/main/resources/deployment/helm/all-in-one \ @@ -181,12 +189,12 @@ jobs: sleep 5s # Wait for Control-/DataPlane and backend-service to become ready - kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=sokratesbackendapplication --timeout=120s || ( kubectl logs -since=0 -l app.kubernetes.io/name=sokratesbackendapplication && exit 1 ) - kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=platobackendapplication --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=platobackendapplication && exit 1 ) - kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=sokratesedcdataplane --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=sokratesedcdataplane && exit 1 ) - kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=platoedcdataplane --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=platoedcdataplane && exit 1 ) - kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=sokratesedccontrolplane --timeout=600s || ( kubectl logs -l app.kubernetes.io/name=sokratesedccontrolplane && exit 1 ) - kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=platoedccontrolplane --timeout=600s || ( kubectl logs -l app.kubernetes.io/name=platoedccontrolplane && exit 1 ) + kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=sokratesbackendapplication --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=sokratesbackendapplication --tail 500 && exit 1 ) + kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=platobackendapplication --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=platobackendapplication --tail 500 && exit 1 ) + kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=sokratesedcdataplane --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=sokratesedcdataplane --tail 500 && exit 1 ) + kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=platoedcdataplane --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=platoedcdataplane --tail 500 && exit 1 ) + kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=sokratesedccontrolplane --timeout=600s || ( kubectl logs -l app.kubernetes.io/name=sokratesedccontrolplane --tail 500 && exit 1 ) + kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=platoedccontrolplane --timeout=600s || ( kubectl logs -l app.kubernetes.io/name=platoedccontrolplane --tail 500 && exit 1 ) ############################################## ### Run Business Tests inside kind cluster ### @@ -220,7 +228,13 @@ jobs: {"name": "SOKRATES_DATA_PLANE_URL", "value": "${SOKRATES_DATA_PLANE_URL}"}, {"name": "PLATO_DATA_MANAGEMENT_URL", "value": "${PLATO_DATA_MANAGEMENT_URL}"}, {"name": "PLATO_IDS_URL", "value": "${PLATO_IDS_URL}"}, - {"name": "PLATO_DATA_PLANE_URL", "value": "${PLATO_DATA_PLANE_URL}"} + {"name": "PLATO_DATA_PLANE_URL", "value": "${PLATO_DATA_PLANE_URL}"}, + {"name": "PLATO_DATABASE_URL", "value": "${PLATO_DATABASE_URL}"}, + {"name": "PLATO_DATABASE_USER", "value": "${PLATO_DATABASE_USER}"}, + {"name": "PLATO_DATABASE_PASSWORD", "value": "${PLATO_DATABASE_PASSWORD}"}, + {"name": "SOKRATES_DATABASE_URL", "value": "${SOKRATES_DATABASE_URL}"}, + {"name": "SOKRATES_DATABASE_USER", "value": "${SOKRATES_DATABASE_USER}"}, + {"name": "SOKRATES_DATABASE_PASSWORD", "value": "${SOKRATES_DATABASE_PASSWORD}"} ], EOF diff --git a/edc-tests/pom.xml b/edc-tests/pom.xml index 75ecc8864..5c22ac3cc 100644 --- a/edc-tests/pom.xml +++ b/edc-tests/pom.xml @@ -89,6 +89,18 @@ ${org.slf4j.api.version} test + + org.postgresql + postgresql + ${org.postgresql.version} + test + + + org.awaitility + awaitility + test + + diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/AssetStepDefs.java b/edc-tests/src/test/java/net/catenax/edc/tests/AssetStepDefs.java index 2d024b889..3c96d9de8 100644 --- a/edc-tests/src/test/java/net/catenax/edc/tests/AssetStepDefs.java +++ b/edc-tests/src/test/java/net/catenax/edc/tests/AssetStepDefs.java @@ -20,21 +20,10 @@ import java.util.List; import java.util.Map; import java.util.UUID; -import java.util.stream.Stream; import net.catenax.edc.tests.data.Asset; public class AssetStepDefs { - @Given("'{connector}' has no assets") - public void hasNoAssets(Connector connector) throws Exception { - final DataManagementAPI api = connector.getDataManagementAPI(); - - Stream assets = api.getAllAssets(); - for (Asset asset : assets.toArray(Asset[]::new)) { - api.deleteAsset(asset.getId()); - } - } - @Given("'{connector}' has the following assets") public void hasAssets(Connector connector, DataTable table) throws Exception { final DataManagementAPI api = connector.getDataManagementAPI(); diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/Connector.java b/edc-tests/src/test/java/net/catenax/edc/tests/Connector.java index e29d9d7ed..6b3c02122 100644 --- a/edc-tests/src/test/java/net/catenax/edc/tests/Connector.java +++ b/edc-tests/src/test/java/net/catenax/edc/tests/Connector.java @@ -17,6 +17,7 @@ import lombok.Getter; import lombok.NonNull; import lombok.RequiredArgsConstructor; +import net.catenax.edc.tests.util.DatabaseCleaner; @RequiredArgsConstructor public class Connector { @@ -28,7 +29,18 @@ public class Connector { @Getter(lazy = true) private final DataManagementAPI dataManagementAPI = loadDataManagementAPI(); + @Getter(lazy = true) + private final DatabaseCleaner databaseCleaner = loadDatabaseCleaner(); + private DataManagementAPI loadDataManagementAPI() { - return new DataManagementAPI(environment.getDataManagementUrl()); + return new DataManagementAPI( + environment.getDataManagementUrl(), environment.getDataManagementAuthKey()); + } + + private DatabaseCleaner loadDatabaseCleaner() { + return new DatabaseCleaner( + environment.getDatabaseUrl(), + environment.getDatabaseUser(), + environment.getDatabasePassword()); } } diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/ConnectorSteps.java b/edc-tests/src/test/java/net/catenax/edc/tests/ConnectorSteps.java new file mode 100644 index 000000000..8af81d655 --- /dev/null +++ b/edc-tests/src/test/java/net/catenax/edc/tests/ConnectorSteps.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial Implementation + * + */ + +package net.catenax.edc.tests; + +import io.cucumber.java.en.Given; +import java.sql.SQLException; + +public class ConnectorSteps { + + @Given("'{connector}' has an empty database") + public void cleanDatabase(Connector connector) throws SQLException { + connector.getDatabaseCleaner().run(); + } +} diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/Constants.java b/edc-tests/src/test/java/net/catenax/edc/tests/Constants.java index eac6e9b3b..c6600af05 100644 --- a/edc-tests/src/test/java/net/catenax/edc/tests/Constants.java +++ b/edc-tests/src/test/java/net/catenax/edc/tests/Constants.java @@ -19,11 +19,11 @@ @UtilityClass public final class Constants { public static final String DATA_MANAGEMENT_URL = "DATA_MANAGEMENT_URL"; - /** refers to edc.api.auth.key resp. EDC_API_AUTH_KEY */ public static final String DATA_MANAGEMENT_API_AUTH_KEY = "DATA_MANAGEMENT_API_AUTH_KEY"; public static final String IDS_URL = "IDS_URL"; public static final String DATA_PLANE_URL = "DATA_PLANE_URL"; - public static final String PLATO = "PLATO"; - public static final String SOKRATES = "SOKRATES"; + public static final String DATABASE_URL = "DATABASE_URL"; + public static final String DATABASE_USER = "DATABASE_USER"; + public static final String DATABASE_PASSWORD = "DATABASE_PASSWORD"; } diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/ContractDefinitionStepDefs.java b/edc-tests/src/test/java/net/catenax/edc/tests/ContractDefinitionStepDefs.java index 5e571a049..11a097e68 100644 --- a/edc-tests/src/test/java/net/catenax/edc/tests/ContractDefinitionStepDefs.java +++ b/edc-tests/src/test/java/net/catenax/edc/tests/ContractDefinitionStepDefs.java @@ -19,23 +19,10 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.stream.Stream; import net.catenax.edc.tests.data.ContractDefinition; public class ContractDefinitionStepDefs { - @Given("'{connector}' has no contract definitions") - public void hasNoContractDefinitions(Connector connector) throws Exception { - - final DataManagementAPI api = connector.getDataManagementAPI(); - - Stream contractDefinitions = api.getAllContractDefinitions(); - for (ContractDefinition contractDefinition : - contractDefinitions.toArray(ContractDefinition[]::new)) { - api.deleteContractDefinition(contractDefinition.getId()); - } - } - @Given("'{connector}' has the following contract definitions") public void hasPolicies(Connector connector, DataTable table) throws Exception { final DataManagementAPI api = connector.getDataManagementAPI(); diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/DataManagementAPI.java b/edc-tests/src/test/java/net/catenax/edc/tests/DataManagementAPI.java index 447386007..3f0899664 100644 --- a/edc-tests/src/test/java/net/catenax/edc/tests/DataManagementAPI.java +++ b/edc-tests/src/test/java/net/catenax/edc/tests/DataManagementAPI.java @@ -23,20 +23,20 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import java.util.stream.Stream; import lombok.Data; import lombok.extern.slf4j.Slf4j; import net.catenax.edc.tests.data.Asset; import net.catenax.edc.tests.data.BusinessPartnerNumberConstraint; import net.catenax.edc.tests.data.Constraint; import net.catenax.edc.tests.data.ContractDefinition; +import net.catenax.edc.tests.data.ContractNegotiation; +import net.catenax.edc.tests.data.ContractNegotiationState; import net.catenax.edc.tests.data.ContractOffer; +import net.catenax.edc.tests.data.PayMeConstraint; import net.catenax.edc.tests.data.Permission; import net.catenax.edc.tests.data.Policy; import org.apache.http.HttpResponse; -import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpRequestBase; @@ -50,20 +50,20 @@ public class DataManagementAPI { private static final String POLICY_PATH = "/policydefinitions"; private static final String CONTRACT_DEFINITIONS_PATH = "/contractdefinitions"; private static final String CATALOG_PATH = "/catalog"; - - private static final String PARAM_NO_LIMIT = "limit=" + Integer.MAX_VALUE; + private static final String NEGOTIATIONS_PATH = "/contractnegotiations"; private final String dataMgmtUrl; + private final String dataMgmtAuthKey; private final HttpClient httpClient; - public DataManagementAPI(String dataManagementUrl) { + public DataManagementAPI(String dataManagementUrl, String dataMgmtAuthKey) { this.httpClient = HttpClientBuilder.create().build(); this.dataMgmtUrl = dataManagementUrl; + this.dataMgmtAuthKey = dataMgmtAuthKey; } public List requestCatalogFrom(String receivingConnectorUrl) throws IOException { - final String encodedUrl = - URLEncoder.encode(receivingConnectorUrl, StandardCharsets.UTF_8.toString()); + final String encodedUrl = URLEncoder.encode(receivingConnectorUrl, StandardCharsets.UTF_8); final DataManagementApiContractOfferCatalog catalog = get( CATALOG_PATH, @@ -75,25 +75,39 @@ public List requestCatalogFrom(String receivingConnectorUrl) thro return catalog.contractOffers.stream().map(this::mapOffer).collect(Collectors.toList()); } - public Asset getAsset(String id) throws IOException { - final DataManagementApiAsset asset = - get(ASSET_PATH + "/" + id, new TypeToken() {}); - return mapAsset(asset); - } + public String initiateNegotiation( + String receivingConnectorUrl, String definitionId, String assetId, Policy policy) + throws IOException { + final DataManagementApiOffer offer = new DataManagementApiOffer(); + offer.offerId = definitionId + ":foo"; + offer.assetId = assetId; + offer.policy = mapPolicy(policy); + offer.policy.permissions.forEach(p -> p.target = assetId); + + final DataManagementApiNegotiationPayload negotiationPayload = + new DataManagementApiNegotiationPayload(); + negotiationPayload.connectorAddress = receivingConnectorUrl; + negotiationPayload.offer = offer; - public Policy getPolicy(String id) throws IOException { - final DataManagementApiPolicyDefinition policyDefinition = - get(POLICY_PATH + "/" + id, new TypeToken() {}); - return mapPolicyDefinition(policyDefinition); + final DataManagementApiNegotiationResponse response = + post( + NEGOTIATIONS_PATH, + negotiationPayload, + new TypeToken() {}); + + if (response == null) + throw new RuntimeException( + "Initiated negotiation. Connector did not answer with negotiation ID."); + + log.debug("Initiated negotiation ( id= " + response.getId() + " )"); + + return response.getId(); } - public ContractDefinition getContractDefinition(String id) - throws IOException, ClientProtocolException { - final DataManagementApiContractDefinition contractDefinition = - get( - CONTRACT_DEFINITIONS_PATH + "/" + id, - new TypeToken() {}); - return mapContractDefinition(contractDefinition); + public ContractNegotiation getNegotiation(String id) throws IOException { + final DataManagementApiNegotiation negotiation = + get(NEGOTIATIONS_PATH + "/" + id, new TypeToken() {}); + return mapNegotiation(negotiation); } public void createAsset(Asset asset) throws IOException { @@ -112,7 +126,7 @@ public void createAsset(Asset asset) throws IOException { post(ASSET_PATH, assetCreate); } - public void createPolicy(Policy policy) throws ClientProtocolException, IOException { + public void createPolicy(Policy policy) throws IOException { post(POLICY_PATH, mapPolicyDefinition(policy)); } @@ -120,42 +134,6 @@ public void createContractDefinition(ContractDefinition contractDefinition) thro post(CONTRACT_DEFINITIONS_PATH, mapContractDefinition(contractDefinition)); } - public Stream getAllAssets() throws IOException, ClientProtocolException { - final List assets = - get(ASSET_PATH, PARAM_NO_LIMIT, new TypeToken>() {}); - return assets.stream().map(this::mapAsset); - } - - public Stream getAllPolicies() throws IOException { - final List policyDefinitions = - get( - POLICY_PATH, - PARAM_NO_LIMIT, - new TypeToken>() {}); - return policyDefinitions.stream().map(this::mapPolicyDefinition); - } - - public Stream getAllContractDefinitions() throws IOException { - final List contractDefinitions = - get( - CONTRACT_DEFINITIONS_PATH, - PARAM_NO_LIMIT, - new TypeToken>() {}); - return contractDefinitions.stream().map(this::mapContractDefinition); - } - - public void deleteAsset(String id) throws IOException { - delete(ASSET_PATH + "/" + id); - } - - public void deletePolicy(String id) throws IOException { - delete(POLICY_PATH + "/" + id); - } - - public void deleteContractDefinition(String id) throws IOException { - delete(CONTRACT_DEFINITIONS_PATH + "/" + id); - } - private T get(String path, String params, TypeToken typeToken) throws IOException { return get(path + "?" + params, typeToken); } @@ -170,13 +148,11 @@ private T get(String path, TypeToken typeToken) throws IOException { return new Gson().fromJson(new String(json, StandardCharsets.UTF_8), typeToken.getType()); } - private void delete(String path) throws IOException { - final HttpDelete delete = new HttpDelete(dataMgmtUrl + path); - - sendRequest(delete); + private void post(String path, Object object) throws IOException { + post(path, object, new TypeToken() {}); } - private void post(String path, Object object) throws IOException { + private T post(String path, Object object, TypeToken typeToken) throws IOException { final String url = String.format("%s%s", dataMgmtUrl, path); final HttpPost post = new HttpPost(url); post.addHeader("Content-Type", "application/json"); @@ -186,11 +162,18 @@ private void post(String path, Object object) throws IOException { log.debug("POST Payload: " + json); post.setEntity(new StringEntity(json)); - sendRequest(post); + + final HttpResponse response = sendRequest(post); + + if (typeToken.equals(new TypeToken() {})) return null; + + final byte[] responseJson = response.getEntity().getContent().readAllBytes(); + return new Gson() + .fromJson(new String(responseJson, StandardCharsets.UTF_8), typeToken.getType()); } private HttpResponse sendRequest(HttpRequestBase request) throws IOException { - request.addHeader("X-Api-Key", "password"); + request.addHeader("X-Api-Key", dataMgmtAuthKey); log.debug(String.format("Send %-6s %s", request.getMethod(), request.getURI())); @@ -204,12 +187,28 @@ private HttpResponse sendRequest(HttpRequestBase request) throws IOException { return response; } - private Asset mapAsset(DataManagementApiAsset DataManagementApiAsset) { - final String id = (String) DataManagementApiAsset.properties.get(DataManagementApiAsset.ID); - final String description = - (String) DataManagementApiAsset.properties.get(DataManagementApiAsset.DESCRIPTION); + private ContractNegotiation mapNegotiation(DataManagementApiNegotiation negotiation) { + + ContractNegotiationState state; + + switch (negotiation.state) { + case "ERROR": + state = ContractNegotiationState.ERROR; + break; + case "INITIAL": + state = ContractNegotiationState.INITIAL; + break; + case "DECLINED": + state = ContractNegotiationState.DECLINED; + break; + case "CONFIRMED": + state = ContractNegotiationState.CONFIRMED; + break; + default: + state = ContractNegotiationState.UNKNOWN; + } - return new Asset(id, description); + return new ContractNegotiation(negotiation.id, negotiation.agreementId, state); } private DataManagementApiAsset mapAsset(Asset asset) { @@ -233,24 +232,19 @@ private Policy mapPolicy(DataManagementApiPolicy dataManagementApiPolicy) { return new Policy(id, permissions); } - private Policy mapPolicyDefinition(DataManagementApiPolicyDefinition policyDefinition) { - final DataManagementApiPolicy policy = policyDefinition.getPolicy(); - final List permission = - policy.getPermissions().stream().map(this::mapPermission).collect(Collectors.toList()); + private DataManagementApiPolicy mapPolicy(Policy policy) { + final List permissions = + policy.getPermission().stream().map(this::mapPermission).collect(Collectors.toList()); + final DataManagementApiPolicy dataManagementApiPolicy = new DataManagementApiPolicy(); + dataManagementApiPolicy.permissions = permissions; - return new Policy(policyDefinition.uid, permission); + return dataManagementApiPolicy; } private DataManagementApiPolicyDefinition mapPolicyDefinition(Policy policy) { - - final List permissions = - policy.getPermission().stream().map(this::mapPermission).collect(Collectors.toList()); - final DataManagementApiPolicy p = new DataManagementApiPolicy(); - p.permissions = permissions; - final DataManagementApiPolicyDefinition apiObject = new DataManagementApiPolicyDefinition(); apiObject.uid = policy.getId(); - apiObject.policy = p; + apiObject.policy = mapPolicy(policy); return apiObject; } @@ -278,20 +272,43 @@ private DataManagementApiPermission mapPermission(Permission permission) { } private DataManagementApiConstraint mapConstraint(Constraint constraint) { - if (!(constraint instanceof BusinessPartnerNumberConstraint)) + + if (BusinessPartnerNumberConstraint.class.equals(constraint.getClass())) { + return mapConstraint((BusinessPartnerNumberConstraint) constraint); + } else if (PayMeConstraint.class.equals(constraint.getClass())) { + return mapConstraint((PayMeConstraint) constraint); + } else { throw new UnsupportedOperationException( "Unsupported constraint type: " + constraint.getClass().getName()); + } + } + + private DataManagementApiConstraint mapConstraint(PayMeConstraint constraint) { + final DataManagementApiLiteralExpression leftExpression = + new DataManagementApiLiteralExpression(); + leftExpression.value = "PayMe"; + + final DataManagementApiLiteralExpression rightExpression = + new DataManagementApiLiteralExpression(); + rightExpression.value = String.valueOf(constraint.getAmount()); + + final DataManagementApiConstraint dataManagementApiConstraint = + new DataManagementApiConstraint(); + dataManagementApiConstraint.leftExpression = leftExpression; + dataManagementApiConstraint.rightExpression = rightExpression; + dataManagementApiConstraint.operator = "EQ"; - final BusinessPartnerNumberConstraint businessPartnerNumberConstraint = - (BusinessPartnerNumberConstraint) constraint; + return dataManagementApiConstraint; + } + private DataManagementApiConstraint mapConstraint(BusinessPartnerNumberConstraint constraint) { final DataManagementApiLiteralExpression leftExpression = new DataManagementApiLiteralExpression(); leftExpression.value = "BusinessPartnerNumber"; final DataManagementApiLiteralExpression rightExpression = new DataManagementApiLiteralExpression(); - rightExpression.value = businessPartnerNumberConstraint.getBusinessPartnerNumber(); + rightExpression.value = constraint.getBusinessPartnerNumber(); final DataManagementApiConstraint dataManagementApiConstraint = new DataManagementApiConstraint(); @@ -315,26 +332,6 @@ private ContractOffer mapOffer(DataManagementApiContractOffer dataManagementApiC return new ContractOffer(id, policy, assetId); } - private ContractDefinition mapContractDefinition( - DataManagementApiContractDefinition dataManagementContractDefinition) { - final String id = dataManagementContractDefinition.id; - final String accessPolicy = dataManagementContractDefinition.accessPolicyId; - final String contractPolicy = dataManagementContractDefinition.contractPolicyId; - - final List assetIds; - assetIds = - dataManagementContractDefinition.getCriteria() == null - ? new ArrayList<>() - : dataManagementContractDefinition.getCriteria().stream() - .filter(c -> c.operandLeft.equals(DataManagementApiAsset.ID)) - .filter(c -> c.operator.equals("=")) - .map(DataManagementApiCriterion::getOperandRight) - .map(c -> (String) c) - .collect(Collectors.toList()); - - return new ContractDefinition(id, contractPolicy, accessPolicy, assetIds); - } - private DataManagementApiContractDefinition mapContractDefinition( ContractDefinition contractDefinition) { @@ -357,13 +354,39 @@ private DataManagementApiContractDefinition mapContractDefinition( } @Data - private class DataManagementApiAssetCreate { + private static class DataManagementApiNegotiationResponse { + private String id; + } + + @Data + private static class DataManagementApiNegotiationPayload { + private String connectorId = "foo"; + private String connectorAddress; + private DataManagementApiOffer offer; + } + + @Data + private static class DataManagementApiNegotiation { + private String id; + private String state; + private String agreementId; + } + + @Data + private static class DataManagementApiOffer { + private String offerId; + private String assetId; + private DataManagementApiPolicy policy; + } + + @Data + private static class DataManagementApiAssetCreate { private DataManagementApiAsset asset; private DataManagementApiDataAddress dataAddress; } @Data - private class DataManagementApiAsset { + private static class DataManagementApiAsset { public static final String ID = "asset:prop:id"; public static final String DESCRIPTION = "asset:prop:description"; @@ -371,25 +394,25 @@ private class DataManagementApiAsset { } @Data - private class DataManagementApiDataAddress { + private static class DataManagementApiDataAddress { public static final String TYPE = "type"; private Map properties; } @Data - private class DataManagementApiPolicyDefinition { + private static class DataManagementApiPolicyDefinition { private String uid; private DataManagementApiPolicy policy; } @Data - private class DataManagementApiPolicy { + private static class DataManagementApiPolicy { private String uid; private List permissions = new ArrayList<>(); } @Data - private class DataManagementApiPermission { + private static class DataManagementApiPermission { private String edctype = "dataspaceconnector:permission"; private DataManagementApiRuleAction action; private String target; @@ -397,7 +420,7 @@ private class DataManagementApiPermission { } @Data - private class DataManagementApiConstraint { + private static class DataManagementApiConstraint { private String edctype = "AtomicConstraint"; private DataManagementApiLiteralExpression leftExpression; private DataManagementApiLiteralExpression rightExpression; @@ -405,18 +428,18 @@ private class DataManagementApiConstraint { } @Data - private class DataManagementApiLiteralExpression { + private static class DataManagementApiLiteralExpression { private String edctype = "dataspaceconnector:literalexpression"; private String value; } @Data - private class DataManagementApiRuleAction { + private static class DataManagementApiRuleAction { private String type; } @Data - private class DataManagementApiContractDefinition { + private static class DataManagementApiContractDefinition { private String id; private String accessPolicyId; private String contractPolicyId; @@ -424,14 +447,14 @@ private class DataManagementApiContractDefinition { } @Data - private class DataManagementApiCriterion { + private static class DataManagementApiCriterion { private Object operandLeft; private String operator; private Object operandRight; } @Data - private class DataManagementApiContractOffer { + private static class DataManagementApiContractOffer { private String id; private DataManagementApiPolicy policy; private DataManagementApiAsset asset; @@ -439,7 +462,7 @@ private class DataManagementApiContractOffer { } @Data - private class DataManagementApiContractOfferCatalog { + private static class DataManagementApiContractOfferCatalog { private String id; private List contractOffers = new ArrayList<>(); } diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/Environment.java b/edc-tests/src/test/java/net/catenax/edc/tests/Environment.java index c1a127580..56919531b 100644 --- a/edc-tests/src/test/java/net/catenax/edc/tests/Environment.java +++ b/edc-tests/src/test/java/net/catenax/edc/tests/Environment.java @@ -14,11 +14,13 @@ package net.catenax.edc.tests; +import static net.catenax.edc.tests.Constants.DATABASE_PASSWORD; +import static net.catenax.edc.tests.Constants.DATABASE_URL; +import static net.catenax.edc.tests.Constants.DATABASE_USER; +import static net.catenax.edc.tests.Constants.DATA_MANAGEMENT_API_AUTH_KEY; import static net.catenax.edc.tests.Constants.DATA_MANAGEMENT_URL; import static net.catenax.edc.tests.Constants.DATA_PLANE_URL; import static net.catenax.edc.tests.Constants.IDS_URL; -import static net.catenax.edc.tests.Constants.PLATO; -import static net.catenax.edc.tests.Constants.SOKRATES; import java.util.Locale; import lombok.AccessLevel; @@ -29,25 +31,25 @@ @Builder(access = AccessLevel.PRIVATE) @Getter class Environment { + @NonNull private final String dataManagementAuthKey; @NonNull private final String dataManagementUrl; @NonNull private final String idsUrl; @NonNull private final String dataPlaneUrl; - - public static Environment plato() { - return byName(PLATO); - } - - public static Environment sokrates() { - return byName(SOKRATES); - } + @NonNull private final String databaseUrl; + @NonNull private final String databaseUser; + @NonNull private final String databasePassword; public static Environment byName(String name) { name = name.toUpperCase(Locale.ROOT); return Environment.builder() .dataManagementUrl(System.getenv(String.join("_", name, DATA_MANAGEMENT_URL))) + .dataManagementAuthKey(System.getenv(String.join("_", name, DATA_MANAGEMENT_API_AUTH_KEY))) .idsUrl(System.getenv(String.join("_", name, IDS_URL))) .dataPlaneUrl(System.getenv(String.join("_", name, DATA_PLANE_URL))) + .databaseUrl(System.getenv(String.join("_", name, DATABASE_URL))) + .databaseUser(System.getenv(String.join("_", name, DATABASE_USER))) + .databasePassword(System.getenv(String.join("_", name, DATABASE_PASSWORD))) .build(); } } diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/NegotiationSteps.java b/edc-tests/src/test/java/net/catenax/edc/tests/NegotiationSteps.java new file mode 100644 index 000000000..bf4bcdd12 --- /dev/null +++ b/edc-tests/src/test/java/net/catenax/edc/tests/NegotiationSteps.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial Implementation + * + */ + +package net.catenax.edc.tests; + +import static org.awaitility.Awaitility.await; + +import io.cucumber.datatable.DataTable; +import io.cucumber.java.en.Then; +import io.cucumber.java.en.When; +import java.io.IOException; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; +import lombok.extern.slf4j.Slf4j; +import net.catenax.edc.tests.data.ContractNegotiation; +import net.catenax.edc.tests.data.ContractNegotiationState; +import net.catenax.edc.tests.data.Permission; +import net.catenax.edc.tests.data.Policy; +import net.catenax.edc.tests.util.Timeouts; +import org.junit.jupiter.api.Assertions; + +@Slf4j +public class NegotiationSteps { + + private ContractNegotiation lastInitiatedNegotiation; + + @When("'{connector}' sends '{connector}' a counter offer without constraints") + public void sendOfferWithoutConstraints(Connector sender, Connector receiver, DataTable table) + throws IOException { + + final DataManagementAPI dataManagementAPI = sender.getDataManagementAPI(); + final String receiverIdsUrl = receiver.getEnvironment().getIdsUrl() + "/data"; + + for (Map map : table.asMaps()) { + final String definitionId = map.get("definition id"); + final String assetId = map.get("asset id"); + + final Permission permission = new Permission("USE", null, new ArrayList<>()); + final Policy policy = new Policy("foo", List.of(permission)); + + final String negotiationId = + dataManagementAPI.initiateNegotiation(receiverIdsUrl, definitionId, assetId, policy); + + // wait for negotiation to complete + await() + .pollDelay(Duration.ofMillis(500)) + .atMost(Timeouts.CONTRACT_NEGOTIATION) + .until(() -> isNegotiationComplete(dataManagementAPI, negotiationId)); + + lastInitiatedNegotiation = dataManagementAPI.getNegotiation(negotiationId); + } + } + + @Then("the negotiation is declined") + public void assertLastNegotiationDeclined() { + Assertions.assertEquals(ContractNegotiationState.DECLINED, lastInitiatedNegotiation.getState()); + } + + private boolean isNegotiationComplete(DataManagementAPI dataManagementAPI, String negotiationId) + throws IOException { + var negotiation = dataManagementAPI.getNegotiation(negotiationId); + return negotiation != null + && Stream.of( + ContractNegotiationState.ERROR, + ContractNegotiationState.CONFIRMED, + ContractNegotiationState.DECLINED) + .anyMatch((l) -> l.equals(negotiation.getState())); + } +} diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/PolicyStepDefs.java b/edc-tests/src/test/java/net/catenax/edc/tests/PolicyStepDefs.java index 2d738317e..81f173c09 100644 --- a/edc-tests/src/test/java/net/catenax/edc/tests/PolicyStepDefs.java +++ b/edc-tests/src/test/java/net/catenax/edc/tests/PolicyStepDefs.java @@ -19,25 +19,14 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.stream.Stream; import net.catenax.edc.tests.data.BusinessPartnerNumberConstraint; import net.catenax.edc.tests.data.Constraint; +import net.catenax.edc.tests.data.PayMeConstraint; import net.catenax.edc.tests.data.Permission; import net.catenax.edc.tests.data.Policy; public class PolicyStepDefs { - @Given("'{connector}' has no policies") - public void hasNoPolicies(Connector connector) throws Exception { - - final DataManagementAPI api = connector.getDataManagementAPI(); - - Stream policies = api.getAllPolicies(); - for (Policy policy : policies.toArray(Policy[]::new)) { - api.deletePolicy(policy.getId()); - } - } - @Given("'{connector}' has the following policies") public void hasPolicies(Connector connector, DataTable table) throws Exception { final DataManagementAPI api = connector.getDataManagementAPI(); @@ -53,12 +42,16 @@ private List parseDataTable(DataTable table) { final String id = map.get("id"); final String action = map.get("action"); - List accessConstraints = new ArrayList<>(); + List constraints = new ArrayList<>(); final String businessPartnerNumber = map.get("businessPartnerNumber"); if (businessPartnerNumber != null && !businessPartnerNumber.isBlank()) - accessConstraints.add(new BusinessPartnerNumberConstraint(businessPartnerNumber)); + constraints.add(new BusinessPartnerNumberConstraint(businessPartnerNumber)); + + final String payMe = map.get("payMe"); + if (payMe != null && !payMe.isBlank()) + constraints.add(new PayMeConstraint(Double.parseDouble(payMe))); - final List permission = List.of(new Permission(action, null, accessConstraints)); + final List permission = List.of(new Permission(action, null, constraints)); policies.add(new Policy(id, permission)); } diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/data/ContractNegotiation.java b/edc-tests/src/test/java/net/catenax/edc/tests/data/ContractNegotiation.java new file mode 100644 index 000000000..aff80ff8b --- /dev/null +++ b/edc-tests/src/test/java/net/catenax/edc/tests/data/ContractNegotiation.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial Implementation + * + */ + +package net.catenax.edc.tests.data; + +import lombok.NonNull; +import lombok.Value; + +@Value +public class ContractNegotiation { + @NonNull String id; + String agreementId; + @NonNull ContractNegotiationState state; +} diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/data/ContractNegotiationState.java b/edc-tests/src/test/java/net/catenax/edc/tests/data/ContractNegotiationState.java new file mode 100644 index 000000000..66b077601 --- /dev/null +++ b/edc-tests/src/test/java/net/catenax/edc/tests/data/ContractNegotiationState.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial Implementation + * + */ + +package net.catenax.edc.tests.data; + +public enum ContractNegotiationState { + UNKNOWN, + INITIAL, + DECLINED, + CONFIRMED, + ERROR +} diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/data/PayMeConstraint.java b/edc-tests/src/test/java/net/catenax/edc/tests/data/PayMeConstraint.java new file mode 100644 index 000000000..3a12fe12f --- /dev/null +++ b/edc-tests/src/test/java/net/catenax/edc/tests/data/PayMeConstraint.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial Implementation + * + */ + +package net.catenax.edc.tests.data; + +import lombok.Value; + +/** + * The PayMe constraint should be used when no constraint validation/enforcement in the EDC is + * intended. + */ +@Value +public class PayMeConstraint implements Constraint { + double amount; +} diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/data/Permission.java b/edc-tests/src/test/java/net/catenax/edc/tests/data/Permission.java index 2b89066b7..d0f15b13f 100644 --- a/edc-tests/src/test/java/net/catenax/edc/tests/data/Permission.java +++ b/edc-tests/src/test/java/net/catenax/edc/tests/data/Permission.java @@ -22,5 +22,5 @@ public class Permission { @NonNull String action; String target; - List constraints; + @NonNull List constraints; } diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/util/DatabaseCleaner.java b/edc-tests/src/test/java/net/catenax/edc/tests/util/DatabaseCleaner.java new file mode 100644 index 000000000..530278e75 --- /dev/null +++ b/edc-tests/src/test/java/net/catenax/edc/tests/util/DatabaseCleaner.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial Implementation + * + */ + +package net.catenax.edc.tests.util; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class DatabaseCleaner { + + private static final String SQL = + "DELETE FROM edc_contract_negotiation;\n" + + "DELETE FROM edc_contract_agreement;\n" + + "DELETE FROM edc_transfer_process;\n" + + "DELETE FROM edc_contract_definitions;\n" + + "DELETE FROM edc_policydefinitions;\n" + + "DELETE FROM edc_asset;\n" + + "DELETE FROM edc_lease;"; + + private final String url; + private final String user; + private final String password; + + public void run() throws SQLException { + try (Connection con = DriverManager.getConnection(url, user, password)) { + Statement st = con.createStatement(); + st.executeUpdate(SQL); + } + } +} diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/util/Timeouts.java b/edc-tests/src/test/java/net/catenax/edc/tests/util/Timeouts.java new file mode 100644 index 000000000..355de752d --- /dev/null +++ b/edc-tests/src/test/java/net/catenax/edc/tests/util/Timeouts.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial Implementation + * + */ + +package net.catenax.edc.tests.util; + +import java.time.Duration; + +public class Timeouts { + private Timeouts() {} + + public static final Duration CONTRACT_NEGOTIATION = Duration.ofSeconds(90); +} diff --git a/edc-tests/src/test/resources/net/catenax/edc/tests/features/ContractNegotiation.feature b/edc-tests/src/test/resources/net/catenax/edc/tests/features/ContractNegotiation.feature new file mode 100644 index 000000000..ebdfb2f52 --- /dev/null +++ b/edc-tests/src/test/resources/net/catenax/edc/tests/features/ContractNegotiation.feature @@ -0,0 +1,34 @@ +# +# Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +# +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# +# Contributors: +# Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation +# + +Feature: Contract Negotiation + + Background: The Connector State + Given 'Plato' has an empty database + Given 'Sokrates' has an empty database + + Scenario: Counter Offers are rejected + Given 'Plato' has the following assets + | id | description | + | asset-1 | Example Asset | + And 'Plato' has the following policies + | id | action | payMe | + | policy-1 | USE | | + | policy-pay-me | USE | 1000 | + And 'Plato' has the following contract definitions + | id | access policy | contract policy | asset | + | contract-definition-1 | policy-1 | policy-pay-me | asset-1 | + When 'Sokrates' sends 'Plato' a counter offer without constraints + | definition id | asset id | + | contract-definition-1 | asset-1 | + # Then the negotiation is declined # Issue https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1791 diff --git a/edc-tests/src/test/resources/net/catenax/edc/tests/features/ContractOffers.feature b/edc-tests/src/test/resources/net/catenax/edc/tests/features/ContractOffers.feature index acded42b2..499fc63a5 100644 --- a/edc-tests/src/test/resources/net/catenax/edc/tests/features/ContractOffers.feature +++ b/edc-tests/src/test/resources/net/catenax/edc/tests/features/ContractOffers.feature @@ -14,9 +14,8 @@ Feature: Contract Offers Background: The Connector State - Given 'Plato' has no contract definitions - Given 'Plato' has no policies - Given 'Plato' has no assets + Given 'Plato' has an empty database + Given 'Sokrates' has an empty database Scenario: Catalog Request Given 'Plato' has the following assets diff --git a/pom.xml b/pom.xml index ae5665d87..890a3867b 100644 --- a/pom.xml +++ b/pom.xml @@ -61,6 +61,7 @@ 0.8.8 1.1.0 3.9.1.2184 + 4.2.0 0.0.1-milestone-5 @@ -1080,6 +1081,12 @@ ${io.rest.assured.version} test + + org.awaitility + awaitility + ${org.awaitility.awaitility.version} + test + From 6ebac7031b5f2b0ac517fc592b34232ede57a1f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Aug 2022 16:03:49 +0200 Subject: [PATCH 215/433] Bump spotless-maven-plugin from 2.24.0 to 2.24.1 (#339) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 890a3867b..b12eff8e0 100644 --- a/pom.xml +++ b/pom.xml @@ -47,7 +47,7 @@ 3.3.0 3.2.2 - 2.24.0 + 2.24.1 3.1.0 3.4.0 2.0.0 From dc5cb0a2b83ef462b3d1f9f82fbe208494bc2b97 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Thu, 11 Aug 2022 09:53:15 +0200 Subject: [PATCH 216/433] New edc-tests backend service for verifying data transfers (#338) --- .github/workflows/business-tests.yaml | 10 +- .../deployment/helm/all-in-one/Chart.yaml | 12 +- .../deployment/helm/all-in-one/values.yaml | 10 +- .../helm/backend-application/.helmignore | 23 -- .../helm/backend-application/Chart.yaml | 26 -- .../helm/backend-application/README.md | 55 ---- .../helm/backend-application/README.md.gotmpl | 19 -- .../templates/_helpers.tpl | 62 ----- .../templates/configmap.yaml | 10 - .../templates/deployment.yaml | 92 ------- .../backend-application/templates/hpa.yaml | 29 -- .../backend-application/templates/pvc.yaml | 26 -- .../templates/service.yaml | 16 -- .../templates/serviceaccount.yaml | 13 - .../helm/backend-application/values.yaml | 164 ----------- .../edc/tests/BackendServiceBackendAPI.java | 254 ++++++++++++++++++ .../edc/tests/BackendServiceSteps.java | 14 + .../java/net/catenax/edc/tests/Connector.java | 7 + .../java/net/catenax/edc/tests/Constants.java | 2 +- .../net/catenax/edc/tests/Environment.java | 4 + 20 files changed, 298 insertions(+), 550 deletions(-) delete mode 100644 edc-tests/src/main/resources/deployment/helm/backend-application/.helmignore delete mode 100644 edc-tests/src/main/resources/deployment/helm/backend-application/Chart.yaml delete mode 100644 edc-tests/src/main/resources/deployment/helm/backend-application/README.md delete mode 100644 edc-tests/src/main/resources/deployment/helm/backend-application/README.md.gotmpl delete mode 100644 edc-tests/src/main/resources/deployment/helm/backend-application/templates/_helpers.tpl delete mode 100644 edc-tests/src/main/resources/deployment/helm/backend-application/templates/configmap.yaml delete mode 100644 edc-tests/src/main/resources/deployment/helm/backend-application/templates/deployment.yaml delete mode 100644 edc-tests/src/main/resources/deployment/helm/backend-application/templates/hpa.yaml delete mode 100644 edc-tests/src/main/resources/deployment/helm/backend-application/templates/pvc.yaml delete mode 100644 edc-tests/src/main/resources/deployment/helm/backend-application/templates/service.yaml delete mode 100644 edc-tests/src/main/resources/deployment/helm/backend-application/templates/serviceaccount.yaml delete mode 100644 edc-tests/src/main/resources/deployment/helm/backend-application/values.yaml create mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/BackendServiceBackendAPI.java create mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/BackendServiceSteps.java diff --git a/.github/workflows/business-tests.yaml b/.github/workflows/business-tests.yaml index f0ec379e5..38757d37d 100644 --- a/.github/workflows/business-tests.yaml +++ b/.github/workflows/business-tests.yaml @@ -226,15 +226,17 @@ jobs: {"name": "SOKRATES_DATA_MANAGEMENT_URL", "value": "${SOKRATES_DATA_MANAGEMENT_URL}"}, {"name": "SOKRATES_IDS_URL", "value": "${SOKRATES_IDS_URL}"}, {"name": "SOKRATES_DATA_PLANE_URL", "value": "${SOKRATES_DATA_PLANE_URL}"}, + {"name": "SOKRATES_BACKEND_SERVICE_BACKEND_API_URL", "value": "http://sokrates-backend-application:8081" }, + {"name": "SOKRATES_DATABASE_URL", "value": "${SOKRATES_DATABASE_URL}"}, + {"name": "SOKRATES_DATABASE_USER", "value": "${SOKRATES_DATABASE_USER}"}, + {"name": "SOKRATES_DATABASE_PASSWORD", "value": "${SOKRATES_DATABASE_PASSWORD}"}, {"name": "PLATO_DATA_MANAGEMENT_URL", "value": "${PLATO_DATA_MANAGEMENT_URL}"}, {"name": "PLATO_IDS_URL", "value": "${PLATO_IDS_URL}"}, {"name": "PLATO_DATA_PLANE_URL", "value": "${PLATO_DATA_PLANE_URL}"}, + {"name": "PLATO_BACKEND_SERVICE_BACKEND_API_URL", "value": "http://plato-backend-application:8081"}, {"name": "PLATO_DATABASE_URL", "value": "${PLATO_DATABASE_URL}"}, {"name": "PLATO_DATABASE_USER", "value": "${PLATO_DATABASE_USER}"}, - {"name": "PLATO_DATABASE_PASSWORD", "value": "${PLATO_DATABASE_PASSWORD}"}, - {"name": "SOKRATES_DATABASE_URL", "value": "${SOKRATES_DATABASE_URL}"}, - {"name": "SOKRATES_DATABASE_USER", "value": "${SOKRATES_DATABASE_USER}"}, - {"name": "SOKRATES_DATABASE_PASSWORD", "value": "${SOKRATES_DATABASE_PASSWORD}"} + {"name": "PLATO_DATABASE_PASSWORD", "value": "${PLATO_DATABASE_PASSWORD}"} ], EOF diff --git a/edc-tests/src/main/resources/deployment/helm/all-in-one/Chart.yaml b/edc-tests/src/main/resources/deployment/helm/all-in-one/Chart.yaml index 176e5014a..36ad07bac 100644 --- a/edc-tests/src/main/resources/deployment/helm/all-in-one/Chart.yaml +++ b/edc-tests/src/main/resources/deployment/helm/all-in-one/Chart.yaml @@ -43,9 +43,9 @@ dependencies: repository: "file://../../../../../../../deployment/helm/edc-dataplane" alias: platoedcdataplane condition: platoedcdataplane.enabled - - name: backend-application - version: ">=0.0.1" - repository: "file://../backend-application" + - name: backend-service + version: "0.0.5" + repository: https://denisneuling.github.io/cx-backend-service alias: platobackendapplication condition: platobackendapplication.enabled - name: vault @@ -70,9 +70,9 @@ dependencies: repository: "file://../../../../../../../deployment/helm/edc-dataplane" alias: sokratesedcdataplane condition: sokratesedcdataplane.enabled - - name: backend-application - version: ">=0.0.1" - repository: "file://../backend-application" + - name: backend-service + version: "0.0.5" + repository: https://denisneuling.github.io/cx-backend-service alias: sokratesbackendapplication condition: sokratesbackendapplication.enabled - name: vault diff --git a/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml b/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml index 38e2a4e4c..6edbc849d 100644 --- a/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml +++ b/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml @@ -76,7 +76,8 @@ platobackendapplication: enabled: true fullnameOverride: "plato-backend-application" service: - port: 80 + frontend: + port: 80 platopostgresql: enabled: true fullnameOverride: "plato-postgresql" @@ -103,7 +104,7 @@ platovault: - | { - sleep 1 + sleep 5 cat << EOF | /bin/vault kv put secret/my-plato-daps-key content=- -----BEGIN PRIVATE KEY----- @@ -305,7 +306,8 @@ sokratesbackendapplication: enabled: true fullnameOverride: "sokrates-backend-application" service: - port: 80 + frontend: + port: 80 sokratespostgresql: enabled: true fullnameOverride: "sokrates-postgresql" @@ -330,7 +332,7 @@ sokratesvault: - | { - sleep 1 + sleep 5 cat << EOF | /bin/vault kv put secret/my-sokrates-daps-key content=- -----BEGIN PRIVATE KEY----- diff --git a/edc-tests/src/main/resources/deployment/helm/backend-application/.helmignore b/edc-tests/src/main/resources/deployment/helm/backend-application/.helmignore deleted file mode 100644 index 0e8a0eb36..000000000 --- a/edc-tests/src/main/resources/deployment/helm/backend-application/.helmignore +++ /dev/null @@ -1,23 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*.orig -*~ -# Various IDEs -.project -.idea/ -*.tmproj -.vscode/ diff --git a/edc-tests/src/main/resources/deployment/helm/backend-application/Chart.yaml b/edc-tests/src/main/resources/deployment/helm/backend-application/Chart.yaml deleted file mode 100644 index 64fa517fb..000000000 --- a/edc-tests/src/main/resources/deployment/helm/backend-application/Chart.yaml +++ /dev/null @@ -1,26 +0,0 @@ ---- -apiVersion: v2 -name: backend-application -description: >- - The Eclipse Dataspace Connector requires the Backend Application to transfer data using the HTTP-TransferMethod. - -# A chart can be either an 'application' or a 'library' chart. -# -# Application charts are a collection of templates that can be packaged into versioned archives -# to be deployed. -# -# Library charts provide useful utilities or functions for the chart developer. They're included as -# a dependency of application charts to inject those utilities and functions into the rendering -# pipeline. Library charts do not define any templates and therefore cannot be deployed. -type: application - -# This is the chart version. This version number should be incremented each time you make changes -# to the chart and its templates, including the app version. -# Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.1 - -# This is the version number of the application being deployed. This version number should be -# incremented each time you make changes to the application. Versions are not expected to -# follow Semantic Versioning. They should reflect the version the application is using. -# It is recommended to use it with quotes. -appVersion: "0.0.1" diff --git a/edc-tests/src/main/resources/deployment/helm/backend-application/README.md b/edc-tests/src/main/resources/deployment/helm/backend-application/README.md deleted file mode 100644 index 717ac040e..000000000 --- a/edc-tests/src/main/resources/deployment/helm/backend-application/README.md +++ /dev/null @@ -1,55 +0,0 @@ -# backend-application - -![Version: 0.0.1](https://img.shields.io/badge/Version-0.0.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.0.1](https://img.shields.io/badge/AppVersion-0.0.1-informational?style=flat-square) - -The Eclipse Dataspace Connector requires the Backend Application to transfer data using the HTTP-TransferMethod. - -## Values - -| Key | Type | Default | Description | -|-----|------|---------|-------------| -| affinity | object | `{}` | [Affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) constrains which nodes the Pod can be scheduled on based on node labels. | -| automountServiceAccountToken | bool | `false` | Whether to [automount kubernetes API credentials](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server) into the pod | -| autoscaling.enabled | bool | `false` | Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) | -| autoscaling.maxReplicas | int | `100` | Maximum replicas if resource consumption exceeds resource threshholds | -| autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | -| autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | -| autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | -| container.port | int | `8080` | | -| fullnameOverride | string | `""` | Overrides the releases full name | -| image.command[0] | string | `"/bin/bash"` | | -| image.command[1] | string | `"-c"` | | -| image.command[2] | string | `"apt-get update && apt-get install -y ucspi-tcp curl jq && rm -rf /var/lib/apt/lists/* && tcpserver -v 0.0.0.0 \"${TCP_SERVER_PORT}\" \"${TCP_SERVER_SCRIPT_PATH}\""` | | -| image.pullPolicy | string | `"IfNotPresent"` | [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use | -| image.repository | string | `"ubuntu"` | Which container image to use | -| image.tag | string | `"22.04"` | Overrides the image tag whose default is the chart appVersion | -| imagePullSecrets | list | `[]` | Image pull secret to create to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) | -| livenessProbe | object | `{"exec":{"command":["/bin/bash","-c","/usr/bin/ps -ef | grep tcpserver | grep -v grep"]},"initialDelaySeconds":10,"periodSeconds":10}` | [Liveness-Probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-a-liveness-command) to detect and remedy broken applications | -| livenessProbe.exec | object | `{"command":["/bin/bash","-c","/usr/bin/ps -ef | grep tcpserver | grep -v grep"]}` | exec command for liveness check | -| livenessProbe.initialDelaySeconds | int | `10` | initialDelaySeconds before performing the first probe | -| livenessProbe.periodSeconds | int | `10` | periodSeconds between each probe | -| nameOverride | string | `""` | Overrides the charts name | -| nodeSelector | object | `{}` | [Node-Selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) to constrain the Pod to nodes with specific labels. | -| persistence.accessMode | string | `nil` | [PersistentVolume Access Modes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes) Access mode to use. One of (ReadOnlyMany, ReadWriteOnce, ReadWriteMany, ReadWriteOncePod) | -| persistence.capacity | string | `"100M"` | Capacity given to the claimed [PersistentVolume](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) | -| persistence.enabled | bool | `false` | Whether to enable persistence via [PersistentVolumeClaim](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#reserving-a-persistentvolume) | -| persistence.storageClassName | string | `nil` | Storage class to use together with the claimed [PersistentVolume](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) | -| podAnnotations | object | `{}` | [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) added to deployed [pods](https://kubernetes.io/docs/concepts/workloads/pods/) | -| podSecurityContext | object | `{}` | | -| readinessProbe | object | `{"exec":{"command":["/bin/bash","-c","/usr/bin/ps -ef | grep tcpserver | grep -v grep"]},"initialDelaySeconds":10,"periodSeconds":10}` | [Readiness-Probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-readiness-probes) to detect ready applications to receive traffic | -| readinessProbe.exec | object | `{"command":["/bin/bash","-c","/usr/bin/ps -ef | grep tcpserver | grep -v grep"]}` | exec command for readiness check | -| readinessProbe.initialDelaySeconds | int | `10` | initialDelaySeconds before performing the first probe | -| readinessProbe.periodSeconds | int | `10` | periodSeconds between each probe | -| replicaCount | int | `1` | | -| resources | object | `{}` | [Resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) applied to the deployed pod | -| script | object | `{"content":"#!/bin/bash\n\nPAYLOAD=\"\"\nPAYLOAD_INCOMING=0\nexport TMOUT=3.5\nwhile IFS= read -r LINE || [ \"$LINE\" ]; do\n if [ $PAYLOAD_INCOMING -eq 1 ]; then\n PAYLOAD=\"${PAYLOAD}${LINE}\"\n break\n fi\n\n if [[ \"${#LINE}\" = \"1\" && \"$(printf \"%d\" \"'${LINE}\")\" = \"13\" ]]; then\n PAYLOAD_INCOMING=1\n fi\ndone\n\nif [ -z \"$PAYLOAD\" ]; then\n echo -ne \"HTTP/1.1 400 Bad Request\\r\\nContent-Length: 2\\r\\nContent-Type: application/json\\r\\nConnection: close\\r\\n\\r\\n{}\"\n exit 1\nfi\n\nENDPOINT=$(echo $PAYLOAD | jq -r '.endpoint')\nif [ -z \"$ENDPOINT\" ]; then\n echo -ne \"HTTP/1.1 400 Bad Request\\r\\nContent-Length: 2\\r\\nContent-Type: application/json\\r\\nConnection: close\\r\\n\\r\\n{}\"\n exit 1\nfi\n\nID=$(echo $PAYLOAD | jq -r '.id')\nAUTH_KEY=$(echo $PAYLOAD | jq -r '.authKey')\nAUTH_CODE=$(echo $PAYLOAD | jq -r '.authCode')\n\nmkdir -p /tmp/data/\necho \"${AUTH_KEY}: ${AUTH_CODE}\" >| header.txt\n\ncurl -L -H @header.txt -o \"/tmp/data/${ID}\" ${ENDPOINT}\nif [ ! $? -eq 0 ]; then\n echo \"calling endpoint ($ENDPOINT) failed ($?)\" 1>&2\n echo -ne \"HTTP/1.1 400 Bad Request\\r\\nContent-Length: 2\\r\\nContent-Type: application/json\\r\\nConnection: close\\r\\n\\r\\n{}\"\n exit 1\nfi\n\necho -ne \"HTTP/1.1 200 OK\\r\\nContent-Length: 2\\r\\nContent-Type: application/json\\r\\nConnection: close\\r\\n\\r\\n{}\"","path":"/opt/tcpserver/handler.sh"}` | script invoked on http calls | -| securityContext | object | `{}` | | -| service.port | int | `80` | [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service) to expose the running application on a set of Pods as a network service. | -| service.type | string | `"ClusterIP"` | [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. | -| serviceAccount.annotations | object | `{}` | [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) to add to the service account | -| serviceAccount.create | bool | `true` | Specifies whether a [service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) should be created per release | -| serviceAccount.name | string | `""` | The name of the service account to use. If not set and create is true, a name is generated using the release's fullname template | -| tolerations | list | `[]` | [Tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) are applied to Pods to schedule onto nodes with matching taints. | - ----------------------------------------------- -Autogenerated from chart metadata using [helm-docs v1.10.0](https://github.com/norwoodj/helm-docs/releases/v1.10.0) diff --git a/edc-tests/src/main/resources/deployment/helm/backend-application/README.md.gotmpl b/edc-tests/src/main/resources/deployment/helm/backend-application/README.md.gotmpl deleted file mode 100644 index 6cfc6fa29..000000000 --- a/edc-tests/src/main/resources/deployment/helm/backend-application/README.md.gotmpl +++ /dev/null @@ -1,19 +0,0 @@ -{{ template "chart.header" . }} - -{{ template "chart.deprecationWarning" . }} - -{{ template "chart.badgesSection" . }} - -{{ template "chart.description" . }} - -{{ template "chart.homepageLine" . }} - -{{ template "chart.maintainersSection" . }} - -{{ template "chart.sourcesSection" . }} - -{{ template "chart.requirementsSection" . }} - -{{ template "chart.valuesSection" . }} - -{{ template "helm-docs.versionFooter" . }} diff --git a/edc-tests/src/main/resources/deployment/helm/backend-application/templates/_helpers.tpl b/edc-tests/src/main/resources/deployment/helm/backend-application/templates/_helpers.tpl deleted file mode 100644 index 84ec93472..000000000 --- a/edc-tests/src/main/resources/deployment/helm/backend-application/templates/_helpers.tpl +++ /dev/null @@ -1,62 +0,0 @@ -{{/* -Expand the name of the chart. -*/}} -{{- define "backend-application.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "backend-application.fullname" -}} -{{- if .Values.fullnameOverride }} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- $name := default .Chart.Name .Values.nameOverride }} -{{- if contains $name .Release.Name }} -{{- .Release.Name | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} -{{- end }} -{{- end }} -{{- end }} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "backend-application.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Common labels -*/}} -{{- define "backend-application.labels" -}} -helm.sh/chart: {{ include "backend-application.chart" . }} -{{ include "backend-application.selectorLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end }} - -{{/* -Selector labels -*/}} -{{- define "backend-application.selectorLabels" -}} -app.kubernetes.io/name: {{ include "backend-application.name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end }} - -{{/* -Create the name of the service account to use -*/}} -{{- define "backend-application.serviceAccountName" -}} -{{- if .Values.serviceAccount.create }} -{{- default (include "backend-application.fullname" .) .Values.serviceAccount.name }} -{{- else }} -{{- default "default" .Values.serviceAccount.name }} -{{- end }} -{{- end }} diff --git a/edc-tests/src/main/resources/deployment/helm/backend-application/templates/configmap.yaml b/edc-tests/src/main/resources/deployment/helm/backend-application/templates/configmap.yaml deleted file mode 100644 index a150eeb45..000000000 --- a/edc-tests/src/main/resources/deployment/helm/backend-application/templates/configmap.yaml +++ /dev/null @@ -1,10 +0,0 @@ ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "backend-application.fullname" . }} - labels: - {{- include "backend-application.labels" . | nindent 4 }} -data: - handler.sh: |- - {{- .Values.script.content | nindent 4 }} diff --git a/edc-tests/src/main/resources/deployment/helm/backend-application/templates/deployment.yaml b/edc-tests/src/main/resources/deployment/helm/backend-application/templates/deployment.yaml deleted file mode 100644 index 603d7b296..000000000 --- a/edc-tests/src/main/resources/deployment/helm/backend-application/templates/deployment.yaml +++ /dev/null @@ -1,92 +0,0 @@ ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "backend-application.fullname" . }} - labels: - {{- include "backend-application.labels" . | nindent 4 }} -spec: - {{- if not .Values.autoscaling.enabled }} - replicas: {{ .Values.replicaCount }} - {{- end }} - selector: - matchLabels: - {{- include "backend-application.selectorLabels" . | nindent 6 }} - template: - metadata: - {{- with .Values.podAnnotations }} - annotations: - {{- toYaml . | nindent 8 }} - {{- end }} - labels: - {{- include "backend-application.selectorLabels" . | nindent 8 }} - spec: - {{- with .Values.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} - {{- end }} - serviceAccountName: {{ include "backend-application.serviceAccountName" . }} - automountServiceAccountToken: {{ .Values.automountServiceAccountToken }} - securityContext: - {{- toYaml .Values.podSecurityContext | nindent 8 }} - containers: - - name: {{ .Chart.Name }} - securityContext: - {{- toYaml .Values.securityContext | nindent 12 }} - image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} - {{ if .Values.image.command -}} - command: - {{ toYaml .Values.image.command | nindent 12 }} - {{ end -}} - {{ if .Values.livenessProbe -}} - livenessProbe: - {{ toYaml .Values.livenessProbe | nindent 12 }} - {{ end -}} - {{ if .Values.readinessProbe -}} - readinessProbe: - {{ toYaml .Values.readinessProbe | nindent 12 }} - {{ end -}} - volumeMounts: - - name: configmap - mountPath: {{ .Values.script.path }} - subPath: handler.sh - - name: data - mountPath: /tmp/data - env: - - name: TCP_SERVER_SCRIPT_PATH - value: {{ .Values.script.path | quote }} - - name: TCP_SERVER_PORT - value: {{ .Values.container.port | quote}} - ports: - - containerPort: {{ .Values.container.port }} - protocol: TCP - name: http - volumes: - - name: configmap - configMap: - name: {{ include "backend-application.fullname" . }} - items: - - key: handler.sh - path: handler.sh - defaultMode: 0744 - - name: data - {{- if .Values.persistence.enabled }} - persistentVolumeClaim: - claimName: {{ include "backend-application.fullname" . }}-pvc - {{ else }} - emptyDir: - {} - {{- end }} - {{- with .Values.nodeSelector }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} diff --git a/edc-tests/src/main/resources/deployment/helm/backend-application/templates/hpa.yaml b/edc-tests/src/main/resources/deployment/helm/backend-application/templates/hpa.yaml deleted file mode 100644 index cda38c67c..000000000 --- a/edc-tests/src/main/resources/deployment/helm/backend-application/templates/hpa.yaml +++ /dev/null @@ -1,29 +0,0 @@ -{{- if .Values.autoscaling.enabled }} ---- -apiVersion: autoscaling/v2beta1 -kind: HorizontalPodAutoscaler -metadata: - name: {{ include "backend-application.fullname" . }} - labels: - {{- include "backend-application.labels" . | nindent 4 }} -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: {{ include "backend-application.fullname" . }} - minReplicas: {{ .Values.autoscaling.minReplicas }} - maxReplicas: {{ .Values.autoscaling.maxReplicas }} - metrics: - {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} - - type: Resource - resource: - name: cpu - targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} - {{- end }} - {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} - - type: Resource - resource: - name: memory - targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} - {{- end }} -{{- end }} diff --git a/edc-tests/src/main/resources/deployment/helm/backend-application/templates/pvc.yaml b/edc-tests/src/main/resources/deployment/helm/backend-application/templates/pvc.yaml deleted file mode 100644 index c4c98cd3a..000000000 --- a/edc-tests/src/main/resources/deployment/helm/backend-application/templates/pvc.yaml +++ /dev/null @@ -1,26 +0,0 @@ -{{ if .Values.persistence.enabled -}} ---- -apiVersion: "v1" -kind: PersistentVolumeClaim -metadata: - name: {{ include "backend-application.fullname" . }}-pvc - labels: - {{- include "backend-application.labels" . | nindent 4 }} -spec: - {{- if .Values.persistence.storageClassName }} - storageClassName: {{ .Values.persistence.storageClassName | quote }} - {{- end }} - accessModes: - {{- if .Values.persistence.accessMode }} - - {{ .Values.persistence.accessMode | quote }} - {{ else }} - {{- if .Values.autoscaling.enabled }} - - ReadWriteMany - {{ else }} - - ReadWriteOnce - {{- end }} - {{- end }} - resources: - requests: - storage: {{ .Values.persistence.capacity | quote }} -{{ end -}} diff --git a/edc-tests/src/main/resources/deployment/helm/backend-application/templates/service.yaml b/edc-tests/src/main/resources/deployment/helm/backend-application/templates/service.yaml deleted file mode 100644 index 77473dcb5..000000000 --- a/edc-tests/src/main/resources/deployment/helm/backend-application/templates/service.yaml +++ /dev/null @@ -1,16 +0,0 @@ ---- -apiVersion: v1 -kind: Service -metadata: - name: {{ include "backend-application.fullname" . }} - labels: - {{- include "backend-application.labels" . | nindent 4 }} -spec: - type: {{ .Values.service.type }} - ports: - - port: {{ .Values.service.port }} - targetPort: http - protocol: TCP - name: http - selector: - {{- include "backend-application.selectorLabels" . | nindent 4 }} diff --git a/edc-tests/src/main/resources/deployment/helm/backend-application/templates/serviceaccount.yaml b/edc-tests/src/main/resources/deployment/helm/backend-application/templates/serviceaccount.yaml deleted file mode 100644 index ca18045bb..000000000 --- a/edc-tests/src/main/resources/deployment/helm/backend-application/templates/serviceaccount.yaml +++ /dev/null @@ -1,13 +0,0 @@ -{{- if .Values.serviceAccount.create -}} ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "backend-application.serviceAccountName" . }} - labels: - {{- include "backend-application.labels" . | nindent 4 }} - {{- with .Values.serviceAccount.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -{{- end }} diff --git a/edc-tests/src/main/resources/deployment/helm/backend-application/values.yaml b/edc-tests/src/main/resources/deployment/helm/backend-application/values.yaml deleted file mode 100644 index 007168a8d..000000000 --- a/edc-tests/src/main/resources/deployment/helm/backend-application/values.yaml +++ /dev/null @@ -1,164 +0,0 @@ ---- -# Default values for backend-application. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - -replicaCount: 1 - -image: - # -- Which container image to use - repository: ubuntu - # -- [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use - pullPolicy: IfNotPresent - # -- Overrides the image tag whose default is the chart appVersion - tag: "22.04" - command: - - /bin/bash - - -c - - apt-get update && apt-get install -y ucspi-tcp curl jq && rm -rf /var/lib/apt/lists/* && tcpserver -v 0.0.0.0 "${TCP_SERVER_PORT}" "${TCP_SERVER_SCRIPT_PATH}" - -# -- Image pull secret to create to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) -imagePullSecrets: [] - -# -- Overrides the charts name -nameOverride: "" - -# -- Overrides the releases full name -fullnameOverride: "" - -serviceAccount: - # -- Specifies whether a [service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) should be created per release - create: true - # -- [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/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 release's fullname template - name: "" - -# -- Whether to [automount kubernetes API credentials](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server) into the pod -automountServiceAccountToken: false - -# -- [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) added to deployed [pods](https://kubernetes.io/docs/concepts/workloads/pods/) -podAnnotations: {} - -# The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment -podSecurityContext: {} - -# The [container security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) defines privilege and access control settings for a Container within a pod -securityContext: {} - -service: - # -- [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. - type: ClusterIP - # -- [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service) to expose the running application on a set of Pods as a network service. - port: 80 - -container: - port: 8080 - -# -- [Resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) applied to the deployed pod -resources: {} - -autoscaling: - # -- Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) - enabled: false - # -- Minimal replicas if resource consumption falls below resource threshholds - minReplicas: 1 - # -- Maximum replicas if resource consumption exceeds resource threshholds - maxReplicas: 100 - # -- targetAverageUtilization of cpu provided to a pod - targetCPUUtilizationPercentage: 80 - # -- targetAverageUtilization of memory provided to a pod - targetMemoryUtilizationPercentage: 80 - -# -- [Liveness-Probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-a-liveness-command) to detect and remedy broken applications -livenessProbe: - # -- exec command for liveness check - exec: - command: - - /bin/bash - - -c - - /usr/bin/ps -ef | grep tcpserver | grep -v grep - # -- initialDelaySeconds before performing the first probe - initialDelaySeconds: 10 - # -- periodSeconds between each probe - periodSeconds: 10 - -# -- [Readiness-Probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-readiness-probes) to detect ready applications to receive traffic -readinessProbe: - # -- exec command for readiness check - exec: - command: - - /bin/bash - - -c - - /usr/bin/ps -ef | grep tcpserver | grep -v grep - # -- initialDelaySeconds before performing the first probe - initialDelaySeconds: 10 - # -- periodSeconds between each probe - periodSeconds: 10 - -# -- [Node-Selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) to constrain the Pod to nodes with specific labels. -nodeSelector: {} - -# -- [Tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) are applied to Pods to schedule onto nodes with matching taints. -tolerations: [] - -# -- [Affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) constrains which nodes the Pod can be scheduled on based on node labels. -affinity: {} - -persistence: - # -- Whether to enable persistence via [PersistentVolumeClaim](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#reserving-a-persistentvolume) - enabled: false - # -- [PersistentVolume Access Modes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes) Access mode to use. One of (ReadOnlyMany, ReadWriteOnce, ReadWriteMany, ReadWriteOncePod) - accessMode: - # -- Storage class to use together with the claimed [PersistentVolume](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) - storageClassName: - # -- Capacity given to the claimed [PersistentVolume](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) - capacity: 100M - -# -- script invoked on http calls -script: - path: /opt/tcpserver/handler.sh - content: |- - #!/bin/bash - - PAYLOAD="" - PAYLOAD_INCOMING=0 - export TMOUT=3.5 - while IFS= read -r LINE || [ "$LINE" ]; do - if [ $PAYLOAD_INCOMING -eq 1 ]; then - PAYLOAD="${PAYLOAD}${LINE}" - break - fi - - if [[ "${#LINE}" = "1" && "$(printf "%d" "'${LINE}")" = "13" ]]; then - PAYLOAD_INCOMING=1 - fi - done - - if [ -z "$PAYLOAD" ]; then - echo -ne "HTTP/1.1 400 Bad Request\r\nContent-Length: 2\r\nContent-Type: application/json\r\nConnection: close\r\n\r\n{}" - exit 1 - fi - - ENDPOINT=$(echo $PAYLOAD | jq -r '.endpoint') - if [ -z "$ENDPOINT" ]; then - echo -ne "HTTP/1.1 400 Bad Request\r\nContent-Length: 2\r\nContent-Type: application/json\r\nConnection: close\r\n\r\n{}" - exit 1 - fi - - ID=$(echo $PAYLOAD | jq -r '.id') - AUTH_KEY=$(echo $PAYLOAD | jq -r '.authKey') - AUTH_CODE=$(echo $PAYLOAD | jq -r '.authCode') - - mkdir -p /tmp/data/ - echo "${AUTH_KEY}: ${AUTH_CODE}" >| header.txt - echo "$PAYLOAD" > /tmp/data/${ID}.payload - - curl -L -H @header.txt -o "/tmp/data/${ID}" ${ENDPOINT} - if [ ! $? -eq 0 ]; then - echo "calling endpoint ($ENDPOINT) failed ($?)" 1>&2 - echo -ne "HTTP/1.1 400 Bad Request\r\nContent-Length: 2\r\nContent-Type: application/json\r\nConnection: close\r\n\r\n{}" - exit 1 - fi - - echo -ne "HTTP/1.1 200 OK\r\nContent-Length: 2\r\nContent-Type: application/json\r\nConnection: close\r\n\r\n{}" diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/BackendServiceBackendAPI.java b/edc-tests/src/test/java/net/catenax/edc/tests/BackendServiceBackendAPI.java new file mode 100644 index 000000000..f68d0da59 --- /dev/null +++ b/edc-tests/src/test/java/net/catenax/edc/tests/BackendServiceBackendAPI.java @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.tests; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URI; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.StatusLine; +import org.apache.http.client.HttpClient; +import org.apache.http.client.HttpResponseException; +import org.apache.http.client.ResponseHandler; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpHead; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.entity.BasicHttpEntity; +import org.apache.http.entity.ContentType; +import org.apache.http.impl.client.AbstractResponseHandler; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.util.EntityUtils; + +public class BackendServiceBackendAPI { + private static final String HTTP_HEADER_ACCEPT = "Accept"; + private static final String HTTP_HEADER_CONTENT_TYPE = "Content-Type"; + private static final String PATH_ROOT = "/"; + private final String backendServiceBackendApiUrl; + private final HttpClient httpClient; + + public BackendServiceBackendAPI(@NonNull final String backendServiceBackendApiUrl) { + this.backendServiceBackendApiUrl = backendServiceBackendApiUrl; + this.httpClient = HttpClientBuilder.create().build(); + } + + /** Lists all files and directories associated by a backend-service path. */ + @SneakyThrows + public List list(/*@Nullable*/ final String path) { + final URI uri = + new URIBuilder(backendServiceBackendApiUrl) + .setPath(Optional.ofNullable(path).orElse(PATH_ROOT)) + .build(); + final HttpGet get = new HttpGet(uri); + get.setHeader(HTTP_HEADER_ACCEPT, ContentType.APPLICATION_JSON.getMimeType()); + + return httpClient.execute(get, ListResponseHandler.INSTANCE); + } + + /** Proves existence of a file or directory associated by a backend-service path. */ + @SneakyThrows + public boolean exists(@NonNull final String path) { + final URI uri = new URIBuilder(backendServiceBackendApiUrl).setPath(path).build(); + final HttpHead head = new HttpHead(uri); + + return httpClient.execute(head, ExistsResponseHandler.INSTANCE); + } + + /** Retrieves file content associated by a backend-service path. */ + @SneakyThrows + public byte[] get(@NonNull final String path) { + final URI uri = new URIBuilder(backendServiceBackendApiUrl).setPath(path).build(); + final HttpGet get = new HttpGet(uri); + get.setHeader(HTTP_HEADER_ACCEPT, ContentType.APPLICATION_OCTET_STREAM.getMimeType()); + + return httpClient.execute(get, GetResponseHandler.INSTANCE); + } + + /** + * Creates a file associated by a backend-service path. If existing truncates and recreates that + * file + */ + @SneakyThrows + public void post( + @NonNull final String path, @NonNull final InputStream inputStream, long length) { + final URI uri = new URIBuilder(backendServiceBackendApiUrl).setPath(path).build(); + final HttpPost post = new HttpPost(uri); + post.addHeader(HTTP_HEADER_CONTENT_TYPE, ContentType.APPLICATION_OCTET_STREAM.getMimeType()); + final BasicHttpEntity entity = new BasicHttpEntity(); + entity.setContent(inputStream); + entity.setContentLength(length); + + post.setEntity(entity); + + httpClient.execute(post, PostResponseHandler.INSTANCE); + } + + @SneakyThrows + public void post(@NonNull final String path, @NonNull final InputStream inputStream) { + post(path, inputStream, -1); + } + + @SneakyThrows + public void post(@NonNull final String path, @NonNull final byte[] content) { + try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(content)) { + post(path, byteArrayInputStream, content.length); + } + } + + /** Deletes files (and directories in a recursive manner) associated by a backend-service path. */ + @SneakyThrows + public void delete(@NonNull final String path) { + final URI uri = new URIBuilder(backendServiceBackendApiUrl).setPath(path).build(); + final HttpDelete delete = new HttpDelete(uri); + + httpClient.execute(delete, DeleteResponseHandler.INSTANCE); + } + + @NoArgsConstructor(access = AccessLevel.PRIVATE) + private static final class PostResponseHandler implements ResponseHandler { + public static final DeleteResponseHandler INSTANCE = new DeleteResponseHandler(); + + private static final List ACCEPTABLE_STATUS_CODES = + Arrays.asList(HttpStatus.SC_OK, HttpStatus.SC_ACCEPTED, HttpStatus.SC_CREATED); + + @Override + public Void handleResponse(@NonNull final HttpResponse response) throws IOException { + final StatusLine statusLine = response.getStatusLine(); + final Integer code = statusLine.getStatusCode(); + final HttpEntity entity = response.getEntity(); + + // not interested into content so throw it away + EntityUtils.consume(entity); + + if (ACCEPTABLE_STATUS_CODES.contains(code)) { + return null; + } + + throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase()); + } + } + + @NoArgsConstructor(access = AccessLevel.PRIVATE) + private static class DeleteResponseHandler implements ResponseHandler { + public static final DeleteResponseHandler INSTANCE = new DeleteResponseHandler(); + + private static final List ACCEPTABLE_STATUS_CODES = + Arrays.asList( + HttpStatus.SC_OK, + HttpStatus.SC_ACCEPTED, + HttpStatus.SC_NO_CONTENT, + HttpStatus.SC_NOT_FOUND); + + @Override + public Void handleResponse(@NonNull final HttpResponse response) throws IOException { + final StatusLine statusLine = response.getStatusLine(); + final Integer code = statusLine.getStatusCode(); + + // not interested into content so throw it away + Optional.ofNullable(response.getEntity()).ifPresent(EntityUtils::consumeQuietly); + + if (ACCEPTABLE_STATUS_CODES.contains(code)) { + return null; + } + + throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase()); + } + } + + @NoArgsConstructor(access = AccessLevel.PRIVATE) + private static class GetResponseHandler extends AbstractResponseHandler { + public static final GetResponseHandler INSTANCE = new GetResponseHandler(); + + private static byte[] readAllBytes(@NonNull final InputStream stream) throws IOException { + final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + final byte[] data = new byte[16384]; + + int i; + while ((i = stream.read(data, 0, data.length)) != -1) { + byteArrayOutputStream.write(data, 0, i); + } + + return byteArrayOutputStream.toByteArray(); + } + + @Override + public byte[] handleEntity(@NonNull final HttpEntity entity) throws IOException { + try (final InputStream inputStream = entity.getContent()) { + return readAllBytes(inputStream); + } + } + } + + @NoArgsConstructor(access = AccessLevel.PRIVATE) + private static class ExistsResponseHandler implements ResponseHandler { + public static final ExistsResponseHandler INSTANCE = new ExistsResponseHandler(); + + @Override + public Boolean handleResponse(@NonNull final HttpResponse response) + throws HttpResponseException { + final StatusLine statusLine = response.getStatusLine(); + final int code = statusLine.getStatusCode(); + + Optional.ofNullable(response.getEntity()).ifPresent(EntityUtils::consumeQuietly); + + switch (code) { + case HttpStatus.SC_OK: + return true; + case HttpStatus.SC_NOT_FOUND: + return false; + default: + throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase()); + } + } + } + + private static class ListResponseHandler extends GsonResponseHandler> { + public static final ListResponseHandler INSTANCE = new ListResponseHandler(); + + private ListResponseHandler() { + super(new TypeToken>() {}); // JVM type erasure: Keep generic args! + } + } + + @RequiredArgsConstructor(access = AccessLevel.PROTECTED) + private static class GsonResponseHandler extends AbstractResponseHandler { + private static final Gson GSON = new Gson(); + + @NonNull private final TypeToken typeToken; + + @Override + public T handleEntity(@NonNull final HttpEntity entity) throws IOException { + try (final InputStreamReader inputStreamReader = new InputStreamReader(entity.getContent())) { + return GSON.fromJson(inputStreamReader, typeToken.getType()); + } + } + } +} diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/BackendServiceSteps.java b/edc-tests/src/test/java/net/catenax/edc/tests/BackendServiceSteps.java new file mode 100644 index 000000000..1d5ec62ca --- /dev/null +++ b/edc-tests/src/test/java/net/catenax/edc/tests/BackendServiceSteps.java @@ -0,0 +1,14 @@ +package net.catenax.edc.tests; + +import io.cucumber.java.en.Given; + +public class BackendServiceSteps { + + @Given("'{connector}' has an empty backend-service") + public void cleanBackendService(Connector connector) { + final BackendServiceBackendAPI backendServiceBackendAPI = + connector.getBackendServiceBackendAPI(); + + backendServiceBackendAPI.list("/").forEach(backendServiceBackendAPI::delete); + } +} diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/Connector.java b/edc-tests/src/test/java/net/catenax/edc/tests/Connector.java index 6b3c02122..137f007cf 100644 --- a/edc-tests/src/test/java/net/catenax/edc/tests/Connector.java +++ b/edc-tests/src/test/java/net/catenax/edc/tests/Connector.java @@ -29,6 +29,9 @@ public class Connector { @Getter(lazy = true) private final DataManagementAPI dataManagementAPI = loadDataManagementAPI(); + @Getter(lazy = true) + private final BackendServiceBackendAPI backendServiceBackendAPI = loadBackendServiceBackendAPI(); + @Getter(lazy = true) private final DatabaseCleaner databaseCleaner = loadDatabaseCleaner(); @@ -43,4 +46,8 @@ private DatabaseCleaner loadDatabaseCleaner() { environment.getDatabaseUser(), environment.getDatabasePassword()); } + + private BackendServiceBackendAPI loadBackendServiceBackendAPI() { + return new BackendServiceBackendAPI(environment.getBackendServiceBackendApiUrl()); + } } diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/Constants.java b/edc-tests/src/test/java/net/catenax/edc/tests/Constants.java index c6600af05..6f16894ea 100644 --- a/edc-tests/src/test/java/net/catenax/edc/tests/Constants.java +++ b/edc-tests/src/test/java/net/catenax/edc/tests/Constants.java @@ -20,9 +20,9 @@ public final class Constants { public static final String DATA_MANAGEMENT_URL = "DATA_MANAGEMENT_URL"; public static final String DATA_MANAGEMENT_API_AUTH_KEY = "DATA_MANAGEMENT_API_AUTH_KEY"; - public static final String IDS_URL = "IDS_URL"; public static final String DATA_PLANE_URL = "DATA_PLANE_URL"; + public static final String BACKEND_SERVICE_BACKEND_API_URL = "BACKEND_SERVICE_BACKEND_API_URL"; public static final String DATABASE_URL = "DATABASE_URL"; public static final String DATABASE_USER = "DATABASE_USER"; public static final String DATABASE_PASSWORD = "DATABASE_PASSWORD"; diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/Environment.java b/edc-tests/src/test/java/net/catenax/edc/tests/Environment.java index 56919531b..699472a63 100644 --- a/edc-tests/src/test/java/net/catenax/edc/tests/Environment.java +++ b/edc-tests/src/test/java/net/catenax/edc/tests/Environment.java @@ -14,6 +14,7 @@ package net.catenax.edc.tests; +import static net.catenax.edc.tests.Constants.BACKEND_SERVICE_BACKEND_API_URL; import static net.catenax.edc.tests.Constants.DATABASE_PASSWORD; import static net.catenax.edc.tests.Constants.DATABASE_URL; import static net.catenax.edc.tests.Constants.DATABASE_USER; @@ -35,6 +36,7 @@ class Environment { @NonNull private final String dataManagementUrl; @NonNull private final String idsUrl; @NonNull private final String dataPlaneUrl; + @NonNull private final String backendServiceBackendApiUrl; @NonNull private final String databaseUrl; @NonNull private final String databaseUser; @NonNull private final String databasePassword; @@ -47,6 +49,8 @@ public static Environment byName(String name) { .dataManagementAuthKey(System.getenv(String.join("_", name, DATA_MANAGEMENT_API_AUTH_KEY))) .idsUrl(System.getenv(String.join("_", name, IDS_URL))) .dataPlaneUrl(System.getenv(String.join("_", name, DATA_PLANE_URL))) + .backendServiceBackendApiUrl( + System.getenv(String.join("_", name, BACKEND_SERVICE_BACKEND_API_URL))) .databaseUrl(System.getenv(String.join("_", name, DATABASE_URL))) .databaseUser(System.getenv(String.join("_", name, DATABASE_USER))) .databasePassword(System.getenv(String.join("_", name, DATABASE_PASSWORD))) From feb034c3b444a33bd9f41f78c8101bd0b8f5d808 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 11 Aug 2022 16:31:48 +0200 Subject: [PATCH 217/433] Bump flyway-core from 9.1.2 to 9.1.3 (#342) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b12eff8e0..80d8efc08 100644 --- a/pom.xml +++ b/pom.xml @@ -67,7 +67,7 @@ 0.0.1-milestone-5 1.2.2 42.4.1 - 9.1.2 + 9.1.3 5.9.0 From 050c20a61f1e13a48fd25a9d3c1b6dfe5023cc06 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 11 Aug 2022 16:32:00 +0200 Subject: [PATCH 218/433] Bump helm/chart-testing-action from 2.2.1 to 2.3.0 (#341) --- .github/workflows/helm-lint.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/helm-lint.yaml b/.github/workflows/helm-lint.yaml index ed4c24513..557d91d5a 100644 --- a/.github/workflows/helm-lint.yaml +++ b/.github/workflows/helm-lint.yaml @@ -42,7 +42,7 @@ jobs: python-version: 3.7 - name: chart-testing (setup) - uses: helm/chart-testing-action@v2.2.1 + uses: helm/chart-testing-action@v2.3.0 ##################### ### Chart Testing ### ##################### From 943cef61f7ee2371d5f13a43888a6c807a1e9a99 Mon Sep 17 00:00:00 2001 From: Sebastian Scherer <59142915+the-tatanka@users.noreply.github.com> Date: Sun, 14 Aug 2022 13:32:47 +0200 Subject: [PATCH 219/433] Update kics.yaml --- .github/workflows/kics.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/kics.yaml b/.github/workflows/kics.yaml index 7bdd6a89f..5130c8022 100644 --- a/.github/workflows/kics.yaml +++ b/.github/workflows/kics.yaml @@ -31,6 +31,8 @@ jobs: path: "." # Fail on HIGH severity results fail_on: high + # Disable secrets detection - we use GitGuardian + disable_secrets: true # when provided with a directory on output_path # it will generate the specified reports file named 'results.{extension}' # in this example it will generate: From a321e35dfa5d6665cf027738830e25b2940346b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 14:35:17 +0200 Subject: [PATCH 220/433] Bump mockito-bom from 4.6.1 to 4.7.0 (#345) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 80d8efc08..61739a9dd 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ 7.6.0 5.1.1 1.1.0 - 4.6.1 + 4.7.0 1.18.24 1.70 4.9.3 From 90e74783980fdd265499ca70d5cc783ad4d74ad8 Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Wed, 17 Aug 2022 10:37:58 +0200 Subject: [PATCH 221/433] update dataplane selector documentation (#337) --- .../dataplane-selector-configuration/README.md | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/edc-extensions/dataplane-selector-configuration/README.md b/edc-extensions/dataplane-selector-configuration/README.md index 5cafbc8db..2c64253f6 100644 --- a/edc-extensions/dataplane-selector-configuration/README.md +++ b/edc-extensions/dataplane-selector-configuration/README.md @@ -1,7 +1,7 @@ # Data Plane Selector Configuration Exception -This control-plane extension makes it possible configure one or more data planes. After a data transfer is -triggered at the control plane will look for a data plane with matching capabilities. +This control-plane extension makes it possible configure one or more Data Plane Instances. During a transfer the control +plane will look for an instance with matching capabilities to transfer data. ## Configuration @@ -14,4 +14,15 @@ Per data plane instance the following settings must be configured. As `.destinationtypes | Destination Types in a comma separated List. | X | HttpProxy | | edc.dataplane.selector..properties | Additional properties of the Data Plane Instance. | (X) | { "publicApiUrl:": "http://plato-edc-dataplane:8185/api/public" } | -The property `publicApiUrl` is mandatory for Data Plane Instances with destination type `HttpProxy`. \ No newline at end of file +The property `publicApiUrl` is mandatory for Data Plane Instances with destination type `HttpProxy`. + +**Helm Example Configuration using environment variables** +```yaml +EDC_DATAPLANE_SELECTOR_PLATOPLANE_URL: http://plato-edc-dataplane:9999/api/dataplane/control +EDC_DATAPLANE_SELECTOR_PLATOPLANE_SOURCETYPES : HttpData +EDC_DATAPLANE_SELECTOR_PLATOPLANE_DESTINATIONTYPES: HttpProxy +EDC_DATAPLANE_SELECTOR_PLATOPLANE_PROPERTIES: >- + { + "publicApiUrl": "http://plato-edc-dataplane:8185/api/public" + } +``` \ No newline at end of file From 70c17412c53f22c8cde8802df3fff82353145edd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Aug 2022 10:52:13 +0200 Subject: [PATCH 222/433] Bump maven-javadoc-plugin from 3.4.0 to 3.4.1 (#344) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 61739a9dd..3bdbc27c3 100644 --- a/pom.xml +++ b/pom.xml @@ -49,7 +49,7 @@ 3.2.2 2.24.1 3.1.0 - 3.4.0 + 3.4.1 2.0.0 2.22.2 3.0.0 From 2074762c5839a5b86077a5013f6b7dd6c52a84a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Aug 2022 10:53:30 +0200 Subject: [PATCH 223/433] Bump flyway-core from 9.1.3 to 9.1.4 (#346) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3bdbc27c3..e7f627a8a 100644 --- a/pom.xml +++ b/pom.xml @@ -67,7 +67,7 @@ 0.0.1-milestone-5 1.2.2 42.4.1 - 9.1.3 + 9.1.4 5.9.0 From 1afaa64a4772b0873a755af72eb6b81857541c36 Mon Sep 17 00:00:00 2001 From: Brendan Cronin Date: Wed, 17 Aug 2022 10:55:42 +0200 Subject: [PATCH 224/433] Kics: rename file Signed-off-by: Brendan Cronin --- .github/workflows/{kics.yaml => checkov.yaml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{kics.yaml => checkov.yaml} (100%) diff --git a/.github/workflows/kics.yaml b/.github/workflows/checkov.yaml similarity index 100% rename from .github/workflows/kics.yaml rename to .github/workflows/checkov.yaml From 310e033c641fe86e1c3dd045bb9c73179129a2f1 Mon Sep 17 00:00:00 2001 From: Brendan Cronin Date: Wed, 17 Aug 2022 15:53:24 +0200 Subject: [PATCH 225/433] Add dummy newline Signed-off-by: Brendan Cronin --- deployment/helm/edc-controlplane/templates/ingress.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/deployment/helm/edc-controlplane/templates/ingress.yaml b/deployment/helm/edc-controlplane/templates/ingress.yaml index e2586a7fc..ccb37ce22 100644 --- a/deployment/helm/edc-controlplane/templates/ingress.yaml +++ b/deployment/helm/edc-controlplane/templates/ingress.yaml @@ -72,3 +72,4 @@ spec: {{- end }} {{- end }}{{- /* end: if .enabled */}} {{- end }}{{- /* end: range .Values.ingresses */}} + From e81cd1e834e6dfedaf7ec442a8f4de200d8773b0 Mon Sep 17 00:00:00 2001 From: Brendan Cronin Date: Wed, 17 Aug 2022 16:07:01 +0200 Subject: [PATCH 226/433] Add manual dispatch Signed-off-by: Brendan Cronin --- .github/workflows/checkov.yaml | 11 ++++------- .../helm/edc-controlplane/templates/ingress.yaml | 1 - 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/.github/workflows/checkov.yaml b/.github/workflows/checkov.yaml index 5130c8022..59c78d465 100644 --- a/.github/workflows/checkov.yaml +++ b/.github/workflows/checkov.yaml @@ -3,12 +3,10 @@ name: "KICS" on: push: branches: [main, master, develop] - # pull_request: - # The branches below must be a subset of the branches above - # branches: [main, master] - # paths-ignore: - # - "**/*.md" - # - "**/*.txt" + pull_request: + branches: [main, master, develop] + workflow_dispatch: + schedule: - cron: "0 0 * * *" @@ -52,4 +50,3 @@ jobs: uses: github/codeql-action/upload-sarif@v1 with: sarif_file: kicsResults/results.sarif - diff --git a/deployment/helm/edc-controlplane/templates/ingress.yaml b/deployment/helm/edc-controlplane/templates/ingress.yaml index ccb37ce22..e2586a7fc 100644 --- a/deployment/helm/edc-controlplane/templates/ingress.yaml +++ b/deployment/helm/edc-controlplane/templates/ingress.yaml @@ -72,4 +72,3 @@ spec: {{- end }} {{- end }}{{- /* end: if .enabled */}} {{- end }}{{- /* end: range .Values.ingresses */}} - From bdc0e2076062bcfbecb9f41ee6a590e8bc259b4d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Aug 2022 23:28:17 +0200 Subject: [PATCH 227/433] Bump flyway-core from 9.1.4 to 9.1.5 (#348) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e7f627a8a..96b519f5f 100644 --- a/pom.xml +++ b/pom.xml @@ -67,7 +67,7 @@ 0.0.1-milestone-5 1.2.2 42.4.1 - 9.1.4 + 9.1.5 5.9.0 From 9c6a5c64d3df30274c9be3bc319452b53c948d65 Mon Sep 17 00:00:00 2001 From: Brendan Cronin Date: Thu, 18 Aug 2022 07:34:04 +0200 Subject: [PATCH 228/433] Test user assignment Signed-off-by: Brendan Cronin --- .../edc-controlplane-memory/src/main/docker/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile index 43eca93dc..d8500ede3 100644 --- a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile @@ -17,6 +17,7 @@ ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-in RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar FROM gcr.io/distroless/java11-debian11 +USER edc ARG JAR ARG LIB From c93dcb3646f9a3d062709f5b9ca5945e69d6c8dc Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Thu, 18 Aug 2022 09:10:59 +0200 Subject: [PATCH 229/433] Update .github/dependabot.yml (#349) --- .github/dependabot.yml | 61 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 1c28f97d5..53a5a64d5 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,6 +1,29 @@ --- version: 2 + +registries: + # EDC snapshot "releases" + sonatype-snapshots: + type: maven-repository + url: https://oss.sonatype.org/content/repositories/snapshots/ + # All other maven dependencies + maven-central: + type: maven-repository + url: https://repo.maven.apache.org/maven2/ + updates: + # Maven + - + package-ecosystem: "maven" + target-branch: develop + directory: / + labels: + - "dependabot" + - "dependencies" + schedule: + interval: "daily" + + # Github Actions - package-ecosystem: "github-actions" target-branch: develop @@ -10,22 +33,50 @@ updates: - "github-actions" schedule: interval: "daily" + + # Docker - package-ecosystem: "docker" target-branch: develop - directory: / + directory: ./edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/ labels: - "dependabot" - "docker" schedule: interval: "daily" - - package-ecosystem: "maven" + package-ecosystem: "docker" target-branch: develop - directory: / + directory: ./edc-controlplane/edc-controlplane-postgresql/src/main/docker/ labels: - "dependabot" - - "dependencies" + - "docker" + schedule: + interval: "daily" + - + package-ecosystem: "docker" + target-branch: develop + directory: ./edc-controlplane/edc-controlplane-memory/src/main/docker/ + labels: + - "dependabot" + - "docker" + schedule: + interval: "daily" + - + package-ecosystem: "docker" + target-branch: develop + directory: ./edc-dataplane/edc-dataplane-azure-vault/src/main/docker/ + labels: + - "dependabot" + - "docker" + schedule: + interval: "daily" + - + package-ecosystem: "docker" + target-branch: develop + directory: ./edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/ + labels: + - "dependabot" + - "docker" schedule: interval: "daily" - From 2cc907d9f0ed164646aff4f0227166ecfb782374 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Thu, 18 Aug 2022 09:55:04 +0200 Subject: [PATCH 230/433] Fixes dependabot.yml, adds sonatype repo for snapshots (#350) --- .github/dependabot.yml | 11 ----------- pom.xml | 15 +++++++++++++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 53a5a64d5..16f8582cb 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,16 +1,5 @@ --- version: 2 - -registries: - # EDC snapshot "releases" - sonatype-snapshots: - type: maven-repository - url: https://oss.sonatype.org/content/repositories/snapshots/ - # All other maven dependencies - maven-central: - type: maven-repository - url: https://repo.maven.apache.org/maven2/ - updates: # Maven - diff --git a/pom.xml b/pom.xml index 96b519f5f..d0fe8796b 100644 --- a/pom.xml +++ b/pom.xml @@ -45,7 +45,8 @@ UTF-8 - 3.3.0 + 3.3.0 + 3.2.2 2.24.1 3.1.0 @@ -96,7 +97,17 @@ Maven Central https://repo1.maven.org/maven2 - + + sonatype-snapshots + + true + + + false + + https://oss.sonatype.org/content/repositories/snapshots/ + + fraunhofer-iais-eis Fraunhofer IAIS https://maven.iais.fraunhofer.de/artifactory/eis-ids-public From db4a3bdd0adb17cca096674f1b5864266503fa1c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Aug 2022 11:02:44 +0200 Subject: [PATCH 231/433] Bump alpine (#351) --- .../src/main/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile index 43eca93dc..ca59ecaf3 100644 --- a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile @@ -10,7 +10,7 @@ # Contributors: # Mercedes-Benz Tech Innovation GmbH - Initial Dockerfile # -FROM alpine:3.16.1 as otel +FROM alpine:3.16.2 as otel ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" From 2b8167de641b871a9b06293d2aa46e72611c3a45 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Aug 2022 11:02:48 +0200 Subject: [PATCH 232/433] Bump alpine in /edc-dataplane/edc-dataplane-azure-vault/src/main/docker (#352) --- .../edc-dataplane-azure-vault/src/main/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile b/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile index 132135fcf..9bada80f3 100644 --- a/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile +++ b/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile @@ -10,7 +10,7 @@ # Contributors: # Mercedes-Benz Tech Innovation GmbH - Initial Dockerfile # -FROM alpine:3.16.1 as otel +FROM alpine:3.16.2 as otel ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" From 77dce825314e9bcb2eae65f6d071bbb833e1e51c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Aug 2022 11:02:55 +0200 Subject: [PATCH 233/433] Bump alpine in /edc-controlplane/edc-controlplane-memory/src/main/docker (#353) --- .../edc-controlplane-memory/src/main/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile index 43eca93dc..ca59ecaf3 100644 --- a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile @@ -10,7 +10,7 @@ # Contributors: # Mercedes-Benz Tech Innovation GmbH - Initial Dockerfile # -FROM alpine:3.16.1 as otel +FROM alpine:3.16.2 as otel ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" From 70bee79ad5311ad0eb7285ff5b0441be8bc3102c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Aug 2022 11:03:02 +0200 Subject: [PATCH 234/433] Bump alpine (#354) --- .../edc-controlplane-postgresql/src/main/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile index 43eca93dc..ca59ecaf3 100644 --- a/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile @@ -10,7 +10,7 @@ # Contributors: # Mercedes-Benz Tech Innovation GmbH - Initial Dockerfile # -FROM alpine:3.16.1 as otel +FROM alpine:3.16.2 as otel ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" From 2ed7b99d5ab1d6e07ddddbd1c71ae4d15d98821b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Aug 2022 11:03:18 +0200 Subject: [PATCH 235/433] Bump postgresql from 42.4.1 to 42.4.2 (#356) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d0fe8796b..14721faa5 100644 --- a/pom.xml +++ b/pom.xml @@ -67,7 +67,7 @@ 0.0.1-milestone-5 1.2.2 - 42.4.1 + 42.4.2 9.1.5 From bd41e82c0d783c02fdf4f40f5c747f0e50bd707e Mon Sep 17 00:00:00 2001 From: Brendan Cronin Date: Thu, 18 Aug 2022 11:39:33 +0200 Subject: [PATCH 236/433] Exclude query in KICS Signed-off-by: Brendan Cronin --- .github/workflows/{checkov.yaml => kics.yaml} | 17 +++-------------- .../src/main/docker/Dockerfile | 1 - 2 files changed, 3 insertions(+), 15 deletions(-) rename .github/workflows/{checkov.yaml => kics.yaml} (50%) diff --git a/.github/workflows/checkov.yaml b/.github/workflows/kics.yaml similarity index 50% rename from .github/workflows/checkov.yaml rename to .github/workflows/kics.yaml index 59c78d465..b1bad81c7 100644 --- a/.github/workflows/checkov.yaml +++ b/.github/workflows/kics.yaml @@ -25,26 +25,15 @@ jobs: - name: KICS scan uses: checkmarx/kics-github-action@master with: - # Scanning directory . path: "." - # Fail on HIGH severity results fail_on: high - # Disable secrets detection - we use GitGuardian disable_secrets: true - # when provided with a directory on output_path - # it will generate the specified reports file named 'results.{extension}' - # in this example it will generate: - # - results-dir/results.json - # - results-dir/results.sarif output_path: kicsResults/ output_formats: "json,sarif" - # If you want KICS to ignore the results and return exit status code 0 unless a KICS engine error happens - # ignore_on_exit: results - # GITHUB_TOKEN enables this github action to access github API and post comments in a pull request - # token: ${{ secrets.GITHUB_TOKEN }} - # enable_comments: true + exclude_queries: "fd54f200-402c-4333-a5a4-36ef6709af2f" + # Excluded queries are: + # fd54f200-402c-4333-a5a4-36ef6709af2f Missing User Instruction - # Upload findings to GitHub Advanced Security Dashboard - name: Upload SARIF file for GitHub Advanced Security Dashboard if: always() uses: github/codeql-action/upload-sarif@v1 diff --git a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile index d8500ede3..43eca93dc 100644 --- a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile @@ -17,7 +17,6 @@ ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-in RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar FROM gcr.io/distroless/java11-debian11 -USER edc ARG JAR ARG LIB From e070afdb44a16a18e19730eda4709b4ffa89ea0e Mon Sep 17 00:00:00 2001 From: Brendan Cronin Date: Thu, 18 Aug 2022 12:09:40 +0200 Subject: [PATCH 237/433] Add exceptions and version images Signed-off-by: Brendan Cronin --- .github/workflows/kics.yaml | 3 ++- .../edc-controlplane-memory/src/main/docker/Dockerfile | 2 +- .../src/main/docker/Dockerfile | 2 +- .../edc-controlplane-postgresql/src/main/docker/Dockerfile | 2 +- .../edc-dataplane-azure-vault/src/main/docker/Dockerfile | 2 +- .../edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile | 2 +- 6 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/kics.yaml b/.github/workflows/kics.yaml index b1bad81c7..c361e7738 100644 --- a/.github/workflows/kics.yaml +++ b/.github/workflows/kics.yaml @@ -30,9 +30,10 @@ jobs: disable_secrets: true output_path: kicsResults/ output_formats: "json,sarif" - exclude_queries: "fd54f200-402c-4333-a5a4-36ef6709af2f" + exclude_queries: "fd54f200-402c-4333-a5a4-36ef6709af2f,b03a748a-542d-44f4-bb86-9199ab4fd2d5" # Excluded queries are: # fd54f200-402c-4333-a5a4-36ef6709af2f Missing User Instruction + # b03a748a-542d-44f4-bb86-9199ab4fd2d5 Healthcheck Instruction Missing - name: Upload SARIF file for GitHub Advanced Security Dashboard if: always() diff --git a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile index 43eca93dc..33b712a99 100644 --- a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile @@ -16,7 +16,7 @@ ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-in RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM gcr.io/distroless/java11-debian11 +FROM gcr.io/distroless/java11-debian11@sha256:188fb9e96dd6070c3ad38b53a2deca86aa14b5dc0378183d6519cce34116ea24 ARG JAR ARG LIB diff --git a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile index 43eca93dc..33b712a99 100644 --- a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile @@ -16,7 +16,7 @@ ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-in RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM gcr.io/distroless/java11-debian11 +FROM gcr.io/distroless/java11-debian11@sha256:188fb9e96dd6070c3ad38b53a2deca86aa14b5dc0378183d6519cce34116ea24 ARG JAR ARG LIB diff --git a/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile index 43eca93dc..33b712a99 100644 --- a/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile @@ -16,7 +16,7 @@ ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-in RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM gcr.io/distroless/java11-debian11 +FROM gcr.io/distroless/java11-debian11@sha256:188fb9e96dd6070c3ad38b53a2deca86aa14b5dc0378183d6519cce34116ea24 ARG JAR ARG LIB diff --git a/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile b/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile index 132135fcf..fdc0e5cf2 100644 --- a/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile +++ b/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile @@ -16,7 +16,7 @@ ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-in RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM gcr.io/distroless/java11-debian11 +FROM gcr.io/distroless/java11-debian11@sha256:188fb9e96dd6070c3ad38b53a2deca86aa14b5dc0378183d6519cce34116ea24 ARG JAR ARG LIB diff --git a/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile b/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile index 132135fcf..fdc0e5cf2 100644 --- a/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile +++ b/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile @@ -16,7 +16,7 @@ ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-in RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM gcr.io/distroless/java11-debian11 +FROM gcr.io/distroless/java11-debian11@sha256:188fb9e96dd6070c3ad38b53a2deca86aa14b5dc0378183d6519cce34116ea24 ARG JAR ARG LIB From c695e8d2e8940ab5434347d4249c405c50ad042a Mon Sep 17 00:00:00 2001 From: Brendan Cronin Date: Thu, 18 Aug 2022 12:30:27 +0200 Subject: [PATCH 238/433] Use tag for image Signed-off-by: Brendan Cronin --- .../edc-controlplane-memory/src/main/docker/Dockerfile | 2 +- .../src/main/docker/Dockerfile | 2 +- .../edc-controlplane-postgresql/src/main/docker/Dockerfile | 2 +- .../edc-dataplane-azure-vault/src/main/docker/Dockerfile | 2 +- .../edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile index 33b712a99..7e0682a63 100644 --- a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile @@ -16,7 +16,7 @@ ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-in RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM gcr.io/distroless/java11-debian11@sha256:188fb9e96dd6070c3ad38b53a2deca86aa14b5dc0378183d6519cce34116ea24 +FROM gcr.io/distroless/java11-debian11:sha256-fc4d68e4e326bb1013f67b4e8f86dd75a8af686e3e499e4115ecfe0f524b2aa6.sig ARG JAR ARG LIB diff --git a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile index 33b712a99..7e0682a63 100644 --- a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile @@ -16,7 +16,7 @@ ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-in RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM gcr.io/distroless/java11-debian11@sha256:188fb9e96dd6070c3ad38b53a2deca86aa14b5dc0378183d6519cce34116ea24 +FROM gcr.io/distroless/java11-debian11:sha256-fc4d68e4e326bb1013f67b4e8f86dd75a8af686e3e499e4115ecfe0f524b2aa6.sig ARG JAR ARG LIB diff --git a/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile index 33b712a99..7e0682a63 100644 --- a/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile @@ -16,7 +16,7 @@ ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-in RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM gcr.io/distroless/java11-debian11@sha256:188fb9e96dd6070c3ad38b53a2deca86aa14b5dc0378183d6519cce34116ea24 +FROM gcr.io/distroless/java11-debian11:sha256-fc4d68e4e326bb1013f67b4e8f86dd75a8af686e3e499e4115ecfe0f524b2aa6.sig ARG JAR ARG LIB diff --git a/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile b/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile index fdc0e5cf2..848b46422 100644 --- a/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile +++ b/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile @@ -16,7 +16,7 @@ ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-in RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM gcr.io/distroless/java11-debian11@sha256:188fb9e96dd6070c3ad38b53a2deca86aa14b5dc0378183d6519cce34116ea24 +FROM gcr.io/distroless/java11-debian11:sha256-fc4d68e4e326bb1013f67b4e8f86dd75a8af686e3e499e4115ecfe0f524b2aa6.sig ARG JAR ARG LIB diff --git a/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile b/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile index fdc0e5cf2..848b46422 100644 --- a/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile +++ b/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile @@ -16,7 +16,7 @@ ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-in RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM gcr.io/distroless/java11-debian11@sha256:188fb9e96dd6070c3ad38b53a2deca86aa14b5dc0378183d6519cce34116ea24 +FROM gcr.io/distroless/java11-debian11:sha256-fc4d68e4e326bb1013f67b4e8f86dd75a8af686e3e499e4115ecfe0f524b2aa6.sig ARG JAR ARG LIB From 6c63db25ad02caea988ccfb83a999feed25d8dd3 Mon Sep 17 00:00:00 2001 From: Brendan Cronin Date: Thu, 18 Aug 2022 13:11:06 +0200 Subject: [PATCH 239/433] Try different tag Signed-off-by: Brendan Cronin --- .../edc-controlplane-memory/src/main/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile index 7e0682a63..124219d9c 100644 --- a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile @@ -16,7 +16,7 @@ ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-in RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM gcr.io/distroless/java11-debian11:sha256-fc4d68e4e326bb1013f67b4e8f86dd75a8af686e3e499e4115ecfe0f524b2aa6.sig +FROM gcr.io/distroless/java11-debian11@sha256:dee9240c64471f1776a6b37f315890aba14ff4bc89ad247eeb34ec79fdeb24f4 ARG JAR ARG LIB From 0374b258e541888571fdd33b741e8ed6fae40329 Mon Sep 17 00:00:00 2001 From: Brendan Cronin Date: Thu, 18 Aug 2022 13:13:56 +0200 Subject: [PATCH 240/433] Consistently use new tag Signed-off-by: Brendan Cronin --- .../src/main/docker/Dockerfile | 2 +- .../edc-controlplane-postgresql/src/main/docker/Dockerfile | 2 +- .../edc-dataplane-azure-vault/src/main/docker/Dockerfile | 2 +- .../edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile index 7e0682a63..124219d9c 100644 --- a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile @@ -16,7 +16,7 @@ ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-in RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM gcr.io/distroless/java11-debian11:sha256-fc4d68e4e326bb1013f67b4e8f86dd75a8af686e3e499e4115ecfe0f524b2aa6.sig +FROM gcr.io/distroless/java11-debian11@sha256:dee9240c64471f1776a6b37f315890aba14ff4bc89ad247eeb34ec79fdeb24f4 ARG JAR ARG LIB diff --git a/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile index 7e0682a63..124219d9c 100644 --- a/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile @@ -16,7 +16,7 @@ ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-in RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM gcr.io/distroless/java11-debian11:sha256-fc4d68e4e326bb1013f67b4e8f86dd75a8af686e3e499e4115ecfe0f524b2aa6.sig +FROM gcr.io/distroless/java11-debian11@sha256:dee9240c64471f1776a6b37f315890aba14ff4bc89ad247eeb34ec79fdeb24f4 ARG JAR ARG LIB diff --git a/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile b/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile index 848b46422..93ff54736 100644 --- a/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile +++ b/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile @@ -16,7 +16,7 @@ ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-in RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM gcr.io/distroless/java11-debian11:sha256-fc4d68e4e326bb1013f67b4e8f86dd75a8af686e3e499e4115ecfe0f524b2aa6.sig +FROM gcr.io/distroless/java11-debian11@sha256:dee9240c64471f1776a6b37f315890aba14ff4bc89ad247eeb34ec79fdeb24f4 ARG JAR ARG LIB diff --git a/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile b/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile index 848b46422..93ff54736 100644 --- a/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile +++ b/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile @@ -16,7 +16,7 @@ ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-in RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM gcr.io/distroless/java11-debian11:sha256-fc4d68e4e326bb1013f67b4e8f86dd75a8af686e3e499e4115ecfe0f524b2aa6.sig +FROM gcr.io/distroless/java11-debian11@sha256:dee9240c64471f1776a6b37f315890aba14ff4bc89ad247eeb34ec79fdeb24f4 ARG JAR ARG LIB From b8cfc4e487cbb7095103ed7ef3853e09f9281cf9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Aug 2022 14:48:53 +0200 Subject: [PATCH 241/433] Bump alpine (#355) --- .../edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile b/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile index 132135fcf..9bada80f3 100644 --- a/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile +++ b/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile @@ -10,7 +10,7 @@ # Contributors: # Mercedes-Benz Tech Innovation GmbH - Initial Dockerfile # -FROM alpine:3.16.1 as otel +FROM alpine:3.16.2 as otel ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" From b40c872eec95fa0a2da7d9191fa4cacc02f55ff8 Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Fri, 19 Aug 2022 09:51:22 +0200 Subject: [PATCH 242/433] Add data-encryption extension (#340) --- .github/workflows/business-tests.yaml | 4 +- docs/data-transfer/Transfer Data.md | 3 +- docs/migration/Version_0.0.x_0.1.x.md | 20 ++- docs/release-notes/Version 0.1.0.md | 64 +++++++++ edc-controlplane/README.md | 15 +- .../edc-controlplane-base/pom.xml | 4 + .../edc-controlplane-memory/pom.xml | 1 + .../pom.xml | 1 + .../edc-controlplane-postgresql/pom.xml | 1 + .../edc-dataplane-azure-vault/pom.xml | 3 +- .../edc-dataplane-hashicorp-vault/pom.xml | 5 +- edc-extensions/data-encryption/README.md | 37 +++++ edc-extensions/data-encryption/pom.xml | 130 ++++++++++++++++++ .../encryption/DataEncryptionExtension.java | 116 ++++++++++++++++ .../algorithms/CryptoAlgorithm.java | 34 +++++ .../algorithms/aes/AesAlgorithm.java | 92 +++++++++++++ .../aes/AesInitializationVectorIterator.java | 67 +++++++++ .../algorithms/aes/ByteCounter.java | 75 ++++++++++ .../edc/data/encryption/data/CryptoData.java | 20 +++ .../encryption/data/CryptoDataFactory.java | 29 ++++ .../data/CryptoDataFactoryImpl.java | 70 ++++++++++ .../data/encryption/data/DecryptedData.java | 16 +++ .../data/encryption/data/EncryptedData.java | 16 +++ .../encrypter/AesDataEncrypterImpl.java | 102 ++++++++++++++ .../encrypter/DataEncrypterConfiguration.java | 27 ++++ .../encrypter/DataEncrypterFactory.java | 88 ++++++++++++ .../edc/data/encryption/key/AesKey.java | 20 +++ .../edc/data/encryption/key/CryptoKey.java | 16 +++ .../data/encryption/key/CryptoKeyFactory.java | 20 +++ .../encryption/key/CryptoKeyFactoryImpl.java | 39 ++++++ .../encryption/provider/AesKeyProvider.java | 62 +++++++++ .../provider/CachingKeyProvider.java | 73 ++++++++++ .../data/encryption/provider/KeyProvider.java | 23 ++++ .../edc/data/encryption/util/ArrayUtil.java | 42 ++++++ ...spaceconnector.spi.system.ServiceExtension | 14 ++ .../DataEncryptionExtensionTest.java | 98 +++++++++++++ .../algorithms/aes/AesAlgorithmTest.java | 86 ++++++++++++ .../AesInitializationVectorIteratorTest.java | 82 +++++++++++ .../algorithms/aes/ByteCounterTest.java | 90 ++++++++++++ .../DataEncrypterAesComponentTest.java | 99 +++++++++++++ .../encrypter/DataEncrypterFactoryTest.java | 87 ++++++++++++ .../key/CryptoKeyFactoryImplTest.java | 38 +++++ .../provider/AesKeyProviderTest.java | 74 ++++++++++ .../provider/CachingKeyProviderTest.java | 95 +++++++++++++ .../data/encryption/util/ArrayUtilTest.java | 66 +++++++++ .../README.md | 2 +- edc-extensions/pom.xml | 1 + .../V0_0_3__Snapshot_20220815_Update.sql | 15 ++ .../V0_0_3__Snapshot_20220815_Update.sql | 15 ++ .../V0_0_4__Snapshot_20220815_Update.sql | 16 +++ .../V0_0_3__Snapshot_20220815_Update.sql | 15 ++ .../V0_0_5__Snapshot_20220815_Update.sql | 18 +++ edc-tests/pom.xml | 4 + .../deployment/helm/all-in-one/Chart.yaml | 4 +- .../deployment/helm/all-in-one/values.yaml | 50 ++++++- .../features/ContractNegotiation.feature | 12 +- pom.xml | 7 +- 57 files changed, 2283 insertions(+), 40 deletions(-) create mode 100644 docs/release-notes/Version 0.1.0.md create mode 100644 edc-extensions/data-encryption/README.md create mode 100644 edc-extensions/data-encryption/pom.xml create mode 100644 edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/DataEncryptionExtension.java create mode 100644 edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/algorithms/CryptoAlgorithm.java create mode 100644 edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/algorithms/aes/AesAlgorithm.java create mode 100644 edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/algorithms/aes/AesInitializationVectorIterator.java create mode 100644 edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/algorithms/aes/ByteCounter.java create mode 100644 edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/data/CryptoData.java create mode 100644 edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/data/CryptoDataFactory.java create mode 100644 edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/data/CryptoDataFactoryImpl.java create mode 100644 edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/data/DecryptedData.java create mode 100644 edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/data/EncryptedData.java create mode 100644 edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/encrypter/AesDataEncrypterImpl.java create mode 100644 edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/encrypter/DataEncrypterConfiguration.java create mode 100644 edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/encrypter/DataEncrypterFactory.java create mode 100644 edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/key/AesKey.java create mode 100644 edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/key/CryptoKey.java create mode 100644 edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/key/CryptoKeyFactory.java create mode 100644 edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/key/CryptoKeyFactoryImpl.java create mode 100644 edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/provider/AesKeyProvider.java create mode 100644 edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/provider/CachingKeyProvider.java create mode 100644 edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/provider/KeyProvider.java create mode 100644 edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/util/ArrayUtil.java create mode 100644 edc-extensions/data-encryption/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension create mode 100644 edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/DataEncryptionExtensionTest.java create mode 100644 edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/algorithms/aes/AesAlgorithmTest.java create mode 100644 edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/algorithms/aes/AesInitializationVectorIteratorTest.java create mode 100644 edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/algorithms/aes/ByteCounterTest.java create mode 100644 edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/encrypter/DataEncrypterAesComponentTest.java create mode 100644 edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/encrypter/DataEncrypterFactoryTest.java create mode 100644 edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/key/CryptoKeyFactoryImplTest.java create mode 100644 edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/provider/AesKeyProviderTest.java create mode 100644 edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/provider/CachingKeyProviderTest.java create mode 100644 edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/util/ArrayUtilTest.java create mode 100644 edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/asset/V0_0_3__Snapshot_20220815_Update.sql create mode 100644 edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractdefinition/V0_0_3__Snapshot_20220815_Update.sql create mode 100644 edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractnegotiation/V0_0_4__Snapshot_20220815_Update.sql create mode 100644 edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/policy/V0_0_3__Snapshot_20220815_Update.sql create mode 100644 edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/transferprocess/V0_0_5__Snapshot_20220815_Update.sql diff --git a/.github/workflows/business-tests.yaml b/.github/workflows/business-tests.yaml index 38757d37d..de3287130 100644 --- a/.github/workflows/business-tests.yaml +++ b/.github/workflows/business-tests.yaml @@ -100,14 +100,14 @@ jobs: echo "SOKRATES_DATA_MANAGEMENT_API_AUTH_KEY=password" | tee -a ${GITHUB_ENV} echo "SOKRATES_DATA_MANAGEMENT_URL=http://sokrates-edc-controlplane:8181/data" | tee -a ${GITHUB_ENV} echo "SOKRATES_IDS_URL=http://sokrates-edc-controlplane:8282/api/v1/ids" | tee -a ${GITHUB_ENV} - echo "SOKRATES_DATA_PLANE_URL=http://sokrates-edc-dataplane:8185/api/public" | tee -a ${GITHUB_ENV} + echo "SOKRATES_DATA_PLANE_URL=http://sokrates-edc-dataplane:8185/api/public/" | tee -a ${GITHUB_ENV} echo "SOKRATES_DATABASE_URL=jdbc:postgresql://plato-postgresql:5432/edc" | tee -a ${GITHUB_ENV} echo "SOKRATES_DATABASE_USER=postgresql_sandbox_user" | tee -a ${GITHUB_ENV} echo "SOKRATES_DATABASE_PASSWORD=psql_password" | tee -a ${GITHUB_ENV} echo "PLATO_DATA_MANAGEMENT_API_AUTH_KEY=password" | tee -a ${GITHUB_ENV} echo "PLATO_DATA_MANAGEMENT_URL=http://plato-edc-controlplane:8181/data" | tee -a ${GITHUB_ENV} echo "PLATO_IDS_URL=http://plato-edc-controlplane:8282/api/v1/ids" | tee -a ${GITHUB_ENV} - echo "PLATO_DATA_PLANE_URL=http://plato-edc-dataplane:8185/api/public" | tee -a ${GITHUB_ENV} + echo "PLATO_DATA_PLANE_URL=http://plato-edc-dataplane:8185/api/public/" | tee -a ${GITHUB_ENV} echo "PLATO_DATABASE_URL=jdbc:postgresql://plato-postgresql:5432/edc" | tee -a ${GITHUB_ENV} echo "PLATO_DATABASE_USER=postgresql_sandbox_user" | tee -a ${GITHUB_ENV} echo "PLATO_DATABASE_PASSWORD=psql_password" | tee -a ${GITHUB_ENV} diff --git a/docs/data-transfer/Transfer Data.md b/docs/data-transfer/Transfer Data.md index dd945fbf6..d9665d0f3 100644 --- a/docs/data-transfer/Transfer Data.md +++ b/docs/data-transfer/Transfer Data.md @@ -48,6 +48,7 @@ Initialize the following environment variables, that are used in the upcoming AP export PLATO_DATAMGMT_URL=$(minikube service plato-edc-controlplane -n edc-all-in-one --url | sed -n 3p) export PLATO_IDS_URL=$(minikube service plato-edc-controlplane -n edc-all-in-one --url | sed -n 5p) export SOKRATES_DATAMGMT_URL=$(minikube service sokrates-edc-controlplane -n edc-all-in-one --url | sed -n 3p) +export SOKRATES_BACKEND_URL=$(minikube service sokrates-backend-application -n edc-all-in-one --url | sed -n 2p) ``` ## 1. Setup Data Offer @@ -146,7 +147,7 @@ locally. In this demo the transfer can be verified by executing a simple `cat` c ![Sequence 1](diagrams/transfer_sequence_5.png) ```bash -echo $(kubectl exec -n edc-all-in-one --stdin --tty `kubectl get pod -n edc-all-in-one -l app.kubernetes.io/name=sokratesbackendapplication --template "{{ with index .items ${POD_INDEX:-0} }}{{ .metadata.name }}{{ end }}"` -- /usr/bin/cat /tmp/data/${TRANSFER_PROCESS_ID}) | jq +curl -X GET ${SOKRATES_BACKEND_URL}/${TRANSFER_PROCESS_ID} -H "Accept: application/octet-stream" -s | jq ``` # Delete All Data diff --git a/docs/migration/Version_0.0.x_0.1.x.md b/docs/migration/Version_0.0.x_0.1.x.md index 245172067..f92accef5 100644 --- a/docs/migration/Version_0.0.x_0.1.x.md +++ b/docs/migration/Version_0.0.x_0.1.x.md @@ -280,15 +280,23 @@ curl -X POST "${DATA_MGMT_ENDPOINT}/data/contractdefinitions" --header "X-Api-Ke +### 2.4 Data Address + +When using a Data Address of type `HttpData` please notice that the property `endpoint` changed to `baseUrl`. This property is mostly used when creating assets. + +

+ +Example Call + +```bash +curl -X POST "$PLATO_DATAMGMT_URL/data/assets" --header "X-Api-Key: password" --header "Content-Type: application/json" --data "{ \"asset\": { \"properties\": { \"asset:prop:id\": \"1\", \"asset:prop:description\": \"Product EDC Demo Asset\" } }, \"dataAddress\": { \"properties\": { \"type\": \"HttpData\", \"baseUrl\": \"https://jsonplaceholder.typicode.com/todos/1\" } } }" -s -o /dev/null -w 'Response Code: %{http_code}\n' +``` + +
+ ## 3. Connector Configuration ### 3.1 Token Validation Endpoint Setting In the past the token validation endpoint was configured in `edc.controlplane.validation-endpoint`. This setting key must be renamed to `edc.dataplane.token.validation.endpoint`. - - - -# TODO -- changed HttpData endpoint to baseUrl -- \ No newline at end of file diff --git a/docs/release-notes/Version 0.1.0.md b/docs/release-notes/Version 0.1.0.md new file mode 100644 index 000000000..c204d6e9a --- /dev/null +++ b/docs/release-notes/Version 0.1.0.md @@ -0,0 +1,64 @@ +# Release Notes Version 0.1.0 +19.08.2022 + +> **BREAKING CHANGES** +> +> When upgrading from version 0.0.x please consolidate the migration documentation before ([link](../migration/Version_0.0.x_0.1.x.md)). + +## 0. Summary + +- 1. Eclipse Dataspace Connector Update +- 2. New Extensions + - 2.1 Data Plane Selector Extensions + - 2.2 Data Encryption Extension + - 2.3 AWS S3 Extension +- 3. Bug Fixes + +## 1. Eclipse Dataspace Connector Update + +Upgraded the Eclipse Dataspace Connector Extensions to version 0.0.1-20220818-SNAPSHOT. Please be aware that this introduces some breaking changes. + +Code Repository +https://github.com/eclipse-dataspaceconnector/DataSpaceConnector + +Snapshot Artifact Repository +https://oss.sonatype.org/#nexus-search;quick~org.eclipse.dataspaceconnector + + +## 2. New Extensions + +The following extensions are now included in the base image of the connector. + +### 2.1 Data Plane Selector Extensions + +> **Introduces new mandatory settings** + +New extension to add data plane instances by configuration. Each data plane instance must be registered at the control plane. + +[Documentation](../../edc-extensions/dataplane-selector-configuration/README.md) + +### 2.2 Data Encryption Extension + +> **Introduces new mandatory settings** + +New extension to protect possibly confidential data, that may be send out to other connectors as transfer tokens. + +[Documentation](../../edc-extensions/data-encryption/README.md) + +### 2.3 AWS S3 Extension + +New extension to transfer data from and to AWS S3 Buckets. + +[Documentation](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/aws/data-plane-s3) + +## 3. Bug Fixes + +This section covers the most relevant bug fixes, included in this version. + +- Contract-Offer-Receiving-Connectors must also pass the ContractPolicy of the ContractDefinition before receiving offers([issue](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1331)) + +- Deletion of Asset becomes impossible when Contract Negotiation exists([issue](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1403)) + +- Deletion of Policy becomes impossible when Contract Definition exists([issue](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1410)) + +- DataAddress is passed unencrypted from DataProvider to DataConsumer ([issue](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1504)) \ No newline at end of file diff --git a/edc-controlplane/README.md b/edc-controlplane/README.md index 280d90230..45e22e578 100644 --- a/edc-controlplane/README.md +++ b/edc-controlplane/README.md @@ -45,22 +45,13 @@ EDC commit the Product-EDC uses. - Contract negotiation not working when `web.http.ids.path` is configured/changed ([issue](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1249)) - **Workaround:** Don't configure `web.http.ids.path`, so that the default path is used. +- HttpProxy Transfer: Provider Control Plane spams Consumer Control Plane + HttpProxy Backend Application with requests([issue](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1840)) + - **Possible Workaround:** Reconfigure data plane URL from `http://dataplane:8185/api/public` to `http://dataplane:8185/api/public/` + **Data Management API** - Contract negotiation not working when initiated with policy id ([issue](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1251)) - **Workaround:** The DataManagement API can also initiate a contract negotiation using the actual policy object. -- Contract-Offer-Receiving-Connectors must also pass the ContractPolicy of the ContractDefinition before receiving offers([issue](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1331)) - -- Deletion of Asset becomes impossible when Contract Negotiation exists([issue](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1403)) - - **Workaround:** Delete Asset from DB manually. Be aware that deleting Assets, that are part of a ContractNegotiation or ContractAgreement, may corrupt the connector instance! - -- Deletion of Policy becomes impossible when Contract Definition exists([issue](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1410)) - - **Workaround:** Delete Policy from DB manually. Be aware that deleting Policies, that are part of a ContractDefinition, ContractNegotiation or ContractAgreement, may corrupt the connector instance! - **Other** - Non-IDS-Transformable-ContractDefinition causes connector to be unable to send out self-descriptions/catalogs([issue](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1265)) - **Workaround:** Delete non-transformable ContractDefinition or Policy. - -**Security** -- DataAddress is passed unencrypted from DataProvider to DataConsumer ([issue](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1504)) - - **Workaround:** Use only test data! diff --git a/edc-controlplane/edc-controlplane-base/pom.xml b/edc-controlplane/edc-controlplane-base/pom.xml index 8d8ae0085..761e63fe0 100644 --- a/edc-controlplane/edc-controlplane-base/pom.xml +++ b/edc-controlplane/edc-controlplane-base/pom.xml @@ -46,6 +46,10 @@ net.catenax.edc.extensions dataplane-selector-configuration
+ + net.catenax.edc.extensions + data-encryption + diff --git a/edc-controlplane/edc-controlplane-memory/pom.xml b/edc-controlplane/edc-controlplane-memory/pom.xml index 51829ab88..cbbe26fbb 100644 --- a/edc-controlplane/edc-controlplane-memory/pom.xml +++ b/edc-controlplane/edc-controlplane-memory/pom.xml @@ -58,6 +58,7 @@ org.eclipse.dataspaceconnector.boot.system.runtime.BaseRuntime + false diff --git a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/pom.xml b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/pom.xml index 85da14e65..00f2fc926 100644 --- a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/pom.xml +++ b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/pom.xml @@ -57,6 +57,7 @@ true lib/ org.eclipse.dataspaceconnector.boot.system.runtime.BaseRuntime + false diff --git a/edc-controlplane/edc-controlplane-postgresql/pom.xml b/edc-controlplane/edc-controlplane-postgresql/pom.xml index 2b1cb54ad..4a356f5ce 100644 --- a/edc-controlplane/edc-controlplane-postgresql/pom.xml +++ b/edc-controlplane/edc-controlplane-postgresql/pom.xml @@ -59,6 +59,7 @@ org.eclipse.dataspaceconnector.boot.system.runtime.BaseRuntime + false diff --git a/edc-dataplane/edc-dataplane-azure-vault/pom.xml b/edc-dataplane/edc-dataplane-azure-vault/pom.xml index 87cc9cee3..88e82c184 100644 --- a/edc-dataplane/edc-dataplane-azure-vault/pom.xml +++ b/edc-dataplane/edc-dataplane-azure-vault/pom.xml @@ -56,7 +56,8 @@ true lib/ - org.eclipse.dataspaceconnector.boot.system.runtime.BaseRuntime + org.eclipse.dataspaceconnector.boot.system.runtime.BaseRuntime + false diff --git a/edc-dataplane/edc-dataplane-hashicorp-vault/pom.xml b/edc-dataplane/edc-dataplane-hashicorp-vault/pom.xml index 656fa6440..228a9ad20 100644 --- a/edc-dataplane/edc-dataplane-hashicorp-vault/pom.xml +++ b/edc-dataplane/edc-dataplane-hashicorp-vault/pom.xml @@ -56,9 +56,8 @@ true lib/ - - org.eclipse.dataspaceconnector.boot.system.runtime.BaseRuntime - + org.eclipse.dataspaceconnector.boot.system.runtime.BaseRuntime + false diff --git a/edc-extensions/data-encryption/README.md b/edc-extensions/data-encryption/README.md new file mode 100644 index 000000000..58a620824 --- /dev/null +++ b/edc-extensions/data-encryption/README.md @@ -0,0 +1,37 @@ +# Data Encryption Extension + +The Eclipse Dataspace Connector encrypts sensitive information inside a token it sends to other applications (from possibly other companies). This extension implements the encryption of this data and should be used with secure keys and algorithms at all times. + +## Configuration + +| Key | Description | Mandatory | Default | +|:--------------------------------------------|:-----------------------------------------------------------------------------------------------------------------|-----------|------------------| +| edc.data.encryption.keys.alias | Keys for encryption and decryption of the data must be stored in the Vault under the configured alias. | X | | +| edc.data.encryption.algorithm | Algorithm for encryption and decryption. Must be ether 'AES' or 'NONE'. | | AES | +| edc.data.encryption.caching.enabled | Enable caching to request only keys from the vault after the cache expires. | | false | +| edc.data.encryption.caching.seconds | Duration in seconds until the cache expires. | | 3600 | + +## Strategies + +### 1. AES + +The Advanced Encryption Standard (AES) is the default encryption algorithm. For Authenticated Encryption with Associated Data (AEAD) it uses the Galois/Counter Mode or GCM. + +When using AES-GCM the key length must be ether 128-, 196- or 256bit. Keys must be stored stored Base64 encoded in the Vault, separated by a comma. + +It's possible to generate Keys using OpenSSL +```bash +# 128 Bit +openssl rand -base64 16 + +# 196 Bit +openssl rand -base64 24 + +# 256 Bit +openssl rand -base64 32 +``` + + +### 2. NONE + +This strategy does apply no encryption at all and should only be used for debugging purposes. Using NONE encryption may leak sensitive data to other connectors! \ No newline at end of file diff --git a/edc-extensions/data-encryption/pom.xml b/edc-extensions/data-encryption/pom.xml new file mode 100644 index 000000000..6f8150db7 --- /dev/null +++ b/edc-extensions/data-encryption/pom.xml @@ -0,0 +1,130 @@ + + + + + edc-extensions + net.catenax.edc.extensions + 0.0.7-SNAPSHOT + + 4.0.0 + + data-encryption + + + ${project.basedir}/src/main/java + ${originalSourceDirectory} + ${project.build.directory}/delombok + ${project.groupId}_${project.artifactId} + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${maven.compiler.source} + ${maven.compiler.target} + ${project.build.sourceEncoding} + + + org.projectlombok + lombok + ${org.projectlombok.lombok.version} + + + + + + + org.projectlombok + lombok-maven-plugin + ${org.projectlombok.lombok.maven.plugin.version} + + + generate-sources + + delombok + + + + + ${originalSourceDirectory} + ${delombokSourceDirectory} + false + UTF-8 + + skip + + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar + + + + + + + + + + + + org.eclipse.dataspaceconnector + core-spi + + + org.eclipse.dataspaceconnector + data-plane-transfer-spi + + + + + org.projectlombok + lombok + + + org.bouncycastle + bcpkix-jdk15on + ${org.bouncycastle.bcpkix-jdk15on.version} + + + + + org.junit.jupiter + junit-jupiter + test + + + org.mockito + mockito-core + test + + + org.mockito + mockito-inline + test + + + diff --git a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/DataEncryptionExtension.java b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/DataEncryptionExtension.java new file mode 100644 index 000000000..31ef2d92d --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/DataEncryptionExtension.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.data.encryption; + +import java.time.Duration; +import java.util.List; +import java.util.stream.Collectors; +import net.catenax.edc.data.encryption.encrypter.DataEncrypterConfiguration; +import net.catenax.edc.data.encryption.encrypter.DataEncrypterFactory; +import net.catenax.edc.data.encryption.key.AesKey; +import net.catenax.edc.data.encryption.key.CryptoKeyFactory; +import net.catenax.edc.data.encryption.key.CryptoKeyFactoryImpl; +import net.catenax.edc.data.encryption.provider.AesKeyProvider; +import org.eclipse.dataspaceconnector.spi.EdcException; +import org.eclipse.dataspaceconnector.spi.EdcSetting; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.security.Vault; +import org.eclipse.dataspaceconnector.spi.system.Provides; +import org.eclipse.dataspaceconnector.spi.system.Requires; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtension; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; +import org.eclipse.dataspaceconnector.transfer.dataplane.spi.security.DataEncrypter; + +@Provides({DataEncrypter.class}) +@Requires({Vault.class}) +public class DataEncryptionExtension implements ServiceExtension { + + public static final String NAME = "Data Encryption Extension"; + + @EdcSetting public static final String ENCRYPTION_KEY_SET = "edc.data.encryption.keys.alias"; + + @EdcSetting public static final String ENCRYPTION_ALGORITHM = "edc.data.encryption.algorithm"; + public static final String ENCRYPTION_ALGORITHM_DEFAULT = DataEncrypterFactory.AES_ALGORITHM; + + @EdcSetting public static final String CACHING_ENABLED = "edc.data.encryption.caching.enabled"; + public static final boolean CACHING_ENABLED_DEFAULT = false; + + @EdcSetting public static final String CACHING_SECONDS = "edc.data.encryption.caching.seconds"; + public static final int CACHING_SECONDS_DEFAULT = 3600; + + private static final CryptoKeyFactory cryptoKeyFactory = new CryptoKeyFactoryImpl(); + + private Monitor monitor; + private Vault vault; + private DataEncrypterConfiguration configuration; + + @Override + public String name() { + return NAME; + } + + @Override + public void start() { + final String keyAlias = configuration.getKeySetAlias(); + final String keySecret = vault.resolveSecret(keyAlias); + if (keySecret == null || keySecret.isEmpty()) { + throw new EdcException(NAME + ": No vault key secret found for alias " + keyAlias); + } + + if (configuration.getAlgorithm().equals(DataEncrypterFactory.AES_ALGORITHM)) { + try { + final AesKeyProvider aesKeyProvider = + createAesKeyProvider(vault, configuration.getKeySetAlias()); + final List keys = aesKeyProvider.getDecryptionKeySet().collect(Collectors.toList()); + monitor.debug( + String.format( + "Started " + NAME + ": Found %s registered AES keys in vault.", keys.size())); + } catch (Exception e) { + throw new EdcException( + NAME + ": AES keys from vault must be comma separated and Base64 encoded.", e); + } + } + } + + @Override + public void initialize(ServiceExtensionContext context) { + monitor = context.getMonitor(); + configuration = getConfiguration(context); + vault = context.getService(Vault.class); + + final DataEncrypterFactory factory = new DataEncrypterFactory(vault, monitor, cryptoKeyFactory); + + final DataEncrypter dataEncrypter = factory.create(configuration); + context.registerService(DataEncrypter.class, dataEncrypter); + } + + private static DataEncrypterConfiguration getConfiguration(ServiceExtensionContext context) { + final String key = context.getSetting(ENCRYPTION_KEY_SET, null); + if (key == null) { + throw new EdcException(NAME + ": Missing setting " + ENCRYPTION_KEY_SET); + } + + final String encryptionStrategy = + context.getSetting(ENCRYPTION_ALGORITHM, ENCRYPTION_ALGORITHM_DEFAULT); + final boolean cachingEnabled = context.getSetting(CACHING_ENABLED, CACHING_ENABLED_DEFAULT); + final int cachingSeconds = context.getSetting(CACHING_SECONDS, CACHING_SECONDS_DEFAULT); + + return new DataEncrypterConfiguration( + encryptionStrategy, key, cachingEnabled, Duration.ofSeconds(cachingSeconds)); + } + + private static AesKeyProvider createAesKeyProvider(Vault vault, String vaultKeySetAlias) { + return new AesKeyProvider(vault, vaultKeySetAlias, cryptoKeyFactory); + } +} diff --git a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/algorithms/CryptoAlgorithm.java b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/algorithms/CryptoAlgorithm.java new file mode 100644 index 000000000..0bc71c673 --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/algorithms/CryptoAlgorithm.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.data.encryption.algorithms; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import net.catenax.edc.data.encryption.data.DecryptedData; +import net.catenax.edc.data.encryption.data.EncryptedData; +import net.catenax.edc.data.encryption.key.CryptoKey; + +public interface CryptoAlgorithm { + EncryptedData encrypt(DecryptedData data, T key) + throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, + NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException; + + DecryptedData decrypt(EncryptedData data, T key) + throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, + NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException; +} diff --git a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/algorithms/aes/AesAlgorithm.java b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/algorithms/aes/AesAlgorithm.java new file mode 100644 index 000000000..8565ffd31 --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/algorithms/aes/AesAlgorithm.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.data.encryption.algorithms.aes; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import lombok.NonNull; +import net.catenax.edc.data.encryption.algorithms.CryptoAlgorithm; +import net.catenax.edc.data.encryption.data.CryptoDataFactory; +import net.catenax.edc.data.encryption.data.DecryptedData; +import net.catenax.edc.data.encryption.data.EncryptedData; +import net.catenax.edc.data.encryption.key.AesKey; +import net.catenax.edc.data.encryption.util.ArrayUtil; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +public class AesAlgorithm implements CryptoAlgorithm { + + private static final String AES_GCM = "AES/GCM/NoPadding"; + private static final String AES = "AES"; + private static final Object MONITOR = new Object(); + + @NonNull private final CryptoDataFactory cryptoDataFactory; + private AesInitializationVectorIterator initializationVectorIterator; + + public AesAlgorithm(CryptoDataFactory cryptoDataFactory) { + this.cryptoDataFactory = cryptoDataFactory; + this.initializationVectorIterator = new AesInitializationVectorIterator(); + } + + @Override + public synchronized EncryptedData encrypt(DecryptedData data, AesKey key) + throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, + NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException { + + final byte[] initializationVector; + synchronized (MONITOR) { + if (!initializationVectorIterator.hasNext()) { + initializationVectorIterator = new AesInitializationVectorIterator(); + } + if (!initializationVectorIterator.isInitialized()) { + initializationVectorIterator.initialize(); + } + + initializationVector = initializationVectorIterator.next(); + } + + Cipher cipher = Cipher.getInstance(AES_GCM, new BouncyCastleProvider()); + final SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), AES); + final GCMParameterSpec gcmParameterSpec = + new GCMParameterSpec(16 * 8 /* =128 */, initializationVector); + cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmParameterSpec); + byte[] encrypted = cipher.doFinal(data.getBytes()); + byte[] encryptedWithVector = ArrayUtil.concat(initializationVector, encrypted); + + return cryptoDataFactory.encryptedFromBytes(encryptedWithVector); + } + + @Override + public DecryptedData decrypt(EncryptedData data, AesKey key) + throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, + NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException { + byte[] encryptedWithVector = data.getBytes(); + byte[] initializationVector = ArrayUtil.subArray(encryptedWithVector, 0, 16); + byte[] encrypted = ArrayUtil.subArray(encryptedWithVector, 16, encryptedWithVector.length - 16); + + Cipher cipher = Cipher.getInstance(AES_GCM, new BouncyCastleProvider()); + final SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), AES); + final GCMParameterSpec gcmParameterSpec = + new GCMParameterSpec(16 * 8 /* =128 */, initializationVector); + cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec); + byte[] decryptedData = cipher.doFinal(encrypted); + return cryptoDataFactory.decryptedFromBytes(decryptedData); + } +} diff --git a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/algorithms/aes/AesInitializationVectorIterator.java b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/algorithms/aes/AesInitializationVectorIterator.java new file mode 100644 index 000000000..7d4dc6c2b --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/algorithms/aes/AesInitializationVectorIterator.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.data.encryption.algorithms.aes; + +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Iterator; +import java.util.NoSuchElementException; +import net.catenax.edc.data.encryption.util.ArrayUtil; + +public class AesInitializationVectorIterator implements Iterator { + + public static final int VECTOR_SIZE = 12; + public static final int NONCE_SIZE = 4; + public static final int SIZE = VECTOR_SIZE + NONCE_SIZE; + + private final ByteCounter counter; + private byte[] vector; + + public AesInitializationVectorIterator() { + counter = new ByteCounter(NONCE_SIZE); + } + + public AesInitializationVectorIterator(ByteCounter byteCounter) { + this.counter = byteCounter; + } + + @Override + public boolean hasNext() { + return !counter.isMaxed(); + } + + @Override + public byte[] next() { + if (vector == null) { + throw new IllegalStateException(getClass().getSimpleName() + " has not been initialized"); + } + if (counter.isMaxed()) { + throw new NoSuchElementException(getClass().getSimpleName() + " has no more elements"); + } + + counter.increment(); + return ArrayUtil.concat(vector, counter.getBytes()); + } + + public void initialize() throws NoSuchAlgorithmException { + SecureRandom random = SecureRandom.getInstanceStrong(); + byte[] newVector = new byte[VECTOR_SIZE]; + random.nextBytes(newVector); + vector = newVector; + } + + public boolean isInitialized() { + return vector != null; + } +} diff --git a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/algorithms/aes/ByteCounter.java b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/algorithms/aes/ByteCounter.java new file mode 100644 index 000000000..4208d4ec6 --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/algorithms/aes/ByteCounter.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.data.encryption.algorithms.aes; + +/** Big Endian Byte Counter */ +public class ByteCounter { + + private final byte[] counter; + + /** + * Constructs a new ByteCounter with the given number of bytes. E.g. a ByteCounter with 4 bytes + * will have a counter value of [0, 0, 0, 0]. + * + * @param size number of bytes used by the counter + */ + public ByteCounter(int size) { + this.counter = new byte[size]; + } + + /** + * Constructs a new ByteCounter with the given counter value. Counter cannot grow bigger than the + * size of the array. + * + * @param counter initial counter value + */ + public ByteCounter(byte[] counter) { + this.counter = counter; + } + + /** Returns the counter value as a byte array. */ + public byte[] getBytes() { + return counter; + } + + /** Returns true if counter is maxed */ + public boolean isMaxed() { + for (byte b : counter) { + if (b != (byte) 0xff) return false; + } + return true; + } + + /** + * Increments the counter by one. + * + * @throws IllegalStateException if the counter is already maxed + */ + public void increment() { + incrementByte(counter.length - 1); + } + + private void incrementByte(int index) { + if (isMaxed()) { + throw new IllegalStateException("Counter is already maxed"); + } + + if (counter[index] == (byte) 0xff) { + incrementByte(index - 1); + counter[index] = (byte) 0x00; + } else { + counter[index]++; + } + } +} diff --git a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/data/CryptoData.java b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/data/CryptoData.java new file mode 100644 index 000000000..697a22ede --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/data/CryptoData.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.data.encryption.data; + +public interface CryptoData { + byte[] getBytes(); + + String getBase64(); +} diff --git a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/data/CryptoDataFactory.java b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/data/CryptoDataFactory.java new file mode 100644 index 000000000..c6abab6e6 --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/data/CryptoDataFactory.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.data.encryption.data; + +public interface CryptoDataFactory { + + DecryptedData decryptedFromText(String text); + + DecryptedData decryptedFromBase64(String base64); + + DecryptedData decryptedFromBytes(byte[] bytes); + + EncryptedData encryptedFromText(String text); + + EncryptedData encryptedFromBase64(String base64); + + EncryptedData encryptedFromBytes(byte[] bytes); +} diff --git a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/data/CryptoDataFactoryImpl.java b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/data/CryptoDataFactoryImpl.java new file mode 100644 index 000000000..07a1e39fe --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/data/CryptoDataFactoryImpl.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.data.encryption.data; + +import lombok.Value; +import org.bouncycastle.util.encoders.Base64; + +public class CryptoDataFactoryImpl implements CryptoDataFactory { + + public DecryptedData decryptedFromText(String text) { + final byte[] bytes = text.getBytes(); + final String base64 = Base64.toBase64String(bytes); + return new DecryptedDataImpl(bytes, base64, text); + } + + public DecryptedData decryptedFromBase64(String base64) { + final byte[] bytes = Base64.decode(base64); + final String text = new String(bytes); + return new DecryptedDataImpl(bytes, base64, text); + } + + public DecryptedData decryptedFromBytes(byte[] bytes) { + final String base64 = Base64.toBase64String(bytes); + final String text = new String(bytes); + return new DecryptedDataImpl(bytes, base64, text); + } + + public EncryptedData encryptedFromText(String text) { + final byte[] bytes = text.getBytes(); + final String base64 = Base64.toBase64String(bytes); + return new EncryptedDataImpl(bytes, base64, text); + } + + public EncryptedData encryptedFromBase64(String base64) { + final byte[] bytes = Base64.decode(base64); + final String text = new String(bytes); + return new EncryptedDataImpl(bytes, base64, text); + } + + public EncryptedData encryptedFromBytes(byte[] bytes) { + final String base64 = Base64.toBase64String(bytes); + final String text = new String(bytes); + return new EncryptedDataImpl(bytes, base64, text); + } + + @Value + private static class DecryptedDataImpl implements DecryptedData { + byte[] bytes; + String base64; + String text; + } + + @Value + private static class EncryptedDataImpl implements EncryptedData { + byte[] bytes; + String base64; + String text; + } +} diff --git a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/data/DecryptedData.java b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/data/DecryptedData.java new file mode 100644 index 000000000..903823757 --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/data/DecryptedData.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.data.encryption.data; + +public interface DecryptedData extends CryptoData {} diff --git a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/data/EncryptedData.java b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/data/EncryptedData.java new file mode 100644 index 000000000..033f0ceeb --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/data/EncryptedData.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.data.encryption.data; + +public interface EncryptedData extends CryptoData {} diff --git a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/encrypter/AesDataEncrypterImpl.java b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/encrypter/AesDataEncrypterImpl.java new file mode 100644 index 000000000..e3ac3e82b --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/encrypter/AesDataEncrypterImpl.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.data.encryption.encrypter; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.Optional; +import javax.crypto.AEADBadTagException; +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import lombok.RequiredArgsConstructor; +import net.catenax.edc.data.encryption.DataEncryptionExtension; +import net.catenax.edc.data.encryption.algorithms.CryptoAlgorithm; +import net.catenax.edc.data.encryption.data.CryptoDataFactory; +import net.catenax.edc.data.encryption.data.DecryptedData; +import net.catenax.edc.data.encryption.data.EncryptedData; +import net.catenax.edc.data.encryption.key.AesKey; +import net.catenax.edc.data.encryption.provider.KeyProvider; +import org.eclipse.dataspaceconnector.spi.EdcException; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.transfer.dataplane.spi.security.DataEncrypter; + +@RequiredArgsConstructor +public class AesDataEncrypterImpl implements DataEncrypter { + + private final CryptoAlgorithm encryptionStrategy; + private final Monitor monitor; + private final KeyProvider keyProvider; + private final CryptoAlgorithm algorithm; + private final CryptoDataFactory cryptoDataFactory; + + @Override + public String encrypt(String value) { + DecryptedData decryptedData = cryptoDataFactory.decryptedFromText(value); + AesKey key = keyProvider.getEncryptionKey(); + + try { + EncryptedData encryptedData = algorithm.encrypt(decryptedData, key); + return encryptedData.getBase64(); + } catch (IllegalBlockSizeException + | BadPaddingException + | InvalidKeyException + | InvalidAlgorithmParameterException + | NoSuchPaddingException + | NoSuchAlgorithmException e) { + throw new EdcException(e); + } + } + + @Override + public String decrypt(String value) { + EncryptedData encryptedData = cryptoDataFactory.encryptedFromBase64(value); + + return keyProvider + .getDecryptionKeySet() + .map(key -> decrypt(encryptedData, key)) + .filter(Optional::isPresent) + .map(Optional::get) + .map(DecryptedData::getBytes) + .map(String::new) + .findFirst() + .orElseThrow( + () -> + new EdcException( + DataEncryptionExtension.NAME + + ": Failed to decrypt data. This can happen if the key set is empty, contains invalid keys, the decryption key rotated out of the key set or because the data was encrypted by a different algorithm.")); + } + + private Optional decrypt(EncryptedData data, AesKey key) { + try { + return Optional.of(encryptionStrategy.decrypt(data, key)); + } catch (AEADBadTagException e) { // thrown when wrong key is used for decryption + return Optional.empty(); + } catch (IllegalBlockSizeException + | BadPaddingException + | InvalidKeyException + | NoSuchPaddingException + | NoSuchAlgorithmException + | InvalidAlgorithmParameterException e) { + monitor.warning( + String.format( + DataEncryptionExtension.NAME + + ": Exception decrypting data using key from rotating key set. %s", + e.getMessage())); + throw new EdcException(e); + } + } +} diff --git a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/encrypter/DataEncrypterConfiguration.java b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/encrypter/DataEncrypterConfiguration.java new file mode 100644 index 000000000..65da7e424 --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/encrypter/DataEncrypterConfiguration.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.data.encryption.encrypter; + +import java.time.Duration; +import lombok.NonNull; +import lombok.Value; + +@Value +public class DataEncrypterConfiguration { + @NonNull String algorithm; + @NonNull String keySetAlias; + boolean cachingEnabled; + Duration cachingDuration; +} diff --git a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/encrypter/DataEncrypterFactory.java b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/encrypter/DataEncrypterFactory.java new file mode 100644 index 000000000..d60604ea6 --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/encrypter/DataEncrypterFactory.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.data.encryption.encrypter; + +import java.util.NoSuchElementException; +import lombok.RequiredArgsConstructor; +import net.catenax.edc.data.encryption.DataEncryptionExtension; +import net.catenax.edc.data.encryption.algorithms.CryptoAlgorithm; +import net.catenax.edc.data.encryption.algorithms.aes.AesAlgorithm; +import net.catenax.edc.data.encryption.data.CryptoDataFactory; +import net.catenax.edc.data.encryption.data.CryptoDataFactoryImpl; +import net.catenax.edc.data.encryption.key.AesKey; +import net.catenax.edc.data.encryption.key.CryptoKeyFactory; +import net.catenax.edc.data.encryption.provider.AesKeyProvider; +import net.catenax.edc.data.encryption.provider.CachingKeyProvider; +import net.catenax.edc.data.encryption.provider.KeyProvider; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.security.Vault; +import org.eclipse.dataspaceconnector.transfer.dataplane.spi.security.DataEncrypter; + +@RequiredArgsConstructor +public class DataEncrypterFactory { + + public static final String AES_ALGORITHM = "AES"; + public static final String NONE = "NONE"; + + private final Vault vault; + private final Monitor monitor; + private final CryptoKeyFactory keyFactory; + + public DataEncrypter create(DataEncrypterConfiguration configuration) { + if (configuration.getAlgorithm().equalsIgnoreCase(AES_ALGORITHM)) { + return createAesEncrypter(configuration); + } else if (configuration.getAlgorithm().equalsIgnoreCase(NONE)) { + return createNoneEncrypter(); + } else { + final String msg = + String.format( + DataEncryptionExtension.NAME + + ": Unsupported encryption algorithm '%s'. Supported algorithms are '%s', %s.", + configuration.getAlgorithm(), + AES_ALGORITHM, + NONE); + throw new NoSuchElementException(msg); + } + } + + public DataEncrypter createAesEncrypter(DataEncrypterConfiguration configuration) { + + KeyProvider keyProvider = + new AesKeyProvider(vault, configuration.getKeySetAlias(), keyFactory); + + if (configuration.isCachingEnabled()) { + keyProvider = new CachingKeyProvider<>(keyProvider, configuration.getCachingDuration()); + } + + final CryptoDataFactory cryptoDataFactory = new CryptoDataFactoryImpl(); + final CryptoAlgorithm algorithm = new AesAlgorithm(cryptoDataFactory); + + return new AesDataEncrypterImpl(algorithm, monitor, keyProvider, algorithm, cryptoDataFactory); + } + + public DataEncrypter createNoneEncrypter() { + return new DataEncrypter() { + @Override + public String encrypt(String data) { + return data; + } + + @Override + public String decrypt(String data) { + return data; + } + }; + } +} diff --git a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/key/AesKey.java b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/key/AesKey.java new file mode 100644 index 000000000..136f0e360 --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/key/AesKey.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.data.encryption.key; + +public interface AesKey extends CryptoKey { + byte[] getBytes(); + + String getBase64(); +} diff --git a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/key/CryptoKey.java b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/key/CryptoKey.java new file mode 100644 index 000000000..cf3ca2da5 --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/key/CryptoKey.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.data.encryption.key; + +public interface CryptoKey {} diff --git a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/key/CryptoKeyFactory.java b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/key/CryptoKeyFactory.java new file mode 100644 index 000000000..b3eadcd50 --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/key/CryptoKeyFactory.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.data.encryption.key; + +public interface CryptoKeyFactory { + AesKey fromBase64(String base64); + + AesKey fromBytes(byte[] key); +} diff --git a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/key/CryptoKeyFactoryImpl.java b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/key/CryptoKeyFactoryImpl.java new file mode 100644 index 000000000..398088a95 --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/key/CryptoKeyFactoryImpl.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.data.encryption.key; + +import lombok.Value; +import org.bouncycastle.util.encoders.Base64; + +public class CryptoKeyFactoryImpl implements CryptoKeyFactory { + + public AesKey fromBase64(String base64) { + return fromBytes(Base64.decode(base64)); + } + + public AesKey fromBytes(byte[] key) { + int bitLength = key.length * Byte.SIZE; + if (!(bitLength == 128 || bitLength == 192 || bitLength == 256)) { + throw new IllegalArgumentException("Invalid AES key length: " + bitLength); + } + + return new AesKeyImpl(key, Base64.toBase64String(key)); + } + + @Value + private static class AesKeyImpl implements AesKey { + byte[] bytes; + String base64; + } +} diff --git a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/provider/AesKeyProvider.java b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/provider/AesKeyProvider.java new file mode 100644 index 000000000..be9802013 --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/provider/AesKeyProvider.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.data.encryption.provider; + +import java.util.Arrays; +import java.util.function.Predicate; +import java.util.stream.Stream; +import lombok.RequiredArgsConstructor; +import net.catenax.edc.data.encryption.DataEncryptionExtension; +import net.catenax.edc.data.encryption.key.AesKey; +import net.catenax.edc.data.encryption.key.CryptoKeyFactory; +import org.bouncycastle.util.encoders.Base64; +import org.eclipse.dataspaceconnector.spi.security.Vault; + +@RequiredArgsConstructor +public class AesKeyProvider implements KeyProvider { + + private static final String KEY_SEPARATOR = ","; + + private final Vault vault; + private final String vaultKeyAlias; + private final CryptoKeyFactory cryptoKeyFactory; + + @Override + public Stream getDecryptionKeySet() { + return getKeysStream(); + } + + @Override + public AesKey getEncryptionKey() { + return getKeysStream() + .findFirst() + .orElseThrow( + () -> + new RuntimeException( + DataEncryptionExtension.NAME + ": Vault must contain at least one key.")); + } + + private Stream getKeysStream() { + return Arrays.stream(getKeys().split(KEY_SEPARATOR)) + .map(String::trim) + .filter(Predicate.not(String::isEmpty)) + .map(Base64::decode) + .map(cryptoKeyFactory::fromBytes); + } + + private String getKeys() { + String keys = vault.resolveSecret(vaultKeyAlias); + return keys == null ? "" : keys; + } +} diff --git a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/provider/CachingKeyProvider.java b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/provider/CachingKeyProvider.java new file mode 100644 index 000000000..e33a39551 --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/provider/CachingKeyProvider.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.data.encryption.provider; + +import java.time.Clock; +import java.time.Duration; +import java.time.Instant; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import lombok.NonNull; +import lombok.Value; +import net.catenax.edc.data.encryption.key.CryptoKey; + +public class CachingKeyProvider implements KeyProvider { + + @NonNull private final KeyProvider decoratedProvider; + @NonNull private final Clock clock; + @NonNull private final Duration cacheExpiration; + + private CachedKeys cachedKeys; + + public CachingKeyProvider(KeyProvider keyProvider, Duration cacheExpiration) { + this(keyProvider, cacheExpiration, Clock.systemUTC()); + } + + public CachingKeyProvider(KeyProvider keyProvider, Duration cacheExpiration, Clock clock) { + + this.decoratedProvider = keyProvider; + this.cacheExpiration = cacheExpiration; + this.clock = clock; + } + + @Override + public T getEncryptionKey() { + checkCache(); + return cachedKeys.getEncryptionKey(); + } + + @Override + public Stream getDecryptionKeySet() { + checkCache(); + return cachedKeys.getDecryptionKeys().stream(); + } + + private void checkCache() { + if (cachedKeys == null || cachedKeys.expiration.isBefore(clock.instant())) { + T encryptionKey = decoratedProvider.getEncryptionKey(); + List decryptionKeys = decoratedProvider.getDecryptionKeySet().collect(Collectors.toList()); + cachedKeys = + new CachedKeys<>(encryptionKey, decryptionKeys, clock.instant().plus(cacheExpiration)); + } + } + + @Value + private static class CachedKeys { + T encryptionKey; + List decryptionKeys; + @NonNull Instant expiration; + } +} diff --git a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/provider/KeyProvider.java b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/provider/KeyProvider.java new file mode 100644 index 000000000..8e9c8006c --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/provider/KeyProvider.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.data.encryption.provider; + +import java.util.stream.Stream; +import net.catenax.edc.data.encryption.key.CryptoKey; + +public interface KeyProvider { + T getEncryptionKey(); + + Stream getDecryptionKeySet(); +} diff --git a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/util/ArrayUtil.java b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/util/ArrayUtil.java new file mode 100644 index 000000000..f632c8121 --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/util/ArrayUtil.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.data.encryption.util; + +public class ArrayUtil { + + private ArrayUtil() {} + + public static byte[] concat(byte[] a, byte[] b) { + byte[] c = new byte[a.length + b.length]; + System.arraycopy(a, 0, c, 0, a.length); + System.arraycopy(b, 0, c, a.length, b.length); + return c; + } + + public static String byteArrayToHex(byte[] a) { + StringBuilder sb = new StringBuilder(a.length * 2); + for (byte b : a) sb.append(String.format("%02x", b)); + return sb.toString(); + } + + public static byte[] subArray(byte[] a, int startIndex, int length) { + if (startIndex + length > a.length) { + throw new IllegalArgumentException("Start index + length is greater than array length"); + } + + byte[] b = new byte[length]; + System.arraycopy(a, startIndex, b, 0, length); + return b; + } +} diff --git a/edc-extensions/data-encryption/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension b/edc-extensions/data-encryption/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension new file mode 100644 index 000000000..8a09148c9 --- /dev/null +++ b/edc-extensions/data-encryption/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension @@ -0,0 +1,14 @@ +# +# Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +# +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# +# Contributors: +# Mercedes-Benz Tech Innovation GmbH - Initial ServiceExtension file +# +# +net.catenax.edc.data.encryption.DataEncryptionExtension diff --git a/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/DataEncryptionExtensionTest.java b/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/DataEncryptionExtensionTest.java new file mode 100644 index 000000000..f5541d727 --- /dev/null +++ b/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/DataEncryptionExtensionTest.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.data.encryption; + +import net.catenax.edc.data.encryption.encrypter.DataEncrypterFactory; +import org.eclipse.dataspaceconnector.spi.EdcException; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.security.Vault; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class DataEncryptionExtensionTest { + + private DataEncryptionExtension extension; + + // mocks + private Monitor monitor; + private ServiceExtensionContext context; + private Vault vault; + + @BeforeEach + void setup() { + monitor = Mockito.mock(Monitor.class); + context = Mockito.mock(ServiceExtensionContext.class); + vault = Mockito.mock(Vault.class); + + extension = new DataEncryptionExtension(); + + Mockito.when(context.getMonitor()).thenReturn(monitor); + Mockito.when(context.getService(Vault.class)).thenReturn(vault); + + Mockito.when( + context.getSetting( + Mockito.eq(DataEncryptionExtension.CACHING_ENABLED), Mockito.anyBoolean())) + .thenAnswer((i) -> i.getArguments()[1]); + Mockito.when( + context.getSetting( + Mockito.eq(DataEncryptionExtension.ENCRYPTION_ALGORITHM), Mockito.anyString())) + .thenAnswer((i) -> i.getArguments()[1]); + Mockito.when( + context.getSetting( + Mockito.eq(DataEncryptionExtension.CACHING_SECONDS), Mockito.anyInt())) + .thenAnswer((i) -> i.getArguments()[1]); + } + + @Test + void testName() { + Assertions.assertEquals(DataEncryptionExtension.NAME, extension.name()); + } + + @Test + void testExceptionOnMissingKeySetAlias() { + Mockito.when(context.getSetting(DataEncryptionExtension.ENCRYPTION_KEY_SET, null)) + .thenReturn(null); + Assertions.assertThrows(EdcException.class, () -> extension.initialize(context)); + } + + @Test + void testStartExceptionOnMissingKeySetInVault() { + final String keySetAlias = "foo"; + Mockito.when(context.getSetting(DataEncryptionExtension.ENCRYPTION_KEY_SET, null)) + .thenReturn(keySetAlias); + Mockito.when(vault.resolveSecret(keySetAlias)).thenReturn(""); + + extension.initialize(context); + + Assertions.assertThrows(EdcException.class, () -> extension.start()); + } + + @Test + void testStartExceptionOnStartWithWrongKeySetAlias() { + final String keySetAlias = "foo"; + Mockito.when(context.getSetting(DataEncryptionExtension.ENCRYPTION_ALGORITHM, null)) + .thenReturn(DataEncrypterFactory.AES_ALGORITHM); + Mockito.when(context.getSetting(DataEncryptionExtension.ENCRYPTION_KEY_SET, null)) + .thenReturn(keySetAlias); + Mockito.when(vault.resolveSecret(keySetAlias)).thenReturn("l8b2YHL7VpA=, invalid-key"); + + extension.initialize(context); + + Assertions.assertThrows(EdcException.class, () -> extension.start()); + } +} diff --git a/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/algorithms/aes/AesAlgorithmTest.java b/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/algorithms/aes/AesAlgorithmTest.java new file mode 100644 index 000000000..d66459fee --- /dev/null +++ b/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/algorithms/aes/AesAlgorithmTest.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.data.encryption.algorithms.aes; + +import lombok.SneakyThrows; +import net.catenax.edc.data.encryption.data.CryptoDataFactory; +import net.catenax.edc.data.encryption.data.CryptoDataFactoryImpl; +import net.catenax.edc.data.encryption.data.DecryptedData; +import net.catenax.edc.data.encryption.data.EncryptedData; +import net.catenax.edc.data.encryption.key.AesKey; +import org.bouncycastle.util.encoders.Base64; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class AesAlgorithmTest { + + private static final byte[] KEY_128_BIT = Base64.decode("dVUjmYJzbwVcntkFZU+lNQ=="); + private static final byte[] KEY_196_BIT = Base64.decode("NcgHzzRTUC+z396tWG9hqIbeihujz0m8"); + private static final byte[] KEY_256_BIT = + Base64.decode("OSD+3NcZAmS/6UXbq6NL8UL+aQIAJDLL7BE2rBX5MtA="); + + private final AesAlgorithm strategy = new AesAlgorithm(new CryptoDataFactoryImpl()); + private final CryptoDataFactory cryptoDataFactory = new CryptoDataFactoryImpl(); + + @Test + void test128BitKey() { + testKey(KEY_128_BIT); + } + + @Test + void test196BitKey() { + testKey(KEY_196_BIT); + } + + @Test + void test256BitKey() { + testKey(KEY_256_BIT); + } + + @Test + @SneakyThrows + void testSameDataEncryptedDifferently() { + final AesKey aesKey = createKey(KEY_128_BIT); + final DecryptedData expected = cryptoDataFactory.decryptedFromText("same data"); + final EncryptedData result1 = strategy.encrypt(expected, aesKey); + final EncryptedData result2 = strategy.encrypt(expected, aesKey); + + Assertions.assertNotEquals(result1.getBase64(), result2.getBase64()); + } + + @SneakyThrows + void testKey(byte[] key) { + final AesKey aesKey = createKey(key); + final DecryptedData expected = cryptoDataFactory.decryptedFromText("I will be encrypted"); + final EncryptedData encryptedResult = strategy.encrypt(expected, aesKey); + final DecryptedData result = strategy.decrypt(encryptedResult, aesKey); + + Assertions.assertEquals(expected.getBase64(), result.getBase64()); + } + + AesKey createKey(byte[] key) { + return new AesKey() { + + @Override + public byte[] getBytes() { + return key; + } + + @Override + public String getBase64() { + return Base64.toBase64String(key); + } + }; + } +} diff --git a/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/algorithms/aes/AesInitializationVectorIteratorTest.java b/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/algorithms/aes/AesInitializationVectorIteratorTest.java new file mode 100644 index 000000000..a42625129 --- /dev/null +++ b/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/algorithms/aes/AesInitializationVectorIteratorTest.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.data.encryption.algorithms.aes; + +import java.util.ArrayList; +import java.util.List; +import java.util.NoSuchElementException; +import lombok.SneakyThrows; +import net.catenax.edc.data.encryption.util.ArrayUtil; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class AesInitializationVectorIteratorTest { + + @Test + @SneakyThrows + void testDistinctVectors() { + final int vectorCount = 100; + AesInitializationVectorIterator iterator = new AesInitializationVectorIterator(); + iterator.initialize(); + + List vectors = new ArrayList<>(); + for (var i = 0; i < vectorCount; i++) { + vectors.add(iterator.next()); + } + + long distinctVectors = vectors.stream().map(ArrayUtil::byteArrayToHex).distinct().count(); + Assertions.assertEquals(vectorCount, distinctVectors); + } + + @Test + @SneakyThrows + void testHasNextTrueOnCounterContinuing() { + ByteCounter counter = Mockito.mock(ByteCounter.class); + AesInitializationVectorIterator iterator = new AesInitializationVectorIterator(counter); + iterator.initialize(); + + Mockito.when(counter.isMaxed()).thenReturn(false); + Assertions.assertTrue(iterator.hasNext()); + } + + @Test + @SneakyThrows + void testHasNextFalseOnCounterEnd() { + ByteCounter counter = Mockito.mock(ByteCounter.class); + AesInitializationVectorIterator iterator = new AesInitializationVectorIterator(counter); + iterator.initialize(); + + Mockito.when(counter.isMaxed()).thenReturn(true); + Assertions.assertFalse(iterator.hasNext()); + } + + @Test + @SneakyThrows + void testNoSuchElementExceptionOnCounterEnd() { + ByteCounter counter = Mockito.mock(ByteCounter.class); + AesInitializationVectorIterator iterator = new AesInitializationVectorIterator(counter); + iterator.initialize(); + + Mockito.when(counter.isMaxed()).thenReturn(true); + Assertions.assertThrows(NoSuchElementException.class, iterator::next); + } + + @Test + @SneakyThrows + void testIllegalStateExceptionOnUninitialized() { + AesInitializationVectorIterator iterator = new AesInitializationVectorIterator(); + Assertions.assertThrows(IllegalStateException.class, iterator::next); + } +} diff --git a/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/algorithms/aes/ByteCounterTest.java b/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/algorithms/aes/ByteCounterTest.java new file mode 100644 index 000000000..9beb2e190 --- /dev/null +++ b/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/algorithms/aes/ByteCounterTest.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.data.encryption.algorithms.aes; + +import java.util.stream.Stream; +import net.catenax.edc.data.encryption.util.ArrayUtil; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; + +class ByteCounterTest { + + @ParameterizedTest + @ArgumentsSource(IncrementArgumentsProvider.class) + void testIncrements(byte[] counterValue, long numberOfIncrements, byte[] expected) { + + ByteCounter initializationVector = new ByteCounter(counterValue); + + for (int i = 0; i < numberOfIncrements; i++) { + initializationVector.increment(); + } + + var result = initializationVector.getBytes(); + Assertions.assertEquals(ArrayUtil.byteArrayToHex(expected), ArrayUtil.byteArrayToHex(result)); + } + + @Test + void testIsMaxed() { + + byte[] counterValue = new byte[] {(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff}; + ByteCounter initializationVector = new ByteCounter(counterValue); + + Assertions.assertTrue(initializationVector.isMaxed()); + } + + @Test + void testOverflow() { + + byte[] counterValue = new byte[] {(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff}; + ByteCounter initializationVector = new ByteCounter(counterValue); + + Assertions.assertThrows(IllegalStateException.class, initializationVector::increment); + } + + private static class IncrementArgumentsProvider implements ArgumentsProvider { + @Override + public Stream provideArguments(ExtensionContext context) throws Exception { + return Stream.of( + Arguments.of( + new byte[] {(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}, + 0, + new byte[] {(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}), + Arguments.of( + new byte[] {(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}, + 1, + new byte[] {(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01}), + Arguments.of( + new byte[] {(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}, + 2, + new byte[] {(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02}), + Arguments.of( + new byte[] {(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}, + 255, + new byte[] {(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xff}), + Arguments.of( + new byte[] {(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}, + 65535, + new byte[] {(byte) 0x00, (byte) 0x00, (byte) 0xff, (byte) 0xff}), + Arguments.of( + new byte[] {(byte) 0xef, (byte) 0xff, (byte) 0xff, (byte) 0xff}, + 1, + new byte[] {(byte) 0xf0, (byte) 0x00, (byte) 0x00, (byte) 0x00})); + } + } +} diff --git a/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/encrypter/DataEncrypterAesComponentTest.java b/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/encrypter/DataEncrypterAesComponentTest.java new file mode 100644 index 000000000..717459e4a --- /dev/null +++ b/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/encrypter/DataEncrypterAesComponentTest.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.data.encryption.encrypter; + +import lombok.SneakyThrows; +import net.catenax.edc.data.encryption.algorithms.CryptoAlgorithm; +import net.catenax.edc.data.encryption.algorithms.aes.AesAlgorithm; +import net.catenax.edc.data.encryption.data.CryptoDataFactory; +import net.catenax.edc.data.encryption.data.CryptoDataFactoryImpl; +import net.catenax.edc.data.encryption.data.DecryptedData; +import net.catenax.edc.data.encryption.data.EncryptedData; +import net.catenax.edc.data.encryption.key.AesKey; +import net.catenax.edc.data.encryption.key.CryptoKeyFactory; +import net.catenax.edc.data.encryption.key.CryptoKeyFactoryImpl; +import net.catenax.edc.data.encryption.provider.AesKeyProvider; +import net.catenax.edc.data.encryption.provider.KeyProvider; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.security.Vault; +import org.eclipse.dataspaceconnector.transfer.dataplane.spi.security.DataEncrypter; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +@SuppressWarnings("FieldCanBeLocal") +class DataEncrypterAesComponentTest { + + private static final String KEY_128_BIT_BASE_64 = "7h6sh6t6tchCmNnHjK2kFA=="; + private static final String KEY_256_BIT_BASE_64 = "OSD+3NcZAmS/6UXbq6NL8UL+aQIAJDLL7BE2rBX5MtA="; + + private DataEncrypter dataEncrypter; + private CryptoAlgorithm algorithm; + private KeyProvider keyProvider; + private CryptoKeyFactory cryptoKeyFactory; + private CryptoDataFactory cryptoDataFactory; + + // mocks + private Monitor monitor; + private Vault vault; + + @BeforeEach + void setup() { + monitor = Mockito.mock(Monitor.class); + vault = Mockito.mock(Vault.class); + + cryptoKeyFactory = new CryptoKeyFactoryImpl(); + cryptoDataFactory = new CryptoDataFactoryImpl(); + algorithm = new AesAlgorithm(cryptoDataFactory); + keyProvider = new AesKeyProvider(vault, "foo", cryptoKeyFactory); + + dataEncrypter = + new AesDataEncrypterImpl(algorithm, monitor, keyProvider, algorithm, cryptoDataFactory); + } + + @Test + @SneakyThrows + void testKeyRotation() { + Mockito.when(vault.resolveSecret(Mockito.anyString())) + .thenReturn( + String.format( + "%s, %s, %s, %s", + KEY_128_BIT_BASE_64, + KEY_128_BIT_BASE_64, + KEY_128_BIT_BASE_64, + KEY_256_BIT_BASE_64)); + + final AesKey key256Bit = cryptoKeyFactory.fromBase64(KEY_256_BIT_BASE_64); + final String expectedResult = "hello"; + final DecryptedData decryptedResult = cryptoDataFactory.decryptedFromText(expectedResult); + final EncryptedData encryptedResult = algorithm.encrypt(decryptedResult, key256Bit); + + var result = dataEncrypter.decrypt(encryptedResult.getBase64()); + + Assertions.assertEquals(expectedResult, result); + } + + @Test + void testEncryption() { + Mockito.when(vault.resolveSecret(Mockito.anyString())).thenReturn(KEY_128_BIT_BASE_64); + + final String expectedResult = "hello world!"; + + var encryptedResult = dataEncrypter.encrypt(expectedResult); + var result = dataEncrypter.decrypt(encryptedResult); + + Assertions.assertEquals(expectedResult, result); + } +} diff --git a/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/encrypter/DataEncrypterFactoryTest.java b/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/encrypter/DataEncrypterFactoryTest.java new file mode 100644 index 000000000..0c0f41155 --- /dev/null +++ b/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/encrypter/DataEncrypterFactoryTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.data.encryption.encrypter; + +import java.time.Duration; +import java.util.NoSuchElementException; +import net.catenax.edc.data.encryption.key.CryptoKeyFactoryImpl; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.security.Vault; +import org.eclipse.dataspaceconnector.transfer.dataplane.spi.security.DataEncrypter; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.Mockito; + +@SuppressWarnings("FieldCanBeLocal") +class DataEncrypterFactoryTest { + + private static final String KEY_SET_ALIAS = "keySetAlias"; + + DataEncrypterFactory factory; + + // mocks + private Vault vault; + private Monitor monitor; + + @BeforeEach + void setup() { + vault = Mockito.mock(Vault.class); + monitor = Mockito.mock(Monitor.class); + + factory = new DataEncrypterFactory(vault, monitor, new CryptoKeyFactoryImpl()); + } + + @Test + void testExceptionOnInvalidStrategy() { + final DataEncrypterConfiguration configuration = newConfiguration("something invalid"); + Assertions.assertThrows(NoSuchElementException.class, () -> factory.create(configuration)); + } + + @ParameterizedTest + @ValueSource(strings = {DataEncrypterFactory.AES_ALGORITHM, DataEncrypterFactory.NONE}) + void testValidStrategies(String strategy) { + final DataEncrypterConfiguration configuration = newConfiguration(strategy); + Assertions.assertDoesNotThrow(() -> factory.create(configuration)); + } + + @Test + void testEncrypterWithCaching() { + Mockito.when(vault.resolveSecret(KEY_SET_ALIAS)).thenReturn("7h6sh6t6tchCmNnHjK2kFA=="); + + final DataEncrypterConfiguration configuration = newConfiguration(true); + final DataEncrypter dataEncrypter = factory.create(configuration); + + final String foo1 = dataEncrypter.encrypt("foo1"); + dataEncrypter.decrypt(foo1); + final String foo2 = dataEncrypter.encrypt("foo2"); + dataEncrypter.decrypt(foo2); + final String foo3 = dataEncrypter.encrypt("foo3"); + dataEncrypter.decrypt(foo3); + + // one invoke to get encryption- and one to cache decryption key + Mockito.verify(vault, Mockito.times(2)).resolveSecret(KEY_SET_ALIAS); + } + + private DataEncrypterConfiguration newConfiguration(String encryptionStrategy) { + return new DataEncrypterConfiguration(encryptionStrategy, KEY_SET_ALIAS, false, null); + } + + private DataEncrypterConfiguration newConfiguration(boolean isCachingEnabled) { + return new DataEncrypterConfiguration( + DataEncrypterFactory.AES_ALGORITHM, KEY_SET_ALIAS, isCachingEnabled, Duration.ofMinutes(1)); + } +} diff --git a/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/key/CryptoKeyFactoryImplTest.java b/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/key/CryptoKeyFactoryImplTest.java new file mode 100644 index 000000000..00ec3e8e0 --- /dev/null +++ b/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/key/CryptoKeyFactoryImplTest.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.data.encryption.key; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class CryptoKeyFactoryImplTest { + + @ParameterizedTest + @ValueSource(ints = {32, 64, 512, 1024, 2048, 4096}) + void throwsIllegalArgumentExceptionWhenInvalidAesKeyLength(int bitLength) { + CryptoKeyFactory cryptoKeyFactory = new CryptoKeyFactoryImpl(); + Assertions.assertThrows( + IllegalArgumentException.class, + () -> cryptoKeyFactory.fromBytes(new byte[bitLength / Byte.SIZE])); + } + + @ParameterizedTest + @ValueSource(ints = {128, 192, 256}) + void throwsNotOnValidAesKeyLength(int bitLength) { + CryptoKeyFactory cryptoKeyFactory = new CryptoKeyFactoryImpl(); + Assertions.assertDoesNotThrow( + () -> cryptoKeyFactory.fromBytes(new byte[bitLength / Byte.SIZE])); + } +} diff --git a/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/provider/AesKeyProviderTest.java b/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/provider/AesKeyProviderTest.java new file mode 100644 index 000000000..f106ba9c4 --- /dev/null +++ b/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/provider/AesKeyProviderTest.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.data.encryption.provider; + +import java.util.List; +import java.util.stream.Collectors; +import net.catenax.edc.data.encryption.key.AesKey; +import net.catenax.edc.data.encryption.key.CryptoKeyFactoryImpl; +import org.eclipse.dataspaceconnector.spi.security.Vault; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class AesKeyProviderTest { + + private static final String KEY_1 = "dVUjmYJzbwVcntkFZU+lNQ=="; + private static final String KEY_2 = "7h6sh6t6tchCmNnHjK2kFA=="; + private static final String KEY_3 = "uyNfJzhsnvfEe9OtQyR9Og=="; + + private static final String KEY_ALIAS = "foo"; + + private AesKeyProvider keyProvider; + + // mocks + private Vault vault; + + @BeforeEach + void setup() { + vault = Mockito.mock(Vault.class); + keyProvider = new AesKeyProvider(vault, KEY_ALIAS, new CryptoKeyFactoryImpl()); + } + + @Test + void testEncryptionKeyAlwaysFirstKey() { + Mockito.when(vault.resolveSecret(KEY_ALIAS)) + .thenReturn(String.format("%s,%s,%s", KEY_1, KEY_2, KEY_3)); + + AesKey key = keyProvider.getEncryptionKey(); + + Assertions.assertEquals(KEY_1, key.getBase64()); + } + + @Test + void testEncryptionThrowsOnNoKey() { + Mockito.when(vault.resolveSecret(KEY_ALIAS)).thenReturn(" "); + + Assertions.assertThrows(RuntimeException.class, () -> keyProvider.getEncryptionKey()); + } + + @Test + void testGetKeys() { + Mockito.when(vault.resolveSecret(KEY_ALIAS)) + .thenReturn(String.format("%s, ,,%s,%s", KEY_1, KEY_2, KEY_3)); + + List keys = + keyProvider.getDecryptionKeySet().map(AesKey::getBase64).collect(Collectors.toList()); + List expected = List.of(KEY_1, KEY_2, KEY_3); + + Assertions.assertEquals(expected, keys); + } +} diff --git a/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/provider/CachingKeyProviderTest.java b/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/provider/CachingKeyProviderTest.java new file mode 100644 index 000000000..ea8fc4ffc --- /dev/null +++ b/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/provider/CachingKeyProviderTest.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.data.encryption.provider; + +import java.time.Clock; +import java.time.Duration; +import java.time.Instant; +import java.util.stream.Stream; +import net.catenax.edc.data.encryption.key.CryptoKey; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +@SuppressWarnings("FieldCanBeLocal") +class CachingKeyProviderTest { + + private CachingKeyProvider cachingKeyProvider; + + private CryptoKey encryptionKey; + private CryptoKey decryptionKey; + + // mocks + private KeyProvider decoratedProvider; + private Duration cacheExpiration; + private Clock clock; + + @BeforeEach + void setup() { + decoratedProvider = Mockito.mock(KeyProvider.class); + cacheExpiration = Duration.ofSeconds(2); + clock = Mockito.mock(Clock.class); + encryptionKey = Mockito.mock(CryptoKey.class); + decryptionKey = Mockito.mock(CryptoKey.class); + + cachingKeyProvider = + new CachingKeyProvider(decoratedProvider, cacheExpiration, clock); + + Mockito.when(decoratedProvider.getEncryptionKey()).thenReturn(encryptionKey); + Mockito.when(decoratedProvider.getDecryptionKeySet()) + .thenAnswer((i) -> Stream.of(decryptionKey)); + } + + @Test + void testCaching() { + + Mockito.when(clock.instant()).thenAnswer((i) -> Instant.now()); + + cachingKeyProvider.getDecryptionKeySet(); + cachingKeyProvider.getEncryptionKey(); + + cachingKeyProvider.getDecryptionKeySet(); + cachingKeyProvider.getEncryptionKey(); + + cachingKeyProvider.getDecryptionKeySet(); + cachingKeyProvider.getEncryptionKey(); + + cachingKeyProvider.getDecryptionKeySet(); + cachingKeyProvider.getEncryptionKey(); + + Mockito.verify(decoratedProvider, Mockito.times(1)).getDecryptionKeySet(); + Mockito.verify(decoratedProvider, Mockito.times(1)).getEncryptionKey(); + } + + @Test + void testCacheUpdate() { + + Mockito.when(clock.instant()).thenAnswer((i) -> Instant.now()); + + cachingKeyProvider.getDecryptionKeySet(); + cachingKeyProvider.getEncryptionKey(); + + cachingKeyProvider.getDecryptionKeySet(); + cachingKeyProvider.getEncryptionKey(); + + Mockito.when(clock.instant()) + .thenAnswer((i) -> Instant.now().plus(cacheExpiration.plusSeconds(1))); + + cachingKeyProvider.getDecryptionKeySet(); + cachingKeyProvider.getEncryptionKey(); + + Mockito.verify(decoratedProvider, Mockito.times(2)).getDecryptionKeySet(); + Mockito.verify(decoratedProvider, Mockito.times(2)).getEncryptionKey(); + } +} diff --git a/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/util/ArrayUtilTest.java b/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/util/ArrayUtilTest.java new file mode 100644 index 000000000..611585c86 --- /dev/null +++ b/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/util/ArrayUtilTest.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.data.encryption.util; + +import java.util.stream.Stream; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; + +class ArrayUtilTest { + + @ParameterizedTest + @ArgumentsSource(ArrayArgumentsProvider.class) + void testConcat(byte[] a, byte[] b, byte[] expected) { + var result = ArrayUtil.concat(a, b); + Assertions.assertEquals(ArrayUtil.byteArrayToHex(expected), ArrayUtil.byteArrayToHex(result)); + } + + @Test + void testSubArray() { + final byte[] expected = new byte[] {0x01, 0x02, 0x03, 0x04, 0x05}; + final byte[] array = new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}; + final byte[] subArray = ArrayUtil.subArray(array, 1, 5); + + Assertions.assertEquals(ArrayUtil.byteArrayToHex(expected), ArrayUtil.byteArrayToHex(subArray)); + } + + private static class ArrayArgumentsProvider implements ArgumentsProvider { + + @Override + public Stream provideArguments(ExtensionContext context) throws Exception { + return Stream.of( + Arguments.of( + new byte[] {0x00, 0x01, 0x02, 0x03}, + new byte[] {0x04, 0x05, 0x06, 0x07}, + new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}), + Arguments.of( + new byte[] {0x00}, + new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, + new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}), + Arguments.of( + new byte[] {}, + new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, + new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}), + Arguments.of( + new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, + new byte[] {}, + new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07})); + } + } +} diff --git a/edc-extensions/dataplane-selector-configuration/README.md b/edc-extensions/dataplane-selector-configuration/README.md index 2c64253f6..b8f24f3f0 100644 --- a/edc-extensions/dataplane-selector-configuration/README.md +++ b/edc-extensions/dataplane-selector-configuration/README.md @@ -12,7 +12,7 @@ Per data plane instance the following settings must be configured. As `.url | URL to connect to the Data Plane Instance. | X | http://plato-edc-dataplane:9999/api/dataplane/control | | edc.dataplane.selector..sourcetypes | Source Types in a comma separated List. | X | HttpData | | edc.dataplane.selector..destinationtypes | Destination Types in a comma separated List. | X | HttpProxy | -| edc.dataplane.selector..properties | Additional properties of the Data Plane Instance. | (X) | { "publicApiUrl:": "http://plato-edc-dataplane:8185/api/public" } | +| edc.dataplane.selector..properties | Additional properties of the Data Plane Instance. | (X) | { "publicApiUrl:": "http://plato-edc-dataplane:8185/api/public/" } | The property `publicApiUrl` is mandatory for Data Plane Instances with destination type `HttpProxy`. diff --git a/edc-extensions/pom.xml b/edc-extensions/pom.xml index c679c735c..fd6201f4e 100644 --- a/edc-extensions/pom.xml +++ b/edc-extensions/pom.xml @@ -34,6 +34,7 @@ postgresql-migration hashicorp-vault dataplane-selector-configuration + data-encryption diff --git a/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/asset/V0_0_3__Snapshot_20220815_Update.sql b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/asset/V0_0_3__Snapshot_20220815_Update.sql new file mode 100644 index 000000000..7438f29a7 --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/asset/V0_0_3__Snapshot_20220815_Update.sql @@ -0,0 +1,15 @@ +-- +-- Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +-- +-- This program and the accompanying materials are made available under the +-- terms of the Apache License, Version 2.0 which is available at +-- https://www.apache.org/licenses/LICENSE-2.0 +-- +-- SPDX-License-Identifier: Apache-2.0 +-- +-- Contributors: +-- Mercedes-Benz Tech Innovation GmbH - EDC Snapshot 20220815 Update +-- + +-- add columns +ALTER TABLE edc_asset ADD COLUMN created_at BIGINT; diff --git a/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractdefinition/V0_0_3__Snapshot_20220815_Update.sql b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractdefinition/V0_0_3__Snapshot_20220815_Update.sql new file mode 100644 index 000000000..23f15bebb --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractdefinition/V0_0_3__Snapshot_20220815_Update.sql @@ -0,0 +1,15 @@ +-- +-- Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +-- +-- This program and the accompanying materials are made available under the +-- terms of the Apache License, Version 2.0 which is available at +-- https://www.apache.org/licenses/LICENSE-2.0 +-- +-- SPDX-License-Identifier: Apache-2.0 +-- +-- Contributors: +-- Mercedes-Benz Tech Innovation GmbH - EDC Snapshot 20220815 Update +-- + +-- add columns +ALTER TABLE edc_contract_definitions ADD COLUMN created_at BIGINT; diff --git a/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractnegotiation/V0_0_4__Snapshot_20220815_Update.sql b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractnegotiation/V0_0_4__Snapshot_20220815_Update.sql new file mode 100644 index 000000000..a4de24d26 --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractnegotiation/V0_0_4__Snapshot_20220815_Update.sql @@ -0,0 +1,16 @@ +-- +-- Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +-- +-- This program and the accompanying materials are made available under the +-- terms of the Apache License, Version 2.0 which is available at +-- https://www.apache.org/licenses/LICENSE-2.0 +-- +-- SPDX-License-Identifier: Apache-2.0 +-- +-- Contributors: +-- Mercedes-Benz Tech Innovation GmbH - EDC Snapshot 20220815 Update +-- + +-- add columns +ALTER TABLE edc_contract_negotiation ADD COLUMN created_at BIGINT; +ALTER TABLE edc_contract_negotiation ADD COLUMN updated_at BIGINT; diff --git a/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/policy/V0_0_3__Snapshot_20220815_Update.sql b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/policy/V0_0_3__Snapshot_20220815_Update.sql new file mode 100644 index 000000000..df531e869 --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/policy/V0_0_3__Snapshot_20220815_Update.sql @@ -0,0 +1,15 @@ +-- +-- Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +-- +-- This program and the accompanying materials are made available under the +-- terms of the Apache License, Version 2.0 which is available at +-- https://www.apache.org/licenses/LICENSE-2.0 +-- +-- SPDX-License-Identifier: Apache-2.0 +-- +-- Contributors: +-- Mercedes-Benz Tech Innovation GmbH - EDC Snapshot 20220815 Update +-- + +-- add columns +ALTER TABLE edc_policydefinitions ADD COLUMN created_at BIGINT; diff --git a/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/transferprocess/V0_0_5__Snapshot_20220815_Update.sql b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/transferprocess/V0_0_5__Snapshot_20220815_Update.sql new file mode 100644 index 000000000..5cdb673f4 --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/transferprocess/V0_0_5__Snapshot_20220815_Update.sql @@ -0,0 +1,18 @@ +-- +-- Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +-- +-- This program and the accompanying materials are made available under the +-- terms of the Apache License, Version 2.0 which is available at +-- https://www.apache.org/licenses/LICENSE-2.0 +-- +-- SPDX-License-Identifier: Apache-2.0 +-- +-- Contributors: +-- Mercedes-Benz Tech Innovation GmbH - EDC Snapshot 20220815 Update +-- + +-- add columns +ALTER TABLE edc_transfer_process ADD COLUMN updated_at BIGINT; + +-- rename columns +ALTER TABLE edc_transfer_process RENAME COLUMN created_time_stamp TO created_at; diff --git a/edc-tests/pom.xml b/edc-tests/pom.xml index 5c22ac3cc..f69f2e218 100644 --- a/edc-tests/pom.xml +++ b/edc-tests/pom.xml @@ -162,6 +162,10 @@ net.catenax.edc.extensions postgresql-migration + + net.catenax.edc.extensions + data-encryption + net.catenax.edc diff --git a/edc-tests/src/main/resources/deployment/helm/all-in-one/Chart.yaml b/edc-tests/src/main/resources/deployment/helm/all-in-one/Chart.yaml index 36ad07bac..63ee4b5b7 100644 --- a/edc-tests/src/main/resources/deployment/helm/all-in-one/Chart.yaml +++ b/edc-tests/src/main/resources/deployment/helm/all-in-one/Chart.yaml @@ -44,7 +44,7 @@ dependencies: alias: platoedcdataplane condition: platoedcdataplane.enabled - name: backend-service - version: "0.0.5" + version: "0.0.6" repository: https://denisneuling.github.io/cx-backend-service alias: platobackendapplication condition: platobackendapplication.enabled @@ -71,7 +71,7 @@ dependencies: alias: sokratesedcdataplane condition: sokratesedcdataplane.enabled - name: backend-service - version: "0.0.5" + version: "0.0.6" repository: https://denisneuling.github.io/cx-backend-service alias: sokratesbackendapplication condition: sokratesbackendapplication.enabled diff --git a/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml b/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml index 6edbc849d..9c1bb978e 100644 --- a/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml +++ b/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml @@ -76,6 +76,7 @@ platobackendapplication: enabled: true fullnameOverride: "plato-backend-application" service: + type: NodePort frontend: port: 80 platopostgresql: @@ -105,7 +106,9 @@ platovault: { sleep 5 - + + /bin/vault kv put secret/data-encryption-aes-keys content=H7j47H6vVQQOv/hbdAYz+w== + cat << EOF | /bin/vault kv put secret/my-plato-daps-key content=- -----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCbX9S8de5FTWl4 @@ -207,6 +210,15 @@ platoedccontrolplane: fullnameOverride: "plato-edc-controlplane" service: type: NodePort + logging: + # -- EDC logging.properties configuring the [java.util.logging subsystem](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html#a1.8) + properties: |- + .level=DEBUG + org.eclipse.dataspaceconnector.level=ALL + handlers=java.util.logging.ConsoleHandler + java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter + java.util.logging.ConsoleHandler.level=ALL + java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n image: repository: &edcControlPlaneImage ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql-hashicorp-vault tag: &edcControlPlaneImageTag develop @@ -281,11 +293,11 @@ platoedccontrolplane: EDC_DATAPLANE_SELECTOR_PLATOPLANE_DESTINATIONTYPES: HttpProxy EDC_DATAPLANE_SELECTOR_PLATOPLANE_PROPERTIES: >- { - "publicApiUrl": "http://plato-edc-dataplane:8185/api/public" + "publicApiUrl": "http://plato-edc-dataplane:8185/api/public/" } # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/data-plane-transfer - EDC_TRANSFER_PROXY_ENDPOINT: http://plato-edc-dataplane:8185/api/public + EDC_TRANSFER_PROXY_ENDPOINT: http://plato-edc-dataplane:8185/api/public/ EDC_TRANSFER_PROXY_TOKEN_SIGNER_PRIVATEKEY_ALIAS: my-plato-daps-key # for simplicity this example re-uses the DAPS keys. EDC_TRANSFER_PROXY_TOKEN_VERIFIER_PUBLICKEY_ALIAS: my-plato-daps-crt # for simplicity this example re-uses the DAPS keys. @@ -299,6 +311,14 @@ platoedccontrolplane: # see extension https://github.com/catenax-ng/product-edc/tree/develop/edc-extensions/hashicorp-vault EDC_VAULT_HASHICORP_URL: http://plato-vault:8200 + ##################### + ## DATA ENCRYPTION ## + ##################### + + # see extension https://github.com/catenax-ng/product-edc/tree/develop/edc-extensions/data-encryption + EDC_DATA_ENCRYPTION_KEYS_ALIAS: data-encryption-aes-keys + EDC_DATA_ENCRYPTION_ALGORITHM: AES + ############ # SOKRATES # ############ @@ -306,6 +326,7 @@ sokratesbackendapplication: enabled: true fullnameOverride: "sokrates-backend-application" service: + type: NodePort frontend: port: 80 sokratespostgresql: @@ -334,6 +355,8 @@ sokratesvault: sleep 5 + /bin/vault kv put secret/data-encryption-aes-keys content=OcvxzWCK8ETSjt1jmZw3RA== + cat << EOF | /bin/vault kv put secret/my-sokrates-daps-key content=- -----BEGIN PRIVATE KEY----- MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCv+NUvK7ppJPiM @@ -436,6 +459,15 @@ sokratesedccontrolplane: fullnameOverride: "sokrates-edc-controlplane" service: type: NodePort + logging: + # -- EDC logging.properties configuring the [java.util.logging subsystem](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html#a1.8) + properties: |- + .level=DEBUG + org.eclipse.dataspaceconnector.level=ALL + handlers=java.util.logging.ConsoleHandler + java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter + java.util.logging.ConsoleHandler.level=ALL + java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n envSecretName: "aio-sokrates-control-secret" image: repository: *edcControlPlaneImage @@ -506,12 +538,12 @@ sokratesedccontrolplane: EDC_DATAPLANE_SELECTOR_SOKRATESPLANE_DESTINATIONTYPES: HttpProxy EDC_DATAPLANE_SELECTOR_SOKRATESPLANE_PROPERTIES: >- { - "publicApiUrl": "http://sokrates-edc-dataplane:8185/api/public" + "publicApiUrl": "http://sokrates-edc-dataplane:8185/api/public/" } # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/data-plane-transfer # TODO Can this be removed? - EDC_TRANSFER_PROXY_ENDPOINT: http://sokrates-edc-dataplane:8185/api/public + EDC_TRANSFER_PROXY_ENDPOINT: http://sokrates-edc-dataplane:8185/api/public/ EDC_TRANSFER_PROXY_TOKEN_SIGNER_PRIVATEKEY_ALIAS: my-sokrates-daps-key # for simplicity this example re-uses the DAPS keys. EDC_TRANSFER_PROXY_TOKEN_VERIFIER_PUBLICKEY_ALIAS: my-sokrates-daps-crt # for simplicity this example re-uses the DAPS keys. @@ -524,3 +556,11 @@ sokratesedccontrolplane: # see extension https://github.com/catenax-ng/product-edc/tree/develop/edc-extensions/hashicorp-vault EDC_VAULT_HASHICORP_URL: http://sokrates-vault:8200 + + ##################### + ## DATA ENCRYPTION ## + ##################### + + # see extension https://github.com/catenax-ng/product-edc/tree/develop/edc-extensions/data-encryption + EDC_DATA_ENCRYPTION_KEYS_ALIAS: data-encryption-aes-keys + EDC_DATA_ENCRYPTION_ALGORITHM: AES diff --git a/edc-tests/src/test/resources/net/catenax/edc/tests/features/ContractNegotiation.feature b/edc-tests/src/test/resources/net/catenax/edc/tests/features/ContractNegotiation.feature index ebdfb2f52..d78afd807 100644 --- a/edc-tests/src/test/resources/net/catenax/edc/tests/features/ContractNegotiation.feature +++ b/edc-tests/src/test/resources/net/catenax/edc/tests/features/ContractNegotiation.feature @@ -22,13 +22,13 @@ Feature: Contract Negotiation | id | description | | asset-1 | Example Asset | And 'Plato' has the following policies - | id | action | payMe | - | policy-1 | USE | | - | policy-pay-me | USE | 1000 | + | id | action | payMe | + | policy-1 | USE | | + | policy-pay-me | USE | 1000 | And 'Plato' has the following contract definitions | id | access policy | contract policy | asset | | contract-definition-1 | policy-1 | policy-pay-me | asset-1 | When 'Sokrates' sends 'Plato' a counter offer without constraints - | definition id | asset id | - | contract-definition-1 | asset-1 | - # Then the negotiation is declined # Issue https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1791 + | definition id | asset id | + | contract-definition-1 | asset-1 | + Then the negotiation is declined diff --git a/pom.xml b/pom.xml index 14721faa5..b440ff426 100644 --- a/pom.xml +++ b/pom.xml @@ -65,7 +65,7 @@ 4.2.0 - 0.0.1-milestone-5 + 0.0.1-20220818-SNAPSHOT 1.2.2 42.4.2 9.1.5 @@ -323,6 +323,11 @@ dataplane-selector-configuration ${project.version} + + net.catenax.edc.extensions + data-encryption + ${project.version} + From d3b365293ec00978ac82fefbd445a144bedf6137 Mon Sep 17 00:00:00 2001 From: "Florian Rusch (ZF Friedrichshafen AG)" Date: Fri, 19 Aug 2022 10:38:05 +0200 Subject: [PATCH 243/433] Enhance migration guide (#359) --- docs/migration/Version_0.0.x_0.1.x.md | 49 ++++++++++++++++++- .../README.md | 2 +- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/docs/migration/Version_0.0.x_0.1.x.md b/docs/migration/Version_0.0.x_0.1.x.md index f92accef5..e6c4539d9 100644 --- a/docs/migration/Version_0.0.x_0.1.x.md +++ b/docs/migration/Version_0.0.x_0.1.x.md @@ -11,8 +11,10 @@ This document contains a list of breaking changes that are introduced in version 1. Policy Path 2. Policy Payload 3. Criteria in Payload of Contract Definitions and Policies + 4. Data Address 3. Connector Configuration 1. Token Validation Endpoint Setting + 2. DataPlane Selector ## 1. PostgreSQL Database @@ -282,7 +284,44 @@ curl -X POST "${DATA_MGMT_ENDPOINT}/data/contractdefinitions" --header "X-Api-Ke ### 2.4 Data Address -When using a Data Address of type `HttpData` please notice that the property `endpoint` changed to `baseUrl`. This property is mostly used when creating assets. +When using a Data Address of type `HttpData` please notice that the property `endpoint` changed to `baseUrl`. This +property is mostly used when creating assets. + + +
+ +DataAddress Comparison + +**Old Asset format**: +```json +{ + "asset": { + ... + }, + "dataAddress": { + "properties": { + "endpoint": "http://provider-backend-service:8080/data/asset-1", + "type": "HttpData" + } + } +} +``` + +**New Asset format**: +```json +{ + "asset": { + ... + }, + "dataAddress": { + "properties": { + "baseUrl": "http://provider-backend-service:8080/data/asset-1", + "type": "HttpData" + } + } +} +``` +
@@ -300,3 +339,11 @@ curl -X POST "$PLATO_DATAMGMT_URL/data/assets" --header "X-Api-Key: password" -- In the past the token validation endpoint was configured in `edc.controlplane.validation-endpoint`. This setting key must be renamed to `edc.dataplane.token.validation.endpoint`. + +### 3.2 DataPlane Selector + +With this version a new feature was introduced which allows to have separate DataPlane instances for different +transfer-flows (HttpProxy, S3, etc.). The Catena-X EDC team has additionally a new extension created which allows a +simpler registration of additional dataplanes. Therefor some changes needs to be applied. Further documentation can +be found in the extension folder: +[dataplane-selector-configuration](/edc-extensions/dataplane-selector-configuration/README.md) diff --git a/edc-extensions/dataplane-selector-configuration/README.md b/edc-extensions/dataplane-selector-configuration/README.md index b8f24f3f0..81db0cc87 100644 --- a/edc-extensions/dataplane-selector-configuration/README.md +++ b/edc-extensions/dataplane-selector-configuration/README.md @@ -12,7 +12,7 @@ Per data plane instance the following settings must be configured. As `.url | URL to connect to the Data Plane Instance. | X | http://plato-edc-dataplane:9999/api/dataplane/control | | edc.dataplane.selector..sourcetypes | Source Types in a comma separated List. | X | HttpData | | edc.dataplane.selector..destinationtypes | Destination Types in a comma separated List. | X | HttpProxy | -| edc.dataplane.selector..properties | Additional properties of the Data Plane Instance. | (X) | { "publicApiUrl:": "http://plato-edc-dataplane:8185/api/public/" } | +| edc.dataplane.selector..properties | Additional properties of the Data Plane Instance. | (X) | { "publicApiUrl": "http://plato-edc-dataplane:8185/api/public/" } | The property `publicApiUrl` is mandatory for Data Plane Instances with destination type `HttpProxy`. From 79977f65d2231dae4e7c67dc5c15cb27a62d589f Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Fri, 19 Aug 2022 11:24:54 +0200 Subject: [PATCH 244/433] Update Changelog and Notice file (#360) --- .github/workflows/publish-new-release.yml | 2 +- CHANGELOG.md | 17 +- NOTICE.md | 222 ++++++++++++------ docs/release-notes/Version 0.1.0.md | 4 +- .../edc-controlplane-base/pom.xml | 13 + .../edc-controlplane-postgresql/pom.xml | 1 - edc-dataplane/edc-dataplane-base/pom.xml | 13 + 7 files changed, 188 insertions(+), 84 deletions(-) diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index 1b52a43c7..9b2cb9a34 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -78,7 +78,7 @@ jobs: name: Deploy run: |- ./mvnw -s settings.xml \ - -Pdelombok -pl '!edc-controlplane,!edc-controlplane/edc-controlplane-memory,!edc-controlplane/edc-controlplane-postgresql,!edc-controlplane/edc-controlplane-postgresql-hashicorp-vault,!edc-dataplane/edc-dataplane-azure-vault,!edc-dataplane/edc-dataplane-hashicorp-vault,!edc-tests' \ + -Pdelombok -pl '!edc-controlplane/edc-controlplane-memory,!edc-controlplane/edc-controlplane-postgresql,!edc-controlplane/edc-controlplane-postgresql-hashicorp-vault,!edc-dataplane/edc-dataplane-azure-vault,!edc-dataplane/edc-dataplane-hashicorp-vault,!edc-tests' \ -DaltReleaseDeploymentRepository=github::https://maven.pkg.github.com/${{ github.repository }} \ -Dmaven.test.skip=true -B package deploy:deploy env: diff --git a/CHANGELOG.md b/CHANGELOG.md index b9cf661e9..ab3344eed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,16 +12,25 @@ corresponding [documentation](/docs/migration/Version_0.0.x_0.1.x.md). ### Added -- control plane extension ([data-plane-selector-client](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/v0.0.1-milestone-5/extensions/data-plane-selector/selector-client)) +- Control-Plane extension ([data-plane-selector-client](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/v0.0.1-milestone-5/extensions/data-plane-selector/selector-client)) - run the EDC with multiple data planes at once -- control plane extension([dataplane-selector-configuration](edc-extensions/dataplane-selector-configuration)) +- Control-Plane extension ([dataplane-selector-configuration](edc-extensions/dataplane-selector-configuration)) - add data plane instances to the control plane by configuration -- data plane extension ([s3-data-plane](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/aws/data-plane-s3)) +- Data-Plane extension ([s3-data-plane](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/aws/data-plane-s3)) - transfer from and to AWS S3 buckets +- Control-Plane extension ([data-encryption](edc-extensions/data-encryption)) + - Data-Plane authentication attribute transmitted during data-plane-transfer can be encrypted symmetrically (AES) ### Changed -- update setting name (`edc.dataplane.token.validation.endpoint` -> `edc.dataplane.token.validation.endpoint`) +- Update setting name (`edc.dataplane.token.validation.endpoint` -> `edc.dataplane.token.validation.endpoint`) +- EDC has been updated to version [0.0.1-20220818-SNAPSHOT](https://oss.sonatype.org/#nexus-search;gav~org.eclipse.dataspaceconnector~~0.0.1-20220818-SNAPSHOT~~) - implications to the behavior of the connector have been covered in the [corresponding migration guide](docs/migration/Version_0.0.x_0.1.x.md) + +### Fixed + +- Contract-Offer-Receiving-Connectors must also pass the ContractPolicy of the ContractDefinition before receiving offers([issue](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1331)) +- Deletion of Asset becomes impossible when Contract Negotiation exists([issue](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1403)) +- Deletion of Policy becomes impossible when Contract Definition exists([issue](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1410)) ## [0.0.6] - 2022-07-29 diff --git a/NOTICE.md b/NOTICE.md index 79c32c49b..e0508c539 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -19,89 +19,137 @@ The project maintains the following source code repositoriy: ## Third-party Content (Overarching All Modules) * aopalliance version 1.0 repackaged as a module under EPL 2.0 or GPL2 w/ CPE +* Apache Commons Codec under Apache License, Version 2.0 * Apache Commons Compress under Apache License, Version 2.0 * Apache Commons Lang under Apache License, Version 2.0 +* Apache Commons Logging under The Apache Software License, Version 2.0 * Apache Commons Pool under Apache License, Version 2.0 -* apache-commons-pool-sql under Apache License, Version 2.0 -* api-configuration under Apache License, Version 2.0 -* api-core under Apache License, Version 2.0 +* Apache Groovy under The Apache Software License, Version 2.0 +* Apache HttpClient under Apache License, Version 2.0 +* Apache HttpClient Mime under Apache License, Version 2.0 +* Apache HttpCore under Apache License, Version 2.0 +* apache-commons-pool-sql under The Apache License, Version 2.0 +* api-configuration under The Apache License, Version 2.0 +* api-core under The Apache License, Version 2.0 * asm under BSD-3-Clause * ASM based accessors helper used by json-smart under The Apache Software License, Version 2.0 * asm-analysis under BSD-3-Clause * asm-commons under BSD-3-Clause * asm-tree under BSD-3-Clause -* asset-api under Apache License, Version 2.0 -* asset-index-sql under Apache License, Version 2.0 -* auth-spi under Apache License, Version 2.0 -* auth-tokenbased under Apache License, Version 2.0 -* azure-vault under Apache License, Version 2.0 +* AssertJ fluent assertions under Apache License, Version 2.0 +* asset under The Apache License, Version 2.0 +* asset-index-sql under The Apache License, Version 2.0 +* auth-spi under The Apache License, Version 2.0 +* auth-tokenbased under The Apache License, Version 2.0 +* Awaitility under Apache 2.0 +* AWS Event Stream under Apache License, Version 2.0 +* AWS Java SDK :: Annotations under Apache License, Version 2.0 +* AWS Java SDK :: Arns under Apache License, Version 2.0 +* AWS Java SDK :: Auth under Apache License, Version 2.0 +* AWS Java SDK :: AWS Core under Apache License, Version 2.0 +* AWS Java SDK :: Core :: Protocols :: AWS Query Protocol under Apache License, Version 2.0 +* AWS Java SDK :: Core :: Protocols :: AWS Xml Protocol under Apache License, Version 2.0 +* AWS Java SDK :: Core :: Protocols :: Protocol Core under Apache License, Version 2.0 +* AWS Java SDK :: HTTP Client Interface under Apache License, Version 2.0 +* AWS Java SDK :: HTTP Clients :: Apache under Apache License, Version 2.0 +* AWS Java SDK :: HTTP Clients :: Netty Non-Blocking I/O under Apache License, Version 2.0 +* AWS Java SDK :: Metrics SPI under Apache License, Version 2.0 +* AWS Java SDK :: Profiles under Apache License, Version 2.0 +* AWS Java SDK :: Regions under Apache License, Version 2.0 +* AWS Java SDK :: SDK Core under Apache License, Version 2.0 +* AWS Java SDK :: Services :: Amazon S3 under Apache License, Version 2.0 +* AWS Java SDK :: Services :: AWS IAM under Apache License, Version 2.0 +* AWS Java SDK :: Services :: AWS STS under Apache License, Version 2.0 +* AWS Java SDK :: Utilities under Apache License, Version 2.0 +* base under The Apache License, Version 2.0 * Bean Validation API under Apache License 2.0 +* boot under The Apache License, Version 2.0 * Bouncy Castle ASN.1 Extension and Utility APIs under Bouncy Castle Licence * Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs under Bouncy Castle Licence * Bouncy Castle Provider under Bouncy Castle Licence * business-partner-validation under Apache License 2.0 * Byte Buddy (without dependencies) under Apache License, Version 2.0 * Byte Buddy agent under Apache License, Version 2.0 -* catalog-api under Apache License, Version 2.0 -* catalog-spi under Apache License, Version 2.0 +* catalog-api under The Apache License, Version 2.0 +* catalog-spi under The Apache License, Version 2.0 * Checker Qual under The MIT License * ClassGraph under The MIT License (MIT) -* common-sql under Apache License, Version 2.0 -* common-util under Apache License, Version 2.0 -* contract under Apache License, Version 2.0 -* contract-spi under Apache License, Version 2.0 -* contractagreement-api under Apache License, Version 2.0 -* contractdefinition-api under Apache License, Version 2.0 -* contractdefinition-store-sql under Apache License, Version 2.0 -* contractnegotiation-api under Apache License, Version 2.0 -* contractnegotiation-store-sql under Apache License, Version 2.0 -* control-api under Apache License, Version 2.0 +* ClassMate under Apache License, Version 2.0 +* common-sql under The Apache License, Version 2.0 +* configuration-fs under The Apache License, Version 2.0 +* contract under The Apache License, Version 2.0 +* contract-definition-store-sql under The Apache License, Version 2.0 +* contract-negotiation-store-sql under The Apache License, Version 2.0 +* contract-spi under The Apache License, Version 2.0 +* contractagreement under The Apache License, Version 2.0 +* contractdefinition under The Apache License, Version 2.0 +* contractnegotiation under The Apache License, Version 2.0 +* control-plane-spi under The Apache License, Version 2.0 * Core functionality for the Reactor Netty library under The Apache Software License, Version 2.0 -* core-base under Apache License, Version 2.0 -* core-boot under Apache License, Version 2.0 -* core-defaults under Apache License, Version 2.0 -* core-micrometer under Apache License, Version 2.0 -* core-spi under Apache License, Version 2.0 -* data-management-api under Apache License, Version 2.0 -* data-plane-api under Apache License, Version 2.0 -* data-plane-framework under Apache License, Version 2.0 -* data-plane-http under Apache License, Version 2.0 -* data-plane-spi under Apache License, Version 2.0 -* data-plane-transfer-spi under Apache License, Version 2.0 -* data-plane-transfer-sync under Apache License, Version 2.0 -* dataloading under Apache License, Version 2.0 +* core-spi under The Apache License, Version 2.0 +* Cucumber CiEnvironment under MIT License +* Cucumber Expressions under MIT License +* Cucumber HTML Formatter under MIT License +* Cucumber Messages under MIT License +* Cucumber Tag Expressions under MIT License +* Cucumber-JVM: Core under MIT License +* Cucumber-JVM: Docstring under MIT License +* Cucumber-JVM: Gherkin under MIT License +* Cucumber-JVM: Gherkin Messages under MIT License +* Cucumber-JVM: Java under MIT License +* Cucumber-JVM: JUnit Platform Engine under MIT License +* Cucumber-JVM: Plugin under MIT License +* Cucumber-JVN: DataTable under MIT License +* daps under The Apache License, Version 2.0 +* data-encryption under Apache License 2.0 +* data-management under The Apache License, Version 2.0 +* data-plane-api under The Apache License, Version 2.0 +* data-plane-framework under The Apache License, Version 2.0 +* data-plane-http under The Apache License, Version 2.0 +* data-plane-s3 under The Apache License, Version 2.0 +* data-plane-spi under The Apache License, Version 2.0 +* data-plane-transfer-spi under The Apache License, Version 2.0 +* data-plane-transfer-sync under The Apache License, Version 2.0 +* dataloading under The Apache License, Version 2.0 +* dataplane-selector-configuration under Apache License 2.0 +* defaults under The Apache License, Version 2.0 * docker-java-api under The Apache Software License, Version 2.0 * docker-java-transport under The Apache Software License, Version 2.0 * docker-java-transport-zerodep under The Apache Software License, Version 2.0 * Duct Tape under MIT * edc-controlplane under Apache License 2.0 +* edc-controlplane-base under Apache License 2.0 * edc-controlplane-memory under Apache License 2.0 * edc-controlplane-postgresql under Apache License 2.0 * edc-controlplane-postgresql-hashicorp-vault under Apache License 2.0 * edc-dataplane under Apache License 2.0 * edc-dataplane-azure-vault under Apache License 2.0 +* edc-dataplane-base under Apache License 2.0 * edc-dataplane-hashicorp-vault under Apache License 2.0 * edc-extensions under Apache License 2.0 +* edc-tests under Apache License 2.0 * Failsafe under Apache License, Version 2.0 -* filesystem-configuration under Apache License, Version 2.0 * flyway-core under Apache License, Version 2.0 +* Gherkin under MIT License +* Gson under Apache-2.0 * Hamcrest under BSD License 3 * Hamcrest Core under New BSD License * hashicorp-vault under Apache License 2.0 * HdrHistogram under Public Domain, per Creative Commons CC0 or BSD-2-Clause +* Hibernate Validator Engine under Apache License 2.0 * HK2 API module under EPL 2.0 or GPL2 w/ CPE * HK2 Implementation Utilities under EPL 2.0 or GPL2 w/ CPE -* http under Apache License, Version 2.0 +* http under The Apache License, Version 2.0 * HTTP functionality for the Reactor Netty library under The Apache Software License, Version 2.0 -* http-receiver under Apache License, Version 2.0 -* iam-daps under Apache License, Version 2.0 -* ids-api-configuration under Apache License, Version 2.0 -* ids-api-multipart-dispatcher-v1 under Apache License, Version 2.0 -* ids-api-multipart-endpoint-v1 under Apache License, Version 2.0 -* ids-api-transform-v1 under Apache License, Version 2.0 -* ids-core under Apache License, Version 2.0 -* ids-spi under Apache License, Version 2.0 -* ids-token-validation under Apache License, Version 2.0 +* http-receiver under The Apache License, Version 2.0 +* ids-api-configuration under The Apache License, Version 2.0 +* ids-api-multipart-dispatcher-v1 under The Apache License, Version 2.0 +* ids-api-multipart-endpoint-v1 under The Apache License, Version 2.0 +* ids-core under The Apache License, Version 2.0 +* ids-jsonld-serdes-lib under The Apache License, Version 2.0 +* ids-spi under The Apache License, Version 2.0 +* ids-token-validation under The Apache License, Version 2.0 +* ids-transform-v1 under The Apache License, Version 2.0 * IntelliJ IDEA Annotations under The Apache Software License, Version 2.0 * Jackson datatype: JSR310 under The Apache Software License, Version 2.0 * Jackson module: Jakarta XML Bind Annotations (jakarta.xml.bind) under The Apache Software License, Version 2.0 @@ -114,27 +162,31 @@ The project maintains the following source code repositoriy: * Jakarta Annotations API under EPL 2.0 or GPL2 w/ CPE * Jakarta Bean Validation API under Apache License 2.0 * Jakarta Dependency Injection under The Apache Software License, Version 2.0 +* Jakarta Expression Language API under Eclipse Public License v. 2.0 or GNU General Public License, version 2 with the GNU Classpath Exception +* Jakarta Expression Language Implementation under Eclipse Public License v. 2.0 or GNU General Public License, version 2 with the GNU Classpath Exception +* Jakarta RESTful WS API under EPL-2.0 or GPL-2.0-with-classpath-exception * Jakarta XML Binding API under Eclipse Distribution License - v 1.0 * jakarta.transaction API under EPL 2.0 or GPL2 w/ CPE -* jakarta.ws.rs-api under EPL-2.0 or GPL-2.0-with-classpath-exception * java under Apache License, Version 2.0 * Java Native Access under LGPL, version 2.1 or Apache License v2.0 * Java Native Access Platform under LGPL, version 2.1 or Apache License v2.0 * Javassist under MPL 1.1 or LGPL 2.1 or Apache License 2.0 +* JBoss Logging 3 under Apache License, version 2.0 * JCIP Annotations under Apache License under Apache License, Version 2.0 -* jdk-logger-monitor under Apache License, Version 2.0 -* jersey under Apache License, Version 2.0 +* jdk-logger-monitor under The Apache License, Version 2.0 +* jersey under The Apache License, Version 2.0 * jersey-container-servlet under EPL 2.0 or GPL2 w/ CPE or EDL 1.0 or BSD 2-Clause or Apache License, 2.0 or Public Domain or Modified BSD or jQuery license or MIT license or W3C license * jersey-container-servlet-core under EPL 2.0 or GPL2 w/ CPE or EDL 1.0 or BSD 2-Clause or Apache License, 2.0 or Public Domain or Modified BSD or jQuery license or MIT license or W3C license * jersey-core-client under EPL 2.0 or GPL2 w/ CPE or EDL 1.0 or BSD 2-Clause or Apache License, 2.0 or Public Domain or Modified BSD or jQuery license or MIT license or W3C license * jersey-core-common under EPL 2.0 or The GNU General Public License (GPL), Version 2, With Classpath Exception or Apache License, 2.0 or Public Domain * jersey-core-server under EPL 2.0 or The GNU General Public License (GPL), Version 2, With Classpath Exception or Apache License, 2.0 or Modified BSD +* jersey-ext-bean-validation under EPL 2.0 or GPL2 w/ CPE or EDL 1.0 or BSD 2-Clause or Apache License, 2.0 or Public Domain or Modified BSD or jQuery license or MIT license or W3C license * jersey-ext-entity-filtering under EPL 2.0 or GPL2 w/ CPE or EDL 1.0 or BSD 2-Clause or Apache License, 2.0 or Public Domain or Modified BSD or jQuery license or MIT license or W3C license * jersey-inject-hk2 under EPL 2.0 or GPL2 w/ CPE or EDL 1.0 or BSD 2-Clause or Apache License, 2.0 or Public Domain or Modified BSD or jQuery license or MIT license or W3C license * jersey-media-json-jackson under EPL 2.0 or The GNU General Public License (GPL), Version 2, With Classpath Exception or Apache License, 2.0 * jersey-media-multipart under EPL 2.0 or GPL2 w/ CPE or EDL 1.0 or BSD 2-Clause or Apache License, 2.0 or Public Domain or Modified BSD or jQuery license or MIT license or W3C license -* jersey-micrometer under Apache License, Version 2.0 -* jetty under Apache License, Version 2.0 +* jersey-micrometer under The Apache License, Version 2.0 +* jetty under The Apache License, Version 2.0 * Jetty :: ALPN :: Client under Eclipse Public License - Version 2.0 or Apache Software License - Version 2.0 * Jetty :: Asynchronous HTTP Client under Eclipse Public License - Version 2.0 or Apache Software License - Version 2.0 * Jetty :: Http Utility under Eclipse Public License - Version 2.0 or Apache Software License - Version 2.0 @@ -157,21 +209,28 @@ The project maintains the following source code repositoriy: * Jetty :: Websocket :: jakarta.websocket :: Server under Eclipse Public License - Version 2.0 or Apache Software License - Version 2.0 * Jetty :: Websocket :: Servlet under Eclipse Public License - Version 2.0 or Apache Software License - Version 2.0 * Jetty :: XML utilities under Eclipse Public License - Version 2.0 or Apache Software License - Version 2.0 -* jetty-micrometer under Apache License, Version 2.0 +* jetty-micrometer under The Apache License, Version 2.0 * JSON Small and Fast Parser under The Apache Software License, Version 2.0 +* json-path under Apache 2.0 * JUL to SLF4J bridge under MIT License -* JUnit under Eclipse Public License 1.0 +* junit under The Apache License, Version 2.0 * JUnit Jupiter (Aggregator) under Eclipse Public License v2.0 * JUnit Jupiter API under Eclipse Public License v2.0 * JUnit Jupiter Engine under Eclipse Public License v2.0 * JUnit Jupiter Params under Eclipse Public License v2.0 * JUnit Platform Commons under Eclipse Public License v2.0 * JUnit Platform Engine API under Eclipse Public License v2.0 -* junit under Apache License, Version 2.0 +* JUnit Platform Launcher under Eclipse Public License v2.0 +* JUnit Platform Suite (Aggregator) under Eclipse Public License v2.0 +* JUnit Platform Suite API under Eclipse Public License v2.0 +* JUnit Platform Suite Commons under Eclipse Public License v2.0 +* JUnit Platform Suite Engine under Eclipse Public License v2.0 +* junit-pioneer under Eclipse Public License v2.0 * LatencyUtils under Public Domain, per Creative Commons CC0 -* lease-sql under Apache License, Version 2.0 +* lease-sql under The Apache License, Version 2.0 * Logback Classic Module under Eclipse Public License - v 1.0 or GNU Lesser General Public License * Logback Core Module under Eclipse Public License - v 1.0 or GNU Lesser General Public License +* micrometer under The Apache License, Version 2.0 * micrometer-core under The Apache Software License, Version 2.0 * Microsoft Azure client library for Identity under The MIT License (MIT) * Microsoft Azure client library for KeyVault Secrets under The MIT License (MIT) @@ -182,6 +241,8 @@ The project maintains the following source code repositoriy: * mockito-inline under The MIT License * msal4j under MIT License * msal4j-persistence-extension under MIT License +* Netty Reactive Streams HTTP support under Apache License, Version 2.0 +* Netty Reactive Streams Implementation under Apache License, Version 2.0 * Netty/Buffer under Apache License, Version 2.0 * Netty/Codec under Apache License, Version 2.0 * Netty/Codec/DNS under Apache License, Version 2.0 @@ -208,10 +269,10 @@ The project maintains the following source code repositoriy: * Nimbus LangTag under The Apache Software License, Version 2.0 * Non-Blocking Reactive Foundation for the JVM under Apache License, Version 2.0 * OAuth 2.0 SDK with OpenID Connect extensions under Apache License, version 2.0 -* oauth2-core under Apache License, Version 2.0 -* oauth2-spi under Apache License, Version 2.0 +* oauth2-core under The Apache License, Version 2.0 +* oauth2-spi under The Apache License, Version 2.0 * Objenesis under Apache License, Version 2.0 -* observability-api under Apache License, Version 2.0 +* observability under The Apache License, Version 2.0 * okhttp under The Apache Software License, Version 2.0 * Okio under The Apache Software License, Version 2.0 * OpenTelemetry Java under The Apache License, Version 2.0 @@ -220,41 +281,50 @@ The project maintains the following source code repositoriy: * org.jetbrains.kotlin:kotlin-stdlib-common under The Apache License, Version 2.0 * org.opentest4j:opentest4j under The Apache License, Version 2.0 * OSGi resource locator under EPL 2.0 or GPL2 w/ CPE -* policy-engine under Apache License, Version 2.0 -* policy-evaluator under Apache License, Version 2.0 -* policy-spi under Apache License, Version 2.0 -* policy-store-sql under Apache License, Version 2.0 -* policydefinition-api under Apache License, Version 2.0 +* policy-engine under The Apache License, Version 2.0 +* policy-evaluator under The Apache License, Version 2.0 +* policy-spi under The Apache License, Version 2.0 +* policy-store-sql under The Apache License, Version 2.0 +* policydefinition under The Apache License, Version 2.0 * PostgreSQL JDBC Driver under BSD-2-Clause * postgresql-migration under Apache License 2.0 -* product-edc-parent under Apache License 2.0 +* product-edc under Apache License 2.0 * Project Lombok under The MIT License * reactive-streams under CC0 +* REST Assured under Apache 2.0 +* rest-assured-common under Apache 2.0 +* s3-core under The Apache License, Version 2.0 +* selector-client under The Apache License, Version 2.0 +* selector-core under The Apache License, Version 2.0 +* selector-spi under The Apache License, Version 2.0 +* selector-store under The Apache License, Version 2.0 * ServiceLocator Default Implementation under EPL 2.0 or GPL2 w/ CPE * SLF4J API Module under MIT License * SnakeYAML under Apache License, Version 2.0 -* spi under Apache License, Version 2.0 -* state-machine-lib under Apache License, Version 2.0 +* state-machine-lib under The Apache License, Version 2.0 * Stax2 API under The BSD License * swagger-annotations-jakarta under Apache License 2.0 * swagger-core-jakarta under Apache License 2.0 * swagger-integration-jakarta under Apache License 2.0 * swagger-jaxrs2-jakarta under Apache License 2.0 * swagger-models-jakarta under Apache License 2.0 +* TagSoup under Apache License 2.0 * Testcontainers :: JUnit Jupiter Extension under MIT * Testcontainers :: Vault under MIT * Testcontainers Core under MIT -* token-generation-lib under Apache License, Version 2.0 -* token-validation-lib under Apache License, Version 2.0 -* transaction-datasource-spi under Apache License, Version 2.0 -* transaction-local under Apache License, Version 2.0 -* transaction-spi under Apache License, Version 2.0 -* transfer under Apache License, Version 2.0 -* transfer-process-store-sql under Apache License, Version 2.0 -* transfer-spi under Apache License, Version 2.0 -* transferprocess-api under Apache License, Version 2.0 -* transport-spi under Apache License, Version 2.0 -* util under Apache License, Version 2.0 -* web-spi under Apache License, Version 2.0 +* token-generation-lib under The Apache License, Version 2.0 +* token-validation-lib under The Apache License, Version 2.0 +* transaction-datasource-spi under The Apache License, Version 2.0 +* transaction-local under The Apache License, Version 2.0 +* transaction-spi under The Apache License, Version 2.0 +* transfer under The Apache License, Version 2.0 +* transfer-process-store-sql under The Apache License, Version 2.0 +* transfer-spi under The Apache License, Version 2.0 +* transferprocess under The Apache License, Version 2.0 +* transport-spi under The Apache License, Version 2.0 +* util under The Apache License, Version 2.0 +* vault under The Apache License, Version 2.0 +* web-spi under The Apache License, Version 2.0 * Woodstox under The Apache License, Version 2.0 +* xml-path under Apache 2.0 diff --git a/docs/release-notes/Version 0.1.0.md b/docs/release-notes/Version 0.1.0.md index c204d6e9a..9cf96c304 100644 --- a/docs/release-notes/Version 0.1.0.md +++ b/docs/release-notes/Version 0.1.0.md @@ -33,7 +33,7 @@ The following extensions are now included in the base image of the connector. > **Introduces new mandatory settings** -New extension to add data plane instances by configuration. Each data plane instance must be registered at the control plane. +New extension to add data plane instances by configuration. All data plane instances must be registered at the control plane. [Documentation](../../edc-extensions/dataplane-selector-configuration/README.md) @@ -41,7 +41,7 @@ New extension to add data plane instances by configuration. Each data plane inst > **Introduces new mandatory settings** -New extension to protect possibly confidential data, that may be send out to other connectors as transfer tokens. +New extension to protect possibly confidential data, that may be sent out to other connectors as transfer tokens. [Documentation](../../edc-extensions/data-encryption/README.md) diff --git a/edc-controlplane/edc-controlplane-base/pom.xml b/edc-controlplane/edc-controlplane-base/pom.xml index 761e63fe0..78d0baf41 100644 --- a/edc-controlplane/edc-controlplane-base/pom.xml +++ b/edc-controlplane/edc-controlplane-base/pom.xml @@ -1,4 +1,17 @@ + diff --git a/edc-controlplane/edc-controlplane-postgresql/pom.xml b/edc-controlplane/edc-controlplane-postgresql/pom.xml index 4a356f5ce..10777b7ba 100644 --- a/edc-controlplane/edc-controlplane-postgresql/pom.xml +++ b/edc-controlplane/edc-controlplane-postgresql/pom.xml @@ -82,7 +82,6 @@ - diff --git a/edc-dataplane/edc-dataplane-base/pom.xml b/edc-dataplane/edc-dataplane-base/pom.xml index bdff3b0cf..c6b32135b 100644 --- a/edc-dataplane/edc-dataplane-base/pom.xml +++ b/edc-dataplane/edc-dataplane-base/pom.xml @@ -1,4 +1,17 @@ + From 770a44b3e0792c57415fe4b6b07562c9b29be5dd Mon Sep 17 00:00:00 2001 From: marcingajek-zf Date: Mon, 22 Aug 2022 14:58:02 +0200 Subject: [PATCH 245/433] control-plane-adapter extension - contract data store - ver.1 (cherry picked from commit 49e4e8f19060f207298323d2b9a039a0cf5912d5) --- .../contractconfirmation/DataStoreLock.java | 41 +++++++++++++++++++ .../ContractAgreementData.java | 30 ++++++++++++++ .../contractdatastore/ContractDataStore.java | 8 ++++ .../InMemoryContractDataStore.java | 24 +++++++++++ 4 files changed, 103 insertions(+) create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/DataStoreLock.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/ContractAgreementData.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/ContractDataStore.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/InMemoryContractDataStore.java diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/DataStoreLock.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/DataStoreLock.java new file mode 100644 index 000000000..661759c12 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/DataStoreLock.java @@ -0,0 +1,41 @@ +package net.catenax.edc.cp.adapter.process.contractconfirmation; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +public class DataStoreLock { + private final Map lock = new HashMap<>(); + + public void readLock(String id) { + addLock(id); + lock.get(id).readLock().lock(); + } + + public void readUnlock(String id) { + addLock(id); + lock.get(id).readLock().unlock(); + } + + public void writeLock(String id) { + addLock(id); + lock.get(id).writeLock().lock(); + } + + public void writeUnlock(String id) { + addLock(id); + lock.get(id).writeLock().unlock(); + } + + public void removeLock(String id) { + addLock(id); + lock.remove(id); + } + + private void addLock(String id) { + synchronized(this){ + lock.putIfAbsent(id, new ReentrantReadWriteLock()); + } + } +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/ContractAgreementData.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/ContractAgreementData.java new file mode 100644 index 000000000..2f0366b71 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/ContractAgreementData.java @@ -0,0 +1,30 @@ +package net.catenax.edc.cp.adapter.process.contractdatastore; + +import lombok.Getter; +import lombok.Setter; +import org.eclipse.dataspaceconnector.spi.types.domain.contract.agreement.ContractAgreement; + +@Getter +@Setter +public class ContractAgreementData { + private String contractNegotiationId; + + private String id; + private String providerAgentId; + private String consumerAgentId; + private long contractSigningDate; + private long contractStartDate; + private long contractEndDate; + private String assetId; + private String policyId; + + public static ContractAgreementData from(ContractAgreement agreement, String contractNegotiationId){ + ContractAgreementData data = new ContractAgreementData(); + data.setAssetId(agreement.getAssetId()); + data.setPolicyId(agreement.getPolicyId()); + data.setContractStartDate(agreement.getContractStartDate()); + data.setContractEndDate(agreement.getContractEndDate()); + data.setContractSigningDate(agreement.getContractSigningDate()); + return data; + } +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/ContractDataStore.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/ContractDataStore.java new file mode 100644 index 000000000..ee98b8e77 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/ContractDataStore.java @@ -0,0 +1,8 @@ +package net.catenax.edc.cp.adapter.process.contractdatastore; + +import org.eclipse.dataspaceconnector.spi.types.domain.contract.agreement.ContractAgreement; + +public interface ContractDataStore { + void add(String assetId, String provider, String contractNegotiationId, ContractAgreement contractAgreement); + ContractAgreementData get(String assetId, String provider); +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/InMemoryContractDataStore.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/InMemoryContractDataStore.java new file mode 100644 index 000000000..a4eefadec --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/InMemoryContractDataStore.java @@ -0,0 +1,24 @@ +package net.catenax.edc.cp.adapter.process.contractdatastore; + +import org.eclipse.dataspaceconnector.spi.types.domain.contract.agreement.ContractAgreement; + +import java.util.HashMap; +import java.util.Map; + +public class InMemoryContractDataStore implements ContractDataStore{ + private final static Map contractMap = new HashMap<>(); + + @Override + public void add(String assetId, String provider, String negotiationId, ContractAgreement agreement) { + contractMap.put(getKey(assetId, provider), ContractAgreementData.from(agreement, negotiationId)); + } + + @Override + public ContractAgreementData get(String assetId, String provider) { + return contractMap.get(getKey(assetId, provider)); + } + + private String getKey(String assetId, String provider){ + return assetId + "::" + provider; + } +} From e64c62aee702cc0ea8e5357bda17b0370a1d5fc4 Mon Sep 17 00:00:00 2001 From: marcingajek-zf Date: Tue, 23 Aug 2022 12:22:19 +0200 Subject: [PATCH 246/433] control-plane-adapter extension - contract caching / synchronizing inMemoryDataStore --- .../edc/cp/adapter/ApiAdapterExtension.java | 10 ++++-- .../ContractConfirmationHandler.java | 22 +++++++----- .../InMemoryDataStore.java | 26 ++++++++++++-- .../ContractAgreementData.java | 5 ++- .../contractdatastore/ContractDataStore.java | 2 +- .../InMemoryContractDataStore.java | 4 +-- .../ContractNegotiationHandler.java | 36 ++++++++++++++++--- .../datareference/DataReferenceHandler.java | 6 ++-- 8 files changed, 82 insertions(+), 29 deletions(-) diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/ApiAdapterExtension.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/ApiAdapterExtension.java index 66e34c5cd..a19be93b6 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/ApiAdapterExtension.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/ApiAdapterExtension.java @@ -7,7 +7,9 @@ import net.catenax.edc.cp.adapter.messaging.InMemoryMessageService; import net.catenax.edc.cp.adapter.messaging.ListenerService; import net.catenax.edc.cp.adapter.process.contractconfirmation.ContractConfirmationHandler; +import net.catenax.edc.cp.adapter.process.contractconfirmation.DataStoreLock; import net.catenax.edc.cp.adapter.process.contractconfirmation.InMemoryDataStore; +import net.catenax.edc.cp.adapter.process.contractdatastore.InMemoryContractDataStore; import net.catenax.edc.cp.adapter.process.contractnegotiation.ContractNegotiationHandler; import net.catenax.edc.cp.adapter.process.datareference.DataReferenceHandler; import net.catenax.edc.cp.adapter.service.ResultService; @@ -89,7 +91,8 @@ private void initContractNegotiationHandler( monitor, messageService, contractNegotiationService, - new CatalogServiceImpl(dispatcher))); + new CatalogServiceImpl(dispatcher), + new InMemoryContractDataStore())); } private void initContractConfirmationHandler( @@ -103,10 +106,11 @@ private void initContractConfirmationHandler( new ContractConfirmationHandler( monitor, messageService, - new InMemoryDataStore(), + new InMemoryDataStore(new DataStoreLock()), contractNegotiationService, new TransferProcessServiceImpl( - transferProcessStore, transferProcessManager, getTransactionContext(monitor))); + transferProcessStore, transferProcessManager, getTransactionContext(monitor)), + new InMemoryContractDataStore()); listenerService.addListener(Channel.CONTRACT_CONFIRMATION, contractConfirmationHandler); if (nonNull(negotiationObservable)) { diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/ContractConfirmationHandler.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/ContractConfirmationHandler.java index fe7f35064..7bf12e3d4 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/ContractConfirmationHandler.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/ContractConfirmationHandler.java @@ -8,6 +8,7 @@ import net.catenax.edc.cp.adapter.messaging.Listener; import net.catenax.edc.cp.adapter.messaging.Message; import net.catenax.edc.cp.adapter.messaging.MessageService; +import net.catenax.edc.cp.adapter.process.contractdatastore.ContractDataStore; import org.eclipse.dataspaceconnector.api.datamanagement.contractnegotiation.service.ContractNegotiationService; import org.eclipse.dataspaceconnector.api.datamanagement.transferprocess.service.TransferProcessService; import org.eclipse.dataspaceconnector.api.result.ServiceResult; @@ -26,6 +27,7 @@ public class ContractConfirmationHandler implements Listener, ContractNegotiatio private final DataStore dataStore; private final ContractNegotiationService contractNegotiationService; private final TransferProcessService transferProcessService; + private final ContractDataStore contractDataStore; @Override public void process(Message message) { @@ -33,8 +35,15 @@ public void process(Message message) { String.format("[%s] ContractConfirmationHandler: received message.", message.getTraceId())); String contractNegotiationId = message.getPayload().getContractNegotiationId(); - if (isContractConfirmed(contractNegotiationId)) { - initiateDataTransfer(message); // TODO - contractAgreementID may be missing + if (message.getPayload().isContractConfirmed()) { + initiateDataTransfer(message); + return; + } + + ContractNegotiation contractNegotiation = contractNegotiationService.findbyId(contractNegotiationId); + if (contractNegotiation.getState() == ContractNegotiationStates.CONFIRMED.code()) { + message.getPayload().setContractAgreementId(contractNegotiation.getContractAgreement().getId()); + initiateDataTransfer(message); return; } @@ -61,15 +70,12 @@ public void preConfirmed(ContractNegotiation negotiation) { } message.getPayload().setContractAgreementId(contractAgreementId); initiateDataTransfer(message); + contractDataStore.add(message.getPayload().getAssetId(), + message.getPayload().getProvider(), + negotiation.getContractAgreement()); dataStore.removeMessage(contractNegotiationId); } - private boolean isContractConfirmed(String contractNegotiationId) { - return contractNegotiationService - .getState(contractNegotiationId) - .equals(ContractNegotiationStates.CONFIRMED.name()); - } - private void initiateDataTransfer(Message message) { sendInitiationRequest(message); message.getPayload().setContractConfirmed(true); diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/InMemoryDataStore.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/InMemoryDataStore.java index de9c3746c..9d9c7bb4d 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/InMemoryDataStore.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/InMemoryDataStore.java @@ -2,39 +2,59 @@ import java.util.HashMap; import java.util.Map; + import net.catenax.edc.cp.adapter.messaging.Message; public class InMemoryDataStore implements DataStore { private final Map messages = new HashMap<>(); private final Map confirmedContracts = new HashMap<>(); + private final DataStoreLock lock; + + public InMemoryDataStore(DataStoreLock lock) { + this.lock = lock; + } @Override public void storeConfirmedContract(String contractNegotiationId, String contractAgreementId) { + lock.writeLock(contractNegotiationId); confirmedContracts.put(contractNegotiationId, contractAgreementId); + lock.writeUnlock(contractNegotiationId); } @Override public String getConfirmedContract(String contractNegotiationId) { - return confirmedContracts.get(contractNegotiationId); + lock.readLock(contractNegotiationId); + String confirmedContract = confirmedContracts.get(contractNegotiationId); + lock.readUnlock(contractNegotiationId); + return confirmedContract; } @Override public void removeConfirmedContract(String contractNegotiationId) { confirmedContracts.remove(contractNegotiationId); + lock.removeLock(contractNegotiationId); } @Override public void storeMessage(Message message) { - messages.put(message.getPayload().getContractNegotiationId(), message); + String contractNegotiationId = message.getPayload().getContractNegotiationId(); + + lock.writeLock(contractNegotiationId); + messages.put(contractNegotiationId, message); + lock.writeUnlock(contractNegotiationId); } @Override public Message getMessage(String contractNegotiationId) { - return messages.get(contractNegotiationId); + lock.readLock(contractNegotiationId); + Message message = messages.get(contractNegotiationId); + lock.readUnlock(contractNegotiationId); + return message; } @Override public void removeMessage(String contractNegotiationId) { messages.remove(contractNegotiationId); + lock.removeLock(contractNegotiationId); } } diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/ContractAgreementData.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/ContractAgreementData.java index 2f0366b71..42b68ee9c 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/ContractAgreementData.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/ContractAgreementData.java @@ -7,8 +7,6 @@ @Getter @Setter public class ContractAgreementData { - private String contractNegotiationId; - private String id; private String providerAgentId; private String consumerAgentId; @@ -18,8 +16,9 @@ public class ContractAgreementData { private String assetId; private String policyId; - public static ContractAgreementData from(ContractAgreement agreement, String contractNegotiationId){ + public static ContractAgreementData from(ContractAgreement agreement){ ContractAgreementData data = new ContractAgreementData(); + data.setId(agreement.getId()); data.setAssetId(agreement.getAssetId()); data.setPolicyId(agreement.getPolicyId()); data.setContractStartDate(agreement.getContractStartDate()); diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/ContractDataStore.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/ContractDataStore.java index ee98b8e77..7d741bc9a 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/ContractDataStore.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/ContractDataStore.java @@ -3,6 +3,6 @@ import org.eclipse.dataspaceconnector.spi.types.domain.contract.agreement.ContractAgreement; public interface ContractDataStore { - void add(String assetId, String provider, String contractNegotiationId, ContractAgreement contractAgreement); + void add(String assetId, String provider, ContractAgreement contractAgreement); ContractAgreementData get(String assetId, String provider); } diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/InMemoryContractDataStore.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/InMemoryContractDataStore.java index a4eefadec..d3599b124 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/InMemoryContractDataStore.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/InMemoryContractDataStore.java @@ -9,8 +9,8 @@ public class InMemoryContractDataStore implements ContractDataStore{ private final static Map contractMap = new HashMap<>(); @Override - public void add(String assetId, String provider, String negotiationId, ContractAgreement agreement) { - contractMap.put(getKey(assetId, provider), ContractAgreementData.from(agreement, negotiationId)); + public void add(String assetId, String provider, ContractAgreement agreement) { + contractMap.put(getKey(assetId, provider), ContractAgreementData.from(agreement)); } @Override diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnegotiation/ContractNegotiationHandler.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnegotiation/ContractNegotiationHandler.java index 0014cf862..3676975da 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnegotiation/ContractNegotiationHandler.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnegotiation/ContractNegotiationHandler.java @@ -1,6 +1,10 @@ package net.catenax.edc.cp.adapter.process.contractnegotiation; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; import java.util.Collections; +import java.util.Objects; import java.util.Optional; import java.util.concurrent.ExecutionException; import lombok.RequiredArgsConstructor; @@ -11,6 +15,8 @@ import net.catenax.edc.cp.adapter.messaging.Listener; import net.catenax.edc.cp.adapter.messaging.Message; import net.catenax.edc.cp.adapter.messaging.MessageService; +import net.catenax.edc.cp.adapter.process.contractdatastore.ContractAgreementData; +import net.catenax.edc.cp.adapter.process.contractdatastore.ContractDataStore; import org.eclipse.dataspaceconnector.api.datamanagement.catalog.service.CatalogService; import org.eclipse.dataspaceconnector.api.datamanagement.contractnegotiation.service.ContractNegotiationService; import org.eclipse.dataspaceconnector.spi.monitor.Monitor; @@ -25,11 +31,24 @@ public class ContractNegotiationHandler implements Listener { private final MessageService messageService; private final ContractNegotiationService contractNegotiationService; private final CatalogService catalogService; + private final ContractDataStore contractDataStore; @Override public void process(Message message) { monitor.debug("RequestHandler: input request: " + message.getPayload()); ProcessData processData = message.getPayload(); + + ContractAgreementData contractData = contractDataStore.get( + message.getPayload().getAssetId(), + message.getPayload().getProvider()); + if (Objects.nonNull(contractData) && isContractValid(contractData)) { + monitor.info(String.format("[%s] ContractAgreement taken from cache.", message.getTraceId())); + message.getPayload().setContractAgreementId(contractData.getId()); + message.getPayload().setContractConfirmed(true); + messageService.send(Channel.CONTRACT_CONFIRMATION, message); + return; + } + ContractOffer contractOffer = findContractOffer(processData.getAssetId(), processData.getProvider()); @@ -41,14 +60,21 @@ public void process(Message message) { messageService.send(Channel.CONTRACT_CONFIRMATION, message); } + private boolean isContractValid(ContractAgreementData contractAgreement) { + long now = Instant.now().getEpochSecond(); + return Objects.nonNull(contractAgreement) && + contractAgreement.getContractStartDate() < now && + contractAgreement.getContractEndDate() > now; + } + private ContractOffer findContractOffer(String assetId, String providerUrl) { Catalog catalog = getCatalog(providerUrl); return Optional.ofNullable(catalog.getContractOffers()).orElse(Collections.emptyList()).stream() - .filter(it -> it.getAsset().getId().equals(assetId)) - .findFirst() - .orElseThrow( - () -> - new ResourceNotFoundException("Could not find Contract Offer for given Asset Id")); + .filter(it -> it.getAsset().getId().equals(assetId)) + .findFirst() + .orElseThrow( + () -> + new ResourceNotFoundException("Could not find Contract Offer for given Asset Id")); } private Catalog getCatalog(String providerUrl) { diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataReferenceHandler.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataReferenceHandler.java index 6e2c9687e..cb5e79dfd 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataReferenceHandler.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataReferenceHandler.java @@ -23,8 +23,7 @@ public class DataReferenceHandler implements Listener, EndpointDataReferenceRece @Override public void process(Message message) { String contractAgreementId = message.getPayload().getContractAgreementId(); - monitor.info( - String.format("[%s] DataReferenceHandler: message received.", message.getTraceId())); + monitor.info(String.format("[%s] DataReference message received.", message.getTraceId())); EndpointDataReference dataReference = dataStore.getDataReference(contractAgreementId); if (isNull(dataReference)) { @@ -35,8 +34,7 @@ public void process(Message message) { message.getPayload().setEndpointDataReference(dataReference); messageService.send(Channel.RESULT, message); dataStore.removeDataReference(contractAgreementId); - monitor.info( - String.format("[%s] DataReferenceHandler: message processed.", message.getTraceId())); + monitor.info(String.format("[%s] DataReference message processed.", message.getTraceId())); } @Override From 614552d2092995728670da2f86cd3b0f4fab7566 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 25 Aug 2022 09:01:02 +0200 Subject: [PATCH 247/433] Bump flyway-core from 9.1.5 to 9.1.6 (#362) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e0870476f..95876649c 100644 --- a/pom.xml +++ b/pom.xml @@ -68,7 +68,7 @@ 0.0.1-20220818-SNAPSHOT 1.2.2 42.4.2 - 9.1.5 + 9.1.6 5.9.0 From df4c53b6bd03935adaa7a2e3a40302783db089df Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 25 Aug 2022 09:01:15 +0200 Subject: [PATCH 248/433] Bump github/codeql-action from 1 to 2 (#365) --- .github/workflows/kics.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/kics.yaml b/.github/workflows/kics.yaml index c361e7738..4430f4512 100644 --- a/.github/workflows/kics.yaml +++ b/.github/workflows/kics.yaml @@ -37,6 +37,6 @@ jobs: - name: Upload SARIF file for GitHub Advanced Security Dashboard if: always() - uses: github/codeql-action/upload-sarif@v1 + uses: github/codeql-action/upload-sarif@v2 with: sarif_file: kicsResults/results.sarif From 9329ebc2c3a9267fa34a47c2930a9137e83a59ed Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 25 Aug 2022 09:02:27 +0200 Subject: [PATCH 249/433] Bump spotless-maven-plugin from 2.24.1 to 2.25.0 (#366) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 95876649c..3580e01e6 100644 --- a/pom.xml +++ b/pom.xml @@ -48,7 +48,7 @@ 3.3.0 3.2.2 - 2.24.1 + 2.25.0 3.1.0 3.4.1 2.0.0 From 4a3835e784c8841799721fd13a73e0aa097bfbc9 Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Thu, 25 Aug 2022 12:05:19 +0200 Subject: [PATCH 250/433] add caten-x configuration example to hashicorp vault README.md (#368) --- edc-extensions/hashicorp-vault/README.md | 26 +++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/edc-extensions/hashicorp-vault/README.md b/edc-extensions/hashicorp-vault/README.md index 0c2ebf424..7f49a4662 100644 --- a/edc-extensions/hashicorp-vault/README.md +++ b/edc-extensions/hashicorp-vault/README.md @@ -85,5 +85,29 @@ cat << EOF | /bin/vault kv put secret/my-daps-key content=- or ```bash - edc.oauth.private.key.alias=my-daps-key +edc.oauth.private.key.alias=my-daps-key +``` + +## Example: Catena-X Argo CD Vault Configuration + + +``` + +######### +# Vault # +######### + +edc.vault.hashicorp.url=https://vault.demo.catena-x.net +# or even better configure token as k8 secret +edc.vault.hashicorp.token= +edc.vault.hashicorp.api.secret.path=/v1// +edc.vault.hashicorp.health.check.standby.ok=true + +######################## +# E.g. OAuth Extension # +######################## + +# from UI: secret stored in https://vault.demo.catena-x.net/ui/vault/secrets//show/my-daps-key +edc.oauth.private.key.alias=my-daps-key + ``` \ No newline at end of file From c1d779d6444058dfc8583f47c7d934a10f6fe38f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 Aug 2022 00:12:44 +0200 Subject: [PATCH 251/433] Bump postgresql from 42.4.2 to 42.5.0 (#370) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3580e01e6..45812fd2f 100644 --- a/pom.xml +++ b/pom.xml @@ -67,7 +67,7 @@ 0.0.1-20220818-SNAPSHOT 1.2.2 - 42.4.2 + 42.5.0 9.1.6 From e94bc0e302126bb9b2d75c45866c74ede515f0a5 Mon Sep 17 00:00:00 2001 From: marcingajek-zf Date: Fri, 26 Aug 2022 08:43:15 +0200 Subject: [PATCH 252/433] control-plane-adapter extension - unit tests (cherry picked from commit 91be2874c95c812bd0b9d4e8f8458cde4179deaf) --- .../edc/cp/adapter/ApiAdapterExtension.java | 2 +- .../edc/cp/adapter/HttpController.java | 2 +- .../ContractConfirmationHandler.java | 18 +- .../contractconfirmation/DataStoreLock.java | 62 +++--- .../InMemoryDataStore.java | 1 - .../ContractAgreementData.java | 36 ++-- .../contractdatastore/ContractDataStore.java | 5 +- .../InMemoryContractDataStore.java | 29 ++- .../ContractNegotiationHandler.java | 24 +-- .../edc/cp/adapter/service/ResultService.java | 14 +- .../edc/cp/adapter/HttpControllerTest.java | 43 ++++ .../messaging/InMemoryMessageServiceTest.java | 52 +++++ .../ContractConfirmationHandlerTest.java | 195 ++++++++++++++++++ .../ContractNegotiationHandlerTest.java | 136 ++++++++++++ .../DataReferenceHandlerTest.java | 98 +++++++++ .../cp/adapter/service/ResultServiceTest.java | 101 +++++++++ 16 files changed, 725 insertions(+), 93 deletions(-) create mode 100644 edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/HttpControllerTest.java create mode 100644 edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/messaging/InMemoryMessageServiceTest.java create mode 100644 edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/contractconfirmation/ContractConfirmationHandlerTest.java create mode 100644 edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/contractnegotiation/ContractNegotiationHandlerTest.java create mode 100644 edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/datareference/DataReferenceHandlerTest.java create mode 100644 edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/service/ResultServiceTest.java diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/ApiAdapterExtension.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/ApiAdapterExtension.java index a19be93b6..4b479796f 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/ApiAdapterExtension.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/ApiAdapterExtension.java @@ -59,7 +59,7 @@ public void initialize(ServiceExtensionContext context) { new ContractNegotiationServiceImpl(store, manager, getTransactionContext(monitor)); ListenerService listenerService = new ListenerService(); InMemoryMessageService messageService = new InMemoryMessageService(monitor, listenerService); - ResultService resultService = new ResultService(monitor); + ResultService resultService = new ResultService(); listenerService.addListener(Channel.RESULT, resultService); initHttpController(monitor, messageService, resultService); diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/HttpController.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/HttpController.java index c77764960..92027c823 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/HttpController.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/HttpController.java @@ -36,7 +36,7 @@ public Response getAssetSynchronous( String traceId = initiateProcess(assetId, providerUrl); try { - return Response.status(Response.Status.OK).entity(resultService.poll(traceId)).build(); + return Response.status(Response.Status.OK).entity(resultService.pull(traceId)).build(); } catch (InterruptedException e) { monitor.severe("InterruptedException", e); return Response.status(Response.Status.NOT_FOUND).build(); diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/ContractConfirmationHandler.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/ContractConfirmationHandler.java index 7bf12e3d4..fd04f6074 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/ContractConfirmationHandler.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/ContractConfirmationHandler.java @@ -2,6 +2,7 @@ import static java.util.Objects.isNull; +import java.util.Objects; import lombok.RequiredArgsConstructor; import net.catenax.edc.cp.adapter.exception.DataReferenceAccessException; import net.catenax.edc.cp.adapter.messaging.Channel; @@ -40,9 +41,13 @@ public void process(Message message) { return; } - ContractNegotiation contractNegotiation = contractNegotiationService.findbyId(contractNegotiationId); - if (contractNegotiation.getState() == ContractNegotiationStates.CONFIRMED.code()) { - message.getPayload().setContractAgreementId(contractNegotiation.getContractAgreement().getId()); + ContractNegotiation contractNegotiation = + contractNegotiationService.findbyId(contractNegotiationId); + if (Objects.nonNull(contractNegotiation) + && contractNegotiation.getState() == ContractNegotiationStates.CONFIRMED.code()) { + message + .getPayload() + .setContractAgreementId(contractNegotiation.getContractAgreement().getId()); initiateDataTransfer(message); return; } @@ -70,9 +75,10 @@ public void preConfirmed(ContractNegotiation negotiation) { } message.getPayload().setContractAgreementId(contractAgreementId); initiateDataTransfer(message); - contractDataStore.add(message.getPayload().getAssetId(), - message.getPayload().getProvider(), - negotiation.getContractAgreement()); + contractDataStore.add( + message.getPayload().getAssetId(), + message.getPayload().getProvider(), + negotiation.getContractAgreement()); dataStore.removeMessage(contractNegotiationId); } diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/DataStoreLock.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/DataStoreLock.java index 661759c12..da60c7877 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/DataStoreLock.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/DataStoreLock.java @@ -6,36 +6,36 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; public class DataStoreLock { - private final Map lock = new HashMap<>(); - - public void readLock(String id) { - addLock(id); - lock.get(id).readLock().lock(); - } - - public void readUnlock(String id) { - addLock(id); - lock.get(id).readLock().unlock(); - } - - public void writeLock(String id) { - addLock(id); - lock.get(id).writeLock().lock(); - } - - public void writeUnlock(String id) { - addLock(id); - lock.get(id).writeLock().unlock(); - } - - public void removeLock(String id) { - addLock(id); - lock.remove(id); - } - - private void addLock(String id) { - synchronized(this){ - lock.putIfAbsent(id, new ReentrantReadWriteLock()); - } + private final Map lock = new HashMap<>(); + + public void readLock(String id) { + addLock(id); + lock.get(id).readLock().lock(); + } + + public void readUnlock(String id) { + addLock(id); + lock.get(id).readLock().unlock(); + } + + public void writeLock(String id) { + addLock(id); + lock.get(id).writeLock().lock(); + } + + public void writeUnlock(String id) { + addLock(id); + lock.get(id).writeLock().unlock(); + } + + public void removeLock(String id) { + addLock(id); + lock.remove(id); + } + + private void addLock(String id) { + synchronized (this) { + lock.putIfAbsent(id, new ReentrantReadWriteLock()); } + } } diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/InMemoryDataStore.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/InMemoryDataStore.java index 9d9c7bb4d..1f2065919 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/InMemoryDataStore.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/InMemoryDataStore.java @@ -2,7 +2,6 @@ import java.util.HashMap; import java.util.Map; - import net.catenax.edc.cp.adapter.messaging.Message; public class InMemoryDataStore implements DataStore { diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/ContractAgreementData.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/ContractAgreementData.java index 42b68ee9c..f475d3361 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/ContractAgreementData.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/ContractAgreementData.java @@ -7,23 +7,23 @@ @Getter @Setter public class ContractAgreementData { - private String id; - private String providerAgentId; - private String consumerAgentId; - private long contractSigningDate; - private long contractStartDate; - private long contractEndDate; - private String assetId; - private String policyId; + private String id; + private String providerAgentId; + private String consumerAgentId; + private long contractSigningDate; + private long contractStartDate; + private long contractEndDate; + private String assetId; + private String policyId; - public static ContractAgreementData from(ContractAgreement agreement){ - ContractAgreementData data = new ContractAgreementData(); - data.setId(agreement.getId()); - data.setAssetId(agreement.getAssetId()); - data.setPolicyId(agreement.getPolicyId()); - data.setContractStartDate(agreement.getContractStartDate()); - data.setContractEndDate(agreement.getContractEndDate()); - data.setContractSigningDate(agreement.getContractSigningDate()); - return data; - } + public static ContractAgreementData from(ContractAgreement agreement) { + ContractAgreementData data = new ContractAgreementData(); + data.setId(agreement.getId()); + data.setAssetId(agreement.getAssetId()); + data.setPolicyId(agreement.getPolicyId()); + data.setContractStartDate(agreement.getContractStartDate()); + data.setContractEndDate(agreement.getContractEndDate()); + data.setContractSigningDate(agreement.getContractSigningDate()); + return data; + } } diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/ContractDataStore.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/ContractDataStore.java index 7d741bc9a..f2c7ebc36 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/ContractDataStore.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/ContractDataStore.java @@ -3,6 +3,7 @@ import org.eclipse.dataspaceconnector.spi.types.domain.contract.agreement.ContractAgreement; public interface ContractDataStore { - void add(String assetId, String provider, ContractAgreement contractAgreement); - ContractAgreementData get(String assetId, String provider); + void add(String assetId, String provider, ContractAgreement contractAgreement); + + ContractAgreementData get(String assetId, String provider); } diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/InMemoryContractDataStore.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/InMemoryContractDataStore.java index d3599b124..da00c0cc9 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/InMemoryContractDataStore.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/InMemoryContractDataStore.java @@ -1,24 +1,23 @@ package net.catenax.edc.cp.adapter.process.contractdatastore; -import org.eclipse.dataspaceconnector.spi.types.domain.contract.agreement.ContractAgreement; - import java.util.HashMap; import java.util.Map; +import org.eclipse.dataspaceconnector.spi.types.domain.contract.agreement.ContractAgreement; -public class InMemoryContractDataStore implements ContractDataStore{ - private final static Map contractMap = new HashMap<>(); +public class InMemoryContractDataStore implements ContractDataStore { + private static final Map contractMap = new HashMap<>(); - @Override - public void add(String assetId, String provider, ContractAgreement agreement) { - contractMap.put(getKey(assetId, provider), ContractAgreementData.from(agreement)); - } + @Override + public void add(String assetId, String provider, ContractAgreement agreement) { + contractMap.put(getKey(assetId, provider), ContractAgreementData.from(agreement)); + } - @Override - public ContractAgreementData get(String assetId, String provider) { - return contractMap.get(getKey(assetId, provider)); - } + @Override + public ContractAgreementData get(String assetId, String provider) { + return contractMap.get(getKey(assetId, provider)); + } - private String getKey(String assetId, String provider){ - return assetId + "::" + provider; - } + private String getKey(String assetId, String provider) { + return assetId + "::" + provider; + } } diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnegotiation/ContractNegotiationHandler.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnegotiation/ContractNegotiationHandler.java index 3676975da..c05a81787 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnegotiation/ContractNegotiationHandler.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnegotiation/ContractNegotiationHandler.java @@ -1,8 +1,6 @@ package net.catenax.edc.cp.adapter.process.contractnegotiation; import java.time.Instant; -import java.time.LocalDateTime; -import java.time.temporal.ChronoUnit; import java.util.Collections; import java.util.Objects; import java.util.Optional; @@ -38,9 +36,9 @@ public void process(Message message) { monitor.debug("RequestHandler: input request: " + message.getPayload()); ProcessData processData = message.getPayload(); - ContractAgreementData contractData = contractDataStore.get( - message.getPayload().getAssetId(), - message.getPayload().getProvider()); + ContractAgreementData contractData = + contractDataStore.get( + message.getPayload().getAssetId(), message.getPayload().getProvider()); if (Objects.nonNull(contractData) && isContractValid(contractData)) { monitor.info(String.format("[%s] ContractAgreement taken from cache.", message.getTraceId())); message.getPayload().setContractAgreementId(contractData.getId()); @@ -62,19 +60,19 @@ public void process(Message message) { private boolean isContractValid(ContractAgreementData contractAgreement) { long now = Instant.now().getEpochSecond(); - return Objects.nonNull(contractAgreement) && - contractAgreement.getContractStartDate() < now && - contractAgreement.getContractEndDate() > now; + return Objects.nonNull(contractAgreement) + && contractAgreement.getContractStartDate() < now + && contractAgreement.getContractEndDate() > now; } private ContractOffer findContractOffer(String assetId, String providerUrl) { Catalog catalog = getCatalog(providerUrl); return Optional.ofNullable(catalog.getContractOffers()).orElse(Collections.emptyList()).stream() - .filter(it -> it.getAsset().getId().equals(assetId)) - .findFirst() - .orElseThrow( - () -> - new ResourceNotFoundException("Could not find Contract Offer for given Asset Id")); + .filter(it -> it.getAsset().getId().equals(assetId)) + .findFirst() + .orElseThrow( + () -> + new ResourceNotFoundException("Could not find Contract Offer for given Asset Id")); } private Catalog getCatalog(String providerUrl) { diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/service/ResultService.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/service/ResultService.java index 08f865fdd..8b72fcece 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/service/ResultService.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/service/ResultService.java @@ -1,5 +1,6 @@ package net.catenax.edc.cp.adapter.service; +import static java.util.Objects.isNull; import static java.util.concurrent.TimeUnit.SECONDS; import java.util.Map; @@ -9,22 +10,20 @@ import lombok.RequiredArgsConstructor; import net.catenax.edc.cp.adapter.messaging.Listener; import net.catenax.edc.cp.adapter.messaging.Message; -import org.eclipse.dataspaceconnector.spi.monitor.Monitor; import org.eclipse.dataspaceconnector.spi.types.domain.edr.EndpointDataReference; @RequiredArgsConstructor public class ResultService implements Listener { private final int CAPACITY = 1; private final int DEFAULT_TIMEOUT = 15; // TODO move to config - private final Monitor monitor; private final Map> dataReference = new ConcurrentHashMap<>(); - public EndpointDataReference poll(String id) throws InterruptedException { - return poll(id, DEFAULT_TIMEOUT, SECONDS); + public EndpointDataReference pull(String id) throws InterruptedException { + return pull(id, DEFAULT_TIMEOUT, SECONDS); } - public EndpointDataReference poll(String id, long timeout, TimeUnit unit) + public EndpointDataReference pull(String id, long timeout, TimeUnit unit) throws InterruptedException { if (!dataReference.containsKey(id)) { initiate(id); @@ -36,6 +35,11 @@ public EndpointDataReference poll(String id, long timeout, TimeUnit unit) @Override public void process(Message message) { + if (isNull(message) + || isNull(message.getPayload()) + || isNull(message.getPayload().getEndpointDataReference())) { + throw new IllegalArgumentException(); + } add(message.getTraceId(), message.getPayload().getEndpointDataReference()); } diff --git a/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/HttpControllerTest.java b/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/HttpControllerTest.java new file mode 100644 index 000000000..ba9c67196 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/HttpControllerTest.java @@ -0,0 +1,43 @@ +package net.catenax.edc.cp.adapter; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import jakarta.ws.rs.core.Response; +import net.catenax.edc.cp.adapter.messaging.MessageService; +import net.catenax.edc.cp.adapter.service.ResultService; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class HttpControllerTest { + + @Test + public void getAssetSynchronous_shouldReturnBadRequestIfNoAssetIdParam() { + // given + Monitor monitor = Mockito.mock(Monitor.class); + ResultService resultService = Mockito.mock(ResultService.class); + MessageService messageService = Mockito.mock(MessageService.class); + HttpController httpController = new HttpController(monitor, resultService, messageService); + + // when + Response response = httpController.getAssetSynchronous(null, "providerUrl"); + + // then + assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus()); + } + + @Test + public void getAssetSynchronous_shouldReturnBadRequestIfNoProviderUrlParam() { + // given + Monitor monitor = Mockito.mock(Monitor.class); + ResultService resultService = Mockito.mock(ResultService.class); + MessageService messageService = Mockito.mock(MessageService.class); + HttpController httpController = new HttpController(monitor, resultService, messageService); + + // when + Response response = httpController.getAssetSynchronous("assetId", null); + + // then + assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus()); + } +} diff --git a/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/messaging/InMemoryMessageServiceTest.java b/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/messaging/InMemoryMessageServiceTest.java new file mode 100644 index 000000000..5208d5235 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/messaging/InMemoryMessageServiceTest.java @@ -0,0 +1,52 @@ +package net.catenax.edc.cp.adapter.messaging; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +public class InMemoryMessageServiceTest { + @Mock Monitor monitor; + @Mock Listener listener; + @Mock ListenerService listenerService; + + @BeforeEach + void init() { + MockitoAnnotations.openMocks(this); + } + + @Test + public void send_shouldCallListenerOnce() throws InterruptedException { + // given + Message message = new Message(null); + when(listenerService.getListener(any())).thenReturn(listener); + MessageService messageService = new InMemoryMessageService(monitor, listenerService); + + // when + messageService.send(Channel.INITIAL, message); + + // then + Thread.sleep(50); + verify(listener, times(1)).process(any(Message.class)); + } + + @Test + public void send_shouldCallListenerWithRetryOnException() throws InterruptedException { + // given + Message message = new Message(null); + when(listenerService.getListener(any())).thenReturn(listener); + doThrow(new IllegalStateException()).doNothing().when(listener).process(any()); + MessageService messageService = new InMemoryMessageService(monitor, listenerService); + + // when + messageService.send(Channel.INITIAL, message); + + // then + Thread.sleep(1000); + verify(listener, times(2)).process(any(Message.class)); + } +} diff --git a/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/contractconfirmation/ContractConfirmationHandlerTest.java b/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/contractconfirmation/ContractConfirmationHandlerTest.java new file mode 100644 index 000000000..39fc7233c --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/contractconfirmation/ContractConfirmationHandlerTest.java @@ -0,0 +1,195 @@ +package net.catenax.edc.cp.adapter.process.contractconfirmation; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +import net.catenax.edc.cp.adapter.dto.ProcessData; +import net.catenax.edc.cp.adapter.messaging.Channel; +import net.catenax.edc.cp.adapter.messaging.Message; +import net.catenax.edc.cp.adapter.messaging.MessageService; +import net.catenax.edc.cp.adapter.process.contractdatastore.ContractDataStore; +import org.eclipse.dataspaceconnector.api.datamanagement.contractnegotiation.service.ContractNegotiationService; +import org.eclipse.dataspaceconnector.api.datamanagement.transferprocess.service.TransferProcessService; +import org.eclipse.dataspaceconnector.api.result.ServiceResult; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.types.domain.contract.agreement.ContractAgreement; +import org.eclipse.dataspaceconnector.spi.types.domain.contract.negotiation.ContractNegotiation; +import org.eclipse.dataspaceconnector.spi.types.domain.contract.negotiation.ContractNegotiationStates; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +public class ContractConfirmationHandlerTest { + @Mock Monitor monitor; + @Mock MessageService messageService; + @Mock ContractNegotiationService contractNegotiationService; + @Mock DataStore dataStore; + @Mock ContractDataStore contractDataStore; + @Mock TransferProcessService transferProcessService; + + @BeforeEach + void init() { + MockitoAnnotations.openMocks(this); + } + + @Test + public void process_shouldSaveMessageWhenContractNotConfirmed() { + // given + when(transferProcessService.initiateTransfer(any())).thenReturn(ServiceResult.success(null)); + ContractConfirmationHandler contractConfirmationHandler = + new ContractConfirmationHandler( + monitor, + messageService, + dataStore, + contractNegotiationService, + transferProcessService, + contractDataStore); + Message message = new Message(new ProcessData("assetId", "providerUrl")); + + // when + contractConfirmationHandler.process(message); + + // then + verify(dataStore, times(1)).storeMessage(any()); + verify(transferProcessService, times(0)).initiateTransfer(any()); + verify(messageService, times(0)).send(eq(Channel.DATA_REFERENCE), any(Message.class)); + } + + @Test + public void process_shouldInitiateTransferWhenContractConfirmedFromCache() { + // given + when(transferProcessService.initiateTransfer(any())).thenReturn(ServiceResult.success(null)); + ContractConfirmationHandler contractConfirmationHandler = + new ContractConfirmationHandler( + monitor, + messageService, + dataStore, + contractNegotiationService, + transferProcessService, + contractDataStore); + Message message = new Message(new ProcessData("assetId", "providerUrl")); + message.getPayload().setContractConfirmed(true); + + // when + contractConfirmationHandler.process(message); + + // then + verify(transferProcessService, times(1)).initiateTransfer(any()); + verify(messageService, times(1)).send(eq(Channel.DATA_REFERENCE), any(Message.class)); + } + + @Test + public void process_shouldInitiateTransferWhenAlreadyContractConfirmedAtProvider() { + // given + when(contractNegotiationService.findbyId(any())).thenReturn(getConfirmedContractNegotiation()); + when(transferProcessService.initiateTransfer(any())).thenReturn(ServiceResult.success(null)); + ContractConfirmationHandler contractConfirmationHandler = + new ContractConfirmationHandler( + monitor, + messageService, + dataStore, + contractNegotiationService, + transferProcessService, + contractDataStore); + Message message = new Message(new ProcessData("assetId", "providerUrl")); + + // when + contractConfirmationHandler.process(message); + + // then + verify(dataStore, times(0)).storeMessage(any()); + verify(transferProcessService, times(1)).initiateTransfer(any()); + verify(messageService, times(1)).send(eq(Channel.DATA_REFERENCE), any(Message.class)); + } + + @Test + public void process_shouldInitiateTransferWhenContractConfirmedByNotification() { + // given + when(dataStore.getConfirmedContract(any())).thenReturn("confirmedContractAgreementId"); + when(transferProcessService.initiateTransfer(any())).thenReturn(ServiceResult.success(null)); + ContractConfirmationHandler contractConfirmationHandler = + new ContractConfirmationHandler( + monitor, + messageService, + dataStore, + contractNegotiationService, + transferProcessService, + contractDataStore); + Message message = new Message(new ProcessData("assetId", "providerUrl")); + + // when + contractConfirmationHandler.process(message); + + // then + verify(dataStore, times(0)).storeMessage(any()); + verify(transferProcessService, times(1)).initiateTransfer(any()); + verify(messageService, times(1)).send(eq(Channel.DATA_REFERENCE), any(Message.class)); + verify(dataStore, times(1)).removeConfirmedContract(any()); + } + + @Test + public void preConfirmed_shouldSaveInfoAboutContractConfirmationIfMessageNotAvailable() { + // given + ContractConfirmationHandler contractConfirmationHandler = + new ContractConfirmationHandler( + monitor, + messageService, + dataStore, + contractNegotiationService, + transferProcessService, + contractDataStore); + ContractNegotiation contractNegotiation = getConfirmedContractNegotiation(); + + // when + contractConfirmationHandler.preConfirmed(contractNegotiation); + + // then + verify(dataStore, times(1)).storeConfirmedContract(any(), any()); + verify(transferProcessService, times(0)).initiateTransfer(any()); + verify(messageService, times(0)).send(eq(Channel.DATA_REFERENCE), any(Message.class)); + } + + @Test + public void preConfirmed_shouldInitiateTransferIfMessageIsAvailable() { + // given + when(dataStore.getMessage(any())) + .thenReturn(new Message(new ProcessData("assetId", "providerUrl"))); + when(transferProcessService.initiateTransfer(any())).thenReturn(ServiceResult.success(null)); + ContractConfirmationHandler contractConfirmationHandler = + new ContractConfirmationHandler( + monitor, + messageService, + dataStore, + contractNegotiationService, + transferProcessService, + contractDataStore); + ContractNegotiation contractNegotiation = getConfirmedContractNegotiation(); + + // when + contractConfirmationHandler.preConfirmed(contractNegotiation); + + // then + verify(dataStore, times(0)).storeConfirmedContract(any(), any()); + verify(transferProcessService, times(1)).initiateTransfer(any()); + verify(messageService, times(1)).send(eq(Channel.DATA_REFERENCE), any(Message.class)); + } + + private ContractNegotiation getConfirmedContractNegotiation() { + return ContractNegotiation.Builder.newInstance() + .state(ContractNegotiationStates.CONFIRMED.code()) + .id("contractNegotiationId") + .counterPartyId("counterPartyId") + .counterPartyAddress("counterPartyAddress") + .protocol("protocol") + .contractAgreement( + ContractAgreement.Builder.newInstance() + .id("contractAgreementId") + .providerAgentId("providerAgentId") + .consumerAgentId("consumerAgentId") + .assetId("assetId") + .policyId("policyId") + .build()) + .build(); + } +} diff --git a/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/contractnegotiation/ContractNegotiationHandlerTest.java b/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/contractnegotiation/ContractNegotiationHandlerTest.java new file mode 100644 index 000000000..b94353ed3 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/contractnegotiation/ContractNegotiationHandlerTest.java @@ -0,0 +1,136 @@ +package net.catenax.edc.cp.adapter.process.contractnegotiation; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.*; + +import java.time.Instant; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import net.catenax.edc.cp.adapter.dto.ProcessData; +import net.catenax.edc.cp.adapter.messaging.Message; +import net.catenax.edc.cp.adapter.messaging.MessageService; +import net.catenax.edc.cp.adapter.process.contractdatastore.ContractAgreementData; +import net.catenax.edc.cp.adapter.process.contractdatastore.ContractDataStore; +import org.eclipse.dataspaceconnector.api.datamanagement.catalog.service.CatalogService; +import org.eclipse.dataspaceconnector.api.datamanagement.contractnegotiation.service.ContractNegotiationService; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.types.domain.asset.Asset; +import org.eclipse.dataspaceconnector.spi.types.domain.catalog.Catalog; +import org.eclipse.dataspaceconnector.spi.types.domain.contract.negotiation.ContractNegotiation; +import org.eclipse.dataspaceconnector.spi.types.domain.contract.offer.ContractOffer; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +public class ContractNegotiationHandlerTest { + @Mock Monitor monitor; + @Mock MessageService messageService; + @Mock ContractNegotiationService contractNegotiationService; + @Mock CatalogService catalogService; + @Mock ContractDataStore contractDataStore; + + @BeforeEach + void init() { + MockitoAnnotations.openMocks(this); + } + + @Test + public void process_shouldNotInitializeContractNegotiationWhenCachedContractAlreadyAvailable() { + // given + ContractNegotiationHandler contractNegotiationHandler = + new ContractNegotiationHandler( + monitor, messageService, contractNegotiationService, catalogService, contractDataStore); + + when(contractDataStore.get(anyString(), anyString())) + .thenReturn(getValidContractAgreementData()); + + // when + contractNegotiationHandler.process(new Message(new ProcessData("asset", "provider"))); + + // then + verify(contractNegotiationService, times(0)).initiateNegotiation(any()); + verify(messageService, times(1)).send(any(), any(Message.class)); + } + + @Test + public void process_shouldInitializeContractNegotiationWhenCachedContractExpired() { + // given + ContractNegotiationHandler contractNegotiationHandler = + new ContractNegotiationHandler( + monitor, messageService, contractNegotiationService, catalogService, contractDataStore); + + when(contractDataStore.get(anyString(), anyString())) + .thenReturn(getExpiredContractAgreementData()); + when(catalogService.getByProviderUrl(anyString())) + .thenReturn(CompletableFuture.completedFuture(getCatalog())); + when(contractNegotiationService.initiateNegotiation(any())) + .thenReturn(getContractNegotiation()); + + // when + contractNegotiationHandler.process(new Message(new ProcessData("assetId", "provider"))); + + // then + verify(contractNegotiationService, times(1)).initiateNegotiation(any()); + verify(messageService, times(1)).send(any(), any(Message.class)); + } + + @Test + public void process_shouldInitiateContractNegotiationAndSendMessageFurtherIfCacheEmpty() { + // given + ContractNegotiationHandler contractNegotiationHandler = new ContractNegotiationHandler( + monitor, messageService, contractNegotiationService, catalogService, contractDataStore); + + when(contractDataStore.get(anyString(), anyString())).thenReturn(null); + when(catalogService.getByProviderUrl(anyString())) + .thenReturn(CompletableFuture.completedFuture(getCatalog())); + when(contractNegotiationService.initiateNegotiation(any())) + .thenReturn(getContractNegotiation()); + + // when + contractNegotiationHandler.process(new Message(new ProcessData("assetId", "provider"))); + + // then + verify(contractNegotiationService, times(1)).initiateNegotiation(any()); + verify(messageService, times(1)).send(any(), any(Message.class)); + } + + private ContractNegotiation getContractNegotiation() { + return ContractNegotiation.Builder.newInstance() + .id("contractNegotiationId") + .counterPartyId("counterPartyId") + .counterPartyAddress("counterPartyAddress") + .protocol("protocol") + .build(); + } + + private Catalog getCatalog() { + return Catalog.Builder.newInstance() + .id("id") + .contractOffers(List.of(getContractOffer())) + .build(); + } + + private ContractOffer getContractOffer() { + Asset asset = Asset.Builder.newInstance().id("assetId").build(); + return ContractOffer.Builder.newInstance().id("id").asset(asset).policyId("policyId").build(); + } + + private ContractAgreementData getValidContractAgreementData() { + long now = Instant.now().getEpochSecond(); + ContractAgreementData contractAgreementData = new ContractAgreementData(); + contractAgreementData.setId("id"); + contractAgreementData.setAssetId("assetId"); + contractAgreementData.setContractStartDate(now - 5000); + contractAgreementData.setContractEndDate(now + 5000); + return contractAgreementData; + } + + private ContractAgreementData getExpiredContractAgreementData() { + long now = Instant.now().getEpochSecond(); + ContractAgreementData contractAgreementData = getValidContractAgreementData(); + contractAgreementData.setContractEndDate(now - 1000); + return contractAgreementData; + } +} diff --git a/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/datareference/DataReferenceHandlerTest.java b/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/datareference/DataReferenceHandlerTest.java new file mode 100644 index 000000000..62799f28a --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/datareference/DataReferenceHandlerTest.java @@ -0,0 +1,98 @@ +package net.catenax.edc.cp.adapter.process.datareference; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +import net.catenax.edc.cp.adapter.dto.ProcessData; +import net.catenax.edc.cp.adapter.messaging.Channel; +import net.catenax.edc.cp.adapter.messaging.Message; +import net.catenax.edc.cp.adapter.messaging.MessageService; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.types.domain.edr.EndpointDataReference; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +public class DataReferenceHandlerTest { + @Mock Monitor monitor; + @Mock MessageService messageService; + @Mock DataStore dataStore; + + @BeforeEach + void init() { + MockitoAnnotations.openMocks(this); + } + + @Test + public void process_shouldSaveMessageWhenDataReferenceNotAvailable() { + // given + DataReferenceHandler dataReferenceHandler = + new DataReferenceHandler(monitor, messageService, dataStore); + Message message = new Message(new ProcessData("assetId", "providerUrl")); + + // when + dataReferenceHandler.process(message); + + // then + verify(dataStore, times(1)).storeMessage(eq(message)); + verify(messageService, times(0)).send(eq(Channel.RESULT), any(Message.class)); + } + + @Test + public void process_shouldSendResultWhenDataReferenceIsAvailable() { + // given + when(dataStore.getDataReference(any())).thenReturn(getEndpointDataReference()); + DataReferenceHandler dataReferenceHandler = + new DataReferenceHandler(monitor, messageService, dataStore); + Message message = new Message(new ProcessData("assetId", "providerUrl")); + + // when + dataReferenceHandler.process(message); + + // then + verify(dataStore, times(0)).storeMessage(eq(message)); + verify(messageService, times(1)).send(eq(Channel.RESULT), any(Message.class)); + verify(dataStore, times(1)).removeDataReference(any()); + } + + @Test + public void send_shouldSaveDataReferenceWhenMessageNotAvailable() { + // given + DataReferenceHandler dataReferenceHandler = + new DataReferenceHandler(monitor, messageService, dataStore); + + // when + dataReferenceHandler.send(getEndpointDataReference()); + + // then + verify(dataStore, times(1)).storeDataReference(any(), any(EndpointDataReference.class)); + verify(messageService, times(0)).send(eq(Channel.RESULT), any(Message.class)); + } + + @Test + public void send_shouldSendResultWhenMessageIsAvailable() { + // given + Message message = new Message(new ProcessData("assetId", "providerUrl")); + when(dataStore.getMessage(any())).thenReturn(message); + DataReferenceHandler dataReferenceHandler = + new DataReferenceHandler(monitor, messageService, dataStore); + + // when + dataReferenceHandler.send(getEndpointDataReference()); + + // then + verify(dataStore, times(0)).storeDataReference(any(), any(EndpointDataReference.class)); + verify(messageService, times(1)).send(eq(Channel.RESULT), any(Message.class)); + verify(dataStore, times(1)).removeMessage(any()); + } + + private EndpointDataReference getEndpointDataReference() { + return EndpointDataReference.Builder.newInstance() + .endpoint("endpoint") + .authCode("authCode") + .authKey("authKey") + .build(); + } +} diff --git a/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/service/ResultServiceTest.java b/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/service/ResultServiceTest.java new file mode 100644 index 000000000..e4616309b --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/service/ResultServiceTest.java @@ -0,0 +1,101 @@ +package net.catenax.edc.cp.adapter.service; + +import static org.junit.jupiter.api.Assertions.fail; + +import java.util.concurrent.TimeUnit; +import net.catenax.edc.cp.adapter.dto.ProcessData; +import net.catenax.edc.cp.adapter.messaging.Message; +import org.eclipse.dataspaceconnector.spi.types.domain.edr.EndpointDataReference; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class ResultServiceTest { + @Test + public void pull_shouldReturnDataReferenceWhenMessageOccursFirst() throws InterruptedException { + // given + ResultService resultService = new ResultService(); + String endpointDataRefId = "456"; + Message message = getMessage(endpointDataRefId); + EndpointDataReference dataReference; + + // when + resultService.process(message); + dataReference = resultService.pull(message.getTraceId(), 200, TimeUnit.MILLISECONDS); + + // then + Assertions.assertEquals(endpointDataRefId, dataReference.getId()); + } + + @Test + public void pull_shouldReturnDataReferenceWhenMessageOccursSecond() throws InterruptedException { + // given + ResultService resultService = new ResultService(); + String endpointDataRefId = "456"; + Message message = getMessage(endpointDataRefId); + EndpointDataReference dataReference; + + // when + processMessageWithDelay(resultService, message); + dataReference = resultService.pull(message.getTraceId(), 1000, TimeUnit.MILLISECONDS); + + // then + Assertions.assertEquals(endpointDataRefId, dataReference.getId()); + } + + private void processMessageWithDelay(ResultService resultService, Message message) { + new Thread( + () -> { + sleep(400); + resultService.process(message); + }) + .start(); + } + + @Test + public void pull_shouldReturnNullOnTimeout() throws InterruptedException { + // given + ResultService resultService = new ResultService(); + + // when + EndpointDataReference dataReference = resultService.pull("123", 500, TimeUnit.MILLISECONDS); + + // then + Assertions.assertNull(dataReference); + } + + @Test + public void process_shouldThrowIllegalArgumentExceptionIfNoDataReference() { + // given + ResultService resultService = new ResultService(); + Message message = new Message(new ProcessData("123", "providerUrl")); + + // when then + try { + resultService.process(message); + fail("Method should throw IllegalArgumentException"); + } catch (IllegalArgumentException ignored) { + } + } + + private Message getMessage(String endpointDataRefId) { + Message message = new Message(new ProcessData("123", "providerUrl")); + message.getPayload() + .setEndpointDataReference( + EndpointDataReference.Builder.newInstance() + .id(endpointDataRefId) + .endpoint("e") + .authCode("c") + .authKey("k") + .build()); + return message; + } + + private void sleep(long milisec) { + try { + Thread.sleep(milisec); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } +} From 87a0d6d827b92986434bdadd542d93903cfd8080 Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Fri, 26 Aug 2022 15:21:23 +0200 Subject: [PATCH 253/433] NONE encryption requires no further configuration (#372) --- docs/diagrams/transfer_sequence_1.png | Bin 29595 -> 0 bytes docs/diagrams/transfer_sequence_1.puml | 34 --------- docs/diagrams/transfer_sequence_2.png | Bin 29371 -> 0 bytes docs/diagrams/transfer_sequence_2.puml | 28 -------- docs/diagrams/transfer_sequence_3.png | Bin 34859 -> 0 bytes docs/diagrams/transfer_sequence_3.puml | 33 --------- docs/diagrams/transfer_sequence_4.png | Bin 60428 -> 0 bytes docs/diagrams/transfer_sequence_4.puml | 44 ------------ docs/diagrams/transfer_sequence_5.png | Bin 21812 -> 0 bytes docs/diagrams/transfer_sequence_5.puml | 27 -------- edc-extensions/data-encryption/README.md | 13 ++-- .../encryption/DataEncryptionExtension.java | 65 +++++++++++------- ...ava => AesDataEncrypterConfiguration.java} | 5 +- .../encrypter/DataEncrypterFactory.java | 46 ++++--------- .../DataEncryptionExtensionTest.java | 19 ++++- .../encrypter/DataEncrypterFactoryTest.java | 25 ++----- 16 files changed, 91 insertions(+), 248 deletions(-) delete mode 100644 docs/diagrams/transfer_sequence_1.png delete mode 100644 docs/diagrams/transfer_sequence_1.puml delete mode 100644 docs/diagrams/transfer_sequence_2.png delete mode 100644 docs/diagrams/transfer_sequence_2.puml delete mode 100644 docs/diagrams/transfer_sequence_3.png delete mode 100644 docs/diagrams/transfer_sequence_3.puml delete mode 100644 docs/diagrams/transfer_sequence_4.png delete mode 100644 docs/diagrams/transfer_sequence_4.puml delete mode 100644 docs/diagrams/transfer_sequence_5.png delete mode 100644 docs/diagrams/transfer_sequence_5.puml rename edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/encrypter/{DataEncrypterConfiguration.java => AesDataEncrypterConfiguration.java} (85%) diff --git a/docs/diagrams/transfer_sequence_1.png b/docs/diagrams/transfer_sequence_1.png deleted file mode 100644 index f52ecbb70a0aadca9e555a2481581d11c86ca1d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29595 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 S*d-L>t**AAR=(!-d;bAdoUX6{ diff --git a/docs/diagrams/transfer_sequence_1.puml b/docs/diagrams/transfer_sequence_1.puml deleted file mode 100644 index a159447ee..000000000 --- a/docs/diagrams/transfer_sequence_1.puml +++ /dev/null @@ -1,34 +0,0 @@ -@startuml - -!define sokratesColor 66CCFF -!define platoColor CCFF99 -!define dapsColor FFFF99 -!define noteColor 9999FF - -actor User as "User" - -box Sokrates - participant SokratesControlPlane as "Control Plane" #sokratesColor - participant SokratesBackendService as "Backend Application" #sokratesColor - participant SokratesDataPlane as "Data Plane" #sokratesColor -end box - -box Plato - participant PlatoControlPlane as "Control Plane" #platoColor - participant PlatoDataPlane as "Data Plane" #platoColor -end box - -participant JsonPlaceHolder as "JsonPlaceHolder" - - -User -> PlatoControlPlane ++ : Create Asset -return 204 - -User -> PlatoControlPlane ++ : Create Policy -return 204 - -User -> PlatoControlPlane ++ : Create Contract Definition -return 204 - - -@enduml diff --git a/docs/diagrams/transfer_sequence_2.png b/docs/diagrams/transfer_sequence_2.png deleted file mode 100644 index ed2f4dd90855fddd2db0b7841d109524f5628246..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29371 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 diff --git a/docs/diagrams/transfer_sequence_2.puml b/docs/diagrams/transfer_sequence_2.puml deleted file mode 100644 index 805bcbeec..000000000 --- a/docs/diagrams/transfer_sequence_2.puml +++ /dev/null @@ -1,28 +0,0 @@ -@startuml - -!define sokratesColor 66CCFF -!define platoColor CCFF99 -!define dapsColor FFFF99 -!define noteColor 9999FF - -actor User as "User" - -box Sokrates - participant SokratesControlPlane as "Control Plane" #sokratesColor - participant SokratesBackendService as "Backend Application" #sokratesColor - participant SokratesDataPlane as "Data Plane" #sokratesColor -end box - -box Plato - participant PlatoControlPlane as "Control Plane" #platoColor - participant PlatoDataPlane as "Data Plane" #platoColor -end box - -participant JsonPlaceHolder as "JsonPlaceHolder" - -User -> SokratesControlPlane ++ : Request Contract Offers from Plato - SokratesControlPlane -> PlatoControlPlane ++ : IDS Description Request Message - return Description -return Contract Offers - -@enduml diff --git a/docs/diagrams/transfer_sequence_3.png b/docs/diagrams/transfer_sequence_3.png deleted file mode 100644 index b1d56ec6c24a1c7ec2913088ff82c1cda4350404..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34859 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 WQZ!@|bb$L0DY1KZb3`A%{Qm%Q&K}?Z diff --git a/docs/diagrams/transfer_sequence_3.puml b/docs/diagrams/transfer_sequence_3.puml deleted file mode 100644 index 43707af36..000000000 --- a/docs/diagrams/transfer_sequence_3.puml +++ /dev/null @@ -1,33 +0,0 @@ -@startuml - -!define sokratesColor 66CCFF -!define platoColor CCFF99 -!define dapsColor FFFF99 -!define noteColor 9999FF - -actor User as "User" - -box Sokrates - participant SokratesControlPlane as "Control Plane" #sokratesColor - participant SokratesBackendService as "Backend Application" #sokratesColor - participant SokratesDataPlane as "Data Plane" #sokratesColor -end box - -box Plato - participant PlatoControlPlane as "Control Plane" #platoColor - participant PlatoDataPlane as "Data Plane" #platoColor -end box - -participant JsonPlaceHolder as "JsonPlaceHolder" - - -User -> SokratesControlPlane ++ : Negotiate Contract for Offer X -SokratesControlPlane --> User: Negotiation ID - SokratesControlPlane -> PlatoControlPlane ++ : IDS Contract Negotiation (simplified) - return Contract Agreement -deactivate SokratesControlPlane - -User -> SokratesControlPlane ++ : Request Negotiation by ID -return Contract Negotiation - -@enduml diff --git a/docs/diagrams/transfer_sequence_4.png b/docs/diagrams/transfer_sequence_4.png deleted file mode 100644 index 43701e1ba1486f8dfa8171ba46b782bea61da0ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60428 zcmdSBcT|)6)-?(!Dk!LsfDI84NT>=bQUnX3x6nI^bfougLm&uJLKA7BcMy;c7C<^k zQKSljNbey1uIzU9IcL9P-1ml+iVk_=lQj@=A3J;1Sly;Qc<3vBq1T8l9m!z zCLuX^jf8}(C(Lm zta3|_NNKv*e6QUht6pWEmCt$?sw=I~QoA4%P#AZdtb4udR=TUp$jZR+Tn7uTFV|w( zNRRf#p89y3`IJ+BIi_1Zpv%^=$J(XRS3tf@%^}L+93y>Rbox=HQk^v^W4n35wC_L3 z8dzjI1P)!bJs>>)F}P?X)8LY>)R9x2($dNP;?1dEtHlw;+N@8W(pG=qJ@$=Tc=1hG zy-B>o`Jv?Jei^3BFZsFUY$MYG{L7Fsq6zsQxOv6P)8%o4oTx7%6OXcII2P{e&vQ4? z$$x#j_%w&_O-iQRCjl8{*QbnXG`w3SMk~21Z4X}M*mTYFJhcrH;GQ2VW$8MfrJ#m-x5pN-?ox>b{DzD1;td7h0RFu4VWAE`G|Kr!D%u59N0C zdqhFW;-Mw?y@I#Y#kkH>$L);`j*Dn@*Of=NpYY^7Phlw`Xa2K+?7O>usJy$vwYGvu z9_?fFd$_KF(9eLN$UFN#p0(9F@ebt)&N~&=SbzS~w&i1`{?XJXkLTXA z{<3Z7f-i$M*EY0En9_yu7Otho54RQcQtHT?861=Rw&SF}eW@&^_G?U<=ly#*bS^QK zK~A41`KiCe6dU50Jf*Sg!U^^V+6LszScjYpcL%bWzx^z*<1rIZrjnkWL(Y7QHVt4E zPcf|MXBvE75wtNI)*{`&UKbQrf?S$DyiE1groeFd?v$EO0tv}O5^3>Us;>I8aTK0Z z8rvVAT=eh5oUnY__XU-k8rH9^bMZ~TMP{c3XJnL2U@@!A118lqZY#GZbZGtketp9r ze=q5a>q^|Ysfd!{eU|jJ@EGCmKYlcO&5v*FzETwBH*J3<=rG^(YBE-s-;R%X9*BR= zzQUjW{T0bwu|(7#FC}hb|9JKMA>of#r$W^*($dm#S2-<~$2-vSF?>$*gXQT8amsYW zqe}eKQlO9R>f-VBmG=+c6GBfJYWK^Bnz@a>w-MbO?{10W<#Zvr8QUScJ<9^0mUx=C zSk`_(-TyMh_4}!W*#VbtAC9xbL#l<*x(T_=mnw>G&MuCAXdWWze8@v*3cqsdq%;

1za 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 e@4pd*Y SokratesControlPlane ++ : Request Negotiation by ID -return Contract Negotiation\n(containing Contract Agreement ID) - -User -> SokratesControlPlane ++ : Initiate Transfer with Agreement ID -SokratesControlPlane --> User : Transfer Process ID - SokratesControlPlane -> PlatoControlPlane ++ : IDS Data Transfer (simplified) -return -SokratesControlPlane -> SokratesBackendService ++ : Data Plane Endpoint + Token - SokratesBackendService -> SokratesDataPlane ++ : Request Data with Token - SokratesDataPlane -> PlatoDataPlane ++ : Request Data - PlatoDataPlane -> JsonPlaceHolder ++ : Request Data - return data - return data - return data - SokratesBackendService -> SokratesBackendService : Write Data to File -return ok -deactivate SokratesControlPlane - -User -> SokratesControlPlane ++ : Request Transfer Process by ID -return Transfer Process - -@enduml diff --git a/docs/diagrams/transfer_sequence_5.png b/docs/diagrams/transfer_sequence_5.png deleted file mode 100644 index 080c26335df9d5ab8a381d22594c99bcd9492f6d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21812 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 IbxsLQ0Pl#yT>t<8 diff --git a/docs/diagrams/transfer_sequence_5.puml b/docs/diagrams/transfer_sequence_5.puml deleted file mode 100644 index b64f2222b..000000000 --- a/docs/diagrams/transfer_sequence_5.puml +++ /dev/null @@ -1,27 +0,0 @@ -@startuml - -!define sokratesColor 66CCFF -!define platoColor CCFF99 -!define dapsColor FFFF99 -!define noteColor 9999FF - -actor User as "User" - -box Sokrates - participant SokratesControlPlane as "Control Plane" #sokratesColor - participant SokratesBackendService as "Backend Application" #sokratesColor - participant SokratesDataPlane as "Data Plane" #sokratesColor -end box - -box Plato - participant PlatoControlPlane as "Control Plane" #platoColor - participant PlatoDataPlane as "Data Plane" #platoColor -end box - -participant JsonPlaceHolder as "JsonPlaceHolder" - -User -> SokratesBackendService ++ : Get File Content -return data - - -@enduml diff --git a/edc-extensions/data-encryption/README.md b/edc-extensions/data-encryption/README.md index 58a620824..60e01245f 100644 --- a/edc-extensions/data-encryption/README.md +++ b/edc-extensions/data-encryption/README.md @@ -2,14 +2,11 @@ The Eclipse Dataspace Connector encrypts sensitive information inside a token it sends to other applications (from possibly other companies). This extension implements the encryption of this data and should be used with secure keys and algorithms at all times. -## Configuration +## Algorithm Configuration | Key | Description | Mandatory | Default | |:--------------------------------------------|:-----------------------------------------------------------------------------------------------------------------|-----------|------------------| -| edc.data.encryption.keys.alias | Keys for encryption and decryption of the data must be stored in the Vault under the configured alias. | X | | | edc.data.encryption.algorithm | Algorithm for encryption and decryption. Must be ether 'AES' or 'NONE'. | | AES | -| edc.data.encryption.caching.enabled | Enable caching to request only keys from the vault after the cache expires. | | false | -| edc.data.encryption.caching.seconds | Duration in seconds until the cache expires. | | 3600 | ## Strategies @@ -31,6 +28,14 @@ openssl rand -base64 24 openssl rand -base64 32 ``` +#### AES Configuration + +| Key | Description | Mandatory | Default | +|:--------------------------------------------|:-----------------------------------------------------------------------------------------------------------------|-----------|------------------| +| edc.data.encryption.keys.alias | Symmetric Keys stored in the Vault under the configured alias. | X | | +| edc.data.encryption.caching.enabled | Enable caching to request only keys from the vault after the cache expires. | | false | +| edc.data.encryption.caching.seconds | Duration in seconds until the cache expires. | | 3600 | + ### 2. NONE diff --git a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/DataEncryptionExtension.java b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/DataEncryptionExtension.java index 31ef2d92d..5f9446dfa 100644 --- a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/DataEncryptionExtension.java +++ b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/DataEncryptionExtension.java @@ -16,7 +16,7 @@ import java.time.Duration; import java.util.List; import java.util.stream.Collectors; -import net.catenax.edc.data.encryption.encrypter.DataEncrypterConfiguration; +import net.catenax.edc.data.encryption.encrypter.AesDataEncrypterConfiguration; import net.catenax.edc.data.encryption.encrypter.DataEncrypterFactory; import net.catenax.edc.data.encryption.key.AesKey; import net.catenax.edc.data.encryption.key.CryptoKeyFactory; @@ -53,7 +53,7 @@ public class DataEncryptionExtension implements ServiceExtension { private Monitor monitor; private Vault vault; - private DataEncrypterConfiguration configuration; + private ServiceExtensionContext context; @Override public String name() { @@ -62,16 +62,24 @@ public String name() { @Override public void start() { - final String keyAlias = configuration.getKeySetAlias(); - final String keySecret = vault.resolveSecret(keyAlias); - if (keySecret == null || keySecret.isEmpty()) { - throw new EdcException(NAME + ": No vault key secret found for alias " + keyAlias); + + final String algorithm = context.getSetting(ENCRYPTION_ALGORITHM, ENCRYPTION_ALGORITHM_DEFAULT); + + if (DataEncrypterFactory.NONE.equalsIgnoreCase(algorithm)) { + return; // no start-up checks for NONE algorithm } - if (configuration.getAlgorithm().equals(DataEncrypterFactory.AES_ALGORITHM)) { + if (DataEncrypterFactory.AES_ALGORITHM.equals(algorithm)) { + + final AesDataEncrypterConfiguration configuration = createAesConfiguration(context); + final String keyAlias = configuration.getKeySetAlias(); + final String keySecret = vault.resolveSecret(keyAlias); + if (keySecret == null || keySecret.isEmpty()) { + throw new EdcException(NAME + ": No vault key secret found for alias " + keyAlias); + } + try { - final AesKeyProvider aesKeyProvider = - createAesKeyProvider(vault, configuration.getKeySetAlias()); + final AesKeyProvider aesKeyProvider = new AesKeyProvider(vault, keyAlias, cryptoKeyFactory); final List keys = aesKeyProvider.getDecryptionKeySet().collect(Collectors.toList()); monitor.debug( String.format( @@ -85,32 +93,43 @@ public void start() { @Override public void initialize(ServiceExtensionContext context) { - monitor = context.getMonitor(); - configuration = getConfiguration(context); - vault = context.getService(Vault.class); - + this.context = context; + this.monitor = context.getMonitor(); + this.vault = context.getService(Vault.class); final DataEncrypterFactory factory = new DataEncrypterFactory(vault, monitor, cryptoKeyFactory); - final DataEncrypter dataEncrypter = factory.create(configuration); + final DataEncrypter dataEncrypter; + final String algorithm = context.getSetting(ENCRYPTION_ALGORITHM, ENCRYPTION_ALGORITHM_DEFAULT); + if (DataEncrypterFactory.NONE.equalsIgnoreCase(algorithm)) { + dataEncrypter = factory.createNoneEncrypter(); + } else if (DataEncrypterFactory.AES_ALGORITHM.equalsIgnoreCase(algorithm)) { + final AesDataEncrypterConfiguration configuration = createAesConfiguration(context); + dataEncrypter = factory.createAesEncrypter(configuration); + } else { + final String msg = + String.format( + DataEncryptionExtension.NAME + + ": Unsupported encryption algorithm '%s'. Supported algorithms are '%s', '%s'.", + algorithm, + DataEncrypterFactory.AES_ALGORITHM, + DataEncrypterFactory.NONE); + throw new EdcException(msg); + } + context.registerService(DataEncrypter.class, dataEncrypter); } - private static DataEncrypterConfiguration getConfiguration(ServiceExtensionContext context) { + private static AesDataEncrypterConfiguration createAesConfiguration( + ServiceExtensionContext context) { final String key = context.getSetting(ENCRYPTION_KEY_SET, null); if (key == null) { throw new EdcException(NAME + ": Missing setting " + ENCRYPTION_KEY_SET); } - final String encryptionStrategy = - context.getSetting(ENCRYPTION_ALGORITHM, ENCRYPTION_ALGORITHM_DEFAULT); final boolean cachingEnabled = context.getSetting(CACHING_ENABLED, CACHING_ENABLED_DEFAULT); final int cachingSeconds = context.getSetting(CACHING_SECONDS, CACHING_SECONDS_DEFAULT); - return new DataEncrypterConfiguration( - encryptionStrategy, key, cachingEnabled, Duration.ofSeconds(cachingSeconds)); - } - - private static AesKeyProvider createAesKeyProvider(Vault vault, String vaultKeySetAlias) { - return new AesKeyProvider(vault, vaultKeySetAlias, cryptoKeyFactory); + return new AesDataEncrypterConfiguration( + key, cachingEnabled, Duration.ofSeconds(cachingSeconds)); } } diff --git a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/encrypter/DataEncrypterConfiguration.java b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/encrypter/AesDataEncrypterConfiguration.java similarity index 85% rename from edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/encrypter/DataEncrypterConfiguration.java rename to edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/encrypter/AesDataEncrypterConfiguration.java index 65da7e424..3357f7eff 100644 --- a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/encrypter/DataEncrypterConfiguration.java +++ b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/encrypter/AesDataEncrypterConfiguration.java @@ -19,9 +19,8 @@ import lombok.Value; @Value -public class DataEncrypterConfiguration { - @NonNull String algorithm; +public class AesDataEncrypterConfiguration { @NonNull String keySetAlias; boolean cachingEnabled; - Duration cachingDuration; + @NonNull Duration cachingDuration; } diff --git a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/encrypter/DataEncrypterFactory.java b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/encrypter/DataEncrypterFactory.java index d60604ea6..382c8086a 100644 --- a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/encrypter/DataEncrypterFactory.java +++ b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/encrypter/DataEncrypterFactory.java @@ -14,9 +14,7 @@ package net.catenax.edc.data.encryption.encrypter; -import java.util.NoSuchElementException; import lombok.RequiredArgsConstructor; -import net.catenax.edc.data.encryption.DataEncryptionExtension; import net.catenax.edc.data.encryption.algorithms.CryptoAlgorithm; import net.catenax.edc.data.encryption.algorithms.aes.AesAlgorithm; import net.catenax.edc.data.encryption.data.CryptoDataFactory; @@ -40,25 +38,21 @@ public class DataEncrypterFactory { private final Monitor monitor; private final CryptoKeyFactory keyFactory; - public DataEncrypter create(DataEncrypterConfiguration configuration) { - if (configuration.getAlgorithm().equalsIgnoreCase(AES_ALGORITHM)) { - return createAesEncrypter(configuration); - } else if (configuration.getAlgorithm().equalsIgnoreCase(NONE)) { - return createNoneEncrypter(); - } else { - final String msg = - String.format( - DataEncryptionExtension.NAME - + ": Unsupported encryption algorithm '%s'. Supported algorithms are '%s', %s.", - configuration.getAlgorithm(), - AES_ALGORITHM, - NONE); - throw new NoSuchElementException(msg); - } - } + public DataEncrypter createNoneEncrypter() { + return new DataEncrypter() { + @Override + public String encrypt(String data) { + return data; + } - public DataEncrypter createAesEncrypter(DataEncrypterConfiguration configuration) { + @Override + public String decrypt(String data) { + return data; + } + }; + } + public DataEncrypter createAesEncrypter(AesDataEncrypterConfiguration configuration) { KeyProvider keyProvider = new AesKeyProvider(vault, configuration.getKeySetAlias(), keyFactory); @@ -71,18 +65,4 @@ public DataEncrypter createAesEncrypter(DataEncrypterConfiguration configuration return new AesDataEncrypterImpl(algorithm, monitor, keyProvider, algorithm, cryptoDataFactory); } - - public DataEncrypter createNoneEncrypter() { - return new DataEncrypter() { - @Override - public String encrypt(String data) { - return data; - } - - @Override - public String decrypt(String data) { - return data; - } - }; - } } diff --git a/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/DataEncryptionExtensionTest.java b/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/DataEncryptionExtensionTest.java index f5541d727..49fd8dd67 100644 --- a/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/DataEncryptionExtensionTest.java +++ b/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/DataEncryptionExtensionTest.java @@ -85,7 +85,9 @@ void testStartExceptionOnMissingKeySetInVault() { @Test void testStartExceptionOnStartWithWrongKeySetAlias() { final String keySetAlias = "foo"; - Mockito.when(context.getSetting(DataEncryptionExtension.ENCRYPTION_ALGORITHM, null)) + Mockito.when( + context.getSetting( + DataEncryptionExtension.ENCRYPTION_ALGORITHM, DataEncrypterFactory.AES_ALGORITHM)) .thenReturn(DataEncrypterFactory.AES_ALGORITHM); Mockito.when(context.getSetting(DataEncryptionExtension.ENCRYPTION_KEY_SET, null)) .thenReturn(keySetAlias); @@ -95,4 +97,19 @@ void testStartExceptionOnStartWithWrongKeySetAlias() { Assertions.assertThrows(EdcException.class, () -> extension.start()); } + + @Test + void testNonEncrypterRequiresNoOtherSetting() { + final String keySetAlias = "foo"; + Mockito.when( + context.getSetting( + DataEncryptionExtension.ENCRYPTION_ALGORITHM, DataEncrypterFactory.AES_ALGORITHM)) + .thenReturn(DataEncrypterFactory.NONE); + Mockito.when(context.getSetting(DataEncryptionExtension.ENCRYPTION_KEY_SET, null)) + .thenReturn(null); + Mockito.when(vault.resolveSecret(keySetAlias)).thenReturn(null); + + Assertions.assertDoesNotThrow(() -> extension.initialize(context)); + Assertions.assertDoesNotThrow(() -> extension.start()); + } } diff --git a/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/encrypter/DataEncrypterFactoryTest.java b/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/encrypter/DataEncrypterFactoryTest.java index 0c0f41155..957983503 100644 --- a/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/encrypter/DataEncrypterFactoryTest.java +++ b/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/encrypter/DataEncrypterFactoryTest.java @@ -14,7 +14,6 @@ package net.catenax.edc.data.encryption.encrypter; import java.time.Duration; -import java.util.NoSuchElementException; import net.catenax.edc.data.encryption.key.CryptoKeyFactoryImpl; import org.eclipse.dataspaceconnector.spi.monitor.Monitor; import org.eclipse.dataspaceconnector.spi.security.Vault; @@ -45,25 +44,19 @@ void setup() { factory = new DataEncrypterFactory(vault, monitor, new CryptoKeyFactoryImpl()); } - @Test - void testExceptionOnInvalidStrategy() { - final DataEncrypterConfiguration configuration = newConfiguration("something invalid"); - Assertions.assertThrows(NoSuchElementException.class, () -> factory.create(configuration)); - } - @ParameterizedTest @ValueSource(strings = {DataEncrypterFactory.AES_ALGORITHM, DataEncrypterFactory.NONE}) void testValidStrategies(String strategy) { - final DataEncrypterConfiguration configuration = newConfiguration(strategy); - Assertions.assertDoesNotThrow(() -> factory.create(configuration)); + final AesDataEncrypterConfiguration configuration = newConfiguration(false); + Assertions.assertDoesNotThrow(() -> factory.createAesEncrypter(configuration)); } @Test void testEncrypterWithCaching() { Mockito.when(vault.resolveSecret(KEY_SET_ALIAS)).thenReturn("7h6sh6t6tchCmNnHjK2kFA=="); - final DataEncrypterConfiguration configuration = newConfiguration(true); - final DataEncrypter dataEncrypter = factory.create(configuration); + final AesDataEncrypterConfiguration configuration = newConfiguration(true); + final DataEncrypter dataEncrypter = factory.createAesEncrypter(configuration); final String foo1 = dataEncrypter.encrypt("foo1"); dataEncrypter.decrypt(foo1); @@ -76,12 +69,8 @@ void testEncrypterWithCaching() { Mockito.verify(vault, Mockito.times(2)).resolveSecret(KEY_SET_ALIAS); } - private DataEncrypterConfiguration newConfiguration(String encryptionStrategy) { - return new DataEncrypterConfiguration(encryptionStrategy, KEY_SET_ALIAS, false, null); - } - - private DataEncrypterConfiguration newConfiguration(boolean isCachingEnabled) { - return new DataEncrypterConfiguration( - DataEncrypterFactory.AES_ALGORITHM, KEY_SET_ALIAS, isCachingEnabled, Duration.ofMinutes(1)); + private AesDataEncrypterConfiguration newConfiguration(boolean isCachingEnabled) { + return new AesDataEncrypterConfiguration( + KEY_SET_ALIAS, isCachingEnabled, Duration.ofMinutes(1)); } } From 35ac4befaf8002611cf5f9d641ce2ae6d5925ab1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Aug 2022 08:44:19 +0200 Subject: [PATCH 254/433] Bump flyway-core from 9.1.6 to 9.2.0 (#369) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 45812fd2f..8e99ffb89 100644 --- a/pom.xml +++ b/pom.xml @@ -68,7 +68,7 @@ 0.0.1-20220818-SNAPSHOT 1.2.2 42.5.0 - 9.1.6 + 9.2.0 5.9.0 From 963870a6f49a20908007aece6338a3e6d3c8f469 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 Aug 2022 10:20:12 +0200 Subject: [PATCH 255/433] Bump logback-classic from 1.2.11 to 1.4.0 (#374) --- edc-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-tests/pom.xml b/edc-tests/pom.xml index a7e2e93d2..a252252c4 100644 --- a/edc-tests/pom.xml +++ b/edc-tests/pom.xml @@ -30,7 +30,7 @@ ${project.groupId}_${project.artifactId} 2.9.1 4.5.13 - 1.2.11 + 1.4.0 1.7.36 From dd584b265924aa1c6a11727e3242e2019e775aa3 Mon Sep 17 00:00:00 2001 From: Lukas Holthof <68239695+lholthof@users.noreply.github.com> Date: Wed, 31 Aug 2022 10:42:16 +0200 Subject: [PATCH 256/433] Minor changes to the Readme (#376) --- .../dataplane-selector-configuration/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/edc-extensions/dataplane-selector-configuration/README.md b/edc-extensions/dataplane-selector-configuration/README.md index 81db0cc87..7a65b8f48 100644 --- a/edc-extensions/dataplane-selector-configuration/README.md +++ b/edc-extensions/dataplane-selector-configuration/README.md @@ -1,4 +1,4 @@ -# Data Plane Selector Configuration Exception +# Data Plane Selector Configuration Extension This control-plane extension makes it possible configure one or more Data Plane Instances. During a transfer the control plane will look for an instance with matching capabilities to transfer data. @@ -9,10 +9,10 @@ Per data plane instance the following settings must be configured. As `.url | URL to connect to the Data Plane Instance. | X | http://plato-edc-dataplane:9999/api/dataplane/control | -| edc.dataplane.selector..sourcetypes | Source Types in a comma separated List. | X | HttpData | -| edc.dataplane.selector..destinationtypes | Destination Types in a comma separated List. | X | HttpProxy | -| edc.dataplane.selector..properties | Additional properties of the Data Plane Instance. | (X) | { "publicApiUrl": "http://plato-edc-dataplane:8185/api/public/" } | +| edc.dataplane.selector.````.url | URL to connect to the Data Plane Instance. | X | http://plato-edc-dataplane:9999/api/dataplane/control | +| edc.dataplane.selector.````.sourcetypes | Source Types in a comma separated List. | X | HttpData | +| edc.dataplane.selector.````.destinationtypes | Destination Types in a comma separated List. | X | HttpProxy | +| edc.dataplane.selector.````.properties | Additional properties of the Data Plane Instance. | (X) | { "publicApiUrl": "http://plato-edc-dataplane:8185/api/public" } | The property `publicApiUrl` is mandatory for Data Plane Instances with destination type `HttpProxy`. @@ -25,4 +25,4 @@ EDC_DATAPLANE_SELECTOR_PLATOPLANE_PROPERTIES: >- { "publicApiUrl": "http://plato-edc-dataplane:8185/api/public" } -``` \ No newline at end of file +``` From 481659035c9d0622c31cf943d027685307ee0545 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 Aug 2022 11:09:22 +0200 Subject: [PATCH 257/433] Bump mikefarah/yq from 4.27.2 to 4.27.3 (#373) --- .github/workflows/draft-new-release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index 056c0a026..7e3096349 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -43,7 +43,7 @@ jobs: GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} - name: Bump version in deployment/helm - uses: mikefarah/yq@v4.27.2 + uses: mikefarah/yq@v4.27.3 with: cmd: |- find deployment/helm -name Chart.yaml | xargs -n1 yq -i '.appVersion = "${{ github.event.inputs.version }}" | .version = "${{ github.event.inputs.version }}"' From 9da98ff182ef0dc7d36b128786f80441789686e5 Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Wed, 31 Aug 2022 12:47:50 +0200 Subject: [PATCH 258/433] ingress name configurable (#377) --- deployment/helm/edc-controlplane/README.md | 8 ++++++-- .../helm/edc-controlplane/templates/ingress.yaml | 6 +++++- deployment/helm/edc-controlplane/values.yaml | 16 ++++++++++++---- deployment/helm/edc-dataplane/README.md | 4 +++- .../helm/edc-dataplane/templates/ingress.yaml | 6 +++++- deployment/helm/edc-dataplane/values.yaml | 8 ++++++-- 6 files changed, 37 insertions(+), 11 deletions(-) diff --git a/deployment/helm/edc-controlplane/README.md b/deployment/helm/edc-controlplane/README.md index ff1b785c8..63a414d97 100644 --- a/deployment/helm/edc-controlplane/README.md +++ b/deployment/helm/edc-controlplane/README.md @@ -51,7 +51,9 @@ $ helm install my-release catenax-ng-product-edc/edc-controlplane --version 0.1. | ingresses[0].enabled | bool | `true` | | | ingresses[0].endpoints | list | `["ids"]` | EDC endpoints exposed by this ingress resource | | ingresses[0].hostname | string | `"edc-controlplane.local"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | -| ingresses[0].tls | bool | `false` | Enables TLS on the ingress resource | +| ingresses[0].tls | object | `{"enabled":false,"secretName":""}` | TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource | +| ingresses[0].tls.enabled | bool | `false` | Enables TLS on the ingress resource | +| ingresses[0].tls.secretName | string | `""` | If present overwrites the default secret name | | ingresses[1].annotations | object | `{}` | Additional ingress annotations to add | | ingresses[1].certManager.clusterIssuer | string | `""` | If preset enables certificate generation via cert-manager cluster-wide issuer | | ingresses[1].certManager.issuer | string | `""` | If preset enables certificate generation via cert-manager namespace scoped issuer | @@ -59,7 +61,9 @@ $ helm install my-release catenax-ng-product-edc/edc-controlplane --version 0.1. | ingresses[1].enabled | bool | `false` | | | ingresses[1].endpoints | list | `["data","control"]` | EDC endpoints exposed by this ingress resource | | ingresses[1].hostname | string | `"edc-controlplane.intranet"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | -| ingresses[1].tls | bool | `false` | Enables TLS on the ingress resource | +| ingresses[1].tls | object | `{"enabled":false,"secretName":""}` | TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource | +| ingresses[1].tls.enabled | bool | `false` | Enables TLS on the ingress resource | +| ingresses[1].tls.secretName | string | `""` | If present overwrites the default secret name | | livenessProbe.enabled | bool | `true` | Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) | | logging.properties | string | `".level=INFO\norg.eclipse.dataspaceconnector.level=ALL\nhandlers=java.util.logging.ConsoleHandler\njava.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter\njava.util.logging.ConsoleHandler.level=ALL\njava.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n"` | EDC logging.properties configuring the [java.util.logging subsystem](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html#a1.8) | | nameOverride | string | `""` | Overrides the charts name | diff --git a/deployment/helm/edc-controlplane/templates/ingress.yaml b/deployment/helm/edc-controlplane/templates/ingress.yaml index e2586a7fc..b61ff47d9 100644 --- a/deployment/helm/edc-controlplane/templates/ingress.yaml +++ b/deployment/helm/edc-controlplane/templates/ingress.yaml @@ -42,11 +42,15 @@ spec: ingressClassName: {{ .className }} {{- end }} {{- if .hostname }} - {{- if .tls }} + {{- if .tls.enabled }} tls: - hosts: - {{ .hostname }} + {{- if .tls.secretName }} + secretName: {{ .tls.secretName }} + {{- else }} secretName: {{ $ingressName }}-tls + {{- end }} {{- end }} rules: - host: {{ .hostname }} diff --git a/deployment/helm/edc-controlplane/values.yaml b/deployment/helm/edc-controlplane/values.yaml index b1605e6fd..249e3d4dd 100644 --- a/deployment/helm/edc-controlplane/values.yaml +++ b/deployment/helm/edc-controlplane/values.yaml @@ -147,8 +147,12 @@ ingresses: - ids # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use className: "" - # -- Enables TLS on the ingress resource - tls: false + # -- TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource + tls: + # -- Enables TLS on the ingress resource + enabled: false + # -- If present overwrites the default secret name + secretName: "" ## Adds [cert-manager](https://cert-manager.io/docs/) annotations to the ingress resource certManager: # -- If preset enables certificate generation via cert-manager namespace scoped issuer @@ -168,8 +172,12 @@ ingresses: - control # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use className: "" - # -- Enables TLS on the ingress resource - tls: false + # -- TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource + tls: + # -- Enables TLS on the ingress resource + enabled: false + # -- If present overwrites the default secret name + secretName: "" ## Adds [cert-manager](https://cert-manager.io/docs/) annotations to the ingress resource certManager: # -- If preset enables certificate generation via cert-manager namespace scoped issuer diff --git a/deployment/helm/edc-dataplane/README.md b/deployment/helm/edc-dataplane/README.md index 485383fe4..079879c52 100644 --- a/deployment/helm/edc-dataplane/README.md +++ b/deployment/helm/edc-dataplane/README.md @@ -47,7 +47,9 @@ $ helm install my-release catenax-ng-product-edc/edc-dataplane --version 0.1.0 | ingresses[0].enabled | bool | `true` | | | ingresses[0].endpoints | list | `["public"]` | EDC endpoints exposed by this ingress resource | | ingresses[0].hostname | string | `"edc-dataplane.local"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | -| ingresses[0].tls | bool | `false` | Enables TLS on the ingress resource | +| ingresses[0].tls | object | `{"enabled":false,"secretName":""}` | TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource | +| ingresses[0].tls.enabled | bool | `false` | Enables TLS on the ingress resource | +| ingresses[0].tls.secretName | string | `""` | If present overwrites the default secret name | | livenessProbe.enabled | bool | `true` | Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) | | logging.properties | string | `".level=INFO\norg.eclipse.dataspaceconnector.level=ALL\nhandlers=java.util.logging.ConsoleHandler\njava.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter\njava.util.logging.ConsoleHandler.level=ALL\njava.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n"` | EDC logging.properties configuring the [java.util.logging subsystem](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html#a1.8) | | nameOverride | string | `""` | Overrides the charts name | diff --git a/deployment/helm/edc-dataplane/templates/ingress.yaml b/deployment/helm/edc-dataplane/templates/ingress.yaml index 77a815083..8401c0760 100644 --- a/deployment/helm/edc-dataplane/templates/ingress.yaml +++ b/deployment/helm/edc-dataplane/templates/ingress.yaml @@ -42,11 +42,15 @@ spec: ingressClassName: {{ .className }} {{- end }} {{- if .hostname }} - {{- if .tls }} + {{- if .tls.enabled }} tls: - hosts: - {{ .hostname }} + {{- if .tls.secretName }} + secretName: {{ .tls.secretName }} + {{- else }} secretName: {{ $ingressName }}-tls + {{- end }} {{- end }} rules: - host: {{ .hostname }} diff --git a/deployment/helm/edc-dataplane/values.yaml b/deployment/helm/edc-dataplane/values.yaml index 48d18fb96..4f9497fa0 100644 --- a/deployment/helm/edc-dataplane/values.yaml +++ b/deployment/helm/edc-dataplane/values.yaml @@ -135,8 +135,12 @@ ingresses: - public # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use className: "" - # -- Enables TLS on the ingress resource - tls: false + # -- TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource + tls: + # -- Enables TLS on the ingress resource + enabled: false + # -- If present overwrites the default secret name + secretName: "" ## Adds [cert-manager](https://cert-manager.io/docs/) annotations to the ingress resource certManager: # -- If preset enables certificate generation via cert-manager namespace scoped issuer From b66fcf9cdd32bb41065422993da111c53c475365 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 Aug 2022 15:04:42 +0200 Subject: [PATCH 259/433] Bump logback-core from 1.2.11 to 1.4.0 (#375) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8e99ffb89..3c11cd001 100644 --- a/pom.xml +++ b/pom.xml @@ -82,7 +82,7 @@ 4.9.3 1.17.3 2.0.0-beta1 - 1.2.11 + 1.4.0 2.2 From cace750f97445080ffc6838f08dd761a2cbb9bf4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 10:24:51 +0200 Subject: [PATCH 260/433] Bump flyway-core from 9.2.0 to 9.2.1 (#380) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3c11cd001..cb3950430 100644 --- a/pom.xml +++ b/pom.xml @@ -68,7 +68,7 @@ 0.0.1-20220818-SNAPSHOT 1.2.2 42.5.0 - 9.2.0 + 9.2.1 5.9.0 From b3718740b8b2939b0e29282e9dc280216af39ab3 Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Thu, 1 Sep 2022 15:24:08 +0200 Subject: [PATCH 261/433] update AES-GCM initialization vector creation (#379) --- .../algorithms/aes/AesAlgorithm.java | 3 -- .../aes/AesInitializationVectorIterator.java | 29 ++++++++----------- .../AesInitializationVectorIteratorTest.java | 11 ------- 3 files changed, 12 insertions(+), 31 deletions(-) diff --git a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/algorithms/aes/AesAlgorithm.java b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/algorithms/aes/AesAlgorithm.java index 8565ffd31..8aaddfb85 100644 --- a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/algorithms/aes/AesAlgorithm.java +++ b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/algorithms/aes/AesAlgorithm.java @@ -55,9 +55,6 @@ public synchronized EncryptedData encrypt(DecryptedData data, AesKey key) if (!initializationVectorIterator.hasNext()) { initializationVectorIterator = new AesInitializationVectorIterator(); } - if (!initializationVectorIterator.isInitialized()) { - initializationVectorIterator.initialize(); - } initializationVector = initializationVectorIterator.next(); } diff --git a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/algorithms/aes/AesInitializationVectorIterator.java b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/algorithms/aes/AesInitializationVectorIterator.java index 7d4dc6c2b..03a5cf789 100644 --- a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/algorithms/aes/AesInitializationVectorIterator.java +++ b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/algorithms/aes/AesInitializationVectorIterator.java @@ -13,23 +13,22 @@ */ package net.catenax.edc.data.encryption.algorithms.aes; -import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Iterator; import java.util.NoSuchElementException; +import lombok.SneakyThrows; import net.catenax.edc.data.encryption.util.ArrayUtil; public class AesInitializationVectorIterator implements Iterator { - public static final int VECTOR_SIZE = 12; - public static final int NONCE_SIZE = 4; - public static final int SIZE = VECTOR_SIZE + NONCE_SIZE; + public static final int RANDOM_SIZE = 12; + public static final int COUNTER_SIZE = 4; + public static final int VECTOR_SIZE = RANDOM_SIZE + COUNTER_SIZE; private final ByteCounter counter; - private byte[] vector; public AesInitializationVectorIterator() { - counter = new ByteCounter(NONCE_SIZE); + counter = new ByteCounter(COUNTER_SIZE); } public AesInitializationVectorIterator(ByteCounter byteCounter) { @@ -43,25 +42,21 @@ public boolean hasNext() { @Override public byte[] next() { - if (vector == null) { - throw new IllegalStateException(getClass().getSimpleName() + " has not been initialized"); - } if (counter.isMaxed()) { throw new NoSuchElementException(getClass().getSimpleName() + " has no more elements"); } + byte[] random = getNextRandom(); counter.increment(); - return ArrayUtil.concat(vector, counter.getBytes()); + + return ArrayUtil.concat(random, counter.getBytes()); } - public void initialize() throws NoSuchAlgorithmException { + @SneakyThrows + public byte[] getNextRandom() { SecureRandom random = SecureRandom.getInstanceStrong(); - byte[] newVector = new byte[VECTOR_SIZE]; + byte[] newVector = new byte[RANDOM_SIZE]; random.nextBytes(newVector); - vector = newVector; - } - - public boolean isInitialized() { - return vector != null; + return newVector; } } diff --git a/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/algorithms/aes/AesInitializationVectorIteratorTest.java b/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/algorithms/aes/AesInitializationVectorIteratorTest.java index a42625129..5a79a42b5 100644 --- a/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/algorithms/aes/AesInitializationVectorIteratorTest.java +++ b/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/algorithms/aes/AesInitializationVectorIteratorTest.java @@ -29,7 +29,6 @@ class AesInitializationVectorIteratorTest { void testDistinctVectors() { final int vectorCount = 100; AesInitializationVectorIterator iterator = new AesInitializationVectorIterator(); - iterator.initialize(); List vectors = new ArrayList<>(); for (var i = 0; i < vectorCount; i++) { @@ -45,7 +44,6 @@ void testDistinctVectors() { void testHasNextTrueOnCounterContinuing() { ByteCounter counter = Mockito.mock(ByteCounter.class); AesInitializationVectorIterator iterator = new AesInitializationVectorIterator(counter); - iterator.initialize(); Mockito.when(counter.isMaxed()).thenReturn(false); Assertions.assertTrue(iterator.hasNext()); @@ -56,7 +54,6 @@ void testHasNextTrueOnCounterContinuing() { void testHasNextFalseOnCounterEnd() { ByteCounter counter = Mockito.mock(ByteCounter.class); AesInitializationVectorIterator iterator = new AesInitializationVectorIterator(counter); - iterator.initialize(); Mockito.when(counter.isMaxed()).thenReturn(true); Assertions.assertFalse(iterator.hasNext()); @@ -67,16 +64,8 @@ void testHasNextFalseOnCounterEnd() { void testNoSuchElementExceptionOnCounterEnd() { ByteCounter counter = Mockito.mock(ByteCounter.class); AesInitializationVectorIterator iterator = new AesInitializationVectorIterator(counter); - iterator.initialize(); Mockito.when(counter.isMaxed()).thenReturn(true); Assertions.assertThrows(NoSuchElementException.class, iterator::next); } - - @Test - @SneakyThrows - void testIllegalStateExceptionOnUninitialized() { - AesInitializationVectorIterator iterator = new AesInitializationVectorIterator(); - Assertions.assertThrows(IllegalStateException.class, iterator::next); - } } From 2178cf4d42d7f7510c0c211b18bb39108c78541f Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Fri, 2 Sep 2022 10:01:57 +0200 Subject: [PATCH 262/433] update docu for milestone 6 (#378) --- docs/README.md | 22 +++++------ docs/data-transfer/Transfer Data.md | 2 +- docs/release-notes/Version 0.1.1.md | 37 +++++++++++++++++++ edc-controlplane/README.md | 8 ++++ .../helm/all-in-one/templates/secret.yaml | 24 ++++++------ .../deployment/helm/all-in-one/values.yaml | 31 ++++++++-------- 6 files changed, 84 insertions(+), 40 deletions(-) create mode 100644 docs/release-notes/Version 0.1.1.md diff --git a/docs/README.md b/docs/README.md index aeb3e9d6d..b5bb8d338 100644 --- a/docs/README.md +++ b/docs/README.md @@ -13,22 +13,22 @@ The three supported setups are. - Setup 1: In Memory & Azure Vault - [Control Plane](../edc-controlplane/edc-controlplane-memory/README.md) - - [IDS DAPS Extensions](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/iam/daps) + - [IDS DAPS Extensions](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/common/iam/oauth2/daps) - In Memory Persistence done by using no extension - - [Azure Key Vault Extension](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/azure/vault) + - [Azure Key Vault Extension](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/common/vault/azure-vault) - [Data Plane](../edc-dataplane/edc-dataplane-azure-vault/README.md) - - [Azure Key Vault Extension](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/azure/vault) + - [Azure Key Vault Extension](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/common/vault/azure-vault) - Setup 2: PostgreSQL & Azure Vault - [Control Plane](../edc-controlplane/edc-controlplane-postgresql/README.md) - - [IDS DAPS Extensions](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/iam/daps) - - [PostgreSQL Persistence Extensions](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql) - - [Azure Key Vault Extension](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/azure/vault) + - [IDS DAPS Extensions](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/common/iam/oauth2/daps) + - [PostgreSQL Persistence Extensions](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/control-plane/store/sql) + - [Azure Key Vault Extension](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/common/vault/azure-vault) - [Data Plane](../edc-dataplane/edc-dataplane-azure-vault/README.md) - - [Azure Key Vault Extension](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/azure/vault) + - [Azure Key Vault Extension](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/common/vault/azure-vault) - Setup 3: PostgreSQL & HashiCorp Vault - [Control Plane](../edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/README.md) - - [IDS DAPS Extensions](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/iam/daps) - - [PostgreSQL Persistence Extensions](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql) + - [IDS DAPS Extensions](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/common/iam/oauth2/daps) + - [PostgreSQL Persistence Extensions](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/control-plane/store/sql) - [HashiCorp Vault Extension](../edc-extensions/hashicorp-vault/README.md) - [Data Plane](../edc-dataplane/edc-dataplane-hashicorp-vault/README.md) - [HashiCorp Vault Extension](../edc-extensions/hashicorp-vault/README.md) @@ -46,9 +46,9 @@ The three supported setups are. **Eclipse Dataspace Connector** -- [EDC Domain Model](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/blob/main/docs/architecture/domain-model.md) +- [EDC Domain Model](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/blob/main/docs/developer/architecture/domain-model.md) - [EDC Open API Spec](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/blob/main/resources/openapi/openapi.yaml) -- [HTTP Receiver Extension](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/http-receiver) +- [HTTP Receiver Extension](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/control-plane/http-receiver) **Catena-X** diff --git a/docs/data-transfer/Transfer Data.md b/docs/data-transfer/Transfer Data.md index d9665d0f3..146193178 100644 --- a/docs/data-transfer/Transfer Data.md +++ b/docs/data-transfer/Transfer Data.md @@ -56,7 +56,7 @@ export SOKRATES_BACKEND_URL=$(minikube service sokrates-backend-application -n e Set up a data offer in **Plato**, so that **Sokrates** has something to consume. In case you are unfamiliar with the EDC terms `Asset`, `Policy` or `ContractDefinition` please have a look at the official open -source documentation ([link](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/blob/main/docs/architecture/domain-model.md)). +source documentation ([link](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/blob/main/docs/developer/architecture/domain-model.md)). ![Sequence 1](diagrams/transfer_sequence_1.png) diff --git a/docs/release-notes/Version 0.1.1.md b/docs/release-notes/Version 0.1.1.md new file mode 100644 index 000000000..7a813488d --- /dev/null +++ b/docs/release-notes/Version 0.1.1.md @@ -0,0 +1,37 @@ +# Release Notes Version 0.1.1 +31.08.2022 + +> **BREAKING CHANGES** +> +> When upgrading from version 0.0.x please consolidate the migration documentation before ([link](../migration/Version_0.0.x_0.1.x.md)). + +## 0. Summary + +- 1. Eclipse Dataspace Connector Update +- 2. New Extensions + - 2.1 IAM OAuth2 Extension +- 3. Bug Fixes + +## 1. Eclipse Dataspace Connector Update + +Upgraded the Eclipse Dataspace Connector Extensions to version 0.0.1-milestone-6. + +Code Repository +https://github.com/eclipse-dataspaceconnector/DataSpaceConnector + + +## 2. New Extensions + +The following extensions are now included in the base image of the connector. + +### 2.1 IAM OAuth2 Extension + +Using the open source OAuth Extension it is possible for a connector to re-use an IDS DAPS Token and forge the own identity (replay attack). To mitigate the security issue for the upcoming release Catena-X introduces its own OAuth2 IAM Extension. + +[Documentation](../../edc-extensions/cx-oauth2/README.md) + +## 3. Bug Fixes + +This section covers the most relevant bug fixes, included in this version. + +- Connectors using the Azure Key Vault could not start ([issue](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1892)) diff --git a/edc-controlplane/README.md b/edc-controlplane/README.md index 45e22e578..230e156d8 100644 --- a/edc-controlplane/README.md +++ b/edc-controlplane/README.md @@ -41,6 +41,12 @@ EDC commit the Product-EDC uses. --- +**Persistence** +- ContractDefinition-AssetSelector of InMemory Connector selects 50 Asset max.([issue](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1779)) + +**Transfer** +- Transfer Process remains 'InProgress' on provider side ([issue](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1287)) + **Configuration** - Contract negotiation not working when `web.http.ids.path` is configured/changed ([issue](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1249)) - **Workaround:** Don't configure `web.http.ids.path`, so that the default path is used. @@ -48,6 +54,8 @@ EDC commit the Product-EDC uses. - HttpProxy Transfer: Provider Control Plane spams Consumer Control Plane + HttpProxy Backend Application with requests([issue](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1840)) - **Possible Workaround:** Reconfigure data plane URL from `http://dataplane:8185/api/public` to `http://dataplane:8185/api/public/` +- Non-telling logs when `edc.transfer.proxy.token.verifier.publickey.alias` setting is missing([issue](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1889)) + **Data Management API** - Contract negotiation not working when initiated with policy id ([issue](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1251)) - **Workaround:** The DataManagement API can also initiate a contract negotiation using the actual policy object. diff --git a/edc-tests/src/main/resources/deployment/helm/all-in-one/templates/secret.yaml b/edc-tests/src/main/resources/deployment/helm/all-in-one/templates/secret.yaml index 8e27ac060..5b5145d3a 100644 --- a/edc-tests/src/main/resources/deployment/helm/all-in-one/templates/secret.yaml +++ b/edc-tests/src/main/resources/deployment/helm/all-in-one/templates/secret.yaml @@ -21,17 +21,17 @@ metadata: {{- include "aio.labels" . | nindent 4 }} type: Opaque stringData: - # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/api/auth-tokenbased + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/common/auth/auth-tokenbased EDC_API_AUTH_KEY: {{ $plato_api_auth_key | toString | quote }} - # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/asset-index-sql + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/control-plane/store/sql/asset-index-sql EDC_DATASOURCE_ASSET_PASSWORD: {{ $plato_psql_password | toString | quote }} - # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/contract-definition-store-sql + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/control-plane/store/sql/contract-definition-store-sql EDC_DATASOURCE_CONTRACTDEFINITION_PASSWORD: {{ $plato_psql_password | toString | quote }} - # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/contract-negotiation-store-sql + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/control-plane/store/sql/contract-negotiation-store-sql EDC_DATASOURCE_CONTRACTNEGOTIATION_PASSWORD: {{ $plato_psql_password | toString | quote }} - # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/policy-store-sql + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/control-plane/store/sql/policy-store-sql EDC_DATASOURCE_POLICY_PASSWORD: {{ $plato_psql_password | toString | quote }} - # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/transfer-process-store-sql + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/control-plane/store/sql/transfer-process-store-sql EDC_DATASOURCE_TRANSFERPROCESS_PASSWORD: {{ $plato_psql_password | toString | quote }} # see extension https://github.com/catenax-ng/product-edc/tree/develop/edc-extensions/hashicorp-vault EDC_VAULT_HASHICORP_TOKEN: {{ $plato_vault_token | toString | quote }} @@ -61,17 +61,17 @@ metadata: {{- include "aio.labels" . | nindent 4 }} type: Opaque stringData: - # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/api/auth-tokenbased + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/common/auth/auth-tokenbased EDC_API_AUTH_KEY: {{ $sokrates_api_auth_key | toString | quote }} - # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/asset-index-sql + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/control-plane/store/sql/asset-index-sql EDC_DATASOURCE_ASSET_PASSWORD: {{ $sokrates_psql_password | toString | quote }} - # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/contract-definition-store-sql + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/control-plane/store/sql/contract-definition-store-sql EDC_DATASOURCE_CONTRACTDEFINITION_PASSWORD: {{ $sokrates_psql_password | toString | quote }} - # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/contract-negotiation-store-sql + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/control-plane/store/sql/contract-negotiation-store-sql EDC_DATASOURCE_CONTRACTNEGOTIATION_PASSWORD: {{ $sokrates_psql_password | toString | quote }} - # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/policy-store-sql + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/control-plane/store/sql/policy-store-sql EDC_DATASOURCE_POLICY_PASSWORD: {{ $sokrates_psql_password | toString | quote }} - # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/transfer-process-store-sql + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/control-plane/store/sql/transfer-process-store-sql EDC_DATASOURCE_TRANSFERPROCESS_PASSWORD: {{ $sokrates_psql_password | toString | quote }} # see extension https://github.com/catenax-ng/product-edc/tree/develop/edc-extensions/hashicorp-vault EDC_VAULT_HASHICORP_TOKEN: {{ $sokrates_vault_token | toString | quote }} diff --git a/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml b/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml index 9c1bb978e..1aae7b764 100644 --- a/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml +++ b/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml @@ -235,7 +235,7 @@ platoedccontrolplane: ## DAPS CONFIGURATION ## ######################## - # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/iam/oauth2/oauth2-core + # see extension https://github.com/catenax-ng/product-edc/tree/develop/edc-extensions/cx-oauth2 EDC_OAUTH_CLIENT_ID: *platoDapsClientId EDC_OAUTH_PROVIDER_JWKS_URL: &edcControlPlaneOauthJwksUrl "http://ids-daps:4567/jwks.json" EDC_OAUTH_TOKEN_URL: &edcControlPlaneOauthTokenUrl "http://ids-daps:4567/token" @@ -258,27 +258,27 @@ platoedccontrolplane: ## POSTGRESQL ## ################ - # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/asset-index-sql + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/control-plane/store/sql/asset-index-sql EDC_DATASOURCE_ASSET_NAME: asset EDC_DATASOURCE_ASSET_USER: *psqlUsername EDC_DATASOURCE_ASSET_URL: &platoPsqlConStr "jdbc:postgresql://plato-postgresql:5432/edc" - # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/contract-definition-store-sql + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/control-plane/store/sql/contract-definition-store-sql EDC_DATASOURCE_CONTRACTDEFINITION_NAME: contractdefinition EDC_DATASOURCE_CONTRACTDEFINITION_USER: *psqlUsername EDC_DATASOURCE_CONTRACTDEFINITION_URL: *platoPsqlConStr - # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/contract-negotiation-store-sql + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/control-plane/store/sql/contract-negotiation-store-sql EDC_DATASOURCE_CONTRACTNEGOTIATION_NAME: contractnegotiation EDC_DATASOURCE_CONTRACTNEGOTIATION_USER: *psqlUsername EDC_DATASOURCE_CONTRACTNEGOTIATION_URL: *platoPsqlConStr - # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/policy-store-sql + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/control-plane/store/sql/policy-store-sql EDC_DATASOURCE_POLICY_NAME: policy EDC_DATASOURCE_POLICY_USER: *psqlUsername EDC_DATASOURCE_POLICY_URL: *platoPsqlConStr - # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/transfer-process-store-sql + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/control-plane/store/sql/transfer-process-store-sql EDC_DATASOURCE_TRANSFERPROCESS_NAME: transferprocess EDC_DATASOURCE_TRANSFERPROCESS_USER: *psqlUsername EDC_DATASOURCE_TRANSFERPROCESS_URL: *platoPsqlConStr @@ -296,12 +296,12 @@ platoedccontrolplane: "publicApiUrl": "http://plato-edc-dataplane:8185/api/public/" } - # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/data-plane-transfer + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/control-plane/data-plane-transfer EDC_TRANSFER_PROXY_ENDPOINT: http://plato-edc-dataplane:8185/api/public/ EDC_TRANSFER_PROXY_TOKEN_SIGNER_PRIVATEKEY_ALIAS: my-plato-daps-key # for simplicity this example re-uses the DAPS keys. EDC_TRANSFER_PROXY_TOKEN_VERIFIER_PUBLICKEY_ALIAS: my-plato-daps-crt # for simplicity this example re-uses the DAPS keys. - # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/http-receiver + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/control-plane/http-receiver EDC_RECEIVER_HTTP_ENDPOINT: http://plato-backend-application ############### @@ -503,27 +503,27 @@ sokratesedccontrolplane: ## POSTGRESQL ## ################ - # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/asset-index-sql + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/control-plane/store/sql/asset-index-sql EDC_DATASOURCE_ASSET_NAME: asset EDC_DATASOURCE_ASSET_USER: *psqlUsername EDC_DATASOURCE_ASSET_URL: &SokratesPsqlConStr "jdbc:postgresql://sokrates-postgresql:5432/edc" - # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/contract-definition-store-sql + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/control-plane/store/sql/contract-definition-store-sql EDC_DATASOURCE_CONTRACTDEFINITION_NAME: contractdefinition EDC_DATASOURCE_CONTRACTDEFINITION_USER: *psqlUsername EDC_DATASOURCE_CONTRACTDEFINITION_URL: *SokratesPsqlConStr - # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/contract-negotiation-store-sql + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/control-plane/store/sql/contract-negotiation-store-sql EDC_DATASOURCE_CONTRACTNEGOTIATION_NAME: contractnegotiation EDC_DATASOURCE_CONTRACTNEGOTIATION_USER: *psqlUsername EDC_DATASOURCE_CONTRACTNEGOTIATION_URL: *SokratesPsqlConStr - # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/policy-store-sql + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/control-plane/store/sql/policy-store-sql EDC_DATASOURCE_POLICY_NAME: policy EDC_DATASOURCE_POLICY_USER: *psqlUsername EDC_DATASOURCE_POLICY_URL: *SokratesPsqlConStr - # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/sql/transfer-process-store-sql + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/control-plane/store/sql/transfer-process-store-sql EDC_DATASOURCE_TRANSFERPROCESS_NAME: transferprocess EDC_DATASOURCE_TRANSFERPROCESS_USER: *psqlUsername EDC_DATASOURCE_TRANSFERPROCESS_URL: *SokratesPsqlConStr @@ -541,13 +541,12 @@ sokratesedccontrolplane: "publicApiUrl": "http://sokrates-edc-dataplane:8185/api/public/" } - # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/data-plane-transfer - # TODO Can this be removed? + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/control-plane/data-plane-transfer EDC_TRANSFER_PROXY_ENDPOINT: http://sokrates-edc-dataplane:8185/api/public/ EDC_TRANSFER_PROXY_TOKEN_SIGNER_PRIVATEKEY_ALIAS: my-sokrates-daps-key # for simplicity this example re-uses the DAPS keys. EDC_TRANSFER_PROXY_TOKEN_VERIFIER_PUBLICKEY_ALIAS: my-sokrates-daps-crt # for simplicity this example re-uses the DAPS keys. - # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/http-receiver + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/control-plane/http-receiver EDC_RECEIVER_HTTP_ENDPOINT: http://sokrates-backend-application ############### From 983385bf347102640843386618516ee8220cef0c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Sep 2022 17:08:27 +0200 Subject: [PATCH 263/433] Bump flyway-core from 9.2.1 to 9.2.2 (#383) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cb3950430..6caaafa0f 100644 --- a/pom.xml +++ b/pom.xml @@ -68,7 +68,7 @@ 0.0.1-20220818-SNAPSHOT 1.2.2 42.5.0 - 9.2.1 + 9.2.2 5.9.0 From ced23d44639d02d681c81827a02716a966ad562b Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Mon, 5 Sep 2022 00:33:29 +0200 Subject: [PATCH 264/433] update relesse notes 0.1.1 (#382) --- docs/migration/Version_0.1.0_0.1.1.md | 91 +++++++++++++++++++++++++++ docs/release-notes/Version 0.1.1.md | 22 ++++--- 2 files changed, 105 insertions(+), 8 deletions(-) create mode 100644 docs/migration/Version_0.1.0_0.1.1.md diff --git a/docs/migration/Version_0.1.0_0.1.1.md b/docs/migration/Version_0.1.0_0.1.1.md new file mode 100644 index 000000000..e0caa4fa2 --- /dev/null +++ b/docs/migration/Version_0.1.0_0.1.1.md @@ -0,0 +1,91 @@ +# Migration Version 0.1.0 to 0.1.1 + +This document contains a list of breaking changes that are introduced in version 0.1.1. + +--- + +**Please Note**: +Due to a change in the DAPS authentication mechanism this version cannot exchange messages with older EDC versions! + +--- + +## 0. Summary + +1. Data Management API + 1. Policy Payload +2. Connector Configuration + 1. CX OAuth Extension + + +## 1. Data Management API + +It might be necessary to update applications and scripts that use the Data Management API. This section covers the most +important changes in endpoints and payloads. + +### 1.1 Policy Payload + +The id field of the PolicyDefinition was renamed from `uid` to `id`. + +

+ +Example + +Old Call +```json +{ + "uid": "1", + "policy": { + "prohibitions": [], + "obligations": [], + "permissions": [ + { + "edctype": "dataspaceconnector:permission", + "action": { + "type": "USE" + }, + "constraints": [] + } + ] + } +} +``` + +New call +```json +{ + "id": "1", + "policy": { + "prohibitions": [], + "obligations": [], + "permissions": [ + { + "edctype": "dataspaceconnector:permission", + "action": { + "type": "USE" + }, + "constraints": [] + } + ] + } +} +``` + +
+ +## 2. Connector Configuration +### 2.1. CX OAuth Extension + +All connectors are now shipped with a new OAuth extension. This extension has an additional mandatory setting called `edc.oauth.endpoint.audience`, that must be set to the IDS path. + +[Documentation](/edc-extensions/cx-oauth2/README.md) + + +
+ +Example + +``` +edc.oauth.endpoint.audience=http://plato-edc-controlplane:8282/api/v1/ids/data +``` + +
diff --git a/docs/release-notes/Version 0.1.1.md b/docs/release-notes/Version 0.1.1.md index 7a813488d..3e5247942 100644 --- a/docs/release-notes/Version 0.1.1.md +++ b/docs/release-notes/Version 0.1.1.md @@ -1,35 +1,41 @@ # Release Notes Version 0.1.1 31.08.2022 + > **BREAKING CHANGES** > -> When upgrading from version 0.0.x please consolidate the migration documentation before ([link](../migration/Version_0.0.x_0.1.x.md)). +> Please consolidate the migration documentation ([link](../migration/Version_0.1.0_0.1.1.md)). ## 0. Summary - 1. Eclipse Dataspace Connector Update - 2. New Extensions - - 2.1 IAM OAuth2 Extension + - 2.1 CX IAM OAuth2 Extension - 3. Bug Fixes ## 1. Eclipse Dataspace Connector Update -Upgraded the Eclipse Dataspace Connector Extensions to version 0.0.1-milestone-6. - -Code Repository -https://github.com/eclipse-dataspaceconnector/DataSpaceConnector +Due to problems with the EDC release pipeline this repository will _again_ build the artifacts agin using Git submodule. +The Git submodule references a commit, older than **0.0.1-milestone-6**. ## 2. New Extensions The following extensions are now included in the base image of the connector. -### 2.1 IAM OAuth2 Extension +### 2.1 CX IAM OAuth2 Extension -Using the open source OAuth Extension it is possible for a connector to re-use an IDS DAPS Token and forge the own identity (replay attack). To mitigate the security issue for the upcoming release Catena-X introduces its own OAuth2 IAM Extension. +Using the open source OAuth Extension it is possible for a connector to re-use an IDS DAPS Token and forge the own identity (replay attack). To mitigate the security issue for the upcoming release Catena-X introduces its own OAuth2 IAM Extension. Except for the audience, the IAM configuration stays similar. [Documentation](../../edc-extensions/cx-oauth2/README.md) + +**New Audience Configuration** + +``` +edc.oauth.endpoint.audience=http://plato-edc-controlplane:8282/api/v1/ids/data +``` + ## 3. Bug Fixes This section covers the most relevant bug fixes, included in this version. From 30241bc84f28da877759201a3a6b153e4a72b763 Mon Sep 17 00:00:00 2001 From: Denis Neuling Date: Mon, 5 Sep 2022 01:12:49 +0200 Subject: [PATCH 265/433] cx oauth extension (#381) --- .github/workflows/build.yaml | 21 ++ .github/workflows/business-tests.yaml | 7 + .github/workflows/publish-new-release.yml | 7 + deployment/helm/edc-dataplane/values.yaml | 2 +- docs/data-transfer/Transfer Data.md | 8 +- edc | 1 + .../edc-controlplane-base/pom.xml | 20 +- .../edc-controlplane-memory/pom.xml | 10 - .../edc-controlplane-postgresql/pom.xml | 11 - edc-extensions/cx-oauth2/README.md | 25 +++ edc-extensions/cx-oauth2/pom.xml | 149 +++++++++++++ .../catenax/edc/oauth2/OAuth2Extension.java | 73 +++++++ .../edc/oauth2/OAuth2IdentityService.java | 128 +++++++++++ .../catenax/edc/oauth2/jwk/JsonWebKey.java | 46 ++++ .../catenax/edc/oauth2/jwk/JsonWebKeySet.java | 26 +++ .../edc/oauth2/jwk/JwkPublicKeyResolver.java | 189 ++++++++++++++++ .../edc/oauth2/jwk/PublicKeyHolder.java | 25 +++ .../edc/oauth2/jwk/PublicKeyReader.java | 22 ++ .../edc/oauth2/jwk/RsaPublicKeyReader.java | 71 ++++++ .../jwt/decorator/DapsJwtDecorator.java | 28 +++ .../oauth2/jwt/decorator/ExpJwtDecorator.java | 35 +++ .../oauth2/jwt/decorator/IatJwtDecorator.java | 33 +++ .../jwt/decorator/IdsAudJwtDecorator.java | 28 +++ .../oauth2/jwt/decorator/IssJwtDecorator.java | 31 +++ .../oauth2/jwt/decorator/JtiJwtDecorator.java | 29 +++ .../jwt/decorator/JwtDecoratorExtension.java | 136 ++++++++++++ ...auth2JwtDecoratorRegistryRegistryImpl.java | 42 ++++ .../oauth2/jwt/decorator/SubJwtDecorator.java | 30 +++ .../oauth2/jwt/decorator/X5tJwtDecorator.java | 48 +++++ .../generator/JwtTokenGenerationService.java | 97 +++++++++ .../JwtTokenGenerationServiceExtension.java | 48 +++++ .../jwt/validation/AudValidationRule.java | 71 ++++++ .../jwt/validation/ExpValidationRule.java | 77 +++++++ .../jwt/validation/IatValidationRule.java | 77 +++++++ .../jwt/validation/IdsValidationRule.java | 86 ++++++++ .../validation/JwtValidationExtension.java | 167 ++++++++++++++ .../jwt/validation/NbfValidationRule.java | 80 +++++++ .../Oauth2ValidationRulesRegistryImpl.java | 40 ++++ .../TokenValidationServiceImpl.java | 106 +++++++++ ...spaceconnector.spi.system.ServiceExtension | 17 ++ .../oauth2/jwk/JwkPublicKeyResolverTest.java | 204 ++++++++++++++++++ .../oauth2/jwk/RsaPublicKeyReaderTest.java | 84 ++++++++ .../jwt/decorator/DapsJwtDecoratorTest.java | 40 ++++ .../jwt/decorator/ExpJwtDecoratorTest.java | 50 +++++ .../jwt/decorator/IatJwtDecoratorTest.java | 48 +++++ .../jwt/decorator/IdsAudJwtDecoratorTest.java | 37 ++++ .../jwt/decorator/IssJwtDecoratorTest.java | 43 ++++ .../jwt/decorator/JtiJwtDecoratorTest.java | 36 ++++ ...2JwtDecoratorRegistryRegistryImplTest.java | 87 ++++++++ .../jwt/decorator/SubJwtDecoratorTest.java | 43 ++++ edc-extensions/data-encryption/pom.xml | 1 + edc-extensions/pom.xml | 1 + edc-tests/pom.xml | 4 + .../deployment/helm/all-in-one/values.yaml | 4 +- .../catenax/edc/tests/DataManagementAPI.java | 4 +- pom.xml | 22 +- 56 files changed, 2815 insertions(+), 40 deletions(-) create mode 160000 edc create mode 100644 edc-extensions/cx-oauth2/README.md create mode 100644 edc-extensions/cx-oauth2/pom.xml create mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/OAuth2Extension.java create mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/OAuth2IdentityService.java create mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/JsonWebKey.java create mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/JsonWebKeySet.java create mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/JwkPublicKeyResolver.java create mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/PublicKeyHolder.java create mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/PublicKeyReader.java create mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/RsaPublicKeyReader.java create mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/DapsJwtDecorator.java create mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/ExpJwtDecorator.java create mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/IatJwtDecorator.java create mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/IdsAudJwtDecorator.java create mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/IssJwtDecorator.java create mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/JtiJwtDecorator.java create mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/JwtDecoratorExtension.java create mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/Oauth2JwtDecoratorRegistryRegistryImpl.java create mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/SubJwtDecorator.java create mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/X5tJwtDecorator.java create mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/generator/JwtTokenGenerationService.java create mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/generator/JwtTokenGenerationServiceExtension.java create mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/AudValidationRule.java create mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/ExpValidationRule.java create mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/IatValidationRule.java create mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/IdsValidationRule.java create mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/JwtValidationExtension.java create mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/NbfValidationRule.java create mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/Oauth2ValidationRulesRegistryImpl.java create mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/TokenValidationServiceImpl.java create mode 100644 edc-extensions/cx-oauth2/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension create mode 100644 edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwk/JwkPublicKeyResolverTest.java create mode 100644 edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwk/RsaPublicKeyReaderTest.java create mode 100644 edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/DapsJwtDecoratorTest.java create mode 100644 edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/ExpJwtDecoratorTest.java create mode 100644 edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/IatJwtDecoratorTest.java create mode 100644 edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/IdsAudJwtDecoratorTest.java create mode 100644 edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/IssJwtDecoratorTest.java create mode 100644 edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/JtiJwtDecoratorTest.java create mode 100644 edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/Oauth2JwtDecoratorRegistryRegistryImplTest.java create mode 100644 edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/SubJwtDecoratorTest.java diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 4d12dbe0c..5f5e29995 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -74,6 +74,13 @@ jobs: java-version: '11' distribution: 'adopt' cache: 'maven' + - + name: Init git submodule + run: git submodule update --init + - + name: Build edc with Gradle to get specific snapshot + run: ./gradlew publishToMavenLocal -Pskip.signing=true -PedcVersion=0.0.1-20220902-SNAPSHOT + working-directory: edc - name: Cache SonarCloud packages uses: actions/cache@v3 @@ -128,6 +135,13 @@ jobs: distribution: 'adopt' cache: 'maven' # Build + - + name: Init git submodule + run: git submodule update --init + - + name: Build edc with Gradle to get specific snapshot + run: ./gradlew publishToMavenLocal -Pskip.signing=true -PedcVersion=0.0.1-20220902-SNAPSHOT + working-directory: edc - name: Build Controlplane run: |- @@ -210,6 +224,13 @@ jobs: distribution: 'adopt' cache: 'maven' # Build + - + name: Init git submodule + run: git submodule update --init + - + name: Build edc with Gradle to get specific snapshot + run: ./gradlew publishToMavenLocal -Pskip.signing=true -PedcVersion=0.0.1-20220902-SNAPSHOT + working-directory: edc - name: Build Dataplane run: |- diff --git a/.github/workflows/business-tests.yaml b/.github/workflows/business-tests.yaml index de3287130..0932f6b6a 100644 --- a/.github/workflows/business-tests.yaml +++ b/.github/workflows/business-tests.yaml @@ -69,6 +69,13 @@ jobs: ############################################## ### Build and load recent images into KinD ### ############################################## + - + name: Init git submodule + run: git submodule update --init + - + name: Build edc with Gradle to get specific snapshot + run: ./gradlew publishToMavenLocal -Pskip.signing=true -PedcVersion=0.0.1-20220902-SNAPSHOT + working-directory: edc - name: Build edc-controlplane-postgresql-hashicorp-vault run: |- diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index 9b2cb9a34..4d5a3f6d0 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -74,6 +74,13 @@ jobs: java-version: '11' distribution: 'adopt' cache: 'maven' + - + name: Init git submodule + run: git submodule update --init + - + name: Build edc with Gradle to get specific snapshot + run: ./gradlew publishToMavenLocal -Pskip.signing=true -PedcVersion=0.0.1-20220902-SNAPSHOT + working-directory: edc - name: Deploy run: |- diff --git a/deployment/helm/edc-dataplane/values.yaml b/deployment/helm/edc-dataplane/values.yaml index 4f9497fa0..03e8ea4e8 100644 --- a/deployment/helm/edc-dataplane/values.yaml +++ b/deployment/helm/edc-dataplane/values.yaml @@ -258,6 +258,7 @@ configuration: # edc.ids.curator= # edc.ids.description= # edc.ids.endpoint= + # edc.ids.endpoint.audience= # edc.ids.id= # edc.ids.maintainer= # edc.ids.security.profile= @@ -276,7 +277,6 @@ configuration: # edc.negotiation.provider.state-machine.batch-size= # edc.oauth.client.id= # edc.oauth.private.key.alias= - # edc.oauth.provider.audience= # edc.oauth.provider.jwks.refresh= # edc.oauth.provider.jwks.url= # edc.oauth.public.key.alias= diff --git a/docs/data-transfer/Transfer Data.md b/docs/data-transfer/Transfer Data.md index 146193178..529c06416 100644 --- a/docs/data-transfer/Transfer Data.md +++ b/docs/data-transfer/Transfer Data.md @@ -46,11 +46,13 @@ Initialize the following environment variables, that are used in the upcoming AP ```bash export PLATO_DATAMGMT_URL=$(minikube service plato-edc-controlplane -n edc-all-in-one --url | sed -n 3p) -export PLATO_IDS_URL=$(minikube service plato-edc-controlplane -n edc-all-in-one --url | sed -n 5p) +export PLATO_IDS_URL="http://plato-edc-controlplane:8282" export SOKRATES_DATAMGMT_URL=$(minikube service sokrates-edc-controlplane -n edc-all-in-one --url | sed -n 3p) export SOKRATES_BACKEND_URL=$(minikube service sokrates-backend-application -n edc-all-in-one --url | sed -n 2p) ``` +Please note: The IDS URL is used for DAPS Token Audience validation. Therefore it must be the internal IDS url, that is configured inside the connector. + ## 1. Setup Data Offer Set up a data offer in **Plato**, so that **Sokrates** has something to consume. @@ -71,7 +73,7 @@ curl -X POST "$PLATO_DATAMGMT_URL/data/assets" --header "X-Api-Key: password" -- ``` ```bash -curl -X POST "$PLATO_DATAMGMT_URL/data/policydefinitions" --header "X-Api-Key: password" --header "Content-Type: application/json" --data "{ \"uid\": \"1\", \"policy\": { \"prohibitions\": [], \"obligations\": [], \"permissions\": [ { \"edctype\": \"dataspaceconnector:permission\", \"action\": { \"type\": \"USE\" }, \"constraints\": [] } ] } }" -s -o /dev/null -w 'Response Code: %{http_code}\n' +curl -X POST "$PLATO_DATAMGMT_URL/data/policydefinitions" --header "X-Api-Key: password" --header "Content-Type: application/json" --data "{ \"id\": \"1\", \"policy\": { \"prohibitions\": [], \"obligations\": [], \"permissions\": [ { \"edctype\": \"dataspaceconnector:permission\", \"action\": { \"type\": \"USE\" }, \"constraints\": [] } ] } }" -s -o /dev/null -w 'Response Code: %{http_code}\n' ``` ```bash @@ -147,7 +149,7 @@ locally. In this demo the transfer can be verified by executing a simple `cat` c ![Sequence 1](diagrams/transfer_sequence_5.png) ```bash -curl -X GET ${SOKRATES_BACKEND_URL}/${TRANSFER_PROCESS_ID} -H "Accept: application/octet-stream" -s | jq +curl -X GET "${SOKRATES_BACKEND_URL}/${TRANSFER_PROCESS_ID}" -H "Accept: application/octet-stream" -s | jq ``` # Delete All Data diff --git a/edc b/edc new file mode 160000 index 000000000..658c5e31a --- /dev/null +++ b/edc @@ -0,0 +1 @@ +Subproject commit 658c5e31accf5f7f4b221e94478763fd30af7d85 diff --git a/edc-controlplane/edc-controlplane-base/pom.xml b/edc-controlplane/edc-controlplane-base/pom.xml index b8a53cebe..191f66668 100644 --- a/edc-controlplane/edc-controlplane-base/pom.xml +++ b/edc-controlplane/edc-controlplane-base/pom.xml @@ -63,6 +63,10 @@ net.catenax.edc.extensions data-encryption
+ + net.catenax.edc.extensions + cx-oauth2 + @@ -111,10 +115,6 @@ org.eclipse.dataspaceconnector ids-spi - - org.eclipse.dataspaceconnector - ids-token-validation - @@ -133,6 +133,16 @@ org.eclipse.dataspaceconnector contract + + org.eclipse.dataspaceconnector + jwt-spi + + @@ -149,7 +159,7 @@ org.eclipse.dataspaceconnector - data-plane-selector-store + data-plane-selector-spi diff --git a/edc-controlplane/edc-controlplane-memory/pom.xml b/edc-controlplane/edc-controlplane-memory/pom.xml index 1c3091a1b..a21118ec9 100644 --- a/edc-controlplane/edc-controlplane-memory/pom.xml +++ b/edc-controlplane/edc-controlplane-memory/pom.xml @@ -104,16 +104,6 @@ - - - org.eclipse.dataspaceconnector - oauth2-core - - - org.eclipse.dataspaceconnector - iam-daps - - com.azure diff --git a/edc-controlplane/edc-controlplane-postgresql/pom.xml b/edc-controlplane/edc-controlplane-postgresql/pom.xml index 33dbb8fbd..7c5433c41 100644 --- a/edc-controlplane/edc-controlplane-postgresql/pom.xml +++ b/edc-controlplane/edc-controlplane-postgresql/pom.xml @@ -133,17 +133,6 @@ policy-store-sql - - - org.eclipse.dataspaceconnector - oauth2-core - - - org.eclipse.dataspaceconnector - iam-daps - - - org.eclipse.dataspaceconnector diff --git a/edc-extensions/cx-oauth2/README.md b/edc-extensions/cx-oauth2/README.md new file mode 100644 index 000000000..5f721e4ea --- /dev/null +++ b/edc-extensions/cx-oauth2/README.md @@ -0,0 +1,25 @@ +# Catena-X OAuth2 Extension + +## Why Catena-X needs this extension + +In IDS the DAPS token audience is always `idsc:IDS_CONNECTORS_ALL`. At first glance this makes it possible for other connectors to steal and reuse an received token. To mitigate this security risk IDS introduces something called `transportCertsSha256`, which couples the connector audience with its corresponding TLS/SSL certificate. + +From [GitHub IDS-G](https://github.com/International-Data-Spaces-Association/IDS-G/tree/main/Components/IdentityProvider/DAPS) + +> - **transportCertsSha256** Contains the public keys of the used transport certificates, hashed using SHA256. The identifying X509 certificate should not be used for the communication encryption. Therefore, the receiving party needs to connect the identity of a connector by relating its hostname (from the communication encryption layer) and the used private/public key pair, with its IDS identity claim of the DAT. The public transportation key must be one of the `transportCertsSha256` values. Otherwise, the receiving connector must expect that the requesting connector is using a false identity claim. In general, this claim holds an Array of Strings, but it may optionally hold a single String instead if the Array would have exactly one element. + +The reason IDS did this is to prevent the IDS DAPS to know, which connectors talk to each other. But this solution introduces a new level of complexity for different deployment scenarios. The Catena-X OAuth2 Extension introduces the classic audience validation again, so that Catena-X does not have to deal with these things for now. + +## Configuration + +| Key | Description | Mandatory | Default | +|:----|:----|----|----| +| edc.oauth.token.url | Token URL of the DAPS | X | | +| edc.oauth.public.key.alias | Vault alias of the public key | X | | +| edc.oauth.client.id | DAPS client id of the connector | X | | +| edc.oauth.private.key.alias | Vault lias of the private key | X | | +| edc.oauth.token.expiration.seconds | | | 5 minutes | +| edc.oauth.validation.nbf.leeway | DAPS token request leeway | | 10 seconds | +| edc.oauth.provider.jwks.refresh | Time between refresh of the DAPS json web key set | | 5 minutes | +| edc.ids.endpoint.audience | The audience the connector requests from the DAPS. Should be the IDS URL of the connector, e.g. `http://plato-edc-controlplane:8282/api/v1/ids/data` | X | | +| edc.ids.validation.referringconnector | Adds checks to the DAPS token. Validation that the `referringConnector` equals the `issuerConnector` and the `securityProfile` of the token is equal to the profile of the IDS message | | false | \ No newline at end of file diff --git a/edc-extensions/cx-oauth2/pom.xml b/edc-extensions/cx-oauth2/pom.xml new file mode 100644 index 000000000..3758e758e --- /dev/null +++ b/edc-extensions/cx-oauth2/pom.xml @@ -0,0 +1,149 @@ + + + + + edc-extensions + net.catenax.edc.extensions + 0.1.0-SNAPSHOT + + 4.0.0 + + cx-oauth2 + jar + + + ${project.basedir}/src/main/java + ${originalSourceDirectory} + ${project.build.directory}/delombok + ${project.groupId}_${project.artifactId} + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${maven.compiler.source} + ${maven.compiler.target} + ${project.build.sourceEncoding} + + + org.projectlombok + lombok + ${org.projectlombok.lombok.version} + + + + + + + org.projectlombok + lombok-maven-plugin + ${org.projectlombok.lombok.maven.plugin.version} + + + generate-sources + + delombok + + + + + ${originalSourceDirectory} + ${delombokSourceDirectory} + false + UTF-8 + + skip + + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar + + + + + + + + + + + org.eclipse.dataspaceconnector + core-spi + + + org.eclipse.dataspaceconnector + oauth2-spi + + + org.eclipse.dataspaceconnector + jwt-spi + + + + + org.eclipse.dataspaceconnector + jwt-spi + + + + + + org.projectlombok + lombok + + + org.slf4j + slf4j-api + + + com.nimbusds + nimbus-jose-jwt + 8.23 + + + com.squareup.okhttp3 + okhttp + + + + + org.junit.jupiter + junit-jupiter + test + + + org.mockito + mockito-core + test + + + org.mockito + mockito-inline + test + + + diff --git a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/OAuth2Extension.java b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/OAuth2Extension.java new file mode 100644 index 000000000..abc008b95 --- /dev/null +++ b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/OAuth2Extension.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.oauth2; + +import java.net.URI; +import lombok.NonNull; +import lombok.Setter; +import okhttp3.OkHttpClient; +import org.eclipse.dataspaceconnector.iam.oauth2.spi.Oauth2JwtDecoratorRegistry; +import org.eclipse.dataspaceconnector.spi.EdcException; +import org.eclipse.dataspaceconnector.spi.EdcSetting; +import org.eclipse.dataspaceconnector.spi.iam.IdentityService; +import org.eclipse.dataspaceconnector.spi.jwt.TokenGenerationService; +import org.eclipse.dataspaceconnector.spi.jwt.TokenValidationService; +import org.eclipse.dataspaceconnector.spi.system.Inject; +import org.eclipse.dataspaceconnector.spi.system.Provides; +import org.eclipse.dataspaceconnector.spi.system.Requires; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtension; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; + +@Provides(IdentityService.class) +@Requires({ + OkHttpClient.class, + Oauth2JwtDecoratorRegistry.class, + TokenGenerationService.class, + TokenValidationService.class +}) +public class OAuth2Extension implements ServiceExtension { + + @EdcSetting private static final String TOKEN_URL = "edc.oauth.token.url"; + + @EdcSetting private static final String PROVIDER_AUDIENCE = "edc.oauth.provider.audience"; + + @Inject @Setter private OkHttpClient okHttpClient; + + @Inject @Setter private Oauth2JwtDecoratorRegistry jwtDecoratorRegistry; + + @Inject @Setter private TokenGenerationService tokenGenerationService; + + @Inject @Setter private TokenValidationService tokenValidationService; + + @Override + public void initialize(@NonNull final ServiceExtensionContext serviceExtensionContext) { + final String tokenUrl = serviceExtensionContext.getSetting(TOKEN_URL, null); + if (tokenUrl == null) { + throw new EdcException("Missing required setting: " + TOKEN_URL); + } + + final URI tokenUri = URI.create(tokenUrl); + + final OAuth2IdentityService oAuth2IdentityService = + new OAuth2IdentityService( + tokenUri, + okHttpClient, + serviceExtensionContext.getTypeManager(), + jwtDecoratorRegistry, + tokenGenerationService, + tokenValidationService); + + serviceExtensionContext.registerService(IdentityService.class, oAuth2IdentityService); + } +} diff --git a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/OAuth2IdentityService.java b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/OAuth2IdentityService.java new file mode 100644 index 000000000..b1c4dac0f --- /dev/null +++ b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/OAuth2IdentityService.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2020 - 2022 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Microsoft Corporation - initial API and implementation + * Fraunhofer Institute for Software and Systems Engineering - Improvements + * Microsoft Corporation - Use IDS Webhook address for JWT audience claim + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - improvements + * Mercedes-Benz Tech Innovation GmbH - Refactoring + * + */ + +package net.catenax.edc.oauth2; + +import java.net.URI; +import java.util.LinkedHashMap; +import java.util.Objects; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import okhttp3.FormBody; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; +import org.eclipse.dataspaceconnector.spi.EdcException; +import org.eclipse.dataspaceconnector.spi.iam.ClaimToken; +import org.eclipse.dataspaceconnector.spi.iam.IdentityService; +import org.eclipse.dataspaceconnector.spi.iam.TokenParameters; +import org.eclipse.dataspaceconnector.spi.iam.TokenRepresentation; +import org.eclipse.dataspaceconnector.spi.jwt.JwtDecorator; +import org.eclipse.dataspaceconnector.spi.jwt.JwtDecoratorRegistry; +import org.eclipse.dataspaceconnector.spi.jwt.TokenGenerationService; +import org.eclipse.dataspaceconnector.spi.jwt.TokenValidationService; +import org.eclipse.dataspaceconnector.spi.result.Result; +import org.eclipse.dataspaceconnector.spi.types.TypeManager; + +@RequiredArgsConstructor +public class OAuth2IdentityService implements IdentityService { + + private static final String GRANT_TYPE_CLIENT_CREDENTIALS = "client_credentials"; + private static final String CLIENT_ASSERTION_TYPE_JWT_BEARER = + "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"; + private static final String CONTENT_TYPE_APPLICATION_FORM_URLENCODED = + "application/x-www-form-urlencoded"; + private static final String CONTENT_TYPE = "Content-Type"; + private static final String RESOURCE = "resource"; + private static final String CLIENT_ASSERTION_TYPE = "client_assertion_type"; + private static final String GRANT_TYPE = "grant_type"; + private static final String CLIENT_ASSERTION = "client_assertion"; + private static final String SCOPE = "scope"; + + @NonNull private final URI tokenUrl; + @NonNull private final OkHttpClient httpClient; + @NonNull private final TypeManager typeManager; + @NonNull private final JwtDecoratorRegistry jwtDecoratorRegistry; + @NonNull private final TokenGenerationService tokenGenerationService; + @NonNull private final TokenValidationService tokenValidationService; + + @Override + public Result obtainClientCredentials( + @NonNull final TokenParameters tokenParameters) { + final Result jwtCreationResult = + tokenGenerationService.generate(jwtDecoratorRegistry.getAll().toArray(JwtDecorator[]::new)); + if (jwtCreationResult.failed()) { + return jwtCreationResult; + } + + final String assertion = jwtCreationResult.getContent().getToken(); + + final FormBody.Builder requestBodyBuilder = + new FormBody.Builder() + .add(CLIENT_ASSERTION_TYPE, CLIENT_ASSERTION_TYPE_JWT_BEARER) + .add(GRANT_TYPE, GRANT_TYPE_CLIENT_CREDENTIALS) + .add(CLIENT_ASSERTION, assertion) + .add(SCOPE, tokenParameters.getScope()) + .add(RESOURCE, tokenParameters.getAudience()); + + try { + final HttpUrl httpUrl = Objects.requireNonNull(HttpUrl.get(tokenUrl)); + final Request request = + new Request.Builder() + .url(httpUrl) + .addHeader(CONTENT_TYPE, CONTENT_TYPE_APPLICATION_FORM_URLENCODED) + .post(requestBodyBuilder.build()) + .build(); + + try (final Response response = httpClient.newCall(request).execute()) { + try (final ResponseBody responseBody = response.body()) { + if (!response.isSuccessful()) { + final String message = responseBody == null ? "" : responseBody.string(); + return Result.failure(message); + } + + if (responseBody == null) { + return Result.failure(""); + } + + final String responsePayload = responseBody.string(); + + @SuppressWarnings("rawtypes") + LinkedHashMap deserialized = typeManager.readValue(responsePayload, LinkedHashMap.class); + + final String token = (String) deserialized.get("access_token"); + + final TokenRepresentation tokenRepresentation = + TokenRepresentation.Builder.newInstance().token(token).build(); + + return Result.success(tokenRepresentation); + } + } + } catch (final Exception exception) { + throw new EdcException(exception); + } + } + + @Override + public Result verifyJwtToken( + @NonNull final TokenRepresentation tokenRepresentation, final String audience) { + return tokenValidationService.validate(tokenRepresentation); + } +} diff --git a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/JsonWebKey.java b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/JsonWebKey.java new file mode 100644 index 000000000..70712fbd7 --- /dev/null +++ b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/JsonWebKey.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020, 2021 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Microsoft Corporation - initial API and implementation + * Mercedes-Benz Tech Innovation GmbH - refactoring + */ + +package net.catenax.edc.oauth2.jwk; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import lombok.Data; + +@Data +public class JsonWebKey { + @JsonProperty("kty") + private String kty; + + @JsonProperty("use") + private String use; + + @JsonProperty("kid") + private String kid; + + @JsonProperty("x5t") + private String x5t; + + @JsonProperty("n") + private String nn; + + @JsonProperty("e") + private String ee; + + @JsonProperty("x5c") + private List x5c; + + @JsonProperty("alg") + private String alg; +} diff --git a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/JsonWebKeySet.java b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/JsonWebKeySet.java new file mode 100644 index 000000000..a4cc93716 --- /dev/null +++ b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/JsonWebKeySet.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2020, 2021 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Microsoft Corporation - initial API and implementation + * Mercedes-Benz Tech Innovation GmbH - refactoring + * + */ + +package net.catenax.edc.oauth2.jwk; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import lombok.Data; + +@Data +public class JsonWebKeySet { + @JsonProperty("keys") + private List keys; +} diff --git a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/JwkPublicKeyResolver.java b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/JwkPublicKeyResolver.java new file mode 100644 index 000000000..80b31255e --- /dev/null +++ b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/JwkPublicKeyResolver.java @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.oauth2.jwk; + +import java.net.URI; +import java.security.PublicKey; +import java.time.Duration; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; +import org.eclipse.dataspaceconnector.spi.EdcException; +import org.eclipse.dataspaceconnector.spi.iam.PublicKeyResolver; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.result.Result; +import org.eclipse.dataspaceconnector.spi.types.TypeManager; +import org.jetbrains.annotations.Nullable; + +@RequiredArgsConstructor +public class JwkPublicKeyResolver implements PublicKeyResolver { + private final Object synchronizationMonitor = new Object(); + + @NonNull private final URI jsonWebKeySetUri; + + @NonNull private final OkHttpClient httpClient; + + @NonNull private final TypeManager typeManager; + + @NonNull private final Monitor monitor; + + @NonNull private final List jsonWebKeyReaders; + + @NonNull private final Duration interval; + + private final Map keys = new HashMap<>(); + private final AtomicReference executorServiceReference = + new AtomicReference<>(); + + public void start() { + synchronized (synchronizationMonitor) { + if (executorServiceReference.get() != null) { + return; + } + + final Result> result = synchronizeKeys(); + if (result.failed()) { + throw new EdcException( + String.format( + "Could not synchronize keys with identity provider (%s): %s", + jsonWebKeySetUri, result.getFailureDetail())); + } + + final ScheduledExecutorService scheduledExecutorService = + Executors.newSingleThreadScheduledExecutor(); + executorServiceReference.set(scheduledExecutorService); + + scheduledExecutorService.scheduleWithFixedDelay( + this::synchronizeKeys, interval.getSeconds(), interval.getSeconds(), TimeUnit.SECONDS); + } + } + + public void stop() { + synchronized (synchronizationMonitor) { + if (executorServiceReference.get() == null) { + return; + } + + final ScheduledExecutorService scheduledExecutorService = + executorServiceReference.getAndSet(null); + + if (scheduledExecutorService.isTerminated()) { + return; + } + + scheduledExecutorService.shutdownNow(); + } + } + + @Override + public @Nullable PublicKey resolveKey(final String keyId) { + if (keyId == null) { + return null; + } + + synchronized (synchronizationMonitor) { + return keys.get(keyId); + } + } + + protected Result> synchronizeKeys() { + final Result> fetchedKeys = fetchKeys(); + + if (fetchedKeys.succeeded()) { + synchronized (synchronizationMonitor) { + keys.clear(); + keys.putAll(fetchedKeys.getContent()); + } + } + + return fetchedKeys; + } + + protected Result> fetchKeys() { + try { + final HttpUrl httpUrl = Objects.requireNonNull(HttpUrl.get(jsonWebKeySetUri)); + final Request request = new Request.Builder().url(httpUrl).get().build(); + try (final Response response = httpClient.newCall(request).execute()) { + if (response.code() == 200) { + final JsonWebKeySet jsonWebKeySet; + try (final ResponseBody body = response.body()) { + + if (body == null) { + final String message = + String.format( + "Unable to refresh identity provider (%s) keys. An empty response was returned.", + jsonWebKeySetUri); + monitor.severe(message); + return Result.failure(message); + } + + jsonWebKeySet = typeManager.readValue(body.string(), JsonWebKeySet.class); + } + + final List nonNullKeys = + Optional.ofNullable(jsonWebKeySet.getKeys()).orElseGet(Collections::emptyList); + if (nonNullKeys.isEmpty()) { + final String message = + String.format("No keys returned from identity provider (%s).", jsonWebKeySetUri); + monitor.warning(message); + return Result.failure(message); + } + + return Result.success(deserializeKeys(nonNullKeys)); + } else { + final String message = + String.format( + "Unable to refresh identity provider (%s) keys. Response code was: %s", + jsonWebKeySetUri, response.code()); + monitor.severe(message); + return Result.failure(message); + } + } + } catch (final Exception exception) { + final String message = + String.format( + "Error resolving identity (%s) provider keys: %s", + jsonWebKeySetUri, exception.getMessage()); + monitor.severe(message, exception); + return Result.failure(message); + } + } + + private Map deserializeKeys(final List jsonWebKeys) { + final Map keyMap = new HashMap<>(); + for (final JsonWebKey jsonWebKey : + Optional.ofNullable(jsonWebKeys).orElseGet(Collections::emptyList)) { + jsonWebKeyReaders.stream() + .filter(reader -> reader.canRead(jsonWebKey)) + .findFirst() + .flatMap(reader -> reader.read(jsonWebKey)) + .ifPresent(keyHolder -> keyMap.put(keyHolder.getKeyId(), keyHolder.getPublicKey())); + } + return keyMap; + } +} diff --git a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/PublicKeyHolder.java b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/PublicKeyHolder.java new file mode 100644 index 000000000..72edfb19f --- /dev/null +++ b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/PublicKeyHolder.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.oauth2.jwk; + +import java.security.PublicKey; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class PublicKeyHolder { + private final String keyId; + private final PublicKey publicKey; +} diff --git a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/PublicKeyReader.java b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/PublicKeyReader.java new file mode 100644 index 000000000..de73e6e3b --- /dev/null +++ b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/PublicKeyReader.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.oauth2.jwk; + +import java.util.Optional; + +public interface PublicKeyReader { + boolean canRead(JsonWebKey jsonWebKey); + + Optional read(JsonWebKey jsonWebKey); +} diff --git a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/RsaPublicKeyReader.java b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/RsaPublicKeyReader.java new file mode 100644 index 000000000..c6098b70b --- /dev/null +++ b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/RsaPublicKeyReader.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.oauth2.jwk; + +import java.math.BigInteger; +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.security.spec.RSAPublicKeySpec; +import java.util.Base64; +import java.util.Optional; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; + +@RequiredArgsConstructor +public class RsaPublicKeyReader implements PublicKeyReader { + private static final String RSA = "RSA"; + private static final KeyFactory KEY_FACTORY; + + static { + try { + KEY_FACTORY = KeyFactory.getInstance(RSA); + } catch (final NoSuchAlgorithmException noSuchAlgorithmException) { + throw new RuntimeException(noSuchAlgorithmException.getMessage(), noSuchAlgorithmException); + } + } + + @NonNull private final Monitor monitor; + + @Override + public boolean canRead(final JsonWebKey jsonWebKey) { + return Optional.ofNullable(jsonWebKey).map(JsonWebKey::getKty).stream() + .allMatch(RSA::equalsIgnoreCase); + } + + @Override + public Optional read(final JsonWebKey jsonWebKey) { + try { + final BigInteger modulus = unsignedInt(jsonWebKey.getNn()); + final BigInteger exponent = unsignedInt(jsonWebKey.getEe()); + final RSAPublicKeySpec rsaPublicKeySpec = new RSAPublicKeySpec(modulus, exponent); + final PublicKey publicKey = KEY_FACTORY.generatePublic(rsaPublicKeySpec); + final PublicKeyHolder publicKeyHolder = + PublicKeyHolder.builder().keyId(jsonWebKey.getKid()).publicKey(publicKey).build(); + + return Optional.of(publicKeyHolder); + } catch (final GeneralSecurityException generalSecurityException) { + monitor.severe( + "Error parsing identity provider public key, skipping. The kid is: " + + jsonWebKey.getKid()); + } + return Optional.empty(); + } + + private BigInteger unsignedInt(@NonNull final String value) { + return new BigInteger(1, Base64.getUrlDecoder().decode(value)); + } +} diff --git a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/DapsJwtDecorator.java b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/DapsJwtDecorator.java new file mode 100644 index 000000000..3469dd075 --- /dev/null +++ b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/DapsJwtDecorator.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2020, 2021 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Amadeus - initial API and implementation + * + */ + +package net.catenax.edc.oauth2.jwt.decorator; + +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jwt.JWTClaimsSet; +import org.eclipse.dataspaceconnector.spi.jwt.JwtDecorator; + +public class DapsJwtDecorator implements JwtDecorator { + @Override + public void decorate(JWSHeader.Builder header, JWTClaimsSet.Builder claimsSet) { + claimsSet + .claim("@context", "https://w3id.org/idsa/contexts/context.jsonld") + .claim("@type", "ids:DatRequestToken"); + } +} diff --git a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/ExpJwtDecorator.java b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/ExpJwtDecorator.java new file mode 100644 index 000000000..4c06d1aea --- /dev/null +++ b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/ExpJwtDecorator.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.oauth2.jwt.decorator; + +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jwt.JWTClaimsSet; +import java.time.Clock; +import java.time.Duration; +import java.util.Date; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import org.eclipse.dataspaceconnector.spi.jwt.JwtDecorator; + +@RequiredArgsConstructor +public class ExpJwtDecorator implements JwtDecorator { + @NonNull private final Clock clock; + + @NonNull private final Duration expiration; + + @Override + public void decorate(final JWSHeader.Builder header, final JWTClaimsSet.Builder claimsSet) { + claimsSet.expirationTime(Date.from(clock.instant().plusSeconds(expiration.toSeconds()))); + } +} diff --git a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/IatJwtDecorator.java b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/IatJwtDecorator.java new file mode 100644 index 000000000..c409ca74f --- /dev/null +++ b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/IatJwtDecorator.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.oauth2.jwt.decorator; + +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jwt.JWTClaimsSet; +import java.time.Clock; +import java.util.Date; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import org.eclipse.dataspaceconnector.spi.jwt.JwtDecorator; + +@RequiredArgsConstructor +public class IatJwtDecorator implements JwtDecorator { + + @NonNull private final Clock clock; + + @Override + public void decorate(final JWSHeader.Builder header, final JWTClaimsSet.Builder claimsSet) { + claimsSet.issueTime(Date.from(clock.instant())); + } +} diff --git a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/IdsAudJwtDecorator.java b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/IdsAudJwtDecorator.java new file mode 100644 index 000000000..df9c71a22 --- /dev/null +++ b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/IdsAudJwtDecorator.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.oauth2.jwt.decorator; + +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jwt.JWTClaimsSet; +import lombok.RequiredArgsConstructor; +import org.eclipse.dataspaceconnector.spi.jwt.JwtDecorator; + +@RequiredArgsConstructor +public class IdsAudJwtDecorator implements JwtDecorator { + + @Override + public void decorate(final JWSHeader.Builder header, final JWTClaimsSet.Builder claimsSet) { + claimsSet.audience("idsc:IDS_CONNECTORS_ALL"); + } +} diff --git a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/IssJwtDecorator.java b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/IssJwtDecorator.java new file mode 100644 index 000000000..54daf908e --- /dev/null +++ b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/IssJwtDecorator.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.oauth2.jwt.decorator; + +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jwt.JWTClaimsSet; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import org.eclipse.dataspaceconnector.spi.jwt.JwtDecorator; + +@RequiredArgsConstructor +public class IssJwtDecorator implements JwtDecorator { + + @NonNull private final String clientId; + + @Override + public void decorate(final JWSHeader.Builder header, final JWTClaimsSet.Builder claimsSet) { + claimsSet.issuer(clientId); + } +} diff --git a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/JtiJwtDecorator.java b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/JtiJwtDecorator.java new file mode 100644 index 000000000..6815a4ad5 --- /dev/null +++ b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/JtiJwtDecorator.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.oauth2.jwt.decorator; + +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jwt.JWTClaimsSet; +import java.util.UUID; +import lombok.NonNull; +import org.eclipse.dataspaceconnector.spi.jwt.JwtDecorator; + +public class JtiJwtDecorator implements JwtDecorator { + + @Override + public void decorate( + @NonNull final JWSHeader.Builder header, @NonNull final JWTClaimsSet.Builder claimsSet) { + claimsSet.jwtID(UUID.randomUUID().toString()); + } +} diff --git a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/JwtDecoratorExtension.java b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/JwtDecoratorExtension.java new file mode 100644 index 000000000..fdff18d80 --- /dev/null +++ b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/JwtDecoratorExtension.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.oauth2.jwt.decorator; + +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.time.Duration; +import java.util.Optional; +import java.util.stream.Stream; +import lombok.NonNull; +import lombok.Setter; +import org.eclipse.dataspaceconnector.iam.oauth2.spi.Oauth2JwtDecoratorRegistry; +import org.eclipse.dataspaceconnector.spi.EdcException; +import org.eclipse.dataspaceconnector.spi.EdcSetting; +import org.eclipse.dataspaceconnector.spi.security.CertificateResolver; +import org.eclipse.dataspaceconnector.spi.system.Inject; +import org.eclipse.dataspaceconnector.spi.system.Provides; +import org.eclipse.dataspaceconnector.spi.system.Requires; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtension; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; + +@Provides(Oauth2JwtDecoratorRegistry.class) +@Requires(CertificateResolver.class) +public class JwtDecoratorExtension implements ServiceExtension { + + @EdcSetting + private static final String TOKEN_EXPIRATION_SECONDS = "edc.oauth.token.expiration.seconds"; + + private static final Duration DEFAULT_EXPIRATION = Duration.ofMinutes(5); + + @EdcSetting private static final String PUBLIC_KEY_ALIAS = "edc.oauth.public.key.alias"; + + @EdcSetting private static final String CLIENT_ID = "edc.oauth.client.id"; + + @Inject @Setter private CertificateResolver certificateResolver; + + @Override + public void initialize(@NonNull final ServiceExtensionContext serviceExtensionContext) { + final Oauth2JwtDecoratorRegistry oauth2JwtDecoratorRegistry = + new Oauth2JwtDecoratorRegistryRegistryImpl(); + + Stream.of( + audJwtDecorator(), + expJwtDecorator(serviceExtensionContext), + iatJwtDecorator(serviceExtensionContext), + issJwtDecorator(serviceExtensionContext), + jtiJwtDecorator(), + subJwtDecorator(serviceExtensionContext), + x5tJwtDecorator(serviceExtensionContext, certificateResolver), + dapsJwtDecorator()) + .forEach(oauth2JwtDecoratorRegistry::register); + + serviceExtensionContext.registerService( + Oauth2JwtDecoratorRegistry.class, oauth2JwtDecoratorRegistry); + } + + private DapsJwtDecorator dapsJwtDecorator() { + return new DapsJwtDecorator(); + } + + private IdsAudJwtDecorator audJwtDecorator() { + return new IdsAudJwtDecorator(); + } + + private ExpJwtDecorator expJwtDecorator( + @NonNull final ServiceExtensionContext serviceExtensionContext) { + final Duration expiration = + Duration.ofSeconds( + serviceExtensionContext + .getConfig() + .getLong(TOKEN_EXPIRATION_SECONDS, DEFAULT_EXPIRATION.toSeconds())); + + return new ExpJwtDecorator(serviceExtensionContext.getClock(), expiration); + } + + private IatJwtDecorator iatJwtDecorator( + @NonNull final ServiceExtensionContext serviceExtensionContext) { + return new IatJwtDecorator(serviceExtensionContext.getClock()); + } + + private IssJwtDecorator issJwtDecorator( + @NonNull final ServiceExtensionContext serviceExtensionContext) { + final String issuer = serviceExtensionContext.getConfig().getString(CLIENT_ID); + + return new IssJwtDecorator(issuer); + } + + private JtiJwtDecorator jtiJwtDecorator() { + return new JtiJwtDecorator(); + } + + private SubJwtDecorator subJwtDecorator( + @NonNull final ServiceExtensionContext serviceExtensionContext) { + final String subject = serviceExtensionContext.getConfig().getString(CLIENT_ID); + + return new SubJwtDecorator(subject); + } + + private X5tJwtDecorator x5tJwtDecorator( + @NonNull final ServiceExtensionContext serviceExtensionContext, + @NonNull final CertificateResolver certificateResolver) { + final String publicKeyAlias = serviceExtensionContext.getSetting(PUBLIC_KEY_ALIAS, null); + if (publicKeyAlias == null) { + throw new EdcException("Missing required setting: " + PUBLIC_KEY_ALIAS); + } + + final X509Certificate certificate = + Optional.ofNullable(certificateResolver.resolveCertificate(publicKeyAlias)) + .orElseThrow( + () -> + new EdcException( + String.format("Public certificate not found: %s", publicKeyAlias))); + + final byte[] encodedCertificate; + try { + encodedCertificate = certificate.getEncoded(); + } catch (final CertificateEncodingException certificateEncodingException) { + throw new EdcException( + "Failed to encode certificate: " + certificateEncodingException.getMessage(), + certificateEncodingException); + } + + return new X5tJwtDecorator(encodedCertificate); + } +} diff --git a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/Oauth2JwtDecoratorRegistryRegistryImpl.java b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/Oauth2JwtDecoratorRegistryRegistryImpl.java new file mode 100644 index 000000000..b57d9737d --- /dev/null +++ b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/Oauth2JwtDecoratorRegistryRegistryImpl.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022 Amadeus + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Amadeus - Initial implementation + * + */ + +package net.catenax.edc.oauth2.jwt.decorator; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import org.eclipse.dataspaceconnector.iam.oauth2.spi.Oauth2JwtDecoratorRegistry; +import org.eclipse.dataspaceconnector.spi.jwt.JwtDecorator; + +/** Registry for Oauth2 JWT decorators. */ +public class Oauth2JwtDecoratorRegistryRegistryImpl implements Oauth2JwtDecoratorRegistry { + private final List list = new CopyOnWriteArrayList<>(); + + @Override + public void register(final JwtDecorator decorator) { + list.add(decorator); + } + + @Override + public void unregister(final JwtDecorator decorator) { + list.remove(decorator); + } + + @Override + public Collection getAll() { + return new ArrayList<>(list); + } +} diff --git a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/SubJwtDecorator.java b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/SubJwtDecorator.java new file mode 100644 index 000000000..d0a9b459c --- /dev/null +++ b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/SubJwtDecorator.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.oauth2.jwt.decorator; + +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jwt.JWTClaimsSet; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import org.eclipse.dataspaceconnector.spi.jwt.JwtDecorator; + +@RequiredArgsConstructor +public class SubJwtDecorator implements JwtDecorator { + @NonNull private final String subject; + + @Override + public void decorate(final JWSHeader.Builder header, final JWTClaimsSet.Builder claimsSet) { + claimsSet.subject(subject); + } +} diff --git a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/X5tJwtDecorator.java b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/X5tJwtDecorator.java new file mode 100644 index 000000000..9d34c0307 --- /dev/null +++ b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/X5tJwtDecorator.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.oauth2.jwt.decorator; + +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.util.Base64URL; +import com.nimbusds.jwt.JWTClaimsSet; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import org.eclipse.dataspaceconnector.spi.EdcException; +import org.eclipse.dataspaceconnector.spi.jwt.JwtDecorator; + +@RequiredArgsConstructor +public class X5tJwtDecorator implements JwtDecorator { + private static final String SHA_1 = "SHA-1"; + + @NonNull private final byte[] encodedCertificate; + + @Override + public void decorate( + @NonNull final JWSHeader.Builder header, @NonNull final JWTClaimsSet.Builder claimsSet) { + header.x509CertThumbprint(new Base64URL(sha1Base64Fingerprint(encodedCertificate))); + } + + public static String sha1Base64Fingerprint(final byte[] bytes) { + try { + final MessageDigest messageDigest = MessageDigest.getInstance(SHA_1); + messageDigest.update(bytes); + return Base64.getEncoder().encodeToString(messageDigest.digest()); + } catch (NoSuchAlgorithmException e) { + throw new EdcException(e); + } + } +} diff --git a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/generator/JwtTokenGenerationService.java b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/generator/JwtTokenGenerationService.java new file mode 100644 index 000000000..e26e461ef --- /dev/null +++ b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/generator/JwtTokenGenerationService.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2022 Amadeus + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Amadeus - initial API and implementation + * Microsoft Corporation - Simplified token representation + * Mercedes Benz Tech Innovation - Rename class, add Type-Safety + * + */ + +package net.catenax.edc.oauth2.jwt.generator; + +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.JWSSigner; +import com.nimbusds.jose.crypto.ECDSASigner; +import com.nimbusds.jose.crypto.RSASSASigner; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; +import java.security.PrivateKey; +import java.security.interfaces.ECPrivateKey; +import java.util.Arrays; +import lombok.NonNull; +import lombok.SneakyThrows; +import org.eclipse.dataspaceconnector.spi.EdcException; +import org.eclipse.dataspaceconnector.spi.iam.TokenRepresentation; +import org.eclipse.dataspaceconnector.spi.jwt.JwtDecorator; +import org.eclipse.dataspaceconnector.spi.jwt.TokenGenerationService; +import org.eclipse.dataspaceconnector.spi.result.Result; +import org.jetbrains.annotations.NotNull; + +public class JwtTokenGenerationService implements TokenGenerationService { + private static final String KEY_ALGO_RSA = "RSA"; + private static final String KEY_ALGO_EC = "EC"; + + private final JWSAlgorithm jwsAlgorithm; + private final JWSSigner jwsSigner; + + public JwtTokenGenerationService(@NonNull final PrivateKey privateKey) { + this.jwsAlgorithm = getJWSAlgorithm(privateKey.getAlgorithm()); + this.jwsSigner = getJWSSigner(privateKey.getAlgorithm(), privateKey); + } + + @SneakyThrows + private static JWSSigner getJWSSigner( + @NonNull final String algorithm, @NonNull final PrivateKey privateKey) { + if (algorithm.equals(KEY_ALGO_EC)) { + return new ECDSASigner((ECPrivateKey) privateKey); + } + + if (algorithm.equals(KEY_ALGO_RSA)) { + return new RSASSASigner(privateKey); + } + + throw new EdcException("Unsupported key algorithm: " + algorithm); + } + + private static JWSAlgorithm getJWSAlgorithm(@NonNull final String algorithm) { + if (algorithm.equals(KEY_ALGO_EC)) { + return JWSAlgorithm.ES256; + } + + if (algorithm.equals(KEY_ALGO_RSA)) { + return JWSAlgorithm.RS256; + } + + throw new EdcException("Unsupported key algorithm: " + algorithm); + } + + @Override + public Result generate(@NotNull @NonNull final JwtDecorator... decorators) { + final JWSHeader.Builder headerBuilder = new JWSHeader.Builder(jwsAlgorithm); + final JWTClaimsSet.Builder claimsBuilder = new JWTClaimsSet.Builder(); + + Arrays.stream(decorators) + .forEach(decorator -> decorator.decorate(headerBuilder, claimsBuilder)); + + final JWTClaimsSet jwtClaimSet = claimsBuilder.build(); + + final SignedJWT signedJwt = new SignedJWT(headerBuilder.build(), jwtClaimSet); + try { + signedJwt.sign(jwsSigner); + } catch (final JOSEException joseException) { + return Result.failure("Failed to sign token"); + } + + return Result.success( + TokenRepresentation.Builder.newInstance().token(signedJwt.serialize()).build()); + } +} diff --git a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/generator/JwtTokenGenerationServiceExtension.java b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/generator/JwtTokenGenerationServiceExtension.java new file mode 100644 index 000000000..eb869ec32 --- /dev/null +++ b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/generator/JwtTokenGenerationServiceExtension.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.oauth2.jwt.generator; + +import java.security.PrivateKey; +import lombok.NonNull; +import lombok.Setter; +import org.eclipse.dataspaceconnector.spi.EdcSetting; +import org.eclipse.dataspaceconnector.spi.jwt.TokenGenerationService; +import org.eclipse.dataspaceconnector.spi.security.PrivateKeyResolver; +import org.eclipse.dataspaceconnector.spi.system.Inject; +import org.eclipse.dataspaceconnector.spi.system.Provides; +import org.eclipse.dataspaceconnector.spi.system.Requires; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtension; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; + +@Provides(TokenGenerationService.class) +@Requires(PrivateKeyResolver.class) +public class JwtTokenGenerationServiceExtension implements ServiceExtension { + + @EdcSetting private static final String PRIVATE_KEY_ALIAS = "edc.oauth.private.key.alias"; + + @Inject @Setter private PrivateKeyResolver privateKeyResolver; + + @Override + public void initialize(@NonNull final ServiceExtensionContext serviceExtensionContext) { + final PrivateKey privateKey = privateKey(serviceExtensionContext); + final TokenGenerationService tokenGenerationService = new JwtTokenGenerationService(privateKey); + + serviceExtensionContext.registerService(TokenGenerationService.class, tokenGenerationService); + } + + private PrivateKey privateKey(final ServiceExtensionContext serviceExtensionContext) { + final String privateKeyAlias = serviceExtensionContext.getConfig().getString(PRIVATE_KEY_ALIAS); + return privateKeyResolver.resolvePrivateKey(privateKeyAlias, PrivateKey.class); + } +} diff --git a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/AudValidationRule.java b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/AudValidationRule.java new file mode 100644 index 000000000..3d05e7f75 --- /dev/null +++ b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/AudValidationRule.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.oauth2.jwt.validation; + +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import org.eclipse.dataspaceconnector.spi.EdcException; +import org.eclipse.dataspaceconnector.spi.jwt.TokenValidationRule; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.result.Result; +import org.jetbrains.annotations.Nullable; + +@RequiredArgsConstructor +public class AudValidationRule implements TokenValidationRule { + @NonNull private final String audience; + + @NonNull private final Monitor monitor; + + /** + * Validates the JWT by checking the audience, nbf, and expiration. Accessible for testing. + * + * @param toVerify The jwt including the claims. + * @param additional No more additional information needed for this validation, can be null. + */ + @Override + @SneakyThrows + public Result checkRule(SignedJWT toVerify, @Nullable Map additional) { + try { + final JWTClaimsSet claimsSet = toVerify.getJWTClaimsSet(); + final List errors = new ArrayList<>(); + + final List audiences = claimsSet.getAudience(); + audiences.forEach(a -> monitor.info("RECEIVED DAP AUDIENCE TO VERIFY: " + a)); + + if (audiences.isEmpty()) { + errors.add("Required audience (aud) claim is missing in token"); + } else if (!audiences.contains(audience)) { + errors.add("Token audience (aud) claim did not contain connector audience: " + audience); + } + + if (errors.isEmpty()) { + return Result.success(toVerify); + } else { + return Result.failure(errors); + } + } catch (final ParseException parseException) { + throw new EdcException( + String.format( + "%s: unable to parse SignedJWT (%s)", + this.getClass().getSimpleName(), parseException.getMessage())); + } + } +} diff --git a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/ExpValidationRule.java b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/ExpValidationRule.java new file mode 100644 index 000000000..9f3c2793e --- /dev/null +++ b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/ExpValidationRule.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.oauth2.jwt.validation; + +import static java.time.ZoneOffset.UTC; + +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; +import java.text.ParseException; +import java.time.Clock; +import java.time.Instant; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import org.eclipse.dataspaceconnector.spi.EdcException; +import org.eclipse.dataspaceconnector.spi.jwt.TokenValidationRule; +import org.eclipse.dataspaceconnector.spi.result.Result; +import org.jetbrains.annotations.Nullable; + +@RequiredArgsConstructor +public class ExpValidationRule implements TokenValidationRule { + + @NonNull private final Clock clock; + + /** + * Validates the JWT by checking the audience, nbf, and expiration. Accessible for testing. + * + * @param toVerify The jwt including the claims. + * @param additional No more additional information needed for this validation, can be null. + */ + @Override + public Result checkRule(SignedJWT toVerify, @Nullable Map additional) { + try { + final JWTClaimsSet claimsSet = toVerify.getJWTClaimsSet(); + final List errors = new ArrayList<>(); + + final Instant now = clock.instant(); + final Date expires = claimsSet.getExpirationTime(); + var expiresSet = expires != null; + if (!expiresSet) { + errors.add("Required expiration time (exp) claim is missing in token"); + } else if (now.isAfter(convertToUtcTime(expires))) { + errors.add("Token has expired (exp)"); + } + + if (errors.isEmpty()) { + return Result.success(toVerify); + } else { + return Result.failure(errors); + } + } catch (final ParseException parseException) { + throw new EdcException( + String.format( + "%s: unable to parse SignedJWT (%s)", + this.getClass().getSimpleName(), parseException.getMessage())); + } + } + + private static Instant convertToUtcTime(Date date) { + return ZonedDateTime.ofInstant(date.toInstant(), UTC).toInstant(); + } +} diff --git a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/IatValidationRule.java b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/IatValidationRule.java new file mode 100644 index 000000000..d4959e4e0 --- /dev/null +++ b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/IatValidationRule.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.oauth2.jwt.validation; + +import static java.time.ZoneOffset.UTC; + +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; +import java.text.ParseException; +import java.time.Clock; +import java.time.Instant; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import org.eclipse.dataspaceconnector.spi.EdcException; +import org.eclipse.dataspaceconnector.spi.jwt.TokenValidationRule; +import org.eclipse.dataspaceconnector.spi.result.Result; +import org.jetbrains.annotations.Nullable; + +@RequiredArgsConstructor +public class IatValidationRule implements TokenValidationRule { + @NonNull private final Clock clock; + + /** + * Validates the JWT by checking the audience, nbf, and expiration. Accessible for testing. + * + * @param toVerify The jwt including the claims. + * @param additional No more additional information needed for this validation, can be null. + */ + @Override + public Result checkRule(SignedJWT toVerify, @Nullable Map additional) { + try { + JWTClaimsSet claimsSet = toVerify.getJWTClaimsSet(); + List errors = new ArrayList<>(); + + Instant now = clock.instant(); + Date issuedAt = claimsSet.getIssueTime(); + if (claimsSet.getExpirationTime() != null) { + if (issuedAt.toInstant().isAfter(claimsSet.getExpirationTime().toInstant())) { + errors.add("Issued at (iat) claim is after expiration time (exp) claim in token"); + } else if (now.isBefore(convertToUtcTime(issuedAt))) { + errors.add("Current date/time before issued at (iat) claim in token"); + } + } + + if (errors.isEmpty()) { + return Result.success(toVerify); + } else { + return Result.failure(errors); + } + } catch (final ParseException parseException) { + throw new EdcException( + String.format( + "%s: unable to parse SignedJWT (%s)", + this.getClass().getSimpleName(), parseException.getMessage())); + } + } + + private static Instant convertToUtcTime(Date date) { + return ZonedDateTime.ofInstant(date.toInstant(), UTC).toInstant(); + } +} diff --git a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/IdsValidationRule.java b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/IdsValidationRule.java new file mode 100644 index 000000000..5d3220cbd --- /dev/null +++ b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/IdsValidationRule.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2022 Fraunhofer Institute for Software and Systems Engineering + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Fraunhofer Institute for Software and Systems Engineering - Initial Implementation + * + */ + +package net.catenax.edc.oauth2.jwt.validation; + +import com.nimbusds.jwt.SignedJWT; +import java.text.ParseException; +import java.util.Map; +import org.eclipse.dataspaceconnector.spi.EdcException; +import org.eclipse.dataspaceconnector.spi.jwt.TokenValidationRule; +import org.eclipse.dataspaceconnector.spi.result.Result; +import org.jetbrains.annotations.Nullable; + +public class IdsValidationRule implements TokenValidationRule { + private final boolean validateReferring; + + public IdsValidationRule(boolean validateReferring) { + this.validateReferring = validateReferring; + } + + /** Validates the JWT by checking extended IDS rules. */ + @Override + public Result checkRule(SignedJWT jwt, @Nullable Map additional) { + if (additional != null) { + var issuerConnector = additional.get("issuerConnector"); + if (issuerConnector == null) { + return Result.failure("Required issuerConnector is missing in message"); + } + + String securityProfile = null; + if (additional.containsKey("securityProfile")) { + securityProfile = additional.get("securityProfile").toString(); + } + + return verifyTokenIds(jwt, issuerConnector.toString(), securityProfile); + + } else { + throw new EdcException("Missing required additional information for IDS token validation"); + } + } + + private Result verifyTokenIds( + SignedJWT jwt, String issuerConnector, @Nullable String securityProfile) { + try { + var claims = jwt.getJWTClaimsSet().getClaims(); + + // referringConnector (DAT, optional) vs issuerConnector (Message-Header, + // mandatory) + var referringConnector = claims.get("referringConnector"); + + if (validateReferring && !issuerConnector.equals(referringConnector)) { + return Result.failure( + "referingConnector in token does not match issuerConnector in message"); + } + + // securityProfile (DAT, mandatory) vs securityProfile (Message-Payload, + // optional) + try { + var tokenSecurityProfile = claims.get("securityProfile"); + + if (securityProfile != null && !securityProfile.equals(tokenSecurityProfile)) { + return Result.failure( + "securityProfile in token does not match securityProfile in payload"); + } + } catch (Exception e) { + // Nothing to do, payload mostly no connector instance + } + } catch (ParseException e) { + throw new EdcException( + "IdsValidationRule: unable to parse SignedJWT (" + e.getMessage() + ")"); + } + + return Result.success(jwt); + } +} diff --git a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/JwtValidationExtension.java b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/JwtValidationExtension.java new file mode 100644 index 000000000..310c979de --- /dev/null +++ b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/JwtValidationExtension.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.oauth2.jwt.validation; + +import java.net.URI; +import java.time.Clock; +import java.time.Duration; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Stream; +import lombok.NonNull; +import lombok.Setter; +import net.catenax.edc.oauth2.jwk.JwkPublicKeyResolver; +import net.catenax.edc.oauth2.jwk.PublicKeyReader; +import net.catenax.edc.oauth2.jwk.RsaPublicKeyReader; +import okhttp3.OkHttpClient; +import org.eclipse.dataspaceconnector.iam.oauth2.spi.Oauth2ValidationRulesRegistry; +import org.eclipse.dataspaceconnector.spi.EdcSetting; +import org.eclipse.dataspaceconnector.spi.jwt.TokenValidationService; +import org.eclipse.dataspaceconnector.spi.system.Inject; +import org.eclipse.dataspaceconnector.spi.system.Provides; +import org.eclipse.dataspaceconnector.spi.system.Requires; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtension; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; + +@Provides(TokenValidationService.class) +@Requires({OkHttpClient.class, Clock.class}) +public class JwtValidationExtension implements ServiceExtension { + + @EdcSetting private static final String EDC_IDS_ENDPOINT_AUDIENCE = "edc.ids.endpoint.audience"; + @EdcSetting private static final String NOT_BEFORE_LEEWAY = "edc.oauth.validation.nbf.leeway"; + private static final Duration DEFAULT_NOT_BEFORE_LEEWAY = Duration.ofSeconds(10); + + @EdcSetting + private static final String PROVIDER_JWKS_REFRESH = + "edc.oauth.provider.jwks.refresh"; // in minutes + + private static final Duration DEFAULT_PROVIDER_JWKS_REFRESH = Duration.ofMinutes(5); + + @EdcSetting private static final String PROVIDER_JWKS_URL = "edc.oauth.provider.jwks.url"; + private static final String DEFAULT_JWKS_URL = "http://localhost/empty_jwks_url"; + + @EdcSetting + public static final String EDC_IDS_VALIDATION_REFERRINGCONNECTOR = + "edc.ids.validation.referringconnector"; + + public static final boolean DEFAULT_EDC_IDS_VALIDATION_REFERRINGCONNECTOR = false; + + @Inject @Setter private OkHttpClient okHttpClient; + + @Inject @Setter private Clock clock; + + private JwkPublicKeyResolver jwkPublicKeyResolver; + + @Override + public void initialize(@NonNull final ServiceExtensionContext serviceExtensionContext) { + + final Oauth2ValidationRulesRegistry oauth2ValidationRulesRegistry = + oauth2ValidationRulesRegistry(serviceExtensionContext); + + this.jwkPublicKeyResolver = jwkPublicKeyResolver(serviceExtensionContext); + + final TokenValidationService tokenValidationService = + new TokenValidationServiceImpl(jwkPublicKeyResolver, oauth2ValidationRulesRegistry); + + serviceExtensionContext.registerService(TokenValidationService.class, tokenValidationService); + } + + @Override + public void start() { + Optional.ofNullable(jwkPublicKeyResolver).ifPresent(JwkPublicKeyResolver::start); + } + + @Override + public void shutdown() { + Optional.ofNullable(jwkPublicKeyResolver).ifPresent(JwkPublicKeyResolver::stop); + } + + private Oauth2ValidationRulesRegistry oauth2ValidationRulesRegistry( + final ServiceExtensionContext serviceExtensionContext) { + final Oauth2ValidationRulesRegistry oauth2ValidationRulesRegistry = + new Oauth2ValidationRulesRegistryImpl(); + Stream.of( + audValidationRule(serviceExtensionContext), + expValidationRule(), + iatValidationRule(), + nbfValidationRule(serviceExtensionContext), + idsValidationRule(serviceExtensionContext)) + .forEach(oauth2ValidationRulesRegistry::addRule); + + return oauth2ValidationRulesRegistry; + } + + private JwkPublicKeyResolver jwkPublicKeyResolver( + final ServiceExtensionContext serviceExtensionContext) { + final URI jsonWebKeySetUri = + URI.create( + serviceExtensionContext.getConfig().getString(PROVIDER_JWKS_URL, DEFAULT_JWKS_URL)); + final Duration refreshInterval = + Duration.ofMinutes( + serviceExtensionContext + .getConfig() + .getLong(PROVIDER_JWKS_REFRESH, DEFAULT_PROVIDER_JWKS_REFRESH.toMinutes())); + + final RsaPublicKeyReader rsaPublicKeyReader = + new RsaPublicKeyReader(serviceExtensionContext.getMonitor()); + final List publicKeyReaders = Collections.singletonList(rsaPublicKeyReader); + + return new JwkPublicKeyResolver( + jsonWebKeySetUri, + okHttpClient, + serviceExtensionContext.getTypeManager(), + serviceExtensionContext.getMonitor(), + publicKeyReaders, + refreshInterval); + } + + private AudValidationRule audValidationRule( + final ServiceExtensionContext serviceExtensionContext) { + final String audience = + Objects.requireNonNull( + serviceExtensionContext.getConfig().getString(EDC_IDS_ENDPOINT_AUDIENCE)); + + return new AudValidationRule(audience, serviceExtensionContext.getMonitor()); + } + + private ExpValidationRule expValidationRule() { + return new ExpValidationRule(clock); + } + + private IatValidationRule iatValidationRule() { + return new IatValidationRule(clock); + } + + private NbfValidationRule nbfValidationRule( + final ServiceExtensionContext serviceExtensionContext) { + final Duration nbfLeeway = + Duration.ofSeconds( + serviceExtensionContext + .getConfig() + .getLong(NOT_BEFORE_LEEWAY, DEFAULT_NOT_BEFORE_LEEWAY.toSeconds())); + + return new NbfValidationRule(nbfLeeway, clock); + } + + private IdsValidationRule idsValidationRule( + final ServiceExtensionContext serviceExtensionContext) { + boolean validateReferring = + serviceExtensionContext.getSetting( + EDC_IDS_VALIDATION_REFERRINGCONNECTOR, DEFAULT_EDC_IDS_VALIDATION_REFERRINGCONNECTOR); + + return new IdsValidationRule(validateReferring); + } +} diff --git a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/NbfValidationRule.java b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/NbfValidationRule.java new file mode 100644 index 000000000..8c4ab3429 --- /dev/null +++ b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/NbfValidationRule.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.oauth2.jwt.validation; + +import static java.time.ZoneOffset.UTC; + +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; +import java.text.ParseException; +import java.time.Clock; +import java.time.Duration; +import java.time.Instant; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import org.eclipse.dataspaceconnector.spi.EdcException; +import org.eclipse.dataspaceconnector.spi.jwt.TokenValidationRule; +import org.eclipse.dataspaceconnector.spi.result.Result; +import org.jetbrains.annotations.Nullable; + +@RequiredArgsConstructor +public class NbfValidationRule implements TokenValidationRule { + @NonNull private final Duration notBeforeValidationLeeway; + + @NonNull private final Clock clock; + + /** + * Validates the JWT by checking the audience, nbf, and expiration. Accessible for testing. + * + * @param toVerify The jwt including the claims. + * @param additional No more additional information needed for this validation, can be null. + */ + @Override + public Result checkRule( + final SignedJWT toVerify, @Nullable final Map additional) { + try { + final JWTClaimsSet claimsSet = toVerify.getJWTClaimsSet(); + final List errors = new ArrayList<>(); + + final Instant now = clock.instant(); + final Instant leewayNow = now.plusSeconds(notBeforeValidationLeeway.toSeconds()); + final Date notBefore = claimsSet.getNotBeforeTime(); + if (notBefore == null) { + errors.add("Required not before (nbf) claim is missing in token"); + } else if (leewayNow.isBefore(dateToInstant(notBefore))) { + errors.add("Current date/time with leeway before the not before (nbf) claim in token"); + } + + if (errors.isEmpty()) { + return Result.success(toVerify); + } else { + return Result.failure(errors); + } + } catch (final ParseException parseException) { + throw new EdcException( + String.format( + "%s: unable to parse SignedJWT (%s)", + this.getClass().getSimpleName(), parseException.getMessage())); + } + } + + private static Instant dateToInstant(final Date date) { + return ZonedDateTime.ofInstant(date.toInstant(), UTC).toInstant(); + } +} diff --git a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/Oauth2ValidationRulesRegistryImpl.java b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/Oauth2ValidationRulesRegistryImpl.java new file mode 100644 index 000000000..b779a96cc --- /dev/null +++ b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/Oauth2ValidationRulesRegistryImpl.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022 Amadeus + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Amadeus - Initial implementation + * + */ + +package net.catenax.edc.oauth2.jwt.validation; + +import java.util.ArrayList; +import java.util.List; +import lombok.NoArgsConstructor; +import org.eclipse.dataspaceconnector.iam.oauth2.spi.Oauth2ValidationRulesRegistry; +import org.eclipse.dataspaceconnector.spi.jwt.TokenValidationRule; +import org.eclipse.dataspaceconnector.spi.jwt.TokenValidationRulesRegistry; + +/** Registry for Oauth2 validation rules. */ +@NoArgsConstructor +public class Oauth2ValidationRulesRegistryImpl + implements Oauth2ValidationRulesRegistry, TokenValidationRulesRegistry { + + private final List rules = new ArrayList<>(); + + @Override + public void addRule(TokenValidationRule rule) { + rules.add(rule); + } + + @Override + public List getRules() { + return new ArrayList<>(rules); + } +} diff --git a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/TokenValidationServiceImpl.java b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/TokenValidationServiceImpl.java new file mode 100644 index 000000000..0ca7d3eeb --- /dev/null +++ b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/TokenValidationServiceImpl.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2022 Amadeus + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Amadeus - initial API and implementation + * + */ + +package net.catenax.edc.oauth2.jwt.validation; + +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.JWSVerifier; +import com.nimbusds.jose.crypto.factories.DefaultJWSVerifierFactory; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; +import java.security.PublicKey; +import java.text.ParseException; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import org.eclipse.dataspaceconnector.spi.iam.ClaimToken; +import org.eclipse.dataspaceconnector.spi.iam.PublicKeyResolver; +import org.eclipse.dataspaceconnector.spi.iam.TokenRepresentation; +import org.eclipse.dataspaceconnector.spi.jwt.TokenValidationRulesRegistry; +import org.eclipse.dataspaceconnector.spi.jwt.TokenValidationService; +import org.eclipse.dataspaceconnector.spi.result.Result; + +@RequiredArgsConstructor +public class TokenValidationServiceImpl implements TokenValidationService { + + @NonNull private final PublicKeyResolver publicKeyResolver; + + @NonNull private final TokenValidationRulesRegistry rulesRegistry; + + @Override + public Result validate(@NonNull final TokenRepresentation tokenRepresentation) { + final String token = tokenRepresentation.getToken(); + final Map additional = tokenRepresentation.getAdditional(); + final JWTClaimsSet claimsSet; + try { + final SignedJWT signedJwt = SignedJWT.parse(token); + final String publicKeyId = signedJwt.getHeader().getKeyID(); + final Result verifierCreationResult = + createVerifier(signedJwt.getHeader(), publicKeyId); + + if (verifierCreationResult.failed()) { + return Result.failure(verifierCreationResult.getFailureMessages()); + } + + if (!signedJwt.verify(verifierCreationResult.getContent())) { + return Result.failure("Token verification failed"); + } + + claimsSet = signedJwt.getJWTClaimsSet(); + + final List errors = + rulesRegistry.getRules().stream() + .map(r -> r.checkRule(signedJwt, additional)) + .filter(Result::failed) + .map(Result::getFailureMessages) + .flatMap(Collection::stream) + .collect(Collectors.toList()); + + if (!errors.isEmpty()) { + return Result.failure(errors); + } + + final ClaimToken.Builder tokenBuilder = ClaimToken.Builder.newInstance(); + + claimsSet.getClaims().entrySet().stream() + .map(entry -> Map.entry(entry.getKey(), Objects.toString(entry.getValue()))) + .filter(entry -> entry.getValue() != null) + .forEach(entry -> tokenBuilder.claim(entry.getKey(), entry.getValue())); + + return Result.success(tokenBuilder.build()); + + } catch (final JOSEException e) { + return Result.failure(e.getMessage()); + } catch (final ParseException e) { + return Result.failure("Failed to decode token"); + } + } + + private Result createVerifier(final JWSHeader header, final String publicKeyId) { + final PublicKey publicKey = publicKeyResolver.resolveKey(publicKeyId); + if (publicKey == null) { + return Result.failure("Failed to resolve public key with id: " + publicKeyId); + } + try { + return Result.success(new DefaultJWSVerifierFactory().createJWSVerifier(header, publicKey)); + } catch (final JOSEException e) { + return Result.failure("Failed to create verifier"); + } + } +} diff --git a/edc-extensions/cx-oauth2/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension b/edc-extensions/cx-oauth2/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension new file mode 100644 index 000000000..0303255e4 --- /dev/null +++ b/edc-extensions/cx-oauth2/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension @@ -0,0 +1,17 @@ +# +# Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +# +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# +# Contributors: +# Mercedes-Benz Tech Innovation GmbH - Initial ServiceExtension file +# +# +net.catenax.edc.oauth2.jwt.decorator.JwtDecoratorExtension +net.catenax.edc.oauth2.jwt.validation.JwtValidationExtension +net.catenax.edc.oauth2.jwt.generator.JwtTokenGenerationServiceExtension +net.catenax.edc.oauth2.OAuth2Extension diff --git a/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwk/JwkPublicKeyResolverTest.java b/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwk/JwkPublicKeyResolverTest.java new file mode 100644 index 000000000..8d90b5000 --- /dev/null +++ b/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwk/JwkPublicKeyResolverTest.java @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ + +package net.catenax.edc.oauth2.jwk; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; + +import java.net.URI; +import java.security.PublicKey; +import java.time.Duration; +import java.util.Collections; +import java.util.Optional; +import okhttp3.Call; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Protocol; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; +import org.eclipse.dataspaceconnector.spi.EdcException; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.types.TypeManager; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class JwkPublicKeyResolverTest { + + private static final String JWKS_KEY_ID = "e600c72b-125a-4b30-86a5-9697af62f2a1"; + private static final String JWKS_PUBLIC_KEY = + "MIICujCCAaKgAwIBAgIECI8fsTANBgkqhkiG9w0BAQsFADAfMR0wGwYDVQQDExR0ZXN0LWFsZXgucmVhY2g1Lm5ldDAeFw0yMDA3MjkwOTM0MjlaFw0yMjAyMTcxNDIwMzNaMB8xHTAbBgNVBAMTFHRlc3QtYWxleC5yZWFjaDUubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlzRszUeQ4WiSqvmYxMP10ngm8ALIoUwMH7Oa8vrZgD5pqalPjetPAxeVcAv2gTyDlOwtB0fGvlQo6n78pd9pTbgrzUjhmFuYN6OCfT6eN/2wu0LmwryFS2mbh7/1DTiKd2tZaRalskPECXTKkeks85HVqanB0860BYlGvQvfgrvhCWXXFJJeXvNwYNFYdDdrFQhoeOAEvRDKg9DdHZf6XzSR6Qk3w51FKn2b7imen/G52itD/kIen1hqqB2Jwt9SWyX5MSGySY2QwC18F6Dfs8L+t0mwCo6grGW9264Z5vlO0PWssEqGIX/ez6nk1ZdHXhoXwJ0W+6QzeQlUN8jNoQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAETbMWro4HI4ZuqtnjMZrgEOpx6WhAtxpMx5XFPVWbdp/DpPySotoWbbD6qCtYc34E+ec7mH7aHVap+Gl2IyeSHTht4FXfF9q/1Oj/fis/4DDi1iq00rJsU18D71mZ9FGWCWlO1nhW1KSTGbRJ3E0wSrNabcvaXcwEHokR3zm+xfRWjtbrq2hQ19R16xyOLVy4zrF95QxP4UN+Cvm8nmYur6bSqv+gCMvDsl+O/gtRHGgpUukHEJwnee1R3+1aIv+9zOF3HaaUC5neOLBFITGmeXgi8G2IhbG+JoXh/GUkb66TZUlUAM3qXYNL9Nf+2MQ7nAPTXcxlmImFUUrnv0c3"; + private static final String JWKS = + String.format( + "{" + + " \"keys\": [" + + " {" + + " \"kty\": \"RSA\"," + + " \"e\": \"AQAB\"," + + " \"x5t\": \"NjU3NDI5ZTZhODU0YjQzMGFiYzkwNGNkZDkwNmZkMzZmOWEzNWVmMQ\"," + + " \"use\": \"sig\"," + + " \"kid\": \"%s\"," + + " \"x5c\": [" + + " \"%s\"" + + " ]," + + " \"alg\": \"RS256\"," + + " \"n\": \"lzRszUeQ4WiSqvmYxMP10ngm8ALIoUwMH7Oa8vrZgD5pqalPjetPAxeVcAv2gTyDlOwtB0fGvlQo6n78pd9pTbgrzUjhmFuYN6OCfT6eN_2wu0LmwryFS2mbh7_1DTiKd2tZaRalskPECXTKkeks85HVqanB0860BYlGvQvfgrvhCWXXFJJeXvNwYNFYdDdrFQhoeOAEvRDKg9DdHZf6XzSR6Qk3w51FKn2b7imen_G52itD_kIen1hqqB2Jwt9SWyX5MSGySY2QwC18F6Dfs8L-t0mwCo6grGW9264Z5vlO0PWssEqGIX_ez6nk1ZdHXhoXwJ0W-6QzeQlUN8jNoQ\"" + + " }" + + " ]" + + "}", + JWKS_KEY_ID, JWKS_PUBLIC_KEY); + + private static final URI JWKS_URI = URI.create("https://localhost/.well-known/jwks.json"); + private static final Duration INTERVAL = Duration.ofSeconds(1); + + private JwkPublicKeyResolver jwkPublicKeyResolver; + + // mocks + private OkHttpClient httpClient; + private Monitor monitor; + private PublicKeyReader publicKeyReader; + + @BeforeEach + void setUp() { + httpClient = mock(OkHttpClient.class); + monitor = mock(Monitor.class); + publicKeyReader = mock(PublicKeyReader.class); + + Mockito.when(publicKeyReader.canRead(any(JsonWebKey.class))).thenReturn(true); + Mockito.when(publicKeyReader.read(any(JsonWebKey.class))) + .thenAnswer( + (i) -> { + final JsonWebKey jsonWebKey = i.getArgument(0); + + if (jsonWebKey.getKid().equals(JWKS_KEY_ID)) { + final PublicKeyHolder publicKeyHolder = + PublicKeyHolder.builder() + .keyId(JWKS_KEY_ID) + .publicKey(Mockito.mock(PublicKey.class)) + .build(); + return Optional.of(publicKeyHolder); + } else { + return Optional.empty(); + } + }); + Mockito.when(httpClient.newCall(any(Request.class))) + .thenAnswer( + (i) -> { + final Response response; + + final Request request = i.getArgument(0); + if (request.url().toString().equals(JWKS_URI.toString())) { + final ResponseBody responseBody = + ResponseBody.create(JWKS, MediaType.get("application/json")); + response = + new Response.Builder() + .request(request) + .protocol(Protocol.HTTP_1_0) + .body(responseBody) + .message("ok") + .code(200) + .build(); + } else { + response = new Response.Builder().code(404).build(); + } + + final Call call = Mockito.mock(Call.class); + Mockito.when(call.execute()).thenReturn(response); + return call; + }); + + final TypeManager typeManager = new TypeManager(); + jwkPublicKeyResolver = + new JwkPublicKeyResolver( + JWKS_URI, + httpClient, + typeManager, + monitor, + Collections.singletonList(publicKeyReader), + INTERVAL); + } + + @Test + void testPublicKeyNullForKeyIdNotFound() { + jwkPublicKeyResolver.start(); + final PublicKey key = jwkPublicKeyResolver.resolveKey("foo"); + + Assertions.assertNull(key); + } + + @Test + void testPublicKeyFoundById() { + jwkPublicKeyResolver.start(); + final PublicKey key = jwkPublicKeyResolver.resolveKey(JWKS_KEY_ID); + + Assertions.assertNotNull(key); + } + + @Test + void testExceptionOnIdentityProviderRespondingWithNon200() { + Mockito.when(httpClient.newCall(any(Request.class))) + .thenAnswer( + (i) -> { + final Response response = new Response.Builder().code(404).build(); + + final Call call = Mockito.mock(Call.class); + Mockito.when(call.execute()).thenReturn(response); + return call; + }); + + Assertions.assertThrows(EdcException.class, () -> jwkPublicKeyResolver.start()); + } + + @Test + void testExceptionOnIdentityProviderRespondingWithEmptyBody() { + Mockito.when(httpClient.newCall(any(Request.class))) + .thenAnswer( + (i) -> { + final Response response = new Response.Builder().code(200).build(); + + final Call call = Mockito.mock(Call.class); + Mockito.when(call.execute()).thenReturn(response); + return call; + }); + + Assertions.assertThrows(EdcException.class, () -> jwkPublicKeyResolver.start()); + } + + @Test + void testExceptionOnIdentityProviderRespondingWithEmptyJwks() { + Mockito.when(httpClient.newCall(any(Request.class))) + .thenAnswer( + (i) -> { + final ResponseBody responseBody = + ResponseBody.create("{ \"keys\": [] }", MediaType.get("application/json")); + final Response response = new Response.Builder().code(200).body(responseBody).build(); + + final Call call = Mockito.mock(Call.class); + Mockito.when(call.execute()).thenReturn(response); + return call; + }); + + Assertions.assertThrows(EdcException.class, () -> jwkPublicKeyResolver.start()); + } + + @Test + void testExceptionOnHttpClientException() { + Mockito.when(httpClient.newCall(any(Request.class))).thenThrow(new RuntimeException()); + + Assertions.assertThrows(EdcException.class, () -> jwkPublicKeyResolver.start()); + } +} diff --git a/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwk/RsaPublicKeyReaderTest.java b/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwk/RsaPublicKeyReaderTest.java new file mode 100644 index 000000000..b530ba980 --- /dev/null +++ b/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwk/RsaPublicKeyReaderTest.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * + */ +package net.catenax.edc.oauth2.jwk; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.Optional; +import lombok.SneakyThrows; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class RsaPublicKeyReaderTest { + private static final String JWKS_KEY_ID = "e600c72b-125a-4b30-86a5-9697af62f2a1"; + private static final String JWKS_PUBLIC_KEY = + "MIICujCCAaKgAwIBAgIECI8fsTANBgkqhkiG9w0BAQsFADAfMR0wGwYDVQQDExR0ZXN0LWFsZXgucmVhY2g1Lm5ldDAeFw0yMDA3MjkwOTM0MjlaFw0yMjAyMTcxNDIwMzNaMB8xHTAbBgNVBAMTFHRlc3QtYWxleC5yZWFjaDUubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlzRszUeQ4WiSqvmYxMP10ngm8ALIoUwMH7Oa8vrZgD5pqalPjetPAxeVcAv2gTyDlOwtB0fGvlQo6n78pd9pTbgrzUjhmFuYN6OCfT6eN/2wu0LmwryFS2mbh7/1DTiKd2tZaRalskPECXTKkeks85HVqanB0860BYlGvQvfgrvhCWXXFJJeXvNwYNFYdDdrFQhoeOAEvRDKg9DdHZf6XzSR6Qk3w51FKn2b7imen/G52itD/kIen1hqqB2Jwt9SWyX5MSGySY2QwC18F6Dfs8L+t0mwCo6grGW9264Z5vlO0PWssEqGIX/ez6nk1ZdHXhoXwJ0W+6QzeQlUN8jNoQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAETbMWro4HI4ZuqtnjMZrgEOpx6WhAtxpMx5XFPVWbdp/DpPySotoWbbD6qCtYc34E+ec7mH7aHVap+Gl2IyeSHTht4FXfF9q/1Oj/fis/4DDi1iq00rJsU18D71mZ9FGWCWlO1nhW1KSTGbRJ3E0wSrNabcvaXcwEHokR3zm+xfRWjtbrq2hQ19R16xyOLVy4zrF95QxP4UN+Cvm8nmYur6bSqv+gCMvDsl+O/gtRHGgpUukHEJwnee1R3+1aIv+9zOF3HaaUC5neOLBFITGmeXgi8G2IhbG+JoXh/GUkb66TZUlUAM3qXYNL9Nf+2MQ7nAPTXcxlmImFUUrnv0c3"; + private static final String JWKS = + String.format( + "{" + + " \"keys\": [" + + " {" + + " \"kty\": \"RSA\"," + + " \"e\": \"AQAB\"," + + " \"x5t\": \"NjU3NDI5ZTZhODU0YjQzMGFiYzkwNGNkZDkwNmZkMzZmOWEzNWVmMQ\"," + + " \"use\": \"sig\"," + + " \"kid\": \"%s\"," + + " \"x5c\": [" + + " \"%s\"" + + " ]," + + " \"alg\": \"RS256\"," + + " \"n\": \"lzRszUeQ4WiSqvmYxMP10ngm8ALIoUwMH7Oa8vrZgD5pqalPjetPAxeVcAv2gTyDlOwtB0fGvlQo6n78pd9pTbgrzUjhmFuYN6OCfT6eN_2wu0LmwryFS2mbh7_1DTiKd2tZaRalskPECXTKkeks85HVqanB0860BYlGvQvfgrvhCWXXFJJeXvNwYNFYdDdrFQhoeOAEvRDKg9DdHZf6XzSR6Qk3w51FKn2b7imen_G52itD_kIen1hqqB2Jwt9SWyX5MSGySY2QwC18F6Dfs8L-t0mwCo6grGW9264Z5vlO0PWssEqGIX_ez6nk1ZdHXhoXwJ0W-6QzeQlUN8jNoQ\"" + + " }" + + " ]" + + "}", + JWKS_KEY_ID, JWKS_PUBLIC_KEY); + + private RsaPublicKeyReader publicKeyReader; + + // mocks + private Monitor monitor; + + @BeforeEach + void setUp() { + monitor = Mockito.mock(Monitor.class); + publicKeyReader = new RsaPublicKeyReader(monitor); + } + + @Test + void testCanRead() { + final JsonWebKey jwk = deserializeKey(); + + Assertions.assertTrue(publicKeyReader.canRead(jwk)); + } + + @Test + void testReadSuccess() { + final JsonWebKey jwk = deserializeKey(); + + final Optional key = publicKeyReader.read(jwk); + + Assertions.assertTrue(key.isPresent()); + } + + @SneakyThrows + private JsonWebKey deserializeKey() { + final ObjectMapper objectMapper = new ObjectMapper(); + final JsonWebKeySet jwks = objectMapper.readValue(JWKS, JsonWebKeySet.class); + final JsonWebKey jwk = jwks.getKeys().get(0); + + return jwk; + } +} diff --git a/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/DapsJwtDecoratorTest.java b/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/DapsJwtDecoratorTest.java new file mode 100644 index 000000000..52921d1f9 --- /dev/null +++ b/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/DapsJwtDecoratorTest.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial Test + * + */ +package net.catenax.edc.oauth2.jwt.decorator; + +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jwt.JWTClaimsSet; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class DapsJwtDecoratorTest { + + @Test + void decorate() { + final DapsJwtDecorator decorator = new DapsJwtDecorator(); + + final JWSHeader.Builder jwsHeaderBuilder = Mockito.mock(JWSHeader.Builder.class); + final JWTClaimsSet.Builder claimsSetBuilder = Mockito.mock(JWTClaimsSet.Builder.class); + + Mockito.when(claimsSetBuilder.claim(Mockito.anyString(), Mockito.anyString())) + .thenReturn(claimsSetBuilder); + + decorator.decorate(jwsHeaderBuilder, claimsSetBuilder); + + Mockito.verify(claimsSetBuilder, Mockito.times(1)) + .claim("@context", "https://w3id.org/idsa/contexts/context.jsonld"); + Mockito.verify(claimsSetBuilder, Mockito.times(1)).claim("@type", "ids:DatRequestToken"); + Mockito.verifyNoMoreInteractions(jwsHeaderBuilder, claimsSetBuilder); + } +} diff --git a/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/ExpJwtDecoratorTest.java b/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/ExpJwtDecoratorTest.java new file mode 100644 index 000000000..a5e36561f --- /dev/null +++ b/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/ExpJwtDecoratorTest.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial Test + * + */ +package net.catenax.edc.oauth2.jwt.decorator; + +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jwt.JWTClaimsSet; +import java.time.Clock; +import java.time.Duration; +import java.time.Instant; +import java.util.Date; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class ExpJwtDecoratorTest { + + @Test + void decorate() { + final Clock clock = Mockito.mock(Clock.class); + final Duration expiration = Duration.ofSeconds(100); + + final ExpJwtDecorator decorator = new ExpJwtDecorator(clock, expiration); + + final JWSHeader.Builder jwsHeaderBuilder = Mockito.mock(JWSHeader.Builder.class); + final JWTClaimsSet.Builder claimsSetBuilder = new JWTClaimsSet.Builder(); + + Mockito.when(clock.instant()).thenReturn(Instant.ofEpochSecond(0)); + decorator.decorate(jwsHeaderBuilder, claimsSetBuilder); + + JWTClaimsSet jwtClaimsSet = claimsSetBuilder.build(); + Assertions.assertNotNull(jwtClaimsSet.getExpirationTime()); + Assertions.assertEquals(new Date(100000), jwtClaimsSet.getExpirationTime()); + } + + @Test + void constructorNull() { + Assertions.assertThrows(NullPointerException.class, () -> new ExpJwtDecorator(null, null)); + } +} diff --git a/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/IatJwtDecoratorTest.java b/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/IatJwtDecoratorTest.java new file mode 100644 index 000000000..9f37c4338 --- /dev/null +++ b/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/IatJwtDecoratorTest.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial Test + * + */ +package net.catenax.edc.oauth2.jwt.decorator; + +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jwt.JWTClaimsSet; +import java.time.Clock; +import java.time.Instant; +import java.util.Date; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class IatJwtDecoratorTest { + + @Test + void decorate() { + final Clock clock = Mockito.mock(Clock.class); + + final IatJwtDecorator decorator = new IatJwtDecorator(clock); + + final JWSHeader.Builder jwsHeaderBuilder = Mockito.mock(JWSHeader.Builder.class); + final JWTClaimsSet.Builder claimsSetBuilder = new JWTClaimsSet.Builder(); + + Mockito.when(clock.instant()).thenReturn(Instant.ofEpochSecond(0)); + decorator.decorate(jwsHeaderBuilder, claimsSetBuilder); + + JWTClaimsSet jwtClaimsSet = claimsSetBuilder.build(); + Assertions.assertNotNull(jwtClaimsSet.getIssueTime()); + Assertions.assertEquals(new Date(0), jwtClaimsSet.getIssueTime()); + } + + @Test + void constructorNull() { + Assertions.assertThrows(NullPointerException.class, () -> new IatJwtDecorator(null)); + } +} diff --git a/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/IdsAudJwtDecoratorTest.java b/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/IdsAudJwtDecoratorTest.java new file mode 100644 index 000000000..8f5ac315b --- /dev/null +++ b/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/IdsAudJwtDecoratorTest.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial Test + * + */ + +package net.catenax.edc.oauth2.jwt.decorator; + +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jwt.JWTClaimsSet; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class IdsAudJwtDecoratorTest { + + @Test + void decorate() { + final String expectedAudience = "idsc:IDS_CONNECTORS_ALL"; + final IdsAudJwtDecorator decorator = new IdsAudJwtDecorator(); + + final JWSHeader.Builder jwsHeaderBuilder = Mockito.mock(JWSHeader.Builder.class); + final JWTClaimsSet.Builder claimsSetBuilder = Mockito.mock(JWTClaimsSet.Builder.class); + + decorator.decorate(jwsHeaderBuilder, claimsSetBuilder); + + Mockito.verify(claimsSetBuilder, Mockito.times(1)).audience(expectedAudience); + Mockito.verifyNoMoreInteractions(jwsHeaderBuilder, claimsSetBuilder); + } +} diff --git a/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/IssJwtDecoratorTest.java b/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/IssJwtDecoratorTest.java new file mode 100644 index 000000000..e74d0ea85 --- /dev/null +++ b/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/IssJwtDecoratorTest.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial Test + * + */ +package net.catenax.edc.oauth2.jwt.decorator; + +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jwt.JWTClaimsSet; +import java.util.UUID; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class IssJwtDecoratorTest { + + @Test + void decorate() { + final String expectedIssuer = UUID.randomUUID().toString(); + final IssJwtDecorator decorator = new IssJwtDecorator(expectedIssuer); + + final JWSHeader.Builder jwsHeaderBuilder = Mockito.mock(JWSHeader.Builder.class); + final JWTClaimsSet.Builder claimsSetBuilder = Mockito.mock(JWTClaimsSet.Builder.class); + + decorator.decorate(jwsHeaderBuilder, claimsSetBuilder); + + Mockito.verify(claimsSetBuilder, Mockito.times(1)).issuer(expectedIssuer); + Mockito.verifyNoMoreInteractions(jwsHeaderBuilder, claimsSetBuilder); + } + + @Test + void constructorNull() { + Assertions.assertThrows(NullPointerException.class, () -> new IssJwtDecorator(null)); + } +} diff --git a/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/JtiJwtDecoratorTest.java b/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/JtiJwtDecoratorTest.java new file mode 100644 index 000000000..ccc6e3dc8 --- /dev/null +++ b/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/JtiJwtDecoratorTest.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial Test + * + */ +package net.catenax.edc.oauth2.jwt.decorator; + +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jwt.JWTClaimsSet; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class JtiJwtDecoratorTest { + + @Test + void decorate() { + final JtiJwtDecorator decorator = new JtiJwtDecorator(); + + final JWSHeader.Builder jwsHeaderBuilder = Mockito.mock(JWSHeader.Builder.class); + final JWTClaimsSet.Builder claimsSetBuilder = new JWTClaimsSet.Builder(); + + decorator.decorate(jwsHeaderBuilder, claimsSetBuilder); + + JWTClaimsSet jwtClaimsSet = claimsSetBuilder.build(); + Assertions.assertNotNull(jwtClaimsSet.getJWTID()); + } +} diff --git a/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/Oauth2JwtDecoratorRegistryRegistryImplTest.java b/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/Oauth2JwtDecoratorRegistryRegistryImplTest.java new file mode 100644 index 000000000..fa81fb35d --- /dev/null +++ b/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/Oauth2JwtDecoratorRegistryRegistryImplTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial Test + * + */ +package net.catenax.edc.oauth2.jwt.decorator; + +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jwt.JWTClaimsSet; +import java.util.Arrays; +import org.eclipse.dataspaceconnector.spi.jwt.JwtDecorator; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class Oauth2JwtDecoratorRegistryRegistryImplTest { + + private final Oauth2JwtDecoratorRegistryRegistryImpl oauth2JwtDecoratorRegistryRegistry = + new Oauth2JwtDecoratorRegistryRegistryImpl(); + + @Test + void test() { + final A_JwtDecorator a = new A_JwtDecorator(); + final B_JwtDecorator b = new B_JwtDecorator(); + final C_JwtDecorator c = new C_JwtDecorator(); + + oauth2JwtDecoratorRegistryRegistry.register(a); + + Assertions.assertNotNull(oauth2JwtDecoratorRegistryRegistry.getAll()); + Assertions.assertEquals(1, oauth2JwtDecoratorRegistryRegistry.getAll().size()); + + oauth2JwtDecoratorRegistryRegistry.register(b); + + Assertions.assertNotNull(oauth2JwtDecoratorRegistryRegistry.getAll()); + Assertions.assertEquals(2, oauth2JwtDecoratorRegistryRegistry.getAll().size()); + + oauth2JwtDecoratorRegistryRegistry.register(c); + + Assertions.assertNotNull(oauth2JwtDecoratorRegistryRegistry.getAll()); + Assertions.assertEquals(3, oauth2JwtDecoratorRegistryRegistry.getAll().size()); + + Assertions.assertTrue( + oauth2JwtDecoratorRegistryRegistry.getAll().containsAll(Arrays.asList(a, b, c))); + + oauth2JwtDecoratorRegistryRegistry.unregister(c); + + Assertions.assertNotNull(oauth2JwtDecoratorRegistryRegistry.getAll()); + Assertions.assertEquals(2, oauth2JwtDecoratorRegistryRegistry.getAll().size()); + + Assertions.assertTrue( + oauth2JwtDecoratorRegistryRegistry.getAll().containsAll(Arrays.asList(a, b))); + + oauth2JwtDecoratorRegistryRegistry.unregister(b); + + Assertions.assertNotNull(oauth2JwtDecoratorRegistryRegistry.getAll()); + Assertions.assertEquals(1, oauth2JwtDecoratorRegistryRegistry.getAll().size()); + + Assertions.assertTrue(oauth2JwtDecoratorRegistryRegistry.getAll().contains(a)); + + oauth2JwtDecoratorRegistryRegistry.unregister(a); + + Assertions.assertNotNull(oauth2JwtDecoratorRegistryRegistry.getAll()); + Assertions.assertTrue(oauth2JwtDecoratorRegistryRegistry.getAll().isEmpty()); + } + + private static class A_JwtDecorator implements JwtDecorator { + @Override + public void decorate(JWSHeader.Builder header, JWTClaimsSet.Builder claimsSet) {} + } + + private static class B_JwtDecorator implements JwtDecorator { + @Override + public void decorate(JWSHeader.Builder header, JWTClaimsSet.Builder claimsSet) {} + } + + private static class C_JwtDecorator implements JwtDecorator { + @Override + public void decorate(JWSHeader.Builder header, JWTClaimsSet.Builder claimsSet) {} + } +} diff --git a/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/SubJwtDecoratorTest.java b/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/SubJwtDecoratorTest.java new file mode 100644 index 000000000..d86ac0f02 --- /dev/null +++ b/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/SubJwtDecoratorTest.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Mercedes-Benz Tech Innovation GmbH - Initial Test + * + */ +package net.catenax.edc.oauth2.jwt.decorator; + +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jwt.JWTClaimsSet; +import java.util.UUID; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class SubJwtDecoratorTest { + + @Test + void decorate() { + final String expectedSubject = UUID.randomUUID().toString(); + final SubJwtDecorator decorator = new SubJwtDecorator(expectedSubject); + + final JWSHeader.Builder jwsHeaderBuilder = Mockito.mock(JWSHeader.Builder.class); + final JWTClaimsSet.Builder claimsSetBuilder = Mockito.mock(JWTClaimsSet.Builder.class); + + decorator.decorate(jwsHeaderBuilder, claimsSetBuilder); + + Mockito.verify(claimsSetBuilder, Mockito.times(1)).subject(expectedSubject); + Mockito.verifyNoMoreInteractions(jwsHeaderBuilder, claimsSetBuilder); + } + + @Test + void constructorNull() { + Assertions.assertThrows(NullPointerException.class, () -> new SubJwtDecorator(null)); + } +} diff --git a/edc-extensions/data-encryption/pom.xml b/edc-extensions/data-encryption/pom.xml index dad2c4b63..b2a47b885 100644 --- a/edc-extensions/data-encryption/pom.xml +++ b/edc-extensions/data-encryption/pom.xml @@ -23,6 +23,7 @@ 4.0.0 data-encryption + jar ${project.basedir}/src/main/java diff --git a/edc-extensions/pom.xml b/edc-extensions/pom.xml index ca9572f87..8675f863f 100644 --- a/edc-extensions/pom.xml +++ b/edc-extensions/pom.xml @@ -35,6 +35,7 @@ hashicorp-vault dataplane-selector-configuration data-encryption + cx-oauth2
diff --git a/edc-tests/pom.xml b/edc-tests/pom.xml index a252252c4..bbfbf658e 100644 --- a/edc-tests/pom.xml +++ b/edc-tests/pom.xml @@ -166,6 +166,10 @@ net.catenax.edc.extensions data-encryption + + net.catenax.edc.extensions + cx-oauth2 + net.catenax.edc diff --git a/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml b/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml index 1aae7b764..4494b580b 100644 --- a/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml +++ b/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml @@ -241,7 +241,6 @@ platoedccontrolplane: EDC_OAUTH_TOKEN_URL: &edcControlPlaneOauthTokenUrl "http://ids-daps:4567/token" EDC_OAUTH_PRIVATE_KEY_ALIAS: my-plato-daps-key EDC_OAUTH_PUBLIC_KEY_ALIAS: my-plato-daps-crt - EDC_OAUTH_PROVIDER_AUDIENCE: &edcControlPlaneOauthAudience idsc:IDS_CONNECTORS_ALL ############# ## GENERAL ## @@ -252,6 +251,7 @@ platoedccontrolplane: # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/data-protocols/ids/ids-core EDC_IDS_ENDPOINT: http://plato-edc-controlplane:8282/api/v1/ids + EDC_IDS_ENDPOINT_AUDIENCE: http://plato-edc-controlplane:8282/api/v1/ids/data EDC_IDS_DESCRIPTION: "Plato Control Plane" ################ @@ -490,13 +490,13 @@ sokratesedccontrolplane: EDC_OAUTH_TOKEN_URL: *edcControlPlaneOauthTokenUrl EDC_OAUTH_PRIVATE_KEY_ALIAS: my-sokrates-daps-key EDC_OAUTH_PUBLIC_KEY_ALIAS: my-sokrates-daps-crt - EDC_OAUTH_PROVIDER_AUDIENCE: *edcControlPlaneOauthAudience ############# ## GENERAL ## ############# IDS_WEBHOOK_ADDRESS: http://sokrates-edc-controlplane:8282 EDC_IDS_ENDPOINT: http://sokrates-edc-controlplane:8282/api/v1/ids + EDC_IDS_ENDPOINT_AUDIENCE: http://sokrates-edc-controlplane:8282/api/v1/ids/data EDC_IDS_DESCRIPTION: "Sokrates Control Plane" ################ diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/DataManagementAPI.java b/edc-tests/src/test/java/net/catenax/edc/tests/DataManagementAPI.java index 3f0899664..5338a4f5e 100644 --- a/edc-tests/src/test/java/net/catenax/edc/tests/DataManagementAPI.java +++ b/edc-tests/src/test/java/net/catenax/edc/tests/DataManagementAPI.java @@ -243,7 +243,7 @@ private DataManagementApiPolicy mapPolicy(Policy policy) { private DataManagementApiPolicyDefinition mapPolicyDefinition(Policy policy) { final DataManagementApiPolicyDefinition apiObject = new DataManagementApiPolicyDefinition(); - apiObject.uid = policy.getId(); + apiObject.id = policy.getId(); apiObject.policy = mapPolicy(policy); return apiObject; } @@ -401,7 +401,7 @@ private static class DataManagementApiDataAddress { @Data private static class DataManagementApiPolicyDefinition { - private String uid; + private String id; private DataManagementApiPolicy policy; } diff --git a/pom.xml b/pom.xml index 6caaafa0f..58245d3ae 100644 --- a/pom.xml +++ b/pom.xml @@ -65,7 +65,7 @@ 4.2.0 - 0.0.1-20220818-SNAPSHOT + 0.0.1-20220902-SNAPSHOT 1.2.2 42.5.0 9.2.2 @@ -328,6 +328,11 @@ data-encryption ${project.version} + + net.catenax.edc.extensions + cx-oauth2 + ${project.version} + @@ -688,11 +693,6 @@ data-plane-selector-spi ${org.eclipse.dataspaceconnector.version} - - org.eclipse.dataspaceconnector - data-plane-selector-store - ${org.eclipse.dataspaceconnector.version} - org.eclipse.dataspaceconnector data-plane-spi @@ -888,6 +888,16 @@ oauth2-spi ${org.eclipse.dataspaceconnector.version} + + org.eclipse.dataspaceconnector + jwt-spi + ${org.eclipse.dataspaceconnector.version} + + + org.eclipse.dataspaceconnector + jwt-core + ${org.eclipse.dataspaceconnector.version} + org.eclipse.dataspaceconnector observability-api From c58a91208f56083120c36149c3d596775e381f41 Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Mon, 5 Sep 2022 09:56:37 +0200 Subject: [PATCH 266/433] Introduce new snapshot version 0.1.1-SNAPSHOT (#387) --- CHANGELOG.md | 37 ++++++++++++++----- deployment/helm/edc-controlplane/Chart.yaml | 4 +- deployment/helm/edc-controlplane/README.md | 4 +- deployment/helm/edc-dataplane/Chart.yaml | 4 +- deployment/helm/edc-dataplane/README.md | 6 +-- .../edc-controlplane-base/pom.xml | 2 +- .../edc-controlplane-memory/pom.xml | 2 +- .../pom.xml | 2 +- .../edc-controlplane-postgresql/pom.xml | 2 +- edc-controlplane/pom.xml | 2 +- .../edc-dataplane-azure-vault/pom.xml | 2 +- edc-dataplane/edc-dataplane-base/pom.xml | 2 +- .../edc-dataplane-hashicorp-vault/pom.xml | 2 +- edc-dataplane/pom.xml | 2 +- .../business-partner-validation/pom.xml | 2 +- edc-extensions/cx-oauth2/pom.xml | 2 +- edc-extensions/data-encryption/pom.xml | 2 +- .../dataplane-selector-configuration/pom.xml | 2 +- edc-extensions/hashicorp-vault/pom.xml | 2 +- edc-extensions/pom.xml | 2 +- edc-extensions/postgresql-migration/pom.xml | 2 +- edc-tests/pom.xml | 2 +- pom.xml | 2 +- 23 files changed, 55 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5e4f843b..5a4429ff6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.1.1] - 2022-09-04 + +**Important Note**: Please consolidate the migration documentation before updating your connector. [documentation](/docs/migration/Version_0.0.x_0.1.x.md). + +### Added + +- Control-Plane Extension ([cx-oauth2](/edc-extensions/cx-oauth2/README.md)) + +### Changed + +- Introduced git submodule to import EDC dependencies (instead of snapshot- or milestone artifact) +- Helm Charts: TLS secret name is now configurable + +### Fixed + +- Connectors with Azure Vault extension are now starting again [link](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1892) + ## [0.1.0] - 2022-08-19 **Important Note**: Version 0.1.0 introduces multiple breaking changes. Before updating **always** consolidate the @@ -48,20 +65,20 @@ corresponding [documentation](/docs/migration/Version_0.0.x_0.1.x.md). ### Changed -- BusinessPartnerNumber constraint supports List structure -- Helm: Confidential EDC settings can be set using k8s secrets -- HashiCorp Vault API path configurable +- BusinessPartnerNumber constraint supports List structure +- Helm: Confidential EDC settings can be set using k8s secrets +- HashiCorp Vault API path configurable ## [0.0.4] - 2022-06-27 ### Added -- HashiCorp Vault Extension -- Control Plane with HashiCorp Vault and PostgreSQL support +- HashiCorp Vault Extension +- Control Plane with HashiCorp Vault and PostgreSQL support ### Changed -- Release Worklow now publishes Product EDC Extensions as Maven Artifacts +- Release Worklow now publishes Product EDC Extensions as Maven Artifacts ### Fixed @@ -70,8 +87,8 @@ corresponding [documentation](/docs/migration/Version_0.0.x_0.1.x.md). ### Removed -- CosmosDB Control Plane -- Control API Extension for all Control Planes +- CosmosDB Control Plane +- Control API Extension for all Control Planes ## [0.0.3] - 2022-05-23 @@ -79,7 +96,9 @@ corresponding [documentation](/docs/migration/Version_0.0.x_0.1.x.md). ## [0.0.1] - 2022-05-13 -[Unreleased]: https://github.com/catenax-ng/product-edc/compare/0.1.0...HEAD +[Unreleased]: https://github.com/catenax-ng/product-edc/compare/0.1.1...HEAD + +[0.1.1]: https://github.com/catenax-ng/product-edc/compare/0.1.0...0.1.1 [0.1.0]: https://github.com/catenax-ng/product-edc/compare/0.0.6...0.1.0 diff --git a/deployment/helm/edc-controlplane/Chart.yaml b/deployment/helm/edc-controlplane/Chart.yaml index 2a778fb17..d81685edc 100644 --- a/deployment/helm/edc-controlplane/Chart.yaml +++ b/deployment/helm/edc-controlplane/Chart.yaml @@ -5,6 +5,6 @@ description: >- EDC Control-Plane - The Eclipse DataSpaceConnector administration layer with responsibility of resource management and govern contracts and data transfers home: https://github.com/catenax-ng/product-edc/deployment/helm/edc-controlplane type: application -appVersion: "0.1.0" -version: 0.1.0 +appVersion: "0.1.1" +version: 0.1.1 maintainers: [] diff --git a/deployment/helm/edc-controlplane/README.md b/deployment/helm/edc-controlplane/README.md index 63a414d97..46e933039 100644 --- a/deployment/helm/edc-controlplane/README.md +++ b/deployment/helm/edc-controlplane/README.md @@ -1,6 +1,6 @@ # edc-controlplane -![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: 0.1.0](https://img.shields.io/badge/AppVersion-0.1.0-informational?style=flat-square) +![Version: 0.1.1](https://img.shields.io/badge/Version-0.1.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.1.1](https://img.shields.io/badge/AppVersion-0.1.1-informational?style=flat-square) EDC Control-Plane - The Eclipse DataSpaceConnector administration layer with responsibility of resource management and govern contracts and data transfers @@ -9,7 +9,7 @@ EDC Control-Plane - The Eclipse DataSpaceConnector administration layer with res ## TL;DR ```shell $ helm repo add catenax-ng-product-edc https://catenax-ng.github.io/product-edc -$ helm install my-release catenax-ng-product-edc/edc-controlplane --version 0.1.0 +$ helm install my-release catenax-ng-product-edc/edc-controlplane --version 0.1.1 ``` ## Values diff --git a/deployment/helm/edc-dataplane/Chart.yaml b/deployment/helm/edc-dataplane/Chart.yaml index 93da28bc0..e9d513ff4 100644 --- a/deployment/helm/edc-dataplane/Chart.yaml +++ b/deployment/helm/edc-dataplane/Chart.yaml @@ -5,6 +5,6 @@ description: >- EDC Data-Plane - The Eclipse DataSpaceConnector data layer with responsibility of transferring and receiving data streams home: https://github.com/catenax-ng/product-edc/deployment/helm/edc-dataplane type: application -appVersion: "0.1.0" -version: 0.1.0 +appVersion: "0.1.1" +version: 0.1.1 maintainers: [] diff --git a/deployment/helm/edc-dataplane/README.md b/deployment/helm/edc-dataplane/README.md index 079879c52..fbedf9dec 100644 --- a/deployment/helm/edc-dataplane/README.md +++ b/deployment/helm/edc-dataplane/README.md @@ -1,6 +1,6 @@ # edc-dataplane -![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: 0.1.0](https://img.shields.io/badge/AppVersion-0.1.0-informational?style=flat-square) +![Version: 0.1.1](https://img.shields.io/badge/Version-0.1.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.1.1](https://img.shields.io/badge/AppVersion-0.1.1-informational?style=flat-square) EDC Data-Plane - The Eclipse DataSpaceConnector data layer with responsibility of transferring and receiving data streams @@ -9,7 +9,7 @@ EDC Data-Plane - The Eclipse DataSpaceConnector data layer with responsibility o ## TL;DR ```shell $ helm repo add catenax-ng-product-edc https://catenax-ng.github.io/product-edc -$ helm install my-release catenax-ng-product-edc/edc-dataplane --version 0.1.0 +$ helm install my-release catenax-ng-product-edc/edc-dataplane --version 0.1.1 ``` ## Values @@ -23,7 +23,7 @@ $ helm install my-release catenax-ng-product-edc/edc-dataplane --version 0.1.0 | autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | | autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | | autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | -| configuration.properties | string | `"# edc.atomikos.checkpoint.interval=\n# edc.atomikos.directory=\n# edc.atomikos.logging=\n# edc.atomikos.threaded2pc=\n# edc.atomikos.timeout=\n# edc.aws.access.key=\n# edc.aws.provision.retry.retries.max=\n# edc.aws.provision.role.duration.session.max=\n# edc.aws.secret.access.key=\n# edc.blobstore.endpoint=\n# edc.dataplane.token.validation.endpoint=\n# edc.core.retry.backoff.max=\n# edc.core.retry.backoff.min=\n# edc.core.retry.retries.max=\n# edc.core.system.health.check.liveness-period=\n# edc.core.system.health.check.readiness-period=\n# edc.core.system.health.check.startup-period=\n# edc.core.system.health.check.threadpool-size=\n# edc.dataplane.queue.capacity=\n# edc.dataplane.wait=\n# edc.dataplane.workers=\n# edc.datasource.asset.name=\"default\"\n# edc.datasource.contractdefinition.name=\"default\"\n# edc.datasource.contractnegotiation.name=\"default\"\n# edc.datasource.policy.name=\"default\"\n# edc.datasource.transferprocess.name=\"default\"\n# edc.datasource.default.pool.maxIdleConnections=\n# edc.datasource.default.pool.maxTotalConnections=\n# edc.datasource.default.pool.minIdleConnections=\n# edc.datasource.default.pool.testConnectionOnBorrow=\n# edc.datasource.default.pool.testConnectionOnCreate=\n# edc.datasource.default.pool.testConnectionOnReturn=\n# edc.datasource.default.pool.testConnectionWhileIdle=\n# edc.datasource.default.pool.testQuery=\n# edc.datasource.default.url=\n# edc.datasource.default.user=\n# edc.datasource.default.password=\n# edc.dpf.selector.url=\n# edc.events.topic.endpoint=\n# edc.events.topic.name=\n# edc.fs.config=\n# edc.hostname=\n# edc.identity.did.url=\n# edc.ids.catalog.id=\n# edc.ids.curator=\n# edc.ids.description=\n# edc.ids.endpoint=\n# edc.ids.id=\n# edc.ids.maintainer=\n# edc.ids.security.profile=\n# edc.ids.title=\n# edc.ids.validation.referringconnector=\n# edc.ion.crawler.did-type=\n# edc.ion.crawler.interval-minutes=\n# edc.ion.crawler.ion.url=\n# edc.metrics.enabled=\n# edc.metrics.executor.enabled=\n# edc.metrics.jersey.enabled=\n# edc.metrics.jetty.enabled=\n# edc.metrics.okhttp.enabled=\n# edc.metrics.system.enabled=\n# edc.negotiation.consumer.state-machine.batch-size=\n# edc.negotiation.provider.state-machine.batch-size=\n# edc.oauth.client.id=\n# edc.oauth.private.key.alias=\n# edc.oauth.provider.audience=\n# edc.oauth.provider.jwks.refresh=\n# edc.oauth.provider.jwks.url=\n# edc.oauth.public.key.alias=\n# edc.oauth.token.url=\n# edc.oauth.validation.nbf.leeway=\n# edc.receiver.http.auth-code=\n# edc.receiver.http.auth-key=\n# edc.receiver.http.endpoint=\n# edc.transfer.functions.check.endpoint=\n# edc.transfer.functions.enabled.protocols=\n# edc.transfer.functions.transfer.endpoint=\n# edc.transfer-process-store.database.name=\n# edc.transfer.state-machine.batch-size=\n# edc.vault=\n# edc.vault.certificate=\n# edc.vault.clientid=\n# edc.vault.clientsecret=\n# edc.vault.name=\n# edc.vault.tenantid=\n# edc.vault.hashicorp.url=\n# edc.vault.hashicorp.token=\n# edc.vault.hashicorp.timeout.seconds=\n# edc.webdid.doh.url=\n# edc.web.rest.cors.enabled=\n# edc.web.rest.cors.headers=\n# edc.web.rest.cors.methods=\n# edc.web.rest.cors.origins="` | EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) | +| configuration.properties | string | `"# edc.atomikos.checkpoint.interval=\n# edc.atomikos.directory=\n# edc.atomikos.logging=\n# edc.atomikos.threaded2pc=\n# edc.atomikos.timeout=\n# edc.aws.access.key=\n# edc.aws.provision.retry.retries.max=\n# edc.aws.provision.role.duration.session.max=\n# edc.aws.secret.access.key=\n# edc.blobstore.endpoint=\n# edc.dataplane.token.validation.endpoint=\n# edc.core.retry.backoff.max=\n# edc.core.retry.backoff.min=\n# edc.core.retry.retries.max=\n# edc.core.system.health.check.liveness-period=\n# edc.core.system.health.check.readiness-period=\n# edc.core.system.health.check.startup-period=\n# edc.core.system.health.check.threadpool-size=\n# edc.dataplane.queue.capacity=\n# edc.dataplane.wait=\n# edc.dataplane.workers=\n# edc.datasource.asset.name=\"default\"\n# edc.datasource.contractdefinition.name=\"default\"\n# edc.datasource.contractnegotiation.name=\"default\"\n# edc.datasource.policy.name=\"default\"\n# edc.datasource.transferprocess.name=\"default\"\n# edc.datasource.default.pool.maxIdleConnections=\n# edc.datasource.default.pool.maxTotalConnections=\n# edc.datasource.default.pool.minIdleConnections=\n# edc.datasource.default.pool.testConnectionOnBorrow=\n# edc.datasource.default.pool.testConnectionOnCreate=\n# edc.datasource.default.pool.testConnectionOnReturn=\n# edc.datasource.default.pool.testConnectionWhileIdle=\n# edc.datasource.default.pool.testQuery=\n# edc.datasource.default.url=\n# edc.datasource.default.user=\n# edc.datasource.default.password=\n# edc.dpf.selector.url=\n# edc.events.topic.endpoint=\n# edc.events.topic.name=\n# edc.fs.config=\n# edc.hostname=\n# edc.identity.did.url=\n# edc.ids.catalog.id=\n# edc.ids.curator=\n# edc.ids.description=\n# edc.ids.endpoint=\n# edc.ids.endpoint.audience=\n# edc.ids.id=\n# edc.ids.maintainer=\n# edc.ids.security.profile=\n# edc.ids.title=\n# edc.ids.validation.referringconnector=\n# edc.ion.crawler.did-type=\n# edc.ion.crawler.interval-minutes=\n# edc.ion.crawler.ion.url=\n# edc.metrics.enabled=\n# edc.metrics.executor.enabled=\n# edc.metrics.jersey.enabled=\n# edc.metrics.jetty.enabled=\n# edc.metrics.okhttp.enabled=\n# edc.metrics.system.enabled=\n# edc.negotiation.consumer.state-machine.batch-size=\n# edc.negotiation.provider.state-machine.batch-size=\n# edc.oauth.client.id=\n# edc.oauth.private.key.alias=\n# edc.oauth.provider.jwks.refresh=\n# edc.oauth.provider.jwks.url=\n# edc.oauth.public.key.alias=\n# edc.oauth.token.url=\n# edc.oauth.validation.nbf.leeway=\n# edc.receiver.http.auth-code=\n# edc.receiver.http.auth-key=\n# edc.receiver.http.endpoint=\n# edc.transfer.functions.check.endpoint=\n# edc.transfer.functions.enabled.protocols=\n# edc.transfer.functions.transfer.endpoint=\n# edc.transfer-process-store.database.name=\n# edc.transfer.state-machine.batch-size=\n# edc.vault=\n# edc.vault.certificate=\n# edc.vault.clientid=\n# edc.vault.clientsecret=\n# edc.vault.name=\n# edc.vault.tenantid=\n# edc.vault.hashicorp.url=\n# edc.vault.hashicorp.token=\n# edc.vault.hashicorp.timeout.seconds=\n# edc.webdid.doh.url=\n# edc.web.rest.cors.enabled=\n# edc.web.rest.cors.headers=\n# edc.web.rest.cors.methods=\n# edc.web.rest.cors.origins="` | EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) | | edc.endpoints.control.path | string | `"/api/dataplane/control"` | The path mapping the "control" api is going to be exposed by | | edc.endpoints.control.port | string | `"9999"` | The network port, which the "control" api is going to be exposed by the container, pod and service | | edc.endpoints.default.path | string | `"/api"` | The path mapping the "default" api is going to be exposed by | diff --git a/edc-controlplane/edc-controlplane-base/pom.xml b/edc-controlplane/edc-controlplane-base/pom.xml index 191f66668..0d4fe04f2 100644 --- a/edc-controlplane/edc-controlplane-base/pom.xml +++ b/edc-controlplane/edc-controlplane-base/pom.xml @@ -18,7 +18,7 @@ edc-controlplane net.catenax.edc - 0.1.0-SNAPSHOT + 0.1.1-SNAPSHOT 4.0.0 diff --git a/edc-controlplane/edc-controlplane-memory/pom.xml b/edc-controlplane/edc-controlplane-memory/pom.xml index a21118ec9..8d3fb82da 100644 --- a/edc-controlplane/edc-controlplane-memory/pom.xml +++ b/edc-controlplane/edc-controlplane-memory/pom.xml @@ -16,7 +16,7 @@ net.catenax.edc edc-controlplane - 0.1.0-SNAPSHOT + 0.1.1-SNAPSHOT 4.0.0 diff --git a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/pom.xml b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/pom.xml index b270201c5..fdb086f50 100644 --- a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/pom.xml +++ b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/pom.xml @@ -17,7 +17,7 @@ net.catenax.edc edc-controlplane - 0.1.0-SNAPSHOT + 0.1.1-SNAPSHOT 4.0.0 diff --git a/edc-controlplane/edc-controlplane-postgresql/pom.xml b/edc-controlplane/edc-controlplane-postgresql/pom.xml index 7c5433c41..7f24329a0 100644 --- a/edc-controlplane/edc-controlplane-postgresql/pom.xml +++ b/edc-controlplane/edc-controlplane-postgresql/pom.xml @@ -17,7 +17,7 @@ net.catenax.edc edc-controlplane - 0.1.0-SNAPSHOT + 0.1.1-SNAPSHOT 4.0.0 diff --git a/edc-controlplane/pom.xml b/edc-controlplane/pom.xml index 7d1def11b..02be5b5ff 100644 --- a/edc-controlplane/pom.xml +++ b/edc-controlplane/pom.xml @@ -17,7 +17,7 @@ net.catenax.edc product-edc-parent - 0.1.0-SNAPSHOT + 0.1.1-SNAPSHOT 4.0.0 diff --git a/edc-dataplane/edc-dataplane-azure-vault/pom.xml b/edc-dataplane/edc-dataplane-azure-vault/pom.xml index 6a653121b..03cf81d13 100644 --- a/edc-dataplane/edc-dataplane-azure-vault/pom.xml +++ b/edc-dataplane/edc-dataplane-azure-vault/pom.xml @@ -17,7 +17,7 @@ net.catenax.edc edc-dataplane - 0.1.0-SNAPSHOT + 0.1.1-SNAPSHOT 4.0.0 diff --git a/edc-dataplane/edc-dataplane-base/pom.xml b/edc-dataplane/edc-dataplane-base/pom.xml index e00a82217..1d945c123 100644 --- a/edc-dataplane/edc-dataplane-base/pom.xml +++ b/edc-dataplane/edc-dataplane-base/pom.xml @@ -18,7 +18,7 @@ edc-dataplane net.catenax.edc - 0.1.0-SNAPSHOT + 0.1.1-SNAPSHOT 4.0.0 diff --git a/edc-dataplane/edc-dataplane-hashicorp-vault/pom.xml b/edc-dataplane/edc-dataplane-hashicorp-vault/pom.xml index d1a27b263..f2544fe5e 100644 --- a/edc-dataplane/edc-dataplane-hashicorp-vault/pom.xml +++ b/edc-dataplane/edc-dataplane-hashicorp-vault/pom.xml @@ -17,7 +17,7 @@ net.catenax.edc edc-dataplane - 0.1.0-SNAPSHOT + 0.1.1-SNAPSHOT 4.0.0 diff --git a/edc-dataplane/pom.xml b/edc-dataplane/pom.xml index ef0697703..56f293316 100644 --- a/edc-dataplane/pom.xml +++ b/edc-dataplane/pom.xml @@ -18,7 +18,7 @@ net.catenax.edc product-edc-parent - 0.1.0-SNAPSHOT + 0.1.1-SNAPSHOT edc-dataplane diff --git a/edc-extensions/business-partner-validation/pom.xml b/edc-extensions/business-partner-validation/pom.xml index 81c9871a3..cf518834e 100644 --- a/edc-extensions/business-partner-validation/pom.xml +++ b/edc-extensions/business-partner-validation/pom.xml @@ -17,7 +17,7 @@ net.catenax.edc.extensions edc-extensions - 0.1.0-SNAPSHOT + 0.1.1-SNAPSHOT 4.0.0 diff --git a/edc-extensions/cx-oauth2/pom.xml b/edc-extensions/cx-oauth2/pom.xml index 3758e758e..6f23ea1de 100644 --- a/edc-extensions/cx-oauth2/pom.xml +++ b/edc-extensions/cx-oauth2/pom.xml @@ -18,7 +18,7 @@ edc-extensions net.catenax.edc.extensions - 0.1.0-SNAPSHOT + 0.1.1-SNAPSHOT 4.0.0 diff --git a/edc-extensions/data-encryption/pom.xml b/edc-extensions/data-encryption/pom.xml index b2a47b885..79450fe43 100644 --- a/edc-extensions/data-encryption/pom.xml +++ b/edc-extensions/data-encryption/pom.xml @@ -18,7 +18,7 @@ edc-extensions net.catenax.edc.extensions - 0.1.0-SNAPSHOT + 0.1.1-SNAPSHOT 4.0.0 diff --git a/edc-extensions/dataplane-selector-configuration/pom.xml b/edc-extensions/dataplane-selector-configuration/pom.xml index 4344be19c..d35d99172 100644 --- a/edc-extensions/dataplane-selector-configuration/pom.xml +++ b/edc-extensions/dataplane-selector-configuration/pom.xml @@ -18,7 +18,7 @@ edc-extensions net.catenax.edc.extensions - 0.1.0-SNAPSHOT + 0.1.1-SNAPSHOT 4.0.0 diff --git a/edc-extensions/hashicorp-vault/pom.xml b/edc-extensions/hashicorp-vault/pom.xml index 762025560..10cf26eff 100644 --- a/edc-extensions/hashicorp-vault/pom.xml +++ b/edc-extensions/hashicorp-vault/pom.xml @@ -17,7 +17,7 @@ net.catenax.edc.extensions edc-extensions - 0.1.0-SNAPSHOT + 0.1.1-SNAPSHOT 4.0.0 diff --git a/edc-extensions/pom.xml b/edc-extensions/pom.xml index 8675f863f..43ef65961 100644 --- a/edc-extensions/pom.xml +++ b/edc-extensions/pom.xml @@ -17,7 +17,7 @@ net.catenax.edc product-edc-parent - 0.1.0-SNAPSHOT + 0.1.1-SNAPSHOT 4.0.0 diff --git a/edc-extensions/postgresql-migration/pom.xml b/edc-extensions/postgresql-migration/pom.xml index c87790a55..ee70e8201 100644 --- a/edc-extensions/postgresql-migration/pom.xml +++ b/edc-extensions/postgresql-migration/pom.xml @@ -17,7 +17,7 @@ edc-extensions net.catenax.edc.extensions - 0.1.0-SNAPSHOT + 0.1.1-SNAPSHOT 4.0.0 diff --git a/edc-tests/pom.xml b/edc-tests/pom.xml index bbfbf658e..b4fd025bf 100644 --- a/edc-tests/pom.xml +++ b/edc-tests/pom.xml @@ -19,7 +19,7 @@ net.catenax.edc product-edc-parent - 0.1.0-SNAPSHOT + 0.1.1-SNAPSHOT net.catenax.edc.tests diff --git a/pom.xml b/pom.xml index 58245d3ae..3ba603c31 100644 --- a/pom.xml +++ b/pom.xml @@ -18,7 +18,7 @@ net.catenax.edc product-edc-parent - 0.1.0-SNAPSHOT + 0.1.1-SNAPSHOT pom product-edc From 6eb23127fd49b97929c77153ab034a33264389f4 Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Mon, 5 Sep 2022 12:08:08 +0200 Subject: [PATCH 267/433] fix link in changelog (#388) --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a4429ff6..4b1b21f7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.1.1] - 2022-09-04 -**Important Note**: Please consolidate the migration documentation before updating your connector. [documentation](/docs/migration/Version_0.0.x_0.1.x.md). +**Important Note**: Please consolidate the migration documentation before updating your connector. [documentation](/docs/migration/Version_0.1.0_0.1.1.md). ### Added From 6d1bb3906e76f0000f9d3619278300b6b9836240 Mon Sep 17 00:00:00 2001 From: marcingajek-zf Date: Mon, 5 Sep 2022 12:16:40 +0200 Subject: [PATCH 268/433] control-plane-adapter extension - error handling (cherry picked from commit 7661f069e4bb7664d0230109ebd005a01df2041c) --- .../edc/cp/adapter/ApiAdapterExtension.java | 17 +-- .../edc/cp/adapter/HttpController.java | 26 +++- .../edc/cp/adapter/dto/ProcessData.java | 9 +- .../exception/ContractOfferNotAvailable.java | 7 -- .../DataReferenceAccessException.java | 7 -- .../exception/ExternalRequestException.java | 11 ++ .../exception/ResourceNotFoundException.java | 8 +- .../edc/cp/adapter/messaging/Channel.java | 3 +- .../messaging/InMemoryMessageService.java | 12 +- .../edc/cp/adapter/messaging/Message.java | 16 ++- .../ContractNegotiationHandler.java | 8 +- .../contractnotification/ContractInfo.java | 32 +++++ .../ContractNotificationHandler.java} | 78 ++++++++++-- .../DataStore.java | 8 +- .../DataStoreLock.java | 2 +- .../InMemoryDataStore.java | 26 +++- .../adapter/service/ErrorResultService.java | 50 ++++++++ .../edc/cp/adapter/service/ResultService.java | 28 ++--- .../ContractNotificationHandlerTest.java} | 116 +++++++++++++----- .../cp/adapter/service/ResultServiceTest.java | 20 +-- 20 files changed, 365 insertions(+), 119 deletions(-) delete mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ContractOfferNotAvailable.java delete mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/DataReferenceAccessException.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ExternalRequestException.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractInfo.java rename edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/{contractconfirmation/ContractConfirmationHandler.java => contractnotification/ContractNotificationHandler.java} (62%) rename edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/{contractconfirmation => contractnotification}/DataStore.java (57%) rename edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/{contractconfirmation => contractnotification}/DataStoreLock.java (93%) rename edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/{contractconfirmation => contractnotification}/InMemoryDataStore.java (60%) create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/service/ErrorResultService.java rename edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/{contractconfirmation/ContractConfirmationHandlerTest.java => contractnotification/ContractNotificationHandlerTest.java} (61%) diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/ApiAdapterExtension.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/ApiAdapterExtension.java index 4b479796f..2fd77bd9a 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/ApiAdapterExtension.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/ApiAdapterExtension.java @@ -6,12 +6,13 @@ import net.catenax.edc.cp.adapter.messaging.Channel; import net.catenax.edc.cp.adapter.messaging.InMemoryMessageService; import net.catenax.edc.cp.adapter.messaging.ListenerService; -import net.catenax.edc.cp.adapter.process.contractconfirmation.ContractConfirmationHandler; -import net.catenax.edc.cp.adapter.process.contractconfirmation.DataStoreLock; -import net.catenax.edc.cp.adapter.process.contractconfirmation.InMemoryDataStore; +import net.catenax.edc.cp.adapter.process.contractnotification.ContractNotificationHandler; +import net.catenax.edc.cp.adapter.process.contractnotification.DataStoreLock; +import net.catenax.edc.cp.adapter.process.contractnotification.InMemoryDataStore; import net.catenax.edc.cp.adapter.process.contractdatastore.InMemoryContractDataStore; import net.catenax.edc.cp.adapter.process.contractnegotiation.ContractNegotiationHandler; import net.catenax.edc.cp.adapter.process.datareference.DataReferenceHandler; +import net.catenax.edc.cp.adapter.service.ErrorResultService; import net.catenax.edc.cp.adapter.service.ResultService; import org.eclipse.dataspaceconnector.api.datamanagement.catalog.service.CatalogServiceImpl; import org.eclipse.dataspaceconnector.api.datamanagement.contractnegotiation.service.ContractNegotiationService; @@ -61,6 +62,8 @@ public void initialize(ServiceExtensionContext context) { InMemoryMessageService messageService = new InMemoryMessageService(monitor, listenerService); ResultService resultService = new ResultService(); listenerService.addListener(Channel.RESULT, resultService); + ErrorResultService errorResultService = new ErrorResultService(monitor, messageService); + listenerService.addListener(Channel.DLQ, errorResultService); initHttpController(monitor, messageService, resultService); initContractNegotiationHandler( @@ -102,8 +105,8 @@ private void initContractConfirmationHandler( InMemoryMessageService messageService, ListenerService listenerService) { - ContractConfirmationHandler contractConfirmationHandler = - new ContractConfirmationHandler( + ContractNotificationHandler contractNotificationHandler = + new ContractNotificationHandler( monitor, messageService, new InMemoryDataStore(new DataStoreLock()), @@ -112,9 +115,9 @@ private void initContractConfirmationHandler( transferProcessStore, transferProcessManager, getTransactionContext(monitor)), new InMemoryContractDataStore()); - listenerService.addListener(Channel.CONTRACT_CONFIRMATION, contractConfirmationHandler); + listenerService.addListener(Channel.CONTRACT_CONFIRMATION, contractNotificationHandler); if (nonNull(negotiationObservable)) { - negotiationObservable.registerListener(contractConfirmationHandler); + negotiationObservable.registerListener(contractNotificationHandler); } } diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/HttpController.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/HttpController.java index 92027c823..567043a88 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/HttpController.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/HttpController.java @@ -13,6 +13,8 @@ import net.catenax.edc.cp.adapter.service.ResultService; import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import java.util.Objects; + @Consumes({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON}) @Path("/adapter/asset") @@ -36,7 +38,29 @@ public Response getAssetSynchronous( String traceId = initiateProcess(assetId, providerUrl); try { - return Response.status(Response.Status.OK).entity(resultService.pull(traceId)).build(); + ProcessData processData = resultService.pull(traceId); + + if (Objects.isNull(processData)) { + return Response.status(Response.Status.NOT_FOUND) + .entity(Response.Status.NOT_FOUND.getReasonPhrase()) + .build(); + } + + if (Objects.nonNull(processData.getErrorStatus())) { + return Response.status(processData.getErrorStatus()) + .entity(processData.getErrorMessage()) + .build(); + } + + if (Objects.nonNull(processData.getEndpointDataReference())) { + return Response.status(Response.Status.OK) + .entity(processData.getEndpointDataReference()) + .build(); + } + + return Response.status(Response.Status.REQUEST_TIMEOUT) + .entity(Response.Status.REQUEST_TIMEOUT.getReasonPhrase()) + .build(); } catch (InterruptedException e) { monitor.severe("InterruptedException", e); return Response.status(Response.Status.NOT_FOUND).build(); diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/dto/ProcessData.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/dto/ProcessData.java index 0465e20a6..d193474f0 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/dto/ProcessData.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/dto/ProcessData.java @@ -2,6 +2,7 @@ import static java.lang.System.currentTimeMillis; +import jakarta.ws.rs.core.Response; import lombok.Getter; import lombok.Setter; import lombok.ToString; @@ -10,22 +11,22 @@ @Getter @ToString public class ProcessData { + private final long timestamp = currentTimeMillis(); + /** request data */ private final String assetId; - private final String provider; private String contractOfferId; - private final long timestamp = currentTimeMillis(); - /** contract data * */ @Setter private String contractNegotiationId; - @Setter private String contractAgreementId; @Setter private boolean isContractConfirmed = false; /** result/response data * */ @Setter private EndpointDataReference endpointDataReference; + @Setter private String errorMessage; + @Setter private Response.Status errorStatus; public ProcessData(String assetId, String provider) { this.assetId = assetId; diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ContractOfferNotAvailable.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ContractOfferNotAvailable.java deleted file mode 100644 index 1f0cb89e9..000000000 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ContractOfferNotAvailable.java +++ /dev/null @@ -1,7 +0,0 @@ -package net.catenax.edc.cp.adapter.exception; - -public class ContractOfferNotAvailable extends RuntimeException { - public ContractOfferNotAvailable(Throwable cause) { - super(cause); - } -} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/DataReferenceAccessException.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/DataReferenceAccessException.java deleted file mode 100644 index 06dcce70a..000000000 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/DataReferenceAccessException.java +++ /dev/null @@ -1,7 +0,0 @@ -package net.catenax.edc.cp.adapter.exception; - -public class DataReferenceAccessException extends RuntimeException { - public DataReferenceAccessException(final String id) { - super(String.format("Data reference initial request failed! AssetId: %s", id)); - } -} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ExternalRequestException.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ExternalRequestException.java new file mode 100644 index 000000000..bdb2105c3 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ExternalRequestException.java @@ -0,0 +1,11 @@ +package net.catenax.edc.cp.adapter.exception; + +public class ExternalRequestException extends RuntimeException { + public ExternalRequestException(String message) { + super(message); + } + + public ExternalRequestException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ResourceNotFoundException.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ResourceNotFoundException.java index b9c419de0..5851b4c66 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ResourceNotFoundException.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ResourceNotFoundException.java @@ -1,7 +1,11 @@ package net.catenax.edc.cp.adapter.exception; public class ResourceNotFoundException extends RuntimeException { - public ResourceNotFoundException(final String id) { - super(String.format("Resource not found for id %s", id)); + public ResourceNotFoundException(String message) { + super(message); + } + + public ResourceNotFoundException(String message, Exception e) { + super(message, e); } } diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/Channel.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/Channel.java index bcd56a5f0..5c2b5ac36 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/Channel.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/Channel.java @@ -4,5 +4,6 @@ public enum Channel { INITIAL, CONTRACT_CONFIRMATION, DATA_REFERENCE, - RESULT + RESULT, + DLQ } diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/InMemoryMessageService.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/InMemoryMessageService.java index 33e8f849b..ea4e58f0f 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/InMemoryMessageService.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/InMemoryMessageService.java @@ -30,18 +30,24 @@ public void send(Channel name, Message message) { protected boolean run(Channel name, Message message) { try { listenerService.getListener(name).process(message); - message.succeeded(); + message.clearErrors(); return true; } catch (Exception e) { monitor.warning(String.format("[%s] Message processing error.", message.getTraceId()), e); if (!message.canRetry()) { monitor.warning(String.format("[%s] Message reached retry limit!", message.getTraceId())); - // TODO move to DLQ + sendMessageToDlq(message, e); return true; } long delayTime = message.unsucceeded(); - executorService.schedule(() -> run(name, message), delayTime, TimeUnit.MILLISECONDS); + executorService.schedule(() -> send(name, message), delayTime, TimeUnit.MILLISECONDS); return false; } } + + private void sendMessageToDlq(Message message, Exception finalException) { + message.clearErrors(); + message.setFinalException(finalException); + run(Channel.DLQ, message); + } } diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/Message.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/Message.java index cc7a08fe8..39db83f59 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/Message.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/Message.java @@ -10,15 +10,16 @@ public class Message { @Getter private final ProcessData payload; private final AtomicInteger errorNumber = new AtomicInteger(); private int retryLimit = 3; // TODO configure + @Getter private Exception finalException; - public Message(String id, ProcessData payload, int retryLimit) { - this(id, payload); + public Message(String traceId, ProcessData payload, int retryLimit) { + this(traceId, payload); this.retryLimit = retryLimit; } - public Message(String id, ProcessData payload) { + public Message(String traceId, ProcessData payload) { this.payload = payload; - this.traceId = id; + this.traceId = traceId; } public Message(ProcessData payload, int retryLimit) { @@ -36,7 +37,7 @@ protected long unsucceeded() { return getDelayTime(); } - protected void succeeded() { + protected void clearErrors() { errorNumber.set(0); } @@ -44,7 +45,10 @@ protected boolean canRetry() { return errorNumber.get() < retryLimit; } - // TODO external configuration? extract to other class implements BackoffPolicy - strategy + protected void setFinalException(Exception e) { + this.finalException = e; + } + private int getDelayTime() { return errorNumber.get() < 5 ? errorNumber.get() * 750 diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnegotiation/ContractNegotiationHandler.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnegotiation/ContractNegotiationHandler.java index c05a81787..3a09214e8 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnegotiation/ContractNegotiationHandler.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnegotiation/ContractNegotiationHandler.java @@ -7,7 +7,7 @@ import java.util.concurrent.ExecutionException; import lombok.RequiredArgsConstructor; import net.catenax.edc.cp.adapter.dto.ProcessData; -import net.catenax.edc.cp.adapter.exception.ContractOfferNotAvailable; +import net.catenax.edc.cp.adapter.exception.ExternalRequestException; import net.catenax.edc.cp.adapter.exception.ResourceNotFoundException; import net.catenax.edc.cp.adapter.messaging.Channel; import net.catenax.edc.cp.adapter.messaging.Listener; @@ -33,7 +33,9 @@ public class ContractNegotiationHandler implements Listener { @Override public void process(Message message) { - monitor.debug("RequestHandler: input request: " + message.getPayload()); + monitor.info(String.format("[%s] RequestHandler: input request: [%s]", + message.getTraceId(), + message.getPayload())); ProcessData processData = message.getPayload(); ContractAgreementData contractData = @@ -79,7 +81,7 @@ private Catalog getCatalog(String providerUrl) { try { return catalogService.getByProviderUrl(providerUrl).get(); } catch (InterruptedException | ExecutionException e) { - throw new ContractOfferNotAvailable(e); + throw new ExternalRequestException("Could not retrieve contract offer.", e); } } diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractInfo.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractInfo.java new file mode 100644 index 000000000..bd9abd3d2 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractInfo.java @@ -0,0 +1,32 @@ +package net.catenax.edc.cp.adapter.process.contractnotification; + +import lombok.Getter; + +public class ContractInfo { + @Getter + private String contractAgreementId; + private ContractState contractState; + + public ContractInfo(String contractAgreementId, ContractState contractState) { + this.contractAgreementId = contractAgreementId; + this.contractState = contractState; + } + + public ContractInfo(ContractState contractState) { + this.contractState = contractState; + } + + public boolean isConfirmed() { + return ContractState.CONFIRMED.equals(contractState); + } + public boolean isDeclined() { + return ContractState.DECLINED.equals(contractState); + } + public boolean isError() { + return ContractState.ERROR.equals(contractState); + } + + protected enum ContractState { + CONFIRMED, DECLINED, ERROR; + } +} \ No newline at end of file diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/ContractConfirmationHandler.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractNotificationHandler.java similarity index 62% rename from edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/ContractConfirmationHandler.java rename to edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractNotificationHandler.java index fd04f6074..470e414e8 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/ContractConfirmationHandler.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractNotificationHandler.java @@ -1,10 +1,11 @@ -package net.catenax.edc.cp.adapter.process.contractconfirmation; +package net.catenax.edc.cp.adapter.process.contractnotification; import static java.util.Objects.isNull; import java.util.Objects; + import lombok.RequiredArgsConstructor; -import net.catenax.edc.cp.adapter.exception.DataReferenceAccessException; +import net.catenax.edc.cp.adapter.exception.ExternalRequestException; import net.catenax.edc.cp.adapter.messaging.Channel; import net.catenax.edc.cp.adapter.messaging.Listener; import net.catenax.edc.cp.adapter.messaging.Message; @@ -21,8 +22,12 @@ import org.eclipse.dataspaceconnector.spi.types.domain.transfer.DataRequest; import org.eclipse.dataspaceconnector.spi.types.domain.transfer.TransferType; +import static jakarta.ws.rs.core.Response.Status; + @RequiredArgsConstructor -public class ContractConfirmationHandler implements Listener, ContractNegotiationListener { +public class ContractNotificationHandler implements Listener, ContractNegotiationListener { + public static final String CONTRACT_DECLINED_MESSAGE = "Contract for asset is declined."; + public static final String CONTRACT_ERROR_MESSAGE = "Contract Error for asset."; private final Monitor monitor; private final MessageService messageService; private final DataStore dataStore; @@ -43,29 +48,33 @@ public void process(Message message) { ContractNegotiation contractNegotiation = contractNegotiationService.findbyId(contractNegotiationId); - if (Objects.nonNull(contractNegotiation) - && contractNegotiation.getState() == ContractNegotiationStates.CONFIRMED.code()) { - message - .getPayload() + if (isContractConfirmed(contractNegotiation)) { + message.getPayload() .setContractAgreementId(contractNegotiation.getContractAgreement().getId()); initiateDataTransfer(message); return; } - String confirmedContractAgreementId = dataStore.getConfirmedContract(contractNegotiationId); - if (isNull(confirmedContractAgreementId)) { + ContractInfo contractInfo = dataStore.getContractInfo(contractNegotiationId); + if (isNull(contractInfo)) { dataStore.storeMessage(message); return; } - message.getPayload().setContractAgreementId(confirmedContractAgreementId); - initiateDataTransfer(message); + if (contractInfo.isConfirmed()) { + message.getPayload().setContractAgreementId(contractInfo.getContractAgreementId()); + initiateDataTransfer(message); + } else { + sendErrorResult(message, Status.BAD_GATEWAY, contractInfo.isDeclined() + ? CONTRACT_DECLINED_MESSAGE + : CONTRACT_ERROR_MESSAGE); + } dataStore.removeConfirmedContract(contractNegotiationId); } @Override public void preConfirmed(ContractNegotiation negotiation) { - monitor.info("ContractConfirmationHandler: received 'ContractNegotiation' event"); + monitor.info("ContractConfirmationHandler: received ContractConfirmation event"); String contractNegotiationId = negotiation.getId(); String contractAgreementId = negotiation.getContractAgreement().getId(); Message message = dataStore.getMessage(contractNegotiationId); @@ -82,6 +91,32 @@ public void preConfirmed(ContractNegotiation negotiation) { dataStore.removeMessage(contractNegotiationId); } + @Override + public void preDeclined(ContractNegotiation negotiation) { + monitor.info("ContractConfirmationHandler: received ContractDeclined event"); + String contractNegotiationId = negotiation.getId(); + Message message = dataStore.getMessage(contractNegotiationId); + if (isNull(message)) { + dataStore.storeDeclinedContract(contractNegotiationId); + return; + } + sendErrorResult(message, Status.BAD_GATEWAY, CONTRACT_DECLINED_MESSAGE); + dataStore.removeMessage(contractNegotiationId); + } + + @Override + public void preError(ContractNegotiation negotiation) { + monitor.info("ContractConfirmationHandler: received ContractError event"); + String contractNegotiationId = negotiation.getId(); + Message message = dataStore.getMessage(contractNegotiationId); + if (isNull(message)) { + dataStore.storeErrorContract(contractNegotiationId); + return; + } + sendErrorResult(message, Status.BAD_GATEWAY, CONTRACT_ERROR_MESSAGE); + dataStore.removeMessage(contractNegotiationId); + } + private void initiateDataTransfer(Message message) { sendInitiationRequest(message); message.getPayload().setContractConfirmed(true); @@ -118,7 +153,24 @@ private void sendInitiationRequest(Message message) { String.format( "[%s] ContractConfirmationHandler: transfer init - end", message.getTraceId())); if (result.failed()) { - throw new DataReferenceAccessException(message.getPayload().getAssetId()); + throwDataRefRequestException(message); } } + + private void throwDataRefRequestException(Message message) { + throw new ExternalRequestException( + String.format("Data reference initial request failed! AssetId: %s", + message.getPayload().getAssetId())); + } + + private void sendErrorResult(Message message, Status status, String errorMessage) { + message.getPayload().setErrorMessage(errorMessage); + message.getPayload().setErrorStatus(status); + messageService.send(Channel.RESULT, message); + } + + private boolean isContractConfirmed(ContractNegotiation contractNegotiation) { + return Objects.nonNull(contractNegotiation) + && contractNegotiation.getState() == ContractNegotiationStates.CONFIRMED.code(); + } } diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/DataStore.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/DataStore.java similarity index 57% rename from edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/DataStore.java rename to edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/DataStore.java index 3247a5464..6054e0975 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/DataStore.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/DataStore.java @@ -1,11 +1,15 @@ -package net.catenax.edc.cp.adapter.process.contractconfirmation; +package net.catenax.edc.cp.adapter.process.contractnotification; import net.catenax.edc.cp.adapter.messaging.Message; public interface DataStore { void storeConfirmedContract(String contractNegotiationId, String contractAgreementId); - String getConfirmedContract(String contractNegotiationId); + void storeDeclinedContract(String contractNegotiationId); + + void storeErrorContract(String contractNegotiationId); + + ContractInfo getContractInfo(String contractNegotiationId); void removeConfirmedContract(String key); diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/DataStoreLock.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/DataStoreLock.java similarity index 93% rename from edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/DataStoreLock.java rename to edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/DataStoreLock.java index da60c7877..b2aeed9c0 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/DataStoreLock.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/DataStoreLock.java @@ -1,4 +1,4 @@ -package net.catenax.edc.cp.adapter.process.contractconfirmation; +package net.catenax.edc.cp.adapter.process.contractnotification; import java.util.HashMap; import java.util.Map; diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/InMemoryDataStore.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/InMemoryDataStore.java similarity index 60% rename from edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/InMemoryDataStore.java rename to edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/InMemoryDataStore.java index 1f2065919..31cf45c89 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractconfirmation/InMemoryDataStore.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/InMemoryDataStore.java @@ -1,4 +1,4 @@ -package net.catenax.edc.cp.adapter.process.contractconfirmation; +package net.catenax.edc.cp.adapter.process.contractnotification; import java.util.HashMap; import java.util.Map; @@ -6,7 +6,7 @@ public class InMemoryDataStore implements DataStore { private final Map messages = new HashMap<>(); - private final Map confirmedContracts = new HashMap<>(); + private final Map confirmedContracts = new HashMap<>(); private final DataStoreLock lock; public InMemoryDataStore(DataStoreLock lock) { @@ -16,16 +16,30 @@ public InMemoryDataStore(DataStoreLock lock) { @Override public void storeConfirmedContract(String contractNegotiationId, String contractAgreementId) { lock.writeLock(contractNegotiationId); - confirmedContracts.put(contractNegotiationId, contractAgreementId); + confirmedContracts.put(contractNegotiationId, new ContractInfo(contractAgreementId, ContractInfo.ContractState.CONFIRMED)); lock.writeUnlock(contractNegotiationId); } @Override - public String getConfirmedContract(String contractNegotiationId) { + public void storeDeclinedContract(String contractNegotiationId) { + lock.writeLock(contractNegotiationId); + confirmedContracts.put(contractNegotiationId, new ContractInfo(ContractInfo.ContractState.DECLINED)); + lock.writeUnlock(contractNegotiationId); + } + + @Override + public void storeErrorContract(String contractNegotiationId) { + lock.writeLock(contractNegotiationId); + confirmedContracts.put(contractNegotiationId, new ContractInfo(ContractInfo.ContractState.ERROR)); + lock.writeUnlock(contractNegotiationId); + } + + @Override + public ContractInfo getContractInfo(String contractNegotiationId) { lock.readLock(contractNegotiationId); - String confirmedContract = confirmedContracts.get(contractNegotiationId); + ContractInfo contractInfo = confirmedContracts.get(contractNegotiationId); lock.readUnlock(contractNegotiationId); - return confirmedContract; + return contractInfo; } @Override diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/service/ErrorResultService.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/service/ErrorResultService.java new file mode 100644 index 000000000..22d6f3a17 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/service/ErrorResultService.java @@ -0,0 +1,50 @@ +package net.catenax.edc.cp.adapter.service; + +import jakarta.ws.rs.core.Response; +import lombok.RequiredArgsConstructor; +import net.catenax.edc.cp.adapter.exception.ExternalRequestException; +import net.catenax.edc.cp.adapter.exception.ResourceNotFoundException; +import net.catenax.edc.cp.adapter.messaging.Channel; +import net.catenax.edc.cp.adapter.messaging.Listener; +import net.catenax.edc.cp.adapter.messaging.Message; +import net.catenax.edc.cp.adapter.messaging.MessageService; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +@RequiredArgsConstructor +public class ErrorResultService implements Listener { + private static final Map, Response.Status> statusOfException = new HashMap<>(); + static { + statusOfException.put(ExternalRequestException.class, Response.Status.BAD_GATEWAY); + statusOfException.put(ResourceNotFoundException.class, Response.Status.NOT_FOUND); + } + + private final Monitor monitor; + private final MessageService messageService; + + @Override + public void process(Message message) { + message.getPayload().setErrorMessage(getErrorMessage(message)); + message.getPayload().setErrorStatus(statusOfException.getOrDefault( + message.getFinalException().getClass(), + Response.Status.INTERNAL_SERVER_ERROR)); + log(message); + messageService.send(Channel.RESULT, message); + } + + private String getErrorMessage(Message message) { + return Objects.nonNull(message.getFinalException()) + ? message.getFinalException().getMessage() + : "Unrecognized Exception."; + } + + private void log(Message message) { + monitor.info(String.format("[%s] Sending ERROR message to RESULT channel: %s / %s ", + message.getTraceId(), + message.getPayload().getErrorMessage(), + message.getPayload().getErrorStatus())); + } +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/service/ResultService.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/service/ResultService.java index 8b72fcece..e46b7d273 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/service/ResultService.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/service/ResultService.java @@ -8,49 +8,47 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import lombok.RequiredArgsConstructor; +import net.catenax.edc.cp.adapter.dto.ProcessData; import net.catenax.edc.cp.adapter.messaging.Listener; import net.catenax.edc.cp.adapter.messaging.Message; -import org.eclipse.dataspaceconnector.spi.types.domain.edr.EndpointDataReference; @RequiredArgsConstructor public class ResultService implements Listener { private final int CAPACITY = 1; private final int DEFAULT_TIMEOUT = 15; // TODO move to config - private final Map> dataReference = + private final Map> results = new ConcurrentHashMap<>(); - public EndpointDataReference pull(String id) throws InterruptedException { + public ProcessData pull(String id) throws InterruptedException { return pull(id, DEFAULT_TIMEOUT, SECONDS); } - public EndpointDataReference pull(String id, long timeout, TimeUnit unit) + public ProcessData pull(String id, long timeout, TimeUnit unit) throws InterruptedException { - if (!dataReference.containsKey(id)) { + if (!results.containsKey(id)) { initiate(id); } - EndpointDataReference result = dataReference.get(id).poll(timeout, unit); - dataReference.remove(id); + ProcessData result = results.get(id).poll(timeout, unit); + results.remove(id); return result; } @Override public void process(Message message) { - if (isNull(message) - || isNull(message.getPayload()) - || isNull(message.getPayload().getEndpointDataReference())) { + if (isNull(message) || isNull(message.getPayload())) { throw new IllegalArgumentException(); } - add(message.getTraceId(), message.getPayload().getEndpointDataReference()); + add(message.getTraceId(), message.getPayload()); } - private void add(String id, EndpointDataReference dataReferenceDto) { - if (!dataReference.containsKey(id)) { + private void add(String id, ProcessData ProcessData) { + if (!results.containsKey(id)) { initiate(id); } - dataReference.get(id).add(dataReferenceDto); + results.get(id).add(ProcessData); } private void initiate(String id) { - dataReference.put(id, new ArrayBlockingQueue<>(CAPACITY)); + results.put(id, new ArrayBlockingQueue<>(CAPACITY)); } } diff --git a/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/contractconfirmation/ContractConfirmationHandlerTest.java b/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractNotificationHandlerTest.java similarity index 61% rename from edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/contractconfirmation/ContractConfirmationHandlerTest.java rename to edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractNotificationHandlerTest.java index 39fc7233c..ce903ee64 100644 --- a/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/contractconfirmation/ContractConfirmationHandlerTest.java +++ b/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractNotificationHandlerTest.java @@ -1,8 +1,9 @@ -package net.catenax.edc.cp.adapter.process.contractconfirmation; +package net.catenax.edc.cp.adapter.process.contractnotification; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; +import jakarta.ws.rs.core.Response; import net.catenax.edc.cp.adapter.dto.ProcessData; import net.catenax.edc.cp.adapter.messaging.Channel; import net.catenax.edc.cp.adapter.messaging.Message; @@ -15,16 +16,19 @@ import org.eclipse.dataspaceconnector.spi.types.domain.contract.agreement.ContractAgreement; import org.eclipse.dataspaceconnector.spi.types.domain.contract.negotiation.ContractNegotiation; import org.eclipse.dataspaceconnector.spi.types.domain.contract.negotiation.ContractNegotiationStates; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -public class ContractConfirmationHandlerTest { +public class ContractNotificationHandlerTest { @Mock Monitor monitor; @Mock MessageService messageService; @Mock ContractNegotiationService contractNegotiationService; - @Mock DataStore dataStore; + @Mock + DataStore dataStore; @Mock ContractDataStore contractDataStore; @Mock TransferProcessService transferProcessService; @@ -34,11 +38,11 @@ void init() { } @Test - public void process_shouldSaveMessageWhenContractNotConfirmed() { + public void process_shouldSaveMessageWhenNoContractNotification() { // given when(transferProcessService.initiateTransfer(any())).thenReturn(ServiceResult.success(null)); - ContractConfirmationHandler contractConfirmationHandler = - new ContractConfirmationHandler( + ContractNotificationHandler contractNotificationHandler = + new ContractNotificationHandler( monitor, messageService, dataStore, @@ -48,7 +52,7 @@ public void process_shouldSaveMessageWhenContractNotConfirmed() { Message message = new Message(new ProcessData("assetId", "providerUrl")); // when - contractConfirmationHandler.process(message); + contractNotificationHandler.process(message); // then verify(dataStore, times(1)).storeMessage(any()); @@ -60,8 +64,8 @@ public void process_shouldSaveMessageWhenContractNotConfirmed() { public void process_shouldInitiateTransferWhenContractConfirmedFromCache() { // given when(transferProcessService.initiateTransfer(any())).thenReturn(ServiceResult.success(null)); - ContractConfirmationHandler contractConfirmationHandler = - new ContractConfirmationHandler( + ContractNotificationHandler contractNotificationHandler = + new ContractNotificationHandler( monitor, messageService, dataStore, @@ -72,7 +76,7 @@ public void process_shouldInitiateTransferWhenContractConfirmedFromCache() { message.getPayload().setContractConfirmed(true); // when - contractConfirmationHandler.process(message); + contractNotificationHandler.process(message); // then verify(transferProcessService, times(1)).initiateTransfer(any()); @@ -80,12 +84,12 @@ public void process_shouldInitiateTransferWhenContractConfirmedFromCache() { } @Test - public void process_shouldInitiateTransferWhenAlreadyContractConfirmedAtProvider() { + public void process_shouldInitiateTransferWhenContractAlreadyConfirmedAtProvider() { // given when(contractNegotiationService.findbyId(any())).thenReturn(getConfirmedContractNegotiation()); when(transferProcessService.initiateTransfer(any())).thenReturn(ServiceResult.success(null)); - ContractConfirmationHandler contractConfirmationHandler = - new ContractConfirmationHandler( + ContractNotificationHandler contractNotificationHandler = + new ContractNotificationHandler( monitor, messageService, dataStore, @@ -95,7 +99,7 @@ public void process_shouldInitiateTransferWhenAlreadyContractConfirmedAtProvider Message message = new Message(new ProcessData("assetId", "providerUrl")); // when - contractConfirmationHandler.process(message); + contractNotificationHandler.process(message); // then verify(dataStore, times(0)).storeMessage(any()); @@ -106,10 +110,10 @@ public void process_shouldInitiateTransferWhenAlreadyContractConfirmedAtProvider @Test public void process_shouldInitiateTransferWhenContractConfirmedByNotification() { // given - when(dataStore.getConfirmedContract(any())).thenReturn("confirmedContractAgreementId"); + when(dataStore.getContractInfo(any())).thenReturn(new ContractInfo("confirmedContractAgreementId", ContractInfo.ContractState.CONFIRMED)); when(transferProcessService.initiateTransfer(any())).thenReturn(ServiceResult.success(null)); - ContractConfirmationHandler contractConfirmationHandler = - new ContractConfirmationHandler( + ContractNotificationHandler contractNotificationHandler = + new ContractNotificationHandler( monitor, messageService, dataStore, @@ -119,7 +123,7 @@ public void process_shouldInitiateTransferWhenContractConfirmedByNotification() Message message = new Message(new ProcessData("assetId", "providerUrl")); // when - contractConfirmationHandler.process(message); + contractNotificationHandler.process(message); // then verify(dataStore, times(0)).storeMessage(any()); @@ -131,8 +135,8 @@ public void process_shouldInitiateTransferWhenContractConfirmedByNotification() @Test public void preConfirmed_shouldSaveInfoAboutContractConfirmationIfMessageNotAvailable() { // given - ContractConfirmationHandler contractConfirmationHandler = - new ContractConfirmationHandler( + ContractNotificationHandler contractNotificationHandler = + new ContractNotificationHandler( monitor, messageService, dataStore, @@ -142,7 +146,7 @@ public void preConfirmed_shouldSaveInfoAboutContractConfirmationIfMessageNotAvai ContractNegotiation contractNegotiation = getConfirmedContractNegotiation(); // when - contractConfirmationHandler.preConfirmed(contractNegotiation); + contractNotificationHandler.preConfirmed(contractNegotiation); // then verify(dataStore, times(1)).storeConfirmedContract(any(), any()); @@ -154,20 +158,20 @@ public void preConfirmed_shouldSaveInfoAboutContractConfirmationIfMessageNotAvai public void preConfirmed_shouldInitiateTransferIfMessageIsAvailable() { // given when(dataStore.getMessage(any())) - .thenReturn(new Message(new ProcessData("assetId", "providerUrl"))); + .thenReturn(new Message(new ProcessData("assetId", "providerUrl"))); when(transferProcessService.initiateTransfer(any())).thenReturn(ServiceResult.success(null)); - ContractConfirmationHandler contractConfirmationHandler = - new ContractConfirmationHandler( - monitor, - messageService, - dataStore, - contractNegotiationService, - transferProcessService, - contractDataStore); + ContractNotificationHandler contractNotificationHandler = + new ContractNotificationHandler( + monitor, + messageService, + dataStore, + contractNegotiationService, + transferProcessService, + contractDataStore); ContractNegotiation contractNegotiation = getConfirmedContractNegotiation(); // when - contractConfirmationHandler.preConfirmed(contractNegotiation); + contractNotificationHandler.preConfirmed(contractNegotiation); // then verify(dataStore, times(0)).storeConfirmedContract(any(), any()); @@ -175,6 +179,56 @@ public void preConfirmed_shouldInitiateTransferIfMessageIsAvailable() { verify(messageService, times(1)).send(eq(Channel.DATA_REFERENCE), any(Message.class)); } + @Test + public void preDeclined_shouldSendErrorResultIfMessageIsAvailable() { + // given + when(dataStore.getMessage(any())) + .thenReturn(new Message(new ProcessData("assetId", "providerUrl"))); + ContractNotificationHandler contractNotificationHandler = + new ContractNotificationHandler( + monitor, + messageService, + dataStore, + contractNegotiationService, + transferProcessService, + contractDataStore); + ContractNegotiation contractNegotiation = getConfirmedContractNegotiation(); + + // when + contractNotificationHandler.preDeclined(contractNegotiation); + + // then + ArgumentCaptor messageArg = ArgumentCaptor.forClass(Message.class); + verify(dataStore, times(0)).storeConfirmedContract(any(), any()); + verify(messageService, times(1)).send(eq(Channel.RESULT), messageArg.capture()); + Assertions.assertEquals(Response.Status.BAD_GATEWAY, messageArg.getValue().getPayload().getErrorStatus()); + } + + @Test + public void preError_shouldSendErrorResultIfMessageIsAvailable() { + // given + when(dataStore.getMessage(any())) + .thenReturn(new Message(new ProcessData("assetId", "providerUrl"))); + ContractNotificationHandler contractNotificationHandler = + new ContractNotificationHandler( + monitor, + messageService, + dataStore, + contractNegotiationService, + transferProcessService, + contractDataStore); + ContractNegotiation contractNegotiation = getConfirmedContractNegotiation(); + + // when + contractNotificationHandler.preError(contractNegotiation); + + // then + ArgumentCaptor messageArg = ArgumentCaptor.forClass(Message.class); + verify(dataStore, times(0)).storeConfirmedContract(any(), any()); + verify(messageService, times(1)).send(eq(Channel.RESULT), messageArg.capture()); + Assertions.assertEquals(Response.Status.BAD_GATEWAY, messageArg.getValue().getPayload().getErrorStatus()); + } + private ContractNegotiation getConfirmedContractNegotiation() { return ContractNegotiation.Builder.newInstance() .state(ContractNegotiationStates.CONFIRMED.code()) diff --git a/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/service/ResultServiceTest.java b/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/service/ResultServiceTest.java index e4616309b..ff272a3dd 100644 --- a/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/service/ResultServiceTest.java +++ b/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/service/ResultServiceTest.java @@ -17,14 +17,14 @@ public void pull_shouldReturnDataReferenceWhenMessageOccursFirst() throws Interr ResultService resultService = new ResultService(); String endpointDataRefId = "456"; Message message = getMessage(endpointDataRefId); - EndpointDataReference dataReference; + ProcessData processData; // when resultService.process(message); - dataReference = resultService.pull(message.getTraceId(), 200, TimeUnit.MILLISECONDS); + processData = resultService.pull(message.getTraceId(), 200, TimeUnit.MILLISECONDS); // then - Assertions.assertEquals(endpointDataRefId, dataReference.getId()); + Assertions.assertEquals(endpointDataRefId, processData.getEndpointDataReference().getId()); } @Test @@ -33,14 +33,14 @@ public void pull_shouldReturnDataReferenceWhenMessageOccursSecond() throws Inter ResultService resultService = new ResultService(); String endpointDataRefId = "456"; Message message = getMessage(endpointDataRefId); - EndpointDataReference dataReference; + ProcessData processData; // when processMessageWithDelay(resultService, message); - dataReference = resultService.pull(message.getTraceId(), 1000, TimeUnit.MILLISECONDS); + processData = resultService.pull(message.getTraceId(), 1000, TimeUnit.MILLISECONDS); // then - Assertions.assertEquals(endpointDataRefId, dataReference.getId()); + Assertions.assertEquals(endpointDataRefId, processData.getEndpointDataReference().getId()); } private void processMessageWithDelay(ResultService resultService, Message message) { @@ -58,17 +58,17 @@ public void pull_shouldReturnNullOnTimeout() throws InterruptedException { ResultService resultService = new ResultService(); // when - EndpointDataReference dataReference = resultService.pull("123", 500, TimeUnit.MILLISECONDS); + ProcessData processData = resultService.pull("123", 500, TimeUnit.MILLISECONDS); // then - Assertions.assertNull(dataReference); + Assertions.assertNull(processData); } @Test - public void process_shouldThrowIllegalArgumentExceptionIfNoDataReference() { + public void process_shouldThrowIllegalArgumentExceptionIfNoDataPayload() { // given ResultService resultService = new ResultService(); - Message message = new Message(new ProcessData("123", "providerUrl")); + Message message = new Message(null); // when then try { From 8b76752daeb65a204818989e5b5662161f578682 Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Mon, 5 Sep 2022 13:27:31 +0200 Subject: [PATCH 269/433] update setting name (#389) --- docs/migration/Version_0.1.0_0.1.1.md | 4 ++-- docs/release-notes/Version 0.1.1.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/migration/Version_0.1.0_0.1.1.md b/docs/migration/Version_0.1.0_0.1.1.md index e0caa4fa2..5797593de 100644 --- a/docs/migration/Version_0.1.0_0.1.1.md +++ b/docs/migration/Version_0.1.0_0.1.1.md @@ -75,7 +75,7 @@ New call ## 2. Connector Configuration ### 2.1. CX OAuth Extension -All connectors are now shipped with a new OAuth extension. This extension has an additional mandatory setting called `edc.oauth.endpoint.audience`, that must be set to the IDS path. +All connectors are now shipped with a new OAuth extension. This extension has an additional mandatory setting called `edc.ids.endpoint.audience`, that must be set to the IDS path. [Documentation](/edc-extensions/cx-oauth2/README.md) @@ -85,7 +85,7 @@ All connectors are now shipped with a new OAuth extension. This extension has an Example ``` -edc.oauth.endpoint.audience=http://plato-edc-controlplane:8282/api/v1/ids/data +edc.ids.endpoint.audience=http://plato-edc-controlplane:8282/api/v1/ids/data ```
diff --git a/docs/release-notes/Version 0.1.1.md b/docs/release-notes/Version 0.1.1.md index 3e5247942..8f6cf67b7 100644 --- a/docs/release-notes/Version 0.1.1.md +++ b/docs/release-notes/Version 0.1.1.md @@ -33,7 +33,7 @@ Using the open source OAuth Extension it is possible for a connector to re-use a **New Audience Configuration** ``` -edc.oauth.endpoint.audience=http://plato-edc-controlplane:8282/api/v1/ids/data +edc.ids.endpoint.audience=http://plato-edc-controlplane:8282/api/v1/ids/data ``` ## 3. Bug Fixes From e10f01acc37e98ab4d64b419f0997c28e4167c65 Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Tue, 6 Sep 2022 10:21:37 +0200 Subject: [PATCH 270/433] update README.md (#391) --- README.md | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 90 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c720d758f..c9cf6eb50 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,61 @@ -# Catena-X specific edc apps +
-This project provides pre-built Control-Plane and Data-Plane [docker](https://www.docker.com/) images and [helm](https://helm.sh/) charts of the [Eclipse DataSpaceConnector Project](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector). + +[![Contributors][contributors-shield]][contributors-url] +[![Stargazers][stars-shield]][stars-url] +[![Apache 2.0 License][license-shield]][license-url] +[![Latest Release][release-shield]][release-url] + + +
+
+ + Logo + + +

Product Eclipse Dataspace Connector

+

Catena-X

+ +

+ Container images and deployments of the Eclipse Dataspace Connector open source project. +
+ Explore the docs » +
+
+ View Eclipse Dataspace Connector + · + Releases + · + Report Bug / Request Feature +

+
+ + + +
+ Table of Contents +
    +
  1. + About The Project +
  2. +
  3. + Inventory +
  4. +
  5. + Getting Started + +
  6. +
  7. License
  8. +
+
+ +## About The Project + +The project provides pre-built control- and data-plane [docker](https://www.docker.com/) images and [helm](https://helm.sh/) charts of the [Eclipse DataSpaceConnector Project](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector). + +

(back to top)

## Inventory @@ -26,10 +81,41 @@ Derivatives of the Data-Plane can be found here * [edc-dataplane-hashicorp-vault](edc-dataplane/edc-dataplane-hashicorp-vault) with dependency onto * [Hashicorp Vault](https://www.vaultproject.io/) -## Prerequisites +

(back to top)

+ +## Getting Started + +

(back to top)

-## Build +### Build + +1. Build EDC Submodule Dependencies +```shell +git submodule update --init +cd edc && ./gradlew publishToMavenLocal -Pskip.signing=true -PedcVersion=0.0.1-20220902-SNAPSHOT && cd .. +``` + +2. Build Product-EDC Container Images ```shell ./mvnw package -Pwith-docker-image ``` + +

(back to top)

+ +## License + +Distributed under the Apache 2.0 License. See [LICENSE](https://github.com/catenax-ng/product-edc/blob/main/LICENSE) for more information. + +

(back to top)

+ + + +[contributors-shield]: https://img.shields.io/github/contributors/catenax-ng/product-edc.svg?style=for-the-badge +[contributors-url]: https://github.com/catenax-ng/product-edc/graphs/contributors +[stars-shield]: https://img.shields.io/github/stars/catenax-ng/product-edc.svg?style=for-the-badge +[stars-url]: https://github.com/catenax-ng/product-edc/stargazers +[license-shield]: https://img.shields.io/github/license/catenax-ng/product-edc.svg?style=for-the-badge +[license-url]: https://github.com/catenax-ng/product-edc/blob/main/LICENSE +[release-shield]: https://img.shields.io/github/v/release/catenax-ng/product-edc.svg?style=for-the-badge +[release-url]: https://github.com/catenax-ng/product-edc/releases From 50a5456f79a656403ce8a260e1d707724a2e7d6f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Sep 2022 10:26:49 +0200 Subject: [PATCH 271/433] Bump nimbus-jose-jwt from 8.23 to 9.24.3 (#390) --- edc-extensions/cx-oauth2/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-extensions/cx-oauth2/pom.xml b/edc-extensions/cx-oauth2/pom.xml index 6f23ea1de..8ac7cf40e 100644 --- a/edc-extensions/cx-oauth2/pom.xml +++ b/edc-extensions/cx-oauth2/pom.xml @@ -122,7 +122,7 @@ com.nimbusds nimbus-jose-jwt - 8.23 + 9.24.3 com.squareup.okhttp3 From 22e729d86369e9a3100d324d6685b047afdc585a Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Tue, 6 Sep 2022 10:48:20 +0200 Subject: [PATCH 272/433] add sequence diagram to cx-oauth2 extension (#392) --- edc-extensions/cx-oauth2/README.md | 10 +++++++- .../cx-oauth2/diagrams/sequence.png | Bin 0 -> 37872 bytes .../cx-oauth2/diagrams/sequence.puml | 24 ++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 edc-extensions/cx-oauth2/diagrams/sequence.png create mode 100644 edc-extensions/cx-oauth2/diagrams/sequence.puml diff --git a/edc-extensions/cx-oauth2/README.md b/edc-extensions/cx-oauth2/README.md index 5f721e4ea..0da6f1ced 100644 --- a/edc-extensions/cx-oauth2/README.md +++ b/edc-extensions/cx-oauth2/README.md @@ -22,4 +22,12 @@ The reason IDS did this is to prevent the IDS DAPS to know, which connectors tal | edc.oauth.validation.nbf.leeway | DAPS token request leeway | | 10 seconds | | edc.oauth.provider.jwks.refresh | Time between refresh of the DAPS json web key set | | 5 minutes | | edc.ids.endpoint.audience | The audience the connector requests from the DAPS. Should be the IDS URL of the connector, e.g. `http://plato-edc-controlplane:8282/api/v1/ids/data` | X | | -| edc.ids.validation.referringconnector | Adds checks to the DAPS token. Validation that the `referringConnector` equals the `issuerConnector` and the `securityProfile` of the token is equal to the profile of the IDS message | | false | \ No newline at end of file +| edc.ids.validation.referringconnector | Adds checks to the DAPS token. Validation that the `referringConnector` equals the `issuerConnector` and the `securityProfile` of the token is equal to the profile of the IDS message | | false | + +## Audience Validation + +Instead of the `idsc:IDS_CONNECTORS_ALL` the connector requests a specific audience from the DAPS. This audience will be the IDS URL, the connector intends to call. + +When a connector receives a message, it will checks the token audience is equal to the configured value in `edc.ids.endpoint.audience`. + +![sequence diagram](./diagrams/sequence.png) \ No newline at end of file diff --git a/edc-extensions/cx-oauth2/diagrams/sequence.png b/edc-extensions/cx-oauth2/diagrams/sequence.png new file mode 100644 index 0000000000000000000000000000000000000000..784441b4e7d2eb43afebeef0548ad15ba572dd48 GIT binary patch literal 37872 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 literal 0 HcmV?d00001 diff --git a/edc-extensions/cx-oauth2/diagrams/sequence.puml b/edc-extensions/cx-oauth2/diagrams/sequence.puml new file mode 100644 index 000000000..d2f20f278 --- /dev/null +++ b/edc-extensions/cx-oauth2/diagrams/sequence.puml @@ -0,0 +1,24 @@ +@startuml + +title CX-DAPS Audience Validation + +participant ConnectorA as "Connector A" +participant DAPS as "IDS DAPS" +participant ConnectorB as "Connector B" + +== Configuration == + +ConnectorB <-? : Configure //edc.ids.endpoint.audience//\nto ///api/v1/ids/data// + +== Request == + +?-> ConnectorA ++: Initiate Catalog Request\n/data/catalog?providerUrl=///api/v1/ids/data// + ConnectorA -> DAPS ++: Request Token for audience\n///api/v1/ids/data// + return DAPS Token + ConnectorA -> ConnectorB ++ : Send Request with Token + ConnectorB -> ConnectorB : Check Audience equals\n/api/v1/ids/data + ... continue request processing ... + return Catalog Response +return Catalog + +@enduml \ No newline at end of file From 631ee0ee6f684e879f4656c4df83aa0d363c80cd Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Tue, 6 Sep 2022 11:18:16 +0200 Subject: [PATCH 273/433] add veracode yaml (#394) --- .github/workflows/veracode.yaml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .github/workflows/veracode.yaml diff --git a/.github/workflows/veracode.yaml b/.github/workflows/veracode.yaml new file mode 100644 index 000000000..f3230f5a2 --- /dev/null +++ b/.github/workflows/veracode.yaml @@ -0,0 +1,2 @@ +# file to satisfy check in https://gh-org-checks.core.demo.catena-x.net/ +# veracode runs inside the build.yaml \ No newline at end of file From 52961ffa8fbf3e2576355b6ccdf7efd31a28ea08 Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Tue, 6 Sep 2022 12:38:13 +0200 Subject: [PATCH 274/433] move /deployment/helm to /charts (#393) --- .github/workflows/build.yaml | 2 +- .github/workflows/draft-new-release.yaml | 8 ++++---- .github/workflows/helm-lint.yaml | 4 ++-- .github/workflows/publish-new-release.yml | 2 +- CHANGELOG.md | 3 +++ {deployment/helm => charts}/README.md | 0 {deployment/helm => charts}/edc-controlplane/.helmignore | 0 {deployment/helm => charts}/edc-controlplane/Chart.yaml | 2 +- {deployment/helm => charts}/edc-controlplane/README.md | 2 +- .../helm => charts}/edc-controlplane/README.md.gotmpl | 0 .../helm => charts}/edc-controlplane/templates/NOTES.txt | 0 .../edc-controlplane/templates/_helpers.tpl | 0 .../edc-controlplane/templates/configmap-env.yaml | 0 .../edc-controlplane/templates/configmap.yaml | 0 .../edc-controlplane/templates/deployment.yaml | 0 .../helm => charts}/edc-controlplane/templates/hpa.yaml | 0 .../edc-controlplane/templates/imagepullsecret.yaml | 0 .../edc-controlplane/templates/ingress.yaml | 0 .../edc-controlplane/templates/service.yaml | 0 .../edc-controlplane/templates/serviceaccount.yaml | 0 {deployment/helm => charts}/edc-controlplane/values.yaml | 0 {deployment/helm => charts}/edc-dataplane/.helmignore | 0 {deployment/helm => charts}/edc-dataplane/Chart.yaml | 2 +- {deployment/helm => charts}/edc-dataplane/README.md | 2 +- .../helm => charts}/edc-dataplane/README.md.gotmpl | 0 .../helm => charts}/edc-dataplane/templates/NOTES.txt | 0 .../helm => charts}/edc-dataplane/templates/_helpers.tpl | 0 .../edc-dataplane/templates/configmap-env.yaml | 0 .../edc-dataplane/templates/configmap.yaml | 0 .../edc-dataplane/templates/deployment.yaml | 0 .../helm => charts}/edc-dataplane/templates/hpa.yaml | 0 .../edc-dataplane/templates/imagepullsecret.yaml | 0 .../helm => charts}/edc-dataplane/templates/ingress.yaml | 0 .../helm => charts}/edc-dataplane/templates/service.yaml | 0 .../edc-dataplane/templates/serviceaccount.yaml | 0 {deployment/helm => charts}/edc-dataplane/values.yaml | 0 .../main/resources/deployment/helm/all-in-one/Chart.yaml | 8 ++++---- 37 files changed, 19 insertions(+), 16 deletions(-) rename {deployment/helm => charts}/README.md (100%) rename {deployment/helm => charts}/edc-controlplane/.helmignore (100%) rename {deployment/helm => charts}/edc-controlplane/Chart.yaml (77%) rename {deployment/helm => charts}/edc-controlplane/README.md (99%) rename {deployment/helm => charts}/edc-controlplane/README.md.gotmpl (100%) rename {deployment/helm => charts}/edc-controlplane/templates/NOTES.txt (100%) rename {deployment/helm => charts}/edc-controlplane/templates/_helpers.tpl (100%) rename {deployment/helm => charts}/edc-controlplane/templates/configmap-env.yaml (100%) rename {deployment/helm => charts}/edc-controlplane/templates/configmap.yaml (100%) rename {deployment/helm => charts}/edc-controlplane/templates/deployment.yaml (100%) rename {deployment/helm => charts}/edc-controlplane/templates/hpa.yaml (100%) rename {deployment/helm => charts}/edc-controlplane/templates/imagepullsecret.yaml (100%) rename {deployment/helm => charts}/edc-controlplane/templates/ingress.yaml (100%) rename {deployment/helm => charts}/edc-controlplane/templates/service.yaml (100%) rename {deployment/helm => charts}/edc-controlplane/templates/serviceaccount.yaml (100%) rename {deployment/helm => charts}/edc-controlplane/values.yaml (100%) rename {deployment/helm => charts}/edc-dataplane/.helmignore (100%) rename {deployment/helm => charts}/edc-dataplane/Chart.yaml (76%) rename {deployment/helm => charts}/edc-dataplane/README.md (99%) rename {deployment/helm => charts}/edc-dataplane/README.md.gotmpl (100%) rename {deployment/helm => charts}/edc-dataplane/templates/NOTES.txt (100%) rename {deployment/helm => charts}/edc-dataplane/templates/_helpers.tpl (100%) rename {deployment/helm => charts}/edc-dataplane/templates/configmap-env.yaml (100%) rename {deployment/helm => charts}/edc-dataplane/templates/configmap.yaml (100%) rename {deployment/helm => charts}/edc-dataplane/templates/deployment.yaml (100%) rename {deployment/helm => charts}/edc-dataplane/templates/hpa.yaml (100%) rename {deployment/helm => charts}/edc-dataplane/templates/imagepullsecret.yaml (100%) rename {deployment/helm => charts}/edc-dataplane/templates/ingress.yaml (100%) rename {deployment/helm => charts}/edc-dataplane/templates/service.yaml (100%) rename {deployment/helm => charts}/edc-dataplane/templates/serviceaccount.yaml (100%) rename {deployment/helm => charts}/edc-dataplane/values.yaml (100%) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 5f5e29995..914bf0e31 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -13,7 +13,7 @@ on: - released pull_request: paths-ignore: - - 'deployment/helm/**' + - 'charts/**' branches: - '*' diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index 7e3096349..b4a8f32aa 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -42,24 +42,24 @@ jobs: GITHUB_PACKAGE_USERNAME: ${{ github.actor }} GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} - - name: Bump version in deployment/helm + name: Bump version in /charts uses: mikefarah/yq@v4.27.3 with: cmd: |- - find deployment/helm -name Chart.yaml | xargs -n1 yq -i '.appVersion = "${{ github.event.inputs.version }}" | .version = "${{ github.event.inputs.version }}"' + find charts -name Chart.yaml | xargs -n1 yq -i '.appVersion = "${{ github.event.inputs.version }}" | .version = "${{ github.event.inputs.version }}"' - name: Update Chart READMEs uses: addnab/docker-run-action@v3 with: image: jnorwood/helm-docs:v1.10.0 - options: -v ${{ github.workspace }}/deployment/helm:/helm-docs + options: -v ${{ github.workspace }}/charts:/helm-docs run: | helm-docs --log-level debug - name: Commit changelog and manifest files id: make-commit run: | - git add CHANGELOG.md $(find -name pom.xml) $(find deployment/helm -name Chart.yaml) $(find deployment/helm -name README.md) + git add CHANGELOG.md $(find -name pom.xml) $(find charts -name Chart.yaml) $(find charts -name README.md) git commit --message "Prepare release ${{ github.event.inputs.version }}" echo "::set-output name=commit::$(git rev-parse HEAD)" diff --git a/.github/workflows/helm-lint.yaml b/.github/workflows/helm-lint.yaml index 557d91d5a..80b5f5695 100644 --- a/.github/workflows/helm-lint.yaml +++ b/.github/workflows/helm-lint.yaml @@ -10,13 +10,13 @@ on: - '[0-9]+.[0-9]+.[0-9]+' paths-ignore: - '**' - - '!deployment/helm/**' + - '!charts/**' pull_request: branches: - '*' paths-ignore: - '**' - - '!deployment/helm/**' + - '!charts/**' jobs: helm-lint: diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index 4d5a3f6d0..300aea6e5 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -121,7 +121,7 @@ jobs: git config user.email noreply@github.com # Package all charts - find deployment/helm -name Chart.yaml -not -path "./edc-tests/*" | xargs -n1 dirname | xargs -n1 helm package -u -d helm-charts + find charts -name Chart.yaml -not -path "./edc-tests/*" | xargs -n1 dirname | xargs -n1 helm package -u -d helm-charts git checkout gh-pages || git checkout -b gh-pages git pull --rebase origin gh-pages diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b1b21f7e..fc4e18f99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed +- Moved helm charts from `deployment/helm` to `charts` + ## [0.1.1] - 2022-09-04 **Important Note**: Please consolidate the migration documentation before updating your connector. [documentation](/docs/migration/Version_0.1.0_0.1.1.md). diff --git a/deployment/helm/README.md b/charts/README.md similarity index 100% rename from deployment/helm/README.md rename to charts/README.md diff --git a/deployment/helm/edc-controlplane/.helmignore b/charts/edc-controlplane/.helmignore similarity index 100% rename from deployment/helm/edc-controlplane/.helmignore rename to charts/edc-controlplane/.helmignore diff --git a/deployment/helm/edc-controlplane/Chart.yaml b/charts/edc-controlplane/Chart.yaml similarity index 77% rename from deployment/helm/edc-controlplane/Chart.yaml rename to charts/edc-controlplane/Chart.yaml index d81685edc..cba43f97f 100644 --- a/deployment/helm/edc-controlplane/Chart.yaml +++ b/charts/edc-controlplane/Chart.yaml @@ -3,7 +3,7 @@ apiVersion: v2 name: edc-controlplane description: >- EDC Control-Plane - The Eclipse DataSpaceConnector administration layer with responsibility of resource management and govern contracts and data transfers -home: https://github.com/catenax-ng/product-edc/deployment/helm/edc-controlplane +home: https://github.com/catenax-ng/product-edc/charts/edc-controlplane type: application appVersion: "0.1.1" version: 0.1.1 diff --git a/deployment/helm/edc-controlplane/README.md b/charts/edc-controlplane/README.md similarity index 99% rename from deployment/helm/edc-controlplane/README.md rename to charts/edc-controlplane/README.md index 46e933039..7337a36a3 100644 --- a/deployment/helm/edc-controlplane/README.md +++ b/charts/edc-controlplane/README.md @@ -4,7 +4,7 @@ EDC Control-Plane - The Eclipse DataSpaceConnector administration layer with responsibility of resource management and govern contracts and data transfers -**Homepage:** +**Homepage:** ## TL;DR ```shell diff --git a/deployment/helm/edc-controlplane/README.md.gotmpl b/charts/edc-controlplane/README.md.gotmpl similarity index 100% rename from deployment/helm/edc-controlplane/README.md.gotmpl rename to charts/edc-controlplane/README.md.gotmpl diff --git a/deployment/helm/edc-controlplane/templates/NOTES.txt b/charts/edc-controlplane/templates/NOTES.txt similarity index 100% rename from deployment/helm/edc-controlplane/templates/NOTES.txt rename to charts/edc-controlplane/templates/NOTES.txt diff --git a/deployment/helm/edc-controlplane/templates/_helpers.tpl b/charts/edc-controlplane/templates/_helpers.tpl similarity index 100% rename from deployment/helm/edc-controlplane/templates/_helpers.tpl rename to charts/edc-controlplane/templates/_helpers.tpl diff --git a/deployment/helm/edc-controlplane/templates/configmap-env.yaml b/charts/edc-controlplane/templates/configmap-env.yaml similarity index 100% rename from deployment/helm/edc-controlplane/templates/configmap-env.yaml rename to charts/edc-controlplane/templates/configmap-env.yaml diff --git a/deployment/helm/edc-controlplane/templates/configmap.yaml b/charts/edc-controlplane/templates/configmap.yaml similarity index 100% rename from deployment/helm/edc-controlplane/templates/configmap.yaml rename to charts/edc-controlplane/templates/configmap.yaml diff --git a/deployment/helm/edc-controlplane/templates/deployment.yaml b/charts/edc-controlplane/templates/deployment.yaml similarity index 100% rename from deployment/helm/edc-controlplane/templates/deployment.yaml rename to charts/edc-controlplane/templates/deployment.yaml diff --git a/deployment/helm/edc-controlplane/templates/hpa.yaml b/charts/edc-controlplane/templates/hpa.yaml similarity index 100% rename from deployment/helm/edc-controlplane/templates/hpa.yaml rename to charts/edc-controlplane/templates/hpa.yaml diff --git a/deployment/helm/edc-controlplane/templates/imagepullsecret.yaml b/charts/edc-controlplane/templates/imagepullsecret.yaml similarity index 100% rename from deployment/helm/edc-controlplane/templates/imagepullsecret.yaml rename to charts/edc-controlplane/templates/imagepullsecret.yaml diff --git a/deployment/helm/edc-controlplane/templates/ingress.yaml b/charts/edc-controlplane/templates/ingress.yaml similarity index 100% rename from deployment/helm/edc-controlplane/templates/ingress.yaml rename to charts/edc-controlplane/templates/ingress.yaml diff --git a/deployment/helm/edc-controlplane/templates/service.yaml b/charts/edc-controlplane/templates/service.yaml similarity index 100% rename from deployment/helm/edc-controlplane/templates/service.yaml rename to charts/edc-controlplane/templates/service.yaml diff --git a/deployment/helm/edc-controlplane/templates/serviceaccount.yaml b/charts/edc-controlplane/templates/serviceaccount.yaml similarity index 100% rename from deployment/helm/edc-controlplane/templates/serviceaccount.yaml rename to charts/edc-controlplane/templates/serviceaccount.yaml diff --git a/deployment/helm/edc-controlplane/values.yaml b/charts/edc-controlplane/values.yaml similarity index 100% rename from deployment/helm/edc-controlplane/values.yaml rename to charts/edc-controlplane/values.yaml diff --git a/deployment/helm/edc-dataplane/.helmignore b/charts/edc-dataplane/.helmignore similarity index 100% rename from deployment/helm/edc-dataplane/.helmignore rename to charts/edc-dataplane/.helmignore diff --git a/deployment/helm/edc-dataplane/Chart.yaml b/charts/edc-dataplane/Chart.yaml similarity index 76% rename from deployment/helm/edc-dataplane/Chart.yaml rename to charts/edc-dataplane/Chart.yaml index e9d513ff4..8046fbbcb 100644 --- a/deployment/helm/edc-dataplane/Chart.yaml +++ b/charts/edc-dataplane/Chart.yaml @@ -3,7 +3,7 @@ apiVersion: v2 name: edc-dataplane description: >- EDC Data-Plane - The Eclipse DataSpaceConnector data layer with responsibility of transferring and receiving data streams -home: https://github.com/catenax-ng/product-edc/deployment/helm/edc-dataplane +home: https://github.com/catenax-ng/product-edc/charts/edc-dataplane type: application appVersion: "0.1.1" version: 0.1.1 diff --git a/deployment/helm/edc-dataplane/README.md b/charts/edc-dataplane/README.md similarity index 99% rename from deployment/helm/edc-dataplane/README.md rename to charts/edc-dataplane/README.md index fbedf9dec..58b6ef4a3 100644 --- a/deployment/helm/edc-dataplane/README.md +++ b/charts/edc-dataplane/README.md @@ -4,7 +4,7 @@ EDC Data-Plane - The Eclipse DataSpaceConnector data layer with responsibility of transferring and receiving data streams -**Homepage:** +**Homepage:** ## TL;DR ```shell diff --git a/deployment/helm/edc-dataplane/README.md.gotmpl b/charts/edc-dataplane/README.md.gotmpl similarity index 100% rename from deployment/helm/edc-dataplane/README.md.gotmpl rename to charts/edc-dataplane/README.md.gotmpl diff --git a/deployment/helm/edc-dataplane/templates/NOTES.txt b/charts/edc-dataplane/templates/NOTES.txt similarity index 100% rename from deployment/helm/edc-dataplane/templates/NOTES.txt rename to charts/edc-dataplane/templates/NOTES.txt diff --git a/deployment/helm/edc-dataplane/templates/_helpers.tpl b/charts/edc-dataplane/templates/_helpers.tpl similarity index 100% rename from deployment/helm/edc-dataplane/templates/_helpers.tpl rename to charts/edc-dataplane/templates/_helpers.tpl diff --git a/deployment/helm/edc-dataplane/templates/configmap-env.yaml b/charts/edc-dataplane/templates/configmap-env.yaml similarity index 100% rename from deployment/helm/edc-dataplane/templates/configmap-env.yaml rename to charts/edc-dataplane/templates/configmap-env.yaml diff --git a/deployment/helm/edc-dataplane/templates/configmap.yaml b/charts/edc-dataplane/templates/configmap.yaml similarity index 100% rename from deployment/helm/edc-dataplane/templates/configmap.yaml rename to charts/edc-dataplane/templates/configmap.yaml diff --git a/deployment/helm/edc-dataplane/templates/deployment.yaml b/charts/edc-dataplane/templates/deployment.yaml similarity index 100% rename from deployment/helm/edc-dataplane/templates/deployment.yaml rename to charts/edc-dataplane/templates/deployment.yaml diff --git a/deployment/helm/edc-dataplane/templates/hpa.yaml b/charts/edc-dataplane/templates/hpa.yaml similarity index 100% rename from deployment/helm/edc-dataplane/templates/hpa.yaml rename to charts/edc-dataplane/templates/hpa.yaml diff --git a/deployment/helm/edc-dataplane/templates/imagepullsecret.yaml b/charts/edc-dataplane/templates/imagepullsecret.yaml similarity index 100% rename from deployment/helm/edc-dataplane/templates/imagepullsecret.yaml rename to charts/edc-dataplane/templates/imagepullsecret.yaml diff --git a/deployment/helm/edc-dataplane/templates/ingress.yaml b/charts/edc-dataplane/templates/ingress.yaml similarity index 100% rename from deployment/helm/edc-dataplane/templates/ingress.yaml rename to charts/edc-dataplane/templates/ingress.yaml diff --git a/deployment/helm/edc-dataplane/templates/service.yaml b/charts/edc-dataplane/templates/service.yaml similarity index 100% rename from deployment/helm/edc-dataplane/templates/service.yaml rename to charts/edc-dataplane/templates/service.yaml diff --git a/deployment/helm/edc-dataplane/templates/serviceaccount.yaml b/charts/edc-dataplane/templates/serviceaccount.yaml similarity index 100% rename from deployment/helm/edc-dataplane/templates/serviceaccount.yaml rename to charts/edc-dataplane/templates/serviceaccount.yaml diff --git a/deployment/helm/edc-dataplane/values.yaml b/charts/edc-dataplane/values.yaml similarity index 100% rename from deployment/helm/edc-dataplane/values.yaml rename to charts/edc-dataplane/values.yaml diff --git a/edc-tests/src/main/resources/deployment/helm/all-in-one/Chart.yaml b/edc-tests/src/main/resources/deployment/helm/all-in-one/Chart.yaml index 63ee4b5b7..ab8eee0cd 100644 --- a/edc-tests/src/main/resources/deployment/helm/all-in-one/Chart.yaml +++ b/edc-tests/src/main/resources/deployment/helm/all-in-one/Chart.yaml @@ -35,12 +35,12 @@ dependencies: # PLATO CONNECTOR - name: edc-controlplane version: ">=0.0.1" - repository: "file://../../../../../../../deployment/helm/edc-controlplane" + repository: "file://../../../../../../../charts/edc-controlplane" alias: platoedccontrolplane condition: platoedccontrolplane.enabled - name: edc-dataplane version: ">=0.0.1" - repository: "file://../../../../../../../deployment/helm/edc-dataplane" + repository: "file://../../../../../../../charts/edc-dataplane" alias: platoedcdataplane condition: platoedcdataplane.enabled - name: backend-service @@ -62,12 +62,12 @@ dependencies: # SOKRATES CONNECTOR - name: edc-controlplane version: ">=0.0.1" - repository: "file://../../../../../../../deployment/helm/edc-controlplane" + repository: "file://../../../../../../../charts/edc-controlplane" alias: sokratesedccontrolplane condition: sokratesedccontrolplane.enabled - name: edc-dataplane version: ">=0.0.1" - repository: "file://../../../../../../../deployment/helm/edc-dataplane" + repository: "file://../../../../../../../charts/edc-dataplane" alias: sokratesedcdataplane condition: sokratesedcdataplane.enabled - name: backend-service From 73a0998c29423b6f4e0fa0e1db7a0bdcab08bee7 Mon Sep 17 00:00:00 2001 From: marcingajek-zf Date: Tue, 6 Sep 2022 12:44:03 +0200 Subject: [PATCH 275/433] control-plane-adapter extension - eliminating race condition (plus spotless plugin code formatting) (cherry picked from commit 202f34ad269aa8ecc401990d68310947346a6e22) --- .../edc/cp/adapter/ApiAdapterExtension.java | 10 +- .../edc/cp/adapter/HttpController.java | 19 ++-- .../exception/ExternalRequestException.java | 12 +-- .../contractnotification/ContractInfo.java | 57 ++++++------ .../ContractNotificationHandler.java | 37 +++----- .../contractnotification/DataStore.java | 14 +-- .../contractnotification/DataStoreLock.java | 41 --------- .../InMemoryDataStore.java | 92 ++++++++++--------- .../datareference/DataReferenceHandler.java | 6 +- .../process/datareference/DataStore.java | 8 +- .../datareference/InMemoryDataStore.java | 47 ++++++---- .../adapter/service/ErrorResultService.java | 62 ++++++------- .../edc/cp/adapter/service/ResultService.java | 6 +- .../catenax/edc/cp/adapter/util/LockMap.java | 30 ++++++ .../ContractNegotiationHandlerTest.java | 6 +- .../ContractNotificationHandlerTest.java | 83 +++++++++-------- .../DataReferenceHandlerTest.java | 13 +-- .../cp/adapter/service/ResultServiceTest.java | 18 ++-- 18 files changed, 273 insertions(+), 288 deletions(-) delete mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/DataStoreLock.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/util/LockMap.java diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/ApiAdapterExtension.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/ApiAdapterExtension.java index 2fd77bd9a..378c927c6 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/ApiAdapterExtension.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/ApiAdapterExtension.java @@ -6,14 +6,14 @@ import net.catenax.edc.cp.adapter.messaging.Channel; import net.catenax.edc.cp.adapter.messaging.InMemoryMessageService; import net.catenax.edc.cp.adapter.messaging.ListenerService; -import net.catenax.edc.cp.adapter.process.contractnotification.ContractNotificationHandler; -import net.catenax.edc.cp.adapter.process.contractnotification.DataStoreLock; -import net.catenax.edc.cp.adapter.process.contractnotification.InMemoryDataStore; import net.catenax.edc.cp.adapter.process.contractdatastore.InMemoryContractDataStore; import net.catenax.edc.cp.adapter.process.contractnegotiation.ContractNegotiationHandler; +import net.catenax.edc.cp.adapter.process.contractnotification.ContractNotificationHandler; +import net.catenax.edc.cp.adapter.process.contractnotification.InMemoryDataStore; import net.catenax.edc.cp.adapter.process.datareference.DataReferenceHandler; import net.catenax.edc.cp.adapter.service.ErrorResultService; import net.catenax.edc.cp.adapter.service.ResultService; +import net.catenax.edc.cp.adapter.util.LockMap; import org.eclipse.dataspaceconnector.api.datamanagement.catalog.service.CatalogServiceImpl; import org.eclipse.dataspaceconnector.api.datamanagement.contractnegotiation.service.ContractNegotiationService; import org.eclipse.dataspaceconnector.api.datamanagement.contractnegotiation.service.ContractNegotiationServiceImpl; @@ -109,7 +109,7 @@ private void initContractConfirmationHandler( new ContractNotificationHandler( monitor, messageService, - new InMemoryDataStore(new DataStoreLock()), + new InMemoryDataStore(new LockMap()), contractNegotiationService, new TransferProcessServiceImpl( transferProcessStore, transferProcessManager, getTransactionContext(monitor)), @@ -128,7 +128,7 @@ private void initDataReferenceHandler( new DataReferenceHandler( monitor, messageService, - new net.catenax.edc.cp.adapter.process.datareference.InMemoryDataStore()); + new net.catenax.edc.cp.adapter.process.datareference.InMemoryDataStore(new LockMap())); listenerService.addListener(Channel.DATA_REFERENCE, dataReferenceHandler); receiverRegistry.registerReceiver(dataReferenceHandler); } diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/HttpController.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/HttpController.java index 567043a88..0a9e1d673 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/HttpController.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/HttpController.java @@ -5,6 +5,7 @@ import jakarta.ws.rs.*; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; +import java.util.Objects; import lombok.RequiredArgsConstructor; import net.catenax.edc.cp.adapter.dto.ProcessData; import net.catenax.edc.cp.adapter.messaging.Channel; @@ -13,8 +14,6 @@ import net.catenax.edc.cp.adapter.service.ResultService; import org.eclipse.dataspaceconnector.spi.monitor.Monitor; -import java.util.Objects; - @Consumes({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON}) @Path("/adapter/asset") @@ -42,25 +41,25 @@ public Response getAssetSynchronous( if (Objects.isNull(processData)) { return Response.status(Response.Status.NOT_FOUND) - .entity(Response.Status.NOT_FOUND.getReasonPhrase()) - .build(); + .entity(Response.Status.NOT_FOUND.getReasonPhrase()) + .build(); } if (Objects.nonNull(processData.getErrorStatus())) { return Response.status(processData.getErrorStatus()) - .entity(processData.getErrorMessage()) - .build(); + .entity(processData.getErrorMessage()) + .build(); } if (Objects.nonNull(processData.getEndpointDataReference())) { return Response.status(Response.Status.OK) - .entity(processData.getEndpointDataReference()) - .build(); + .entity(processData.getEndpointDataReference()) + .build(); } return Response.status(Response.Status.REQUEST_TIMEOUT) - .entity(Response.Status.REQUEST_TIMEOUT.getReasonPhrase()) - .build(); + .entity(Response.Status.REQUEST_TIMEOUT.getReasonPhrase()) + .build(); } catch (InterruptedException e) { monitor.severe("InterruptedException", e); return Response.status(Response.Status.NOT_FOUND).build(); diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ExternalRequestException.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ExternalRequestException.java index bdb2105c3..6ddecd980 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ExternalRequestException.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ExternalRequestException.java @@ -1,11 +1,11 @@ package net.catenax.edc.cp.adapter.exception; public class ExternalRequestException extends RuntimeException { - public ExternalRequestException(String message) { - super(message); - } + public ExternalRequestException(String message) { + super(message); + } - public ExternalRequestException(String message, Throwable cause) { - super(message, cause); - } + public ExternalRequestException(String message, Throwable cause) { + super(message, cause); + } } diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractInfo.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractInfo.java index bd9abd3d2..44e0bc91c 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractInfo.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractInfo.java @@ -3,30 +3,33 @@ import lombok.Getter; public class ContractInfo { - @Getter - private String contractAgreementId; - private ContractState contractState; - - public ContractInfo(String contractAgreementId, ContractState contractState) { - this.contractAgreementId = contractAgreementId; - this.contractState = contractState; - } - - public ContractInfo(ContractState contractState) { - this.contractState = contractState; - } - - public boolean isConfirmed() { - return ContractState.CONFIRMED.equals(contractState); - } - public boolean isDeclined() { - return ContractState.DECLINED.equals(contractState); - } - public boolean isError() { - return ContractState.ERROR.equals(contractState); - } - - protected enum ContractState { - CONFIRMED, DECLINED, ERROR; - } -} \ No newline at end of file + @Getter private String contractAgreementId; + private ContractState contractState; + + public ContractInfo(String contractAgreementId, ContractState contractState) { + this.contractAgreementId = contractAgreementId; + this.contractState = contractState; + } + + public ContractInfo(ContractState contractState) { + this.contractState = contractState; + } + + public boolean isConfirmed() { + return ContractState.CONFIRMED.equals(contractState); + } + + public boolean isDeclined() { + return ContractState.DECLINED.equals(contractState); + } + + public boolean isError() { + return ContractState.ERROR.equals(contractState); + } + + protected enum ContractState { + CONFIRMED, + DECLINED, + ERROR; + } +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractNotificationHandler.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractNotificationHandler.java index 470e414e8..0bfda5796 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractNotificationHandler.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractNotificationHandler.java @@ -1,9 +1,9 @@ package net.catenax.edc.cp.adapter.process.contractnotification; +import static jakarta.ws.rs.core.Response.Status; import static java.util.Objects.isNull; import java.util.Objects; - import lombok.RequiredArgsConstructor; import net.catenax.edc.cp.adapter.exception.ExternalRequestException; import net.catenax.edc.cp.adapter.messaging.Channel; @@ -22,8 +22,6 @@ import org.eclipse.dataspaceconnector.spi.types.domain.transfer.DataRequest; import org.eclipse.dataspaceconnector.spi.types.domain.transfer.TransferType; -import static jakarta.ws.rs.core.Response.Status; - @RequiredArgsConstructor public class ContractNotificationHandler implements Listener, ContractNegotiationListener { public static final String CONTRACT_DECLINED_MESSAGE = "Contract for asset is declined."; @@ -55,9 +53,8 @@ public void process(Message message) { return; } - ContractInfo contractInfo = dataStore.getContractInfo(contractNegotiationId); + ContractInfo contractInfo = dataStore.exchangeMessage(message); if (isNull(contractInfo)) { - dataStore.storeMessage(message); return; } @@ -67,37 +64,34 @@ public void process(Message message) { } else { sendErrorResult(message, Status.BAD_GATEWAY, contractInfo.isDeclined() ? CONTRACT_DECLINED_MESSAGE - : CONTRACT_ERROR_MESSAGE); - } - dataStore.removeConfirmedContract(contractNegotiationId); + : CONTRACT_ERROR_MESSAGE); } + dataStore.removeContractInfo(contractNegotiationId); } @Override public void preConfirmed(ContractNegotiation negotiation) { monitor.info("ContractConfirmationHandler: received ContractConfirmation event"); - String contractNegotiationId = negotiation.getId(); - String contractAgreementId = negotiation.getContractAgreement().getId(); - Message message = dataStore.getMessage(contractNegotiationId); + String negotiationId = negotiation.getId(); + String agreementId = negotiation.getContractAgreement().getId(); + Message message = dataStore.exchangeConfirmedContract(negotiationId, agreementId); if (isNull(message)) { - dataStore.storeConfirmedContract(contractNegotiationId, contractAgreementId); return; } - message.getPayload().setContractAgreementId(contractAgreementId); + message.getPayload().setContractAgreementId(agreementId); initiateDataTransfer(message); contractDataStore.add( message.getPayload().getAssetId(), message.getPayload().getProvider(), negotiation.getContractAgreement()); - dataStore.removeMessage(contractNegotiationId); + dataStore.removeMessage(negotiationId); } @Override public void preDeclined(ContractNegotiation negotiation) { monitor.info("ContractConfirmationHandler: received ContractDeclined event"); String contractNegotiationId = negotiation.getId(); - Message message = dataStore.getMessage(contractNegotiationId); + Message message = dataStore.exchangeDeclinedContract(contractNegotiationId); if (isNull(message)) { - dataStore.storeDeclinedContract(contractNegotiationId); return; } sendErrorResult(message, Status.BAD_GATEWAY, CONTRACT_DECLINED_MESSAGE); @@ -108,9 +102,8 @@ public void preDeclined(ContractNegotiation negotiation) { public void preError(ContractNegotiation negotiation) { monitor.info("ContractConfirmationHandler: received ContractError event"); String contractNegotiationId = negotiation.getId(); - Message message = dataStore.getMessage(contractNegotiationId); + Message message = dataStore.exchangeErrorContract(contractNegotiationId); if (isNull(message)) { - dataStore.storeErrorContract(contractNegotiationId); return; } sendErrorResult(message, Status.BAD_GATEWAY, CONTRACT_ERROR_MESSAGE); @@ -158,9 +151,9 @@ private void sendInitiationRequest(Message message) { } private void throwDataRefRequestException(Message message) { - throw new ExternalRequestException( - String.format("Data reference initial request failed! AssetId: %s", - message.getPayload().getAssetId())); + throw new ExternalRequestException(String.format( + "Data reference initial request failed! AssetId: %s", + message.getPayload().getAssetId())); } private void sendErrorResult(Message message, Status status, String errorMessage) { @@ -171,6 +164,6 @@ private void sendErrorResult(Message message, Status status, String errorMessage private boolean isContractConfirmed(ContractNegotiation contractNegotiation) { return Objects.nonNull(contractNegotiation) - && contractNegotiation.getState() == ContractNegotiationStates.CONFIRMED.code(); + && contractNegotiation.getState() == ContractNegotiationStates.CONFIRMED.code(); } } diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/DataStore.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/DataStore.java index 6054e0975..434b074c0 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/DataStore.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/DataStore.java @@ -3,19 +3,15 @@ import net.catenax.edc.cp.adapter.messaging.Message; public interface DataStore { - void storeConfirmedContract(String contractNegotiationId, String contractAgreementId); + Message exchangeConfirmedContract(String contractNegotiationId, String contractAgreementId); - void storeDeclinedContract(String contractNegotiationId); + Message exchangeDeclinedContract(String contractNegotiationId); - void storeErrorContract(String contractNegotiationId); + Message exchangeErrorContract(String contractNegotiationId); - ContractInfo getContractInfo(String contractNegotiationId); + ContractInfo exchangeMessage(Message message); - void removeConfirmedContract(String key); - - void storeMessage(Message message); - - Message getMessage(String key); + void removeContractInfo(String key); void removeMessage(String key); } diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/DataStoreLock.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/DataStoreLock.java deleted file mode 100644 index b2aeed9c0..000000000 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/DataStoreLock.java +++ /dev/null @@ -1,41 +0,0 @@ -package net.catenax.edc.cp.adapter.process.contractnotification; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; - -public class DataStoreLock { - private final Map lock = new HashMap<>(); - - public void readLock(String id) { - addLock(id); - lock.get(id).readLock().lock(); - } - - public void readUnlock(String id) { - addLock(id); - lock.get(id).readLock().unlock(); - } - - public void writeLock(String id) { - addLock(id); - lock.get(id).writeLock().lock(); - } - - public void writeUnlock(String id) { - addLock(id); - lock.get(id).writeLock().unlock(); - } - - public void removeLock(String id) { - addLock(id); - lock.remove(id); - } - - private void addLock(String id) { - synchronized (this) { - lock.putIfAbsent(id, new ReentrantReadWriteLock()); - } - } -} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/InMemoryDataStore.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/InMemoryDataStore.java index 31cf45c89..12a58b086 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/InMemoryDataStore.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/InMemoryDataStore.java @@ -1,73 +1,83 @@ package net.catenax.edc.cp.adapter.process.contractnotification; +import static java.util.Objects.isNull; + import java.util.HashMap; import java.util.Map; import net.catenax.edc.cp.adapter.messaging.Message; +import net.catenax.edc.cp.adapter.util.LockMap; public class InMemoryDataStore implements DataStore { private final Map messages = new HashMap<>(); - private final Map confirmedContracts = new HashMap<>(); - private final DataStoreLock lock; + private final Map contractInfoMap = new HashMap<>(); + private final LockMap locks; - public InMemoryDataStore(DataStoreLock lock) { - this.lock = lock; + public InMemoryDataStore(LockMap locks) { + this.locks = locks; } @Override - public void storeConfirmedContract(String contractNegotiationId, String contractAgreementId) { - lock.writeLock(contractNegotiationId); - confirmedContracts.put(contractNegotiationId, new ContractInfo(contractAgreementId, ContractInfo.ContractState.CONFIRMED)); - lock.writeUnlock(contractNegotiationId); + public Message exchangeConfirmedContract(String negotiationId, String agreementId) { + locks.lock(negotiationId); + Message message = messages.get(negotiationId); + if (isNull(message)) { + contractInfoMap.put(negotiationId, + new ContractInfo(agreementId, ContractInfo.ContractState.CONFIRMED)); + } + locks.unlock(negotiationId); + return message; } @Override - public void storeDeclinedContract(String contractNegotiationId) { - lock.writeLock(contractNegotiationId); - confirmedContracts.put(contractNegotiationId, new ContractInfo(ContractInfo.ContractState.DECLINED)); - lock.writeUnlock(contractNegotiationId); + public Message exchangeDeclinedContract(String negotiationId) { + locks.lock(negotiationId); + Message message = messages.get(negotiationId); + if (isNull(message)) { + contractInfoMap.put( + negotiationId, + new ContractInfo(ContractInfo.ContractState.DECLINED)); + } + locks.unlock(negotiationId); + return message; } @Override - public void storeErrorContract(String contractNegotiationId) { - lock.writeLock(contractNegotiationId); - confirmedContracts.put(contractNegotiationId, new ContractInfo(ContractInfo.ContractState.ERROR)); - lock.writeUnlock(contractNegotiationId); - } + public Message exchangeErrorContract(String negotiationId) { + locks.lock(negotiationId); + Message message = messages.get(negotiationId); + if (isNull(message)) { + contractInfoMap.put( + negotiationId, + new ContractInfo(ContractInfo.ContractState.ERROR)); + } - @Override - public ContractInfo getContractInfo(String contractNegotiationId) { - lock.readLock(contractNegotiationId); - ContractInfo contractInfo = confirmedContracts.get(contractNegotiationId); - lock.readUnlock(contractNegotiationId); - return contractInfo; + locks.unlock(negotiationId); + return message; } @Override - public void removeConfirmedContract(String contractNegotiationId) { - confirmedContracts.remove(contractNegotiationId); - lock.removeLock(contractNegotiationId); - } + public ContractInfo exchangeMessage(Message message) { + String negotiationId = message.getPayload().getContractNegotiationId(); - @Override - public void storeMessage(Message message) { - String contractNegotiationId = message.getPayload().getContractNegotiationId(); + locks.lock(negotiationId); + ContractInfo contractInfo = contractInfoMap.get(negotiationId); + if (isNull(contractInfo)) { + messages.put(negotiationId, message); + } - lock.writeLock(contractNegotiationId); - messages.put(contractNegotiationId, message); - lock.writeUnlock(contractNegotiationId); + locks.unlock(negotiationId); + return contractInfo; } @Override - public Message getMessage(String contractNegotiationId) { - lock.readLock(contractNegotiationId); - Message message = messages.get(contractNegotiationId); - lock.readUnlock(contractNegotiationId); - return message; + public void removeContractInfo(String negotiationId) { + contractInfoMap.remove(negotiationId); + locks.removeLock(negotiationId); } @Override - public void removeMessage(String contractNegotiationId) { - messages.remove(contractNegotiationId); - lock.removeLock(contractNegotiationId); + public void removeMessage(String negotiationId) { + messages.remove(negotiationId); + locks.removeLock(negotiationId); } } diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataReferenceHandler.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataReferenceHandler.java index cb5e79dfd..3d5c97696 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataReferenceHandler.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataReferenceHandler.java @@ -25,9 +25,8 @@ public void process(Message message) { String contractAgreementId = message.getPayload().getContractAgreementId(); monitor.info(String.format("[%s] DataReference message received.", message.getTraceId())); - EndpointDataReference dataReference = dataStore.getDataReference(contractAgreementId); + EndpointDataReference dataReference = dataStore.exchangeMessage(message, contractAgreementId); if (isNull(dataReference)) { - dataStore.storeMessage(message); return; } @@ -42,9 +41,8 @@ public CompletableFuture> send(@NotNull EndpointDataReference dataR String contractAgreementId = dataReference.getProperties().get("cid"); monitor.info(String.format("DataReference received, contractAgr.: %s", contractAgreementId)); - Message message = dataStore.getMessage(contractAgreementId); + Message message = dataStore.exchangeDataReference(dataReference, contractAgreementId); if (isNull(message)) { - dataStore.storeDataReference(contractAgreementId, dataReference); return CompletableFuture.completedFuture(Result.success()); } message.getPayload().setEndpointDataReference(dataReference); diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataStore.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataStore.java index 4f4a61f6b..3914fa1ac 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataStore.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataStore.java @@ -4,15 +4,11 @@ import org.eclipse.dataspaceconnector.spi.types.domain.edr.EndpointDataReference; public interface DataStore { - void storeDataReference(String contractAgreementId, EndpointDataReference endpointDataReference); + EndpointDataReference exchangeMessage(Message message, String contractAgreementId); - EndpointDataReference getDataReference(String contractAgreementId); + Message exchangeDataReference(EndpointDataReference dataReference, String contractAgreementId); void removeDataReference(String contractAgreementId); - void storeMessage(Message message); - - Message getMessage(String key); - void removeMessage(String key); } diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/InMemoryDataStore.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/InMemoryDataStore.java index 8c2c75634..8eb21c991 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/InMemoryDataStore.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/InMemoryDataStore.java @@ -1,42 +1,51 @@ package net.catenax.edc.cp.adapter.process.datareference; +import static java.util.Objects.isNull; + import java.util.HashMap; import java.util.Map; +import lombok.RequiredArgsConstructor; import net.catenax.edc.cp.adapter.messaging.Message; +import net.catenax.edc.cp.adapter.util.LockMap; import org.eclipse.dataspaceconnector.spi.types.domain.edr.EndpointDataReference; +@RequiredArgsConstructor public class InMemoryDataStore implements DataStore { private final Map messages = new HashMap<>(); private final Map dataReferences = new HashMap<>(); + private final LockMap locks; @Override - public void storeDataReference( - String contractAgreementId, EndpointDataReference endpointDataReference) { - dataReferences.put(contractAgreementId, endpointDataReference); - } - - @Override - public EndpointDataReference getDataReference(String contractAgreementId) { - return dataReferences.get(contractAgreementId); - } - - @Override - public void removeDataReference(String contractAgreementId) { - dataReferences.remove(contractAgreementId); + public EndpointDataReference exchangeMessage(Message message, String agreementId) { + locks.lock(agreementId); + EndpointDataReference dataReference = dataReferences.get(agreementId); + if (isNull(dataReference)) { + messages.put(agreementId, message); + } + locks.unlock(agreementId); + return dataReference; } @Override - public void storeMessage(Message message) { - messages.put(message.getPayload().getContractAgreementId(), message); + public Message exchangeDataReference(EndpointDataReference dataReference, String agreementId) { + locks.lock(agreementId); + Message message = messages.get(agreementId); + if (isNull(message)) { + dataReferences.put(agreementId, dataReference); + } + locks.unlock(agreementId); + return message; } @Override - public Message getMessage(String contractAgreementId) { - return messages.get(contractAgreementId); + public void removeDataReference(String agreementId) { + dataReferences.remove(agreementId); + locks.removeLock(agreementId); } @Override - public void removeMessage(String contractAgreementId) { - messages.remove(contractAgreementId); + public void removeMessage(String agreementId) { + messages.remove(agreementId); + locks.removeLock(agreementId); } } diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/service/ErrorResultService.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/service/ErrorResultService.java index 22d6f3a17..a470fbe74 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/service/ErrorResultService.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/service/ErrorResultService.java @@ -1,6 +1,9 @@ package net.catenax.edc.cp.adapter.service; import jakarta.ws.rs.core.Response; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; import lombok.RequiredArgsConstructor; import net.catenax.edc.cp.adapter.exception.ExternalRequestException; import net.catenax.edc.cp.adapter.exception.ResourceNotFoundException; @@ -10,41 +13,38 @@ import net.catenax.edc.cp.adapter.messaging.MessageService; import org.eclipse.dataspaceconnector.spi.monitor.Monitor; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; - @RequiredArgsConstructor public class ErrorResultService implements Listener { - private static final Map, Response.Status> statusOfException = new HashMap<>(); - static { - statusOfException.put(ExternalRequestException.class, Response.Status.BAD_GATEWAY); - statusOfException.put(ResourceNotFoundException.class, Response.Status.NOT_FOUND); - } + private static final Map, Response.Status> statusOfException = new HashMap<>(); + + static { + statusOfException.put(ExternalRequestException.class, Response.Status.BAD_GATEWAY); + statusOfException.put(ResourceNotFoundException.class, Response.Status.NOT_FOUND); + } - private final Monitor monitor; - private final MessageService messageService; + private final Monitor monitor; + private final MessageService messageService; - @Override - public void process(Message message) { - message.getPayload().setErrorMessage(getErrorMessage(message)); - message.getPayload().setErrorStatus(statusOfException.getOrDefault( - message.getFinalException().getClass(), - Response.Status.INTERNAL_SERVER_ERROR)); - log(message); - messageService.send(Channel.RESULT, message); - } + @Override + public void process(Message message) { + message.getPayload().setErrorMessage(getErrorMessage(message)); + message.getPayload().setErrorStatus(statusOfException.getOrDefault( + message.getFinalException().getClass(), + Response.Status.INTERNAL_SERVER_ERROR)); + log(message); + messageService.send(Channel.RESULT, message); + } - private String getErrorMessage(Message message) { - return Objects.nonNull(message.getFinalException()) - ? message.getFinalException().getMessage() - : "Unrecognized Exception."; - } + private String getErrorMessage(Message message) { + return Objects.nonNull(message.getFinalException()) + ? message.getFinalException().getMessage() + : "Unrecognized Exception."; + } - private void log(Message message) { - monitor.info(String.format("[%s] Sending ERROR message to RESULT channel: %s / %s ", - message.getTraceId(), - message.getPayload().getErrorMessage(), - message.getPayload().getErrorStatus())); - } + private void log(Message message) { + monitor.info(String.format("[%s] Sending ERROR message to RESULT channel: %s / %s ", + message.getTraceId(), + message.getPayload().getErrorMessage(), + message.getPayload().getErrorStatus())); + } } diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/service/ResultService.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/service/ResultService.java index e46b7d273..c34c30148 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/service/ResultService.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/service/ResultService.java @@ -16,15 +16,13 @@ public class ResultService implements Listener { private final int CAPACITY = 1; private final int DEFAULT_TIMEOUT = 15; // TODO move to config - private final Map> results = - new ConcurrentHashMap<>(); + private final Map> results = new ConcurrentHashMap<>(); public ProcessData pull(String id) throws InterruptedException { return pull(id, DEFAULT_TIMEOUT, SECONDS); } - public ProcessData pull(String id, long timeout, TimeUnit unit) - throws InterruptedException { + public ProcessData pull(String id, long timeout, TimeUnit unit) throws InterruptedException { if (!results.containsKey(id)) { initiate(id); } diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/util/LockMap.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/util/LockMap.java new file mode 100644 index 000000000..3ccbb18e2 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/util/LockMap.java @@ -0,0 +1,30 @@ +package net.catenax.edc.cp.adapter.util; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.locks.ReentrantLock; + +public class LockMap { + private final Map lock = new HashMap<>(); + + public void lock(String id) { + addLock(id); + lock.get(id).lock(); + } + + public void unlock(String id) { + addLock(id); + lock.get(id).unlock(); + } + + public void removeLock(String id) { + addLock(id); + lock.remove(id); + } + + private void addLock(String id) { + synchronized (this) { + lock.putIfAbsent(id, new ReentrantLock()); + } + } +} diff --git a/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/contractnegotiation/ContractNegotiationHandlerTest.java b/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/contractnegotiation/ContractNegotiationHandlerTest.java index b94353ed3..548b21cb6 100644 --- a/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/contractnegotiation/ContractNegotiationHandlerTest.java +++ b/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/contractnegotiation/ContractNegotiationHandlerTest.java @@ -39,8 +39,7 @@ void init() { @Test public void process_shouldNotInitializeContractNegotiationWhenCachedContractAlreadyAvailable() { // given - ContractNegotiationHandler contractNegotiationHandler = - new ContractNegotiationHandler( + ContractNegotiationHandler contractNegotiationHandler = new ContractNegotiationHandler( monitor, messageService, contractNegotiationService, catalogService, contractDataStore); when(contractDataStore.get(anyString(), anyString())) @@ -57,8 +56,7 @@ public void process_shouldNotInitializeContractNegotiationWhenCachedContractAlre @Test public void process_shouldInitializeContractNegotiationWhenCachedContractExpired() { // given - ContractNegotiationHandler contractNegotiationHandler = - new ContractNegotiationHandler( + ContractNegotiationHandler contractNegotiationHandler = new ContractNegotiationHandler( monitor, messageService, contractNegotiationService, catalogService, contractDataStore); when(contractDataStore.get(anyString(), anyString())) diff --git a/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractNotificationHandlerTest.java b/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractNotificationHandlerTest.java index ce903ee64..658fb906b 100644 --- a/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractNotificationHandlerTest.java +++ b/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractNotificationHandlerTest.java @@ -27,8 +27,7 @@ public class ContractNotificationHandlerTest { @Mock Monitor monitor; @Mock MessageService messageService; @Mock ContractNegotiationService contractNegotiationService; - @Mock - DataStore dataStore; + @Mock DataStore dataStore; @Mock ContractDataStore contractDataStore; @Mock TransferProcessService transferProcessService; @@ -38,7 +37,7 @@ void init() { } @Test - public void process_shouldSaveMessageWhenNoContractNotification() { + public void process_shouldNotInitiateTransferWhenNoContractNotification() { // given when(transferProcessService.initiateTransfer(any())).thenReturn(ServiceResult.success(null)); ContractNotificationHandler contractNotificationHandler = @@ -55,7 +54,7 @@ public void process_shouldSaveMessageWhenNoContractNotification() { contractNotificationHandler.process(message); // then - verify(dataStore, times(1)).storeMessage(any()); + verify(dataStore, times(1)).exchangeMessage(any()); verify(transferProcessService, times(0)).initiateTransfer(any()); verify(messageService, times(0)).send(eq(Channel.DATA_REFERENCE), any(Message.class)); } @@ -102,7 +101,7 @@ public void process_shouldInitiateTransferWhenContractAlreadyConfirmedAtProvider contractNotificationHandler.process(message); // then - verify(dataStore, times(0)).storeMessage(any()); + verify(dataStore, times(0)).exchangeMessage(any()); verify(transferProcessService, times(1)).initiateTransfer(any()); verify(messageService, times(1)).send(eq(Channel.DATA_REFERENCE), any(Message.class)); } @@ -110,7 +109,9 @@ public void process_shouldInitiateTransferWhenContractAlreadyConfirmedAtProvider @Test public void process_shouldInitiateTransferWhenContractConfirmedByNotification() { // given - when(dataStore.getContractInfo(any())).thenReturn(new ContractInfo("confirmedContractAgreementId", ContractInfo.ContractState.CONFIRMED)); + when(dataStore.exchangeMessage(any())) + .thenReturn( + new ContractInfo("confirmedContractAgreementId", ContractInfo.ContractState.CONFIRMED)); when(transferProcessService.initiateTransfer(any())).thenReturn(ServiceResult.success(null)); ContractNotificationHandler contractNotificationHandler = new ContractNotificationHandler( @@ -126,14 +127,13 @@ public void process_shouldInitiateTransferWhenContractConfirmedByNotification() contractNotificationHandler.process(message); // then - verify(dataStore, times(0)).storeMessage(any()); verify(transferProcessService, times(1)).initiateTransfer(any()); verify(messageService, times(1)).send(eq(Channel.DATA_REFERENCE), any(Message.class)); - verify(dataStore, times(1)).removeConfirmedContract(any()); + verify(dataStore, times(1)).removeContractInfo(any()); } @Test - public void preConfirmed_shouldSaveInfoAboutContractConfirmationIfMessageNotAvailable() { + public void preConfirmed_shouldNotInitiateTransferIfMessageNotAvailable() { // given ContractNotificationHandler contractNotificationHandler = new ContractNotificationHandler( @@ -149,7 +149,7 @@ public void preConfirmed_shouldSaveInfoAboutContractConfirmationIfMessageNotAvai contractNotificationHandler.preConfirmed(contractNegotiation); // then - verify(dataStore, times(1)).storeConfirmedContract(any(), any()); + verify(dataStore, times(1)).exchangeConfirmedContract(any(), any()); verify(transferProcessService, times(0)).initiateTransfer(any()); verify(messageService, times(0)).send(eq(Channel.DATA_REFERENCE), any(Message.class)); } @@ -157,24 +157,23 @@ public void preConfirmed_shouldSaveInfoAboutContractConfirmationIfMessageNotAvai @Test public void preConfirmed_shouldInitiateTransferIfMessageIsAvailable() { // given - when(dataStore.getMessage(any())) - .thenReturn(new Message(new ProcessData("assetId", "providerUrl"))); + when(dataStore.exchangeConfirmedContract(any(), any())) + .thenReturn(new Message(new ProcessData("assetId", "providerUrl"))); when(transferProcessService.initiateTransfer(any())).thenReturn(ServiceResult.success(null)); ContractNotificationHandler contractNotificationHandler = - new ContractNotificationHandler( - monitor, - messageService, - dataStore, - contractNegotiationService, - transferProcessService, - contractDataStore); + new ContractNotificationHandler( + monitor, + messageService, + dataStore, + contractNegotiationService, + transferProcessService, + contractDataStore); ContractNegotiation contractNegotiation = getConfirmedContractNegotiation(); // when contractNotificationHandler.preConfirmed(contractNegotiation); // then - verify(dataStore, times(0)).storeConfirmedContract(any(), any()); verify(transferProcessService, times(1)).initiateTransfer(any()); verify(messageService, times(1)).send(eq(Channel.DATA_REFERENCE), any(Message.class)); } @@ -182,16 +181,16 @@ public void preConfirmed_shouldInitiateTransferIfMessageIsAvailable() { @Test public void preDeclined_shouldSendErrorResultIfMessageIsAvailable() { // given - when(dataStore.getMessage(any())) - .thenReturn(new Message(new ProcessData("assetId", "providerUrl"))); + when(dataStore.exchangeDeclinedContract(any())) + .thenReturn(new Message(new ProcessData("assetId", "providerUrl"))); ContractNotificationHandler contractNotificationHandler = - new ContractNotificationHandler( - monitor, - messageService, - dataStore, - contractNegotiationService, - transferProcessService, - contractDataStore); + new ContractNotificationHandler( + monitor, + messageService, + dataStore, + contractNegotiationService, + transferProcessService, + contractDataStore); ContractNegotiation contractNegotiation = getConfirmedContractNegotiation(); // when @@ -199,24 +198,24 @@ public void preDeclined_shouldSendErrorResultIfMessageIsAvailable() { // then ArgumentCaptor messageArg = ArgumentCaptor.forClass(Message.class); - verify(dataStore, times(0)).storeConfirmedContract(any(), any()); verify(messageService, times(1)).send(eq(Channel.RESULT), messageArg.capture()); - Assertions.assertEquals(Response.Status.BAD_GATEWAY, messageArg.getValue().getPayload().getErrorStatus()); + Assertions.assertEquals( + Response.Status.BAD_GATEWAY, messageArg.getValue().getPayload().getErrorStatus()); } @Test public void preError_shouldSendErrorResultIfMessageIsAvailable() { // given - when(dataStore.getMessage(any())) - .thenReturn(new Message(new ProcessData("assetId", "providerUrl"))); + when(dataStore.exchangeErrorContract(any())) + .thenReturn(new Message(new ProcessData("assetId", "providerUrl"))); ContractNotificationHandler contractNotificationHandler = - new ContractNotificationHandler( - monitor, - messageService, - dataStore, - contractNegotiationService, - transferProcessService, - contractDataStore); + new ContractNotificationHandler( + monitor, + messageService, + dataStore, + contractNegotiationService, + transferProcessService, + contractDataStore); ContractNegotiation contractNegotiation = getConfirmedContractNegotiation(); // when @@ -224,9 +223,9 @@ public void preError_shouldSendErrorResultIfMessageIsAvailable() { // then ArgumentCaptor messageArg = ArgumentCaptor.forClass(Message.class); - verify(dataStore, times(0)).storeConfirmedContract(any(), any()); verify(messageService, times(1)).send(eq(Channel.RESULT), messageArg.capture()); - Assertions.assertEquals(Response.Status.BAD_GATEWAY, messageArg.getValue().getPayload().getErrorStatus()); + Assertions.assertEquals( + Response.Status.BAD_GATEWAY, messageArg.getValue().getPayload().getErrorStatus()); } private ContractNegotiation getConfirmedContractNegotiation() { diff --git a/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/datareference/DataReferenceHandlerTest.java b/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/datareference/DataReferenceHandlerTest.java index 62799f28a..173fd4a7e 100644 --- a/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/datareference/DataReferenceHandlerTest.java +++ b/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/datareference/DataReferenceHandlerTest.java @@ -26,7 +26,7 @@ void init() { } @Test - public void process_shouldSaveMessageWhenDataReferenceNotAvailable() { + public void process_shouldNotSendResultWhenDataReferenceNotAvailable() { // given DataReferenceHandler dataReferenceHandler = new DataReferenceHandler(monitor, messageService, dataStore); @@ -36,14 +36,14 @@ public void process_shouldSaveMessageWhenDataReferenceNotAvailable() { dataReferenceHandler.process(message); // then - verify(dataStore, times(1)).storeMessage(eq(message)); + verify(dataStore, times(1)).exchangeMessage(eq(message), any()); verify(messageService, times(0)).send(eq(Channel.RESULT), any(Message.class)); } @Test public void process_shouldSendResultWhenDataReferenceIsAvailable() { // given - when(dataStore.getDataReference(any())).thenReturn(getEndpointDataReference()); + when(dataStore.exchangeMessage(any(), any())).thenReturn(getEndpointDataReference()); DataReferenceHandler dataReferenceHandler = new DataReferenceHandler(monitor, messageService, dataStore); Message message = new Message(new ProcessData("assetId", "providerUrl")); @@ -52,13 +52,12 @@ public void process_shouldSendResultWhenDataReferenceIsAvailable() { dataReferenceHandler.process(message); // then - verify(dataStore, times(0)).storeMessage(eq(message)); verify(messageService, times(1)).send(eq(Channel.RESULT), any(Message.class)); verify(dataStore, times(1)).removeDataReference(any()); } @Test - public void send_shouldSaveDataReferenceWhenMessageNotAvailable() { + public void send_shouldNotSendResultWhenMessageNotAvailable() { // given DataReferenceHandler dataReferenceHandler = new DataReferenceHandler(monitor, messageService, dataStore); @@ -67,7 +66,6 @@ public void send_shouldSaveDataReferenceWhenMessageNotAvailable() { dataReferenceHandler.send(getEndpointDataReference()); // then - verify(dataStore, times(1)).storeDataReference(any(), any(EndpointDataReference.class)); verify(messageService, times(0)).send(eq(Channel.RESULT), any(Message.class)); } @@ -75,7 +73,7 @@ public void send_shouldSaveDataReferenceWhenMessageNotAvailable() { public void send_shouldSendResultWhenMessageIsAvailable() { // given Message message = new Message(new ProcessData("assetId", "providerUrl")); - when(dataStore.getMessage(any())).thenReturn(message); + when(dataStore.exchangeDataReference(any(), any())).thenReturn(message); DataReferenceHandler dataReferenceHandler = new DataReferenceHandler(monitor, messageService, dataStore); @@ -83,7 +81,6 @@ public void send_shouldSendResultWhenMessageIsAvailable() { dataReferenceHandler.send(getEndpointDataReference()); // then - verify(dataStore, times(0)).storeDataReference(any(), any(EndpointDataReference.class)); verify(messageService, times(1)).send(eq(Channel.RESULT), any(Message.class)); verify(dataStore, times(1)).removeMessage(any()); } diff --git a/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/service/ResultServiceTest.java b/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/service/ResultServiceTest.java index ff272a3dd..d5b5a89d8 100644 --- a/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/service/ResultServiceTest.java +++ b/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/service/ResultServiceTest.java @@ -6,7 +6,6 @@ import net.catenax.edc.cp.adapter.dto.ProcessData; import net.catenax.edc.cp.adapter.messaging.Message; import org.eclipse.dataspaceconnector.spi.types.domain.edr.EndpointDataReference; -import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -80,14 +79,15 @@ public void process_shouldThrowIllegalArgumentExceptionIfNoDataPayload() { private Message getMessage(String endpointDataRefId) { Message message = new Message(new ProcessData("123", "providerUrl")); - message.getPayload() - .setEndpointDataReference( - EndpointDataReference.Builder.newInstance() - .id(endpointDataRefId) - .endpoint("e") - .authCode("c") - .authKey("k") - .build()); + message + .getPayload() + .setEndpointDataReference( + EndpointDataReference.Builder.newInstance() + .id(endpointDataRefId) + .endpoint("e") + .authCode("c") + .authKey("k") + .build()); return message; } From 524a4bdbcd802c27480ffc344add1773487714bf Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Tue, 6 Sep 2022 14:40:18 +0200 Subject: [PATCH 276/433] add DEPENDENCIES file (#395) --- DEPENDENCIES | 161 ++++++++++++++++++++++++++++++++++++ docs/development/Release.md | 27 ++++++ pom.xml | 17 ++++ 3 files changed, 205 insertions(+) create mode 100644 DEPENDENCIES create mode 100644 docs/development/Release.md diff --git a/DEPENDENCIES b/DEPENDENCIES new file mode 100644 index 000000000..67be50f14 --- /dev/null +++ b/DEPENDENCIES @@ -0,0 +1,161 @@ +maven/mavencentral/com.azure/azure-core-http-netty/1.12.0, MIT, approved, clearlydefined +maven/mavencentral/com.azure/azure-core/1.28.0, MIT, approved, clearlydefined +maven/mavencentral/com.azure/azure-identity/1.5.1, MIT, approved, clearlydefined +maven/mavencentral/com.azure/azure-security-keyvault-secrets/4.4.2, MIT, approved, clearlydefined +maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.13.3, Apache-2.0, approved, CQ24135 +maven/mavencentral/com.fasterxml.jackson.core/jackson-core/2.13.3, Apache-2.0, approved, CQ24134 +maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.13.3, Apache-2.0, approved, CQ24136 +maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-xml/2.13.2, Apache-2.0, restricted, clearlydefined +maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jsr310/2.13.3, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.fasterxml.woodstox/woodstox-core/6.2.7, Apache-2.0, approved, CQ23040 +maven/mavencentral/com.github.stephenc.jcip/jcip-annotations/1.0-1, Apache-2.0, approved, CQ21949 +maven/mavencentral/com.microsoft.azure/msal4j-persistence-extension/1.1.0, MIT, approved, clearlydefined +maven/mavencentral/com.microsoft.azure/msal4j/1.12.0, MIT, approved, clearlydefined +maven/mavencentral/com.nimbusds/content-type/2.2, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.nimbusds/lang-tag/1.6, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.nimbusds/nimbus-jose-jwt/9.21, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.nimbusds/nimbus-jose-jwt/9.24.3, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.nimbusds/oauth2-oidc-sdk/9.32, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.squareup.okhttp3/okhttp/4.9.3, Apache-2.0, restricted, clearlydefined +maven/mavencentral/com.squareup.okio/okio/2.8.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/de.fraunhofer.iais.eis.ids.infomodel/java/4.1.3, , restricted, clearlydefined +maven/mavencentral/de.fraunhofer.iais.eis.infomodel/util/4.1.3, , restricted, clearlydefined +maven/mavencentral/dev.failsafe/failsafe/3.2.4, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.micrometer/micrometer-core/1.8.2, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.netty/netty-buffer/4.1.76.Final, Apache-2.0, approved, CQ21842 +maven/mavencentral/io.netty/netty-codec-dns/4.1.75.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-codec-http/4.1.76.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-codec-http2/4.1.76.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-codec-socks/4.1.76.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-codec/4.1.76.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-common/4.1.76.Final, Apache-2.0 AND MIT AND CC0-1.0, approved, CQ21843 +maven/mavencentral/io.netty/netty-handler-proxy/4.1.76.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-handler/4.1.76.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-resolver-dns-classes-macos/4.1.75.Final, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.netty/netty-resolver-dns-native-macos/4.1.75.Final, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.netty/netty-resolver-dns/4.1.75.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-resolver/4.1.76.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-tcnative-boringssl-static/2.0.51.Final, Apache-2.0 OR LicenseRef-Public-Domain OR BSD-2-Clause OR MIT, approved, CQ15280 +maven/mavencentral/io.netty/netty-tcnative-classes/2.0.51.Final, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.netty/netty-transport-classes-epoll/4.1.76.Final, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.netty/netty-transport-classes-kqueue/4.1.76.Final, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.netty/netty-transport-native-epoll/4.1.76.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-transport-native-kqueue/4.1.76.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-transport-native-unix-common/4.1.76.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-transport/4.1.76.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.projectreactor.netty/reactor-netty-core/1.0.18, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.projectreactor.netty/reactor-netty-http/1.0.18, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.projectreactor/reactor-core/3.4.17, Apache-2.0, approved, clearlydefined +maven/mavencentral/jakarta.ws.rs/jakarta.ws.rs-api/3.1.0, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.rest +maven/mavencentral/javax.validation/validation-api/2.0.1.Final, Apache-2.0, approved, CQ15302 +maven/mavencentral/net.catenax.edc.extensions/business-partner-validation/0.1.1-SNAPSHOT, , restricted, clearlydefined +maven/mavencentral/net.catenax.edc.extensions/cx-oauth2/0.1.1-SNAPSHOT, , restricted, clearlydefined +maven/mavencentral/net.catenax.edc.extensions/data-encryption/0.1.1-SNAPSHOT, , restricted, clearlydefined +maven/mavencentral/net.catenax.edc.extensions/dataplane-selector-configuration/0.1.1-SNAPSHOT, , restricted, clearlydefined +maven/mavencentral/net.catenax.edc.extensions/hashicorp-vault/0.1.1-SNAPSHOT, , restricted, clearlydefined +maven/mavencentral/net.catenax.edc.extensions/postgresql-migration/0.1.1-SNAPSHOT, , restricted, clearlydefined +maven/mavencentral/net.catenax.edc/edc-controlplane-base/0.1.1-SNAPSHOT, , restricted, clearlydefined +maven/mavencentral/net.catenax.edc/edc-controlplane-postgresql/0.1.1-SNAPSHOT, , restricted, clearlydefined +maven/mavencentral/net.catenax.edc/edc-dataplane-base/0.1.1-SNAPSHOT, , restricted, clearlydefined +maven/mavencentral/net.java.dev.jna/jna-platform/5.6.0, Apache-2.0 OR LGPL-2.1-or-later, approved, CQ22390 +maven/mavencentral/net.java.dev.jna/jna/5.5.0, Apache-2.0 or LGPL-2.1, approved, #1508 +maven/mavencentral/net.minidev/accessors-smart/2.4.8, Apache-2.0, approved, clearlydefined +maven/mavencentral/net.minidev/json-smart/2.4.8, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.bouncycastle/bcpkix-jdk15on/1.70, MIT, approved, clearlydefined +maven/mavencentral/org.bouncycastle/bcprov-jdk15on/1.70, MIT, approved, #1712 +maven/mavencentral/org.bouncycastle/bcutil-jdk15on/1.70, MIT, approved, clearlydefined +maven/mavencentral/org.codehaus.woodstox/stax2-api/4.2.1, BSD-2-Clause, approved, #2670 +maven/mavencentral/org.eclipse.dataspaceconnector/apache-commons-pool-sql/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/api-configuration/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/api-core/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/asset-api/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/asset-index-sql/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/auth-spi/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/auth-tokenbased/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/azure-vault/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/catalog-api/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/catalog-spi/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/common-sql/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/common-util/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/contract-spi/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/contract/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/contractagreement-api/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/contractdefinition-api/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/contractdefinition-store-sql/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/contractnegotiation-api/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/contractnegotiation-store-sql/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/control-plane-spi/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/core-base/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/core-boot/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/core-micrometer/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/core-spi/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/data-management-api/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/data-plane-api/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/data-plane-framework/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/data-plane-http/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/data-plane-s3/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/data-plane-selector-client/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/data-plane-selector-core/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/data-plane-selector-spi/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/data-plane-spi/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/data-plane-transfer-spi/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/data-plane-transfer-sync/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/filesystem-configuration/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/http-receiver/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/http/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/ids-api-configuration/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/ids-api-multipart-dispatcher-v1/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/ids-api-multipart-endpoint-v1/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/ids-api-transform-v1/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/ids-core/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/ids-spi/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/jdk-logger-monitor/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/jersey-micrometer/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/jersey/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/jetty-micrometer/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/jetty/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/jwt-spi/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/oauth2-spi/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/observability-api/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/policy-engine/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/policy-evaluator/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/policy-spi/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/policy-store-sql/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/policydefinition-api/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/s3-core/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/transaction-datasource-spi/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/transaction-local/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/transaction-spi/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/transfer-process-store-sql/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/transfer-spi/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/transfer/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/transferprocess-api/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/transport-spi/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/web-spi/0.0.1-20220902-SNAPSHOT, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.flywaydb/flyway-core/9.2.2, NOASSERTION, restricted, clearlydefined +maven/mavencentral/org.hdrhistogram/HdrHistogram/2.1.12, , approved, CQ13192 +maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib/1.4.10, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.jetbrains/annotations/15.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.ow2.asm/asm/9.2, BSD-3-Clause, approved, CQ23635 +maven/mavencentral/org.postgresql/postgresql/42.5.0, BSD-2-Clause, restricted, clearlydefined +maven/mavencentral/org.projectlombok/lombok/1.18.24, MIT AND LicenseRef-Public-Domain, approved, CQ23907 +maven/mavencentral/org.reactivestreams/reactive-streams/1.0.3, CC0-1.0, approved, CQ16332 +maven/mavencentral/org.slf4j/slf4j-api/2.0.0-beta1, MIT, approved, CQ24150 +maven/mavencentral/software.amazon.awssdk/annotations/2.16.60, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/arns/2.16.60, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/auth/2.16.60, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/aws-core/2.16.60, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/aws-query-protocol/2.16.60, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/aws-xml-protocol/2.16.60, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/http-client-spi/2.16.60, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/iam/2.16.60, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/metrics-spi/2.16.60, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/profiles/2.16.60, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/protocol-core/2.16.60, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/regions/2.16.60, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/s3/2.16.60, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/sdk-core/2.16.60, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/sts/2.16.60, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/utils/2.16.60, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.eventstream/eventstream/1.0.1, Apache-2.0, approved, clearlydefined diff --git a/docs/development/Release.md b/docs/development/Release.md new file mode 100644 index 000000000..ad6e66777 --- /dev/null +++ b/docs/development/Release.md @@ -0,0 +1,27 @@ +# Release + +## Prerequisites + +[![Apache Maven][maven-shield]][maven-url] + +## Update DEPENDENCIES file +### 1. Setup Eclipse Dash License Tool Maven Plugin locally + +At the time of writing there maven plugin could not be downloaded from the repository. +As alternative check out the repository and build the plugin locally. + +#### 1.1 Checkout repository + +`git clone https://github.com/eclipse/dash-licenses.git` + +#### 1.2 Install Plugin in local maven repository + +`mvn clean install` + +### 2. Generate DEPENDENCIES file + +`./mvnw org.eclipse.dash:license-tool-plugin:license-check -Ddash.summary=DEPENDENCIES` + + +[maven-shield]: https://img.shields.io/badge/Apache%20Maven-URL-blue +[maven-url]: https://maven.apache.org \ No newline at end of file diff --git a/pom.xml b/pom.xml index 3ba603c31..990c4b546 100644 --- a/pom.xml +++ b/pom.xml @@ -63,6 +63,7 @@ 1.1.0 3.9.1.2184 4.2.0 + 0.0.1-SNAPSHOT 0.0.1-20220902-SNAPSHOT @@ -112,6 +113,16 @@ Fraunhofer IAIS https://maven.iais.fraunhofer.de/artifactory/eis-ids-public
+ + dash-licenses-snapshots + https://repo.eclipse.org/content/repositories/dash-licenses-snapshots + + true + + + false + +
@@ -192,6 +203,11 @@ maven-compiler-plugin ${org.apache.maven.plugins.compiler.version} + + org.eclipse.dash + license-tool-plugin + ${org.eclipse.dash.license.tool.plugin.version} + org.projectlombok lombok-maven-plugin @@ -1206,5 +1222,6 @@ + From 9e94105f107c8e12b0c56b3b6206be86db6d03d6 Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Tue, 6 Sep 2022 14:40:30 +0200 Subject: [PATCH 277/433] Feature/rename workflow (#396) --- .github/workflows/{kics.yaml => kics.yml} | 0 .github/workflows/{trivy.yaml => trivy.yml} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{kics.yaml => kics.yml} (100%) rename .github/workflows/{trivy.yaml => trivy.yml} (100%) diff --git a/.github/workflows/kics.yaml b/.github/workflows/kics.yml similarity index 100% rename from .github/workflows/kics.yaml rename to .github/workflows/kics.yml diff --git a/.github/workflows/trivy.yaml b/.github/workflows/trivy.yml similarity index 100% rename from .github/workflows/trivy.yaml rename to .github/workflows/trivy.yml From 640c60a859d6f9835486e1ca9a8557e32d7d1e7a Mon Sep 17 00:00:00 2001 From: Dominik-Pinsel Date: Tue, 6 Sep 2022 16:20:24 +0200 Subject: [PATCH 278/433] add control-plane-core extension to inmemory image (#397) --- docs/release-notes/Version 0.1.1.md | 4 ++++ edc-controlplane/edc-controlplane-memory/pom.xml | 4 ++++ pom.xml | 5 +++++ 3 files changed, 13 insertions(+) diff --git a/docs/release-notes/Version 0.1.1.md b/docs/release-notes/Version 0.1.1.md index 8f6cf67b7..a56d1f307 100644 --- a/docs/release-notes/Version 0.1.1.md +++ b/docs/release-notes/Version 0.1.1.md @@ -6,6 +6,10 @@ > > Please consolidate the migration documentation ([link](../migration/Version_0.1.0_0.1.1.md)). +> **Important Notice** +> +> The **InMemoryControlPlane** image is broken. Please use another control plane instead. + ## 0. Summary - 1. Eclipse Dataspace Connector Update diff --git a/edc-controlplane/edc-controlplane-memory/pom.xml b/edc-controlplane/edc-controlplane-memory/pom.xml index 8d3fb82da..d9f574bdd 100644 --- a/edc-controlplane/edc-controlplane-memory/pom.xml +++ b/edc-controlplane/edc-controlplane-memory/pom.xml @@ -87,6 +87,10 @@ net.catenax.edc edc-controlplane-base + + org.eclipse.dataspaceconnector + control-plane-core + diff --git a/pom.xml b/pom.xml index 990c4b546..e8c822602 100644 --- a/pom.xml +++ b/pom.xml @@ -609,6 +609,11 @@ control-api ${org.eclipse.dataspaceconnector.version} + + org.eclipse.dataspaceconnector + control-plane-core + ${org.eclipse.dataspaceconnector.version} + org.eclipse.dataspaceconnector core From 9e5d21460057bb440e7942db96ae797b2ae10007 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Sep 2022 13:53:03 +0200 Subject: [PATCH 279/433] Bump rest-assured from 5.1.1 to 5.2.0 (#407) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e8c822602..a2053a457 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ 5.9.0 1.8.2 7.6.0 - 5.1.1 + 5.2.0 1.1.0 4.7.0 1.18.24 From 4453f264eb5b2180ec514f23e870892da6ebb9cf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Sep 2022 13:53:17 +0200 Subject: [PATCH 280/433] Bump mikefarah/yq from 4.27.3 to 4.27.5 (#404) --- .github/workflows/draft-new-release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index b4a8f32aa..318101a6e 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -43,7 +43,7 @@ jobs: GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} - name: Bump version in /charts - uses: mikefarah/yq@v4.27.3 + uses: mikefarah/yq@v4.27.5 with: cmd: |- find charts -name Chart.yaml | xargs -n1 yq -i '.appVersion = "${{ github.event.inputs.version }}" | .version = "${{ github.event.inputs.version }}"' From c0bb0b3982fe3eed4af8f881fc9fbc3a560375c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Sep 2022 13:53:30 +0200 Subject: [PATCH 281/433] Bump mockito-bom from 4.7.0 to 4.8.0 (#401) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a2053a457..df01955dc 100644 --- a/pom.xml +++ b/pom.xml @@ -77,7 +77,7 @@ 7.6.0 5.2.0 1.1.0 - 4.7.0 + 4.8.0 1.18.24 1.70 4.9.3 From c306d9079a8e947d7d390dd975045125e0cf1be7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Sep 2022 13:57:14 +0200 Subject: [PATCH 282/433] Bump flyway-core from 9.2.2 to 9.3.0 (#406) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index df01955dc..2c4f1d635 100644 --- a/pom.xml +++ b/pom.xml @@ -69,7 +69,7 @@ 0.0.1-20220902-SNAPSHOT 1.2.2 42.5.0 - 9.2.2 + 9.3.0 5.9.0 From 543ed342b06d5233ea7da706ad291192088f5cd3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Sep 2022 13:58:41 +0200 Subject: [PATCH 283/433] Bump actions/setup-java from 3.4.1 to 3.5.0 (#399) --- .github/workflows/build.yaml | 8 ++++---- .github/workflows/business-tests.yaml | 2 +- .github/workflows/draft-new-release.yaml | 2 +- .github/workflows/publish-new-release.yml | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 914bf0e31..666df70df 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -46,7 +46,7 @@ jobs: fetch-depth: 0 - name: Set up JDK 11 - uses: actions/setup-java@v3.4.1 + uses: actions/setup-java@v3.5.0 with: java-version: '11' distribution: 'adopt' @@ -69,7 +69,7 @@ jobs: fetch-depth: 0 - name: Set up JDK 11 - uses: actions/setup-java@v3.3.0 + uses: actions/setup-java@v3.5.0 with: java-version: '11' distribution: 'adopt' @@ -129,7 +129,7 @@ jobs: password: ${{ secrets.CXNG_GHCR_PAT }} - name: Set up JDK 11 - uses: actions/setup-java@v3.4.0 + uses: actions/setup-java@v3.5.0 with: java-version: '11' distribution: 'adopt' @@ -218,7 +218,7 @@ jobs: password: ${{ secrets.CXNG_GHCR_PAT }} - name: Set up JDK 11 - uses: actions/setup-java@v3.4.1 + uses: actions/setup-java@v3.5.0 with: java-version: '11' distribution: 'adopt' diff --git a/.github/workflows/business-tests.yaml b/.github/workflows/business-tests.yaml index 0932f6b6a..a82d161bd 100644 --- a/.github/workflows/business-tests.yaml +++ b/.github/workflows/business-tests.yaml @@ -23,7 +23,7 @@ jobs: uses: actions/checkout@v3 - name: Set-Up JDK 11 - uses: actions/setup-java@v3.4.0 + uses: actions/setup-java@v3.5.0 with: java-version: '11' distribution: 'adopt' diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index 318101a6e..e67a925a1 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -29,7 +29,7 @@ jobs: git config user.email noreply@github.com - name: Set up JDK 11 - uses: actions/setup-java@v3.4.1 + uses: actions/setup-java@v3.5.0 with: java-version: '11' distribution: 'adopt' diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index 300aea6e5..d08bf014c 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -69,7 +69,7 @@ jobs: uses: actions/checkout@v3 - name: Set up JDK 11 - uses: actions/setup-java@v3.4.1 + uses: actions/setup-java@v3.5.0 with: java-version: '11' distribution: 'adopt' @@ -181,7 +181,7 @@ jobs: prerelease: false - name: Set up JDK 11 - uses: actions/setup-java@v3.4.1 + uses: actions/setup-java@v3.5.0 with: java-version: '11' distribution: 'adopt' From 62e4a545c212449f61c95396e3be3ae1f686c06b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 15 Sep 2022 14:47:20 +0200 Subject: [PATCH 284/433] Bump cucumber.version from 7.6.0 to 7.7.0 (#400) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2c4f1d635..ac051f18b 100644 --- a/pom.xml +++ b/pom.xml @@ -74,7 +74,7 @@ 5.9.0 1.8.2 - 7.6.0 + 7.7.0 5.2.0 1.1.0 4.8.0 From 57a9c79ae15e1bd8ba365998507bd1b1fda73b28 Mon Sep 17 00:00:00 2001 From: marcingajek-zf Date: Thu, 15 Sep 2022 12:52:39 +0200 Subject: [PATCH 285/433] control-plane-adapter extension - code review fixes --- .../control-plane-adapter/README.md | 47 +++++++++++ edc-extensions/control-plane-adapter/pom.xml | 73 +++++++++++++++++- .../edc/cp/adapter/ApiAdapterConfig.java | 14 ++++ .../edc/cp/adapter/ApiAdapterExtension.java | 14 ++++ .../edc/cp/adapter/HttpController.java | 14 ++++ .../edc/cp/adapter/dto/ProcessData.java | 20 ++++- .../exception/ConfigurationException.java | 14 ++++ .../exception/ExternalRequestException.java | 14 ++++ .../exception/ResourceNotFoundException.java | 14 ++++ .../edc/cp/adapter/messaging/Channel.java | 14 ++++ .../messaging/InMemoryMessageService.java | 14 ++++ .../edc/cp/adapter/messaging/Listener.java | 14 ++++ .../cp/adapter/messaging/ListenerService.java | 14 ++++ .../edc/cp/adapter/messaging/Message.java | 14 ++++ .../cp/adapter/messaging/MessageService.java | 14 ++++ .../messaging/PersistentMessageService.java | 14 ++++ .../ContractAgreementData.java | 14 ++++ .../contractdatastore/ContractDataStore.java | 14 ++++ .../InMemoryContractDataStore.java | 14 ++++ .../ContractNegotiationHandler.java | 21 ++++- .../contractnotification/ContractInfo.java | 14 ++++ .../ContractNotificationHandler.java | 28 +++++-- .../contractnotification/DataStore.java | 14 ++++ .../InMemoryDataStore.java | 26 +++++-- .../datareference/DataReferenceHandler.java | 14 ++++ .../process/datareference/DataStore.java | 14 ++++ .../datareference/InMemoryDataStore.java | 14 ++++ .../adapter/service/ErrorResultService.java | 26 ++++++- .../edc/cp/adapter/service/ResultService.java | 14 ++++ .../catenax/edc/cp/adapter/util/LockMap.java | 14 ++++ ...spaceconnector.spi.system.ServiceExtension | 16 ++-- .../main/resources/control-plane-adapter.jpg | Bin 0 -> 460215 bytes .../edc/cp/adapter/HttpControllerTest.java | 14 ++++ .../messaging/InMemoryMessageServiceTest.java | 14 ++++ .../ContractNegotiationHandlerTest.java | 23 +++++- .../ContractNotificationHandlerTest.java | 14 ++++ .../DataReferenceHandlerTest.java | 14 ++++ .../cp/adapter/service/ResultServiceTest.java | 14 ++++ edc-extensions/pom.xml | 1 + edc-tests/pom.xml | 4 + pom.xml | 5 ++ 41 files changed, 646 insertions(+), 36 deletions(-) create mode 100644 edc-extensions/control-plane-adapter/README.md create mode 100644 edc-extensions/control-plane-adapter/src/main/resources/control-plane-adapter.jpg diff --git a/edc-extensions/control-plane-adapter/README.md b/edc-extensions/control-plane-adapter/README.md new file mode 100644 index 000000000..fdd6d227a --- /dev/null +++ b/edc-extensions/control-plane-adapter/README.md @@ -0,0 +1,47 @@ +# Control Plane Adapter Extension + +The goal of this extension is to simplify the process of retrieving data out of EDC. It returns "EndpointDataReference" object, hiding all the details about contract offers, contract negotiation process and retrieving DataReference from EDC control-plane. + +Additional requirements, that affects the architecture of the extension: +- can return data both in SYNC and ASYNC mode (currently only SYNC endpoint available) +- can be persistent, so that process can be restored from the point where it was before application was stopped (not implemented yet) +- prepared to scale horizontally (not implemented yet, as EDC itself is not scalable) +- can retry failed part of the process + +The simplified scenario, from the perspective of the client of the extension is as follows: +1. client sends a GET request with two parameters: + * assetId + * url of the provider control-plane +2. EndpointDataReference object is returned +3. client, using the DataReference, retrieves the Asset through data-plane + +Example of the EndpointDataReference response: + +```json +{ + "id": "ee8b758a-4b02-4cca-bb37-d0256b4638e7", + "endpoint": "http://consumer-dataplane:9192/public", + "authKey": "Authorization", + "authCode": "eyJhbGciOiJSUzI1NiJ9.eyJkYWQiOi..................", + "properties": { + "cid": "1:b2367617-5f51-48c5-9f25-e30a7299235c" + } +} +``` + +Diagram below shows the internal design of the extension: + +![diagram](src/main/resources/control-plane-adapter.jpg) + + + +TODO + +* new, separate endpoint to return EndpointDataReference in ASYNCHRONOUS way +* persistence of the process, now it works in "in memory" mode +* query assets by type, not just by ID +* optional parameters, like: + * request timeout + * turning on/off contract negotiation result cache + * ID of contract offer that should be used for negotiations +* DataReference error handling (now, if there is an error while waiting for EndpointDataReference notification, extension is not aware of it and process is stuck) diff --git a/edc-extensions/control-plane-adapter/pom.xml b/edc-extensions/control-plane-adapter/pom.xml index 04dd64788..54a3fa780 100644 --- a/edc-extensions/control-plane-adapter/pom.xml +++ b/edc-extensions/control-plane-adapter/pom.xml @@ -1,21 +1,90 @@ + - edc-extensions net.catenax.edc.extensions - 0.0.5-SNAPSHOT + edc-extensions + 0.1.1-SNAPSHOT 4.0.0 control-plane-adapter + jar 18 18 + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${maven.compiler.source} + ${maven.compiler.target} + ${project.build.sourceEncoding} + + + org.projectlombok + lombok + ${org.projectlombok.lombok.version} + + + + + + + org.projectlombok + lombok-maven-plugin + ${org.projectlombok.lombok.maven.plugin.version} + + + generate-sources + + delombok + + + + + ${originalSourceDirectory} + ${delombokSourceDirectory} + false + UTF-8 + + skip + + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar + + + + + + + diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/ApiAdapterConfig.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/ApiAdapterConfig.java index 3b2e91b1e..d00064bba 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/ApiAdapterConfig.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/ApiAdapterConfig.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * ZF Friedrichshafen AG - Initial API and Implementation + * + */ + package net.catenax.edc.cp.adapter; import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/ApiAdapterExtension.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/ApiAdapterExtension.java index 378c927c6..06d82c2fd 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/ApiAdapterExtension.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/ApiAdapterExtension.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * ZF Friedrichshafen AG - Initial API and Implementation + * + */ + package net.catenax.edc.cp.adapter; import static java.util.Objects.nonNull; diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/HttpController.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/HttpController.java index 0a9e1d673..58d70c19d 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/HttpController.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/HttpController.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * ZF Friedrichshafen AG - Initial API and Implementation + * + */ + package net.catenax.edc.cp.adapter; import static java.util.Objects.isNull; diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/dto/ProcessData.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/dto/ProcessData.java index d193474f0..91e8ddfe9 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/dto/ProcessData.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/dto/ProcessData.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * ZF Friedrichshafen AG - Initial API and Implementation + * + */ + package net.catenax.edc.cp.adapter.dto; import static java.lang.System.currentTimeMillis; @@ -13,17 +27,17 @@ public class ProcessData { private final long timestamp = currentTimeMillis(); - /** request data */ + // request data private final String assetId; private final String provider; private String contractOfferId; - /** contract data * */ + // contract data @Setter private String contractNegotiationId; @Setter private String contractAgreementId; @Setter private boolean isContractConfirmed = false; - /** result/response data * */ + // result data @Setter private EndpointDataReference endpointDataReference; @Setter private String errorMessage; @Setter private Response.Status errorStatus; diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ConfigurationException.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ConfigurationException.java index 8a9ea2236..260000136 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ConfigurationException.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ConfigurationException.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * ZF Friedrichshafen AG - Initial API and Implementation + * + */ + package net.catenax.edc.cp.adapter.exception; public class ConfigurationException extends RuntimeException { diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ExternalRequestException.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ExternalRequestException.java index 6ddecd980..a082a16dd 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ExternalRequestException.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ExternalRequestException.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * ZF Friedrichshafen AG - Initial API and Implementation + * + */ + package net.catenax.edc.cp.adapter.exception; public class ExternalRequestException extends RuntimeException { diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ResourceNotFoundException.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ResourceNotFoundException.java index 5851b4c66..825e032cd 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ResourceNotFoundException.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ResourceNotFoundException.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * ZF Friedrichshafen AG - Initial API and Implementation + * + */ + package net.catenax.edc.cp.adapter.exception; public class ResourceNotFoundException extends RuntimeException { diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/Channel.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/Channel.java index 5c2b5ac36..4ec21b96b 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/Channel.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/Channel.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * ZF Friedrichshafen AG - Initial API and Implementation + * + */ + package net.catenax.edc.cp.adapter.messaging; public enum Channel { diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/InMemoryMessageService.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/InMemoryMessageService.java index ea4e58f0f..256ee263d 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/InMemoryMessageService.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/InMemoryMessageService.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * ZF Friedrichshafen AG - Initial API and Implementation + * + */ + package net.catenax.edc.cp.adapter.messaging; import static java.util.Objects.isNull; diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/Listener.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/Listener.java index 948b40f9e..c9c2cee5d 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/Listener.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/Listener.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * ZF Friedrichshafen AG - Initial API and Implementation + * + */ + package net.catenax.edc.cp.adapter.messaging; public interface Listener { diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/ListenerService.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/ListenerService.java index 6bf0164eb..4418bd661 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/ListenerService.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/ListenerService.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * ZF Friedrichshafen AG - Initial API and Implementation + * + */ + package net.catenax.edc.cp.adapter.messaging; import static java.util.Objects.isNull; diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/Message.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/Message.java index 39db83f59..8920c9bcf 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/Message.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/Message.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * ZF Friedrichshafen AG - Initial API and Implementation + * + */ + package net.catenax.edc.cp.adapter.messaging; import java.util.UUID; diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/MessageService.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/MessageService.java index 31775e230..12227699f 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/MessageService.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/MessageService.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * ZF Friedrichshafen AG - Initial API and Implementation + * + */ + package net.catenax.edc.cp.adapter.messaging; public interface MessageService { diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/PersistentMessageService.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/PersistentMessageService.java index 1c7cce244..cd173724c 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/PersistentMessageService.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/PersistentMessageService.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * ZF Friedrichshafen AG - Initial API and Implementation + * + */ + package net.catenax.edc.cp.adapter.messaging; import org.eclipse.dataspaceconnector.spi.monitor.Monitor; diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/ContractAgreementData.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/ContractAgreementData.java index f475d3361..d59f68e4b 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/ContractAgreementData.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/ContractAgreementData.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * ZF Friedrichshafen AG - Initial API and Implementation + * + */ + package net.catenax.edc.cp.adapter.process.contractdatastore; import lombok.Getter; diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/ContractDataStore.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/ContractDataStore.java index f2c7ebc36..71e3876b3 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/ContractDataStore.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/ContractDataStore.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * ZF Friedrichshafen AG - Initial API and Implementation + * + */ + package net.catenax.edc.cp.adapter.process.contractdatastore; import org.eclipse.dataspaceconnector.spi.types.domain.contract.agreement.ContractAgreement; diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/InMemoryContractDataStore.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/InMemoryContractDataStore.java index da00c0cc9..be6c692db 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/InMemoryContractDataStore.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/InMemoryContractDataStore.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * ZF Friedrichshafen AG - Initial API and Implementation + * + */ + package net.catenax.edc.cp.adapter.process.contractdatastore; import java.util.HashMap; diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnegotiation/ContractNegotiationHandler.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnegotiation/ContractNegotiationHandler.java index 3a09214e8..0306cd9bc 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnegotiation/ContractNegotiationHandler.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnegotiation/ContractNegotiationHandler.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * ZF Friedrichshafen AG - Initial API and Implementation + * + */ + package net.catenax.edc.cp.adapter.process.contractnegotiation; import java.time.Instant; @@ -33,9 +47,10 @@ public class ContractNegotiationHandler implements Listener { @Override public void process(Message message) { - monitor.info(String.format("[%s] RequestHandler: input request: [%s]", - message.getTraceId(), - message.getPayload())); + monitor.info( + String.format( + "[%s] RequestHandler: input request: [%s]", + message.getTraceId(), message.getPayload())); ProcessData processData = message.getPayload(); ContractAgreementData contractData = diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractInfo.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractInfo.java index 44e0bc91c..46bd038d3 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractInfo.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractInfo.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * ZF Friedrichshafen AG - Initial API and Implementation + * + */ + package net.catenax.edc.cp.adapter.process.contractnotification; import lombok.Getter; diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractNotificationHandler.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractNotificationHandler.java index 0bfda5796..9c302cea4 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractNotificationHandler.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractNotificationHandler.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * ZF Friedrichshafen AG - Initial API and Implementation + * + */ + package net.catenax.edc.cp.adapter.process.contractnotification; import static jakarta.ws.rs.core.Response.Status; @@ -47,7 +61,8 @@ public void process(Message message) { ContractNegotiation contractNegotiation = contractNegotiationService.findbyId(contractNegotiationId); if (isContractConfirmed(contractNegotiation)) { - message.getPayload() + message + .getPayload() .setContractAgreementId(contractNegotiation.getContractAgreement().getId()); initiateDataTransfer(message); return; @@ -62,9 +77,11 @@ public void process(Message message) { message.getPayload().setContractAgreementId(contractInfo.getContractAgreementId()); initiateDataTransfer(message); } else { - sendErrorResult(message, Status.BAD_GATEWAY, contractInfo.isDeclined() - ? CONTRACT_DECLINED_MESSAGE - : CONTRACT_ERROR_MESSAGE); } + sendErrorResult( + message, + Status.BAD_GATEWAY, + contractInfo.isDeclined() ? CONTRACT_DECLINED_MESSAGE : CONTRACT_ERROR_MESSAGE); + } dataStore.removeContractInfo(contractNegotiationId); } @@ -151,7 +168,8 @@ private void sendInitiationRequest(Message message) { } private void throwDataRefRequestException(Message message) { - throw new ExternalRequestException(String.format( + throw new ExternalRequestException( + String.format( "Data reference initial request failed! AssetId: %s", message.getPayload().getAssetId())); } diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/DataStore.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/DataStore.java index 434b074c0..c58642cfa 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/DataStore.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/DataStore.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * ZF Friedrichshafen AG - Initial API and Implementation + * + */ + package net.catenax.edc.cp.adapter.process.contractnotification; import net.catenax.edc.cp.adapter.messaging.Message; diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/InMemoryDataStore.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/InMemoryDataStore.java index 12a58b086..e7b20d66b 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/InMemoryDataStore.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/InMemoryDataStore.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * ZF Friedrichshafen AG - Initial API and Implementation + * + */ + package net.catenax.edc.cp.adapter.process.contractnotification; import static java.util.Objects.isNull; @@ -21,8 +35,8 @@ public Message exchangeConfirmedContract(String negotiationId, String agreementI locks.lock(negotiationId); Message message = messages.get(negotiationId); if (isNull(message)) { - contractInfoMap.put(negotiationId, - new ContractInfo(agreementId, ContractInfo.ContractState.CONFIRMED)); + contractInfoMap.put( + negotiationId, new ContractInfo(agreementId, ContractInfo.ContractState.CONFIRMED)); } locks.unlock(negotiationId); return message; @@ -33,9 +47,7 @@ public Message exchangeDeclinedContract(String negotiationId) { locks.lock(negotiationId); Message message = messages.get(negotiationId); if (isNull(message)) { - contractInfoMap.put( - negotiationId, - new ContractInfo(ContractInfo.ContractState.DECLINED)); + contractInfoMap.put(negotiationId, new ContractInfo(ContractInfo.ContractState.DECLINED)); } locks.unlock(negotiationId); return message; @@ -46,9 +58,7 @@ public Message exchangeErrorContract(String negotiationId) { locks.lock(negotiationId); Message message = messages.get(negotiationId); if (isNull(message)) { - contractInfoMap.put( - negotiationId, - new ContractInfo(ContractInfo.ContractState.ERROR)); + contractInfoMap.put(negotiationId, new ContractInfo(ContractInfo.ContractState.ERROR)); } locks.unlock(negotiationId); diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataReferenceHandler.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataReferenceHandler.java index 3d5c97696..625038692 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataReferenceHandler.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataReferenceHandler.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * ZF Friedrichshafen AG - Initial API and Implementation + * + */ + package net.catenax.edc.cp.adapter.process.datareference; import static java.util.Objects.isNull; diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataStore.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataStore.java index 3914fa1ac..adef288f6 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataStore.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataStore.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * ZF Friedrichshafen AG - Initial API and Implementation + * + */ + package net.catenax.edc.cp.adapter.process.datareference; import net.catenax.edc.cp.adapter.messaging.Message; diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/InMemoryDataStore.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/InMemoryDataStore.java index 8eb21c991..41ee2a129 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/InMemoryDataStore.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/InMemoryDataStore.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * ZF Friedrichshafen AG - Initial API and Implementation + * + */ + package net.catenax.edc.cp.adapter.process.datareference; import static java.util.Objects.isNull; diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/service/ErrorResultService.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/service/ErrorResultService.java index a470fbe74..9b4e0b008 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/service/ErrorResultService.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/service/ErrorResultService.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * ZF Friedrichshafen AG - Initial API and Implementation + * + */ + package net.catenax.edc.cp.adapter.service; import jakarta.ws.rs.core.Response; @@ -28,9 +42,11 @@ public class ErrorResultService implements Listener { @Override public void process(Message message) { message.getPayload().setErrorMessage(getErrorMessage(message)); - message.getPayload().setErrorStatus(statusOfException.getOrDefault( - message.getFinalException().getClass(), - Response.Status.INTERNAL_SERVER_ERROR)); + message + .getPayload() + .setErrorStatus( + statusOfException.getOrDefault( + message.getFinalException().getClass(), Response.Status.INTERNAL_SERVER_ERROR)); log(message); messageService.send(Channel.RESULT, message); } @@ -42,7 +58,9 @@ private String getErrorMessage(Message message) { } private void log(Message message) { - monitor.info(String.format("[%s] Sending ERROR message to RESULT channel: %s / %s ", + monitor.info( + String.format( + "[%s] Sending ERROR message to RESULT channel: %s / %s ", message.getTraceId(), message.getPayload().getErrorMessage(), message.getPayload().getErrorStatus())); diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/service/ResultService.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/service/ResultService.java index c34c30148..786f78252 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/service/ResultService.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/service/ResultService.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * ZF Friedrichshafen AG - Initial API and Implementation + * + */ + package net.catenax.edc.cp.adapter.service; import static java.util.Objects.isNull; diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/util/LockMap.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/util/LockMap.java index 3ccbb18e2..f3c020c6e 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/util/LockMap.java +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/util/LockMap.java @@ -1,3 +1,17 @@ +/* + * Copyright (c) 2022 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * ZF Friedrichshafen AG - Initial API and Implementation + * + */ + package net.catenax.edc.cp.adapter.util; import java.util.HashMap; diff --git a/edc-extensions/control-plane-adapter/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension b/edc-extensions/control-plane-adapter/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension index 7e411103b..6d37b24d2 100644 --- a/edc-extensions/control-plane-adapter/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension +++ b/edc-extensions/control-plane-adapter/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension @@ -1,14 +1,14 @@ # -# Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2022 ZF Friedrichshafen AG # -# This program and the accompanying materials are made available under the -# terms of the Apache License, Version 2.0 which is available at -# https://www.apache.org/licenses/LICENSE-2.0 +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 # -# SPDX-License-Identifier: Apache-2.0 -# -# Contributors: -# Mercedes-Benz Tech Innovation GmbH - Initial ServiceExtension file +# SPDX-License-Identifier: Apache-2.0 # +# Contributors: +# ZF Friedrichshafen AG - Initial API and Implementation # + net.catenax.edc.cp.adapter.ApiAdapterExtension diff --git a/edc-extensions/control-plane-adapter/src/main/resources/control-plane-adapter.jpg b/edc-extensions/control-plane-adapter/src/main/resources/control-plane-adapter.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3e20f29b73ba3e0d4b5cb0ad81ddf4abc131bbe2 GIT binary patch literal 460215 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