From 194c6040addcd247442ebbd8b49722329e9923b1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 30 Nov 2022 16:58:37 +0100 Subject: [PATCH] Release version 0.1.3 (#571) --- .github/workflows/build.yaml | 90 +- .github/workflows/business-tests.yaml | 189 +++-- .github/workflows/draft-new-release.yaml | 6 +- .github/workflows/helm-lint.yaml | 4 +- .github/workflows/kics.yml | 6 +- .github/workflows/publish-new-release.yml | 19 +- .github/workflows/trivy.yml | 5 +- .github/workflows/veracode.yaml | 140 ++- CHANGELOG.md | 25 +- CODE_OF_CONDUCT.md | 46 + CONTRIBUTING.md | 58 ++ DEPENDENCIES | 287 +++---- NOTICE.md | 349 +------- README.md | 4 + SECURITY.md | 67 +- charts/edc-controlplane/Chart.yaml | 4 +- charts/edc-controlplane/README.md | 7 +- .../edc-controlplane/templates/_helpers.tpl | 10 + .../templates/deployment.yaml | 6 + charts/edc-controlplane/values.yaml | 9 + charts/edc-dataplane/Chart.yaml | 4 +- charts/edc-dataplane/README.md | 5 +- charts/edc-dataplane/templates/_helpers.tpl | 10 + charts/edc-dataplane/values.yaml | 3 + charts/tractusx-connector/.helmignore | 23 + charts/tractusx-connector/Chart.yaml | 21 + charts/tractusx-connector/README.md | 230 +++++ charts/tractusx-connector/README.md.gotmpl | 25 + charts/tractusx-connector/templates/NOTES.txt | 45 + .../tractusx-connector/templates/_helpers.tpl | 175 ++++ .../templates/configmap-controlplane.yaml | 14 + .../templates/configmap-dataplane.yaml | 14 + .../templates/deployment-controlplane.yaml | 345 ++++++++ .../templates/deployment-dataplane.yaml | 219 +++++ .../templates/hpa-controlplane.yaml | 29 + .../templates/hpa-dataplane.yaml | 29 + .../templates/ingress-controlplane.yaml | 77 ++ .../templates/ingress-dataplane.yaml | 77 ++ .../templates/service-controlplane.yaml | 37 + .../templates/service-dataplane.yaml | 29 + .../templates/serviceaccount.yaml | 16 + charts/tractusx-connector/values.yaml | 504 +++++++++++ ct.yaml | 2 +- docs/data-transfer/Transfer Data.md | 76 +- .../diagrams/transfer_sequence_1.png | Bin 29595 -> 28233 bytes .../diagrams/transfer_sequence_1.puml | 24 +- .../diagrams/transfer_sequence_2.png | Bin 29371 -> 27684 bytes .../diagrams/transfer_sequence_2.puml | 22 +- .../diagrams/transfer_sequence_3.png | Bin 34859 -> 32718 bytes .../diagrams/transfer_sequence_3.puml | 28 +- .../diagrams/transfer_sequence_4.png | Bin 60428 -> 56765 bytes .../diagrams/transfer_sequence_4.puml | 40 +- .../diagrams/transfer_sequence_5.png | Bin 21812 -> 21142 bytes .../diagrams/transfer_sequence_5.puml | 20 +- docs/development/Release.md | 16 + docs/development/postman/README.md | 27 + docs/development/postman/collection.json | 798 ++++++++++++++++++ .../development/postman/images/screenshot.png | Bin 0 -> 77083 bytes docs/migration/Version_0.1.2_0.1.3.md | 20 + docs/release-notes/Version 0.1.3.md | 91 ++ edc | 1 - .../edc-controlplane-base/pom.xml | 87 +- .../pom.xml | 174 ++++ .../src/main/docker/Dockerfile | 69 ++ .../edc-controlplane-memory/pom.xml | 20 +- .../src/main/docker/Dockerfile | 25 +- .../pom.xml | 22 +- .../src/main/docker/Dockerfile | 25 +- .../edc-controlplane-postgresql/pom.xml | 22 +- .../src/main/docker/Dockerfile | 25 +- edc-controlplane/pom.xml | 19 +- .../edc-dataplane-azure-vault/pom.xml | 20 +- .../src/main/docker/Dockerfile | 25 +- edc-dataplane/edc-dataplane-base/pom.xml | 36 +- .../edc-dataplane-hashicorp-vault/pom.xml | 22 +- .../src/main/docker/Dockerfile | 25 +- edc-dataplane/pom.xml | 18 +- .../business-partner-validation/README.md | 59 +- .../business-partner-validation/pom.xml | 18 +- .../BusinessPartnerDutyFunction.java | 35 - .../BusinessPartnerValidationExtension.java | 45 +- .../AbstractBusinessPartnerValidation.java | 78 +- .../BusinessPartnerDutyFunction.java | 41 + .../BusinessPartnerPermissionFunction.java | 22 +- .../BusinessPartnerProhibitionFunction.java | 22 +- ...spaceconnector.spi.system.ServiceExtension | 16 +- ...usinessPartnerValidationExtensionTest.java | 30 +- ...AbstractBusinessPartnerValidationTest.java | 32 +- .../control-plane-adapter/README.md | 70 ++ edc-extensions/control-plane-adapter/pom.xml | 133 +++ .../edc/cp/adapter/ApiAdapterConfig.java | 55 ++ .../edc/cp/adapter/ApiAdapterExtension.java | 158 ++++ .../edc/cp/adapter/HttpController.java | 122 +++ .../dto/DataReferenceRetrievalDto.java | 23 + .../edc/cp/adapter/dto/ProcessData.java | 48 ++ .../exception/ConfigurationException.java | 21 + .../exception/ExternalRequestException.java | 25 + .../exception/ResourceNotFoundException.java | 25 + .../edc/cp/adapter/messaging/Channel.java | 23 + .../adapter/messaging/InMemoryMessageBus.java | 69 ++ .../edc/cp/adapter/messaging/Listener.java | 19 + .../cp/adapter/messaging/ListenerService.java | 44 + .../edc/cp/adapter/messaging/Message.java | 62 ++ .../edc/cp/adapter/messaging/MessageBus.java | 19 + .../ContractAgreementData.java | 42 + .../contractdatastore/ContractDataStore.java | 23 + .../InMemoryContractDataStore.java | 37 + .../ContractNegotiationHandler.java | 141 ++++ .../ContractInMemorySyncService.java | 94 +++ .../contractnotification/ContractInfo.java | 49 ++ .../ContractNegotiationListenerImpl.java | 79 ++ .../ContractNotificationHandler.java | 91 ++ .../ContractNotificationSyncService.java | 32 + .../DataTransferInitializer.java | 56 ++ .../DataRefInMemorySyncService.java | 65 ++ .../DataRefNotificationSyncService.java | 29 + .../datareference/DataReferenceHandler.java | 48 ++ .../EndpointDataReferenceReceiverImpl.java | 39 + .../adapter/service/ErrorResultService.java | 67 ++ .../edc/cp/adapter/service/ResultService.java | 66 ++ .../edc/cp/adapter/util/ExpiringMap.java | 71 ++ .../catenax/edc/cp/adapter/util/LockMap.java | 44 + ...spaceconnector.spi.system.ServiceExtension | 14 + .../main/resources/control-plane-adapter.jpg | Bin 0 -> 205271 bytes .../edc/cp/adapter/HttpControllerTest.java | 64 ++ .../messaging/InMemoryMessageBusTest.java | 68 ++ .../ContractNegotiationHandlerTest.java | 182 ++++ .../ContractNegotiationListenerTest.java | 140 +++ .../ContractNotificationHandlerTest.java | 141 ++++ .../DataReferenceHandlerTest.java | 85 ++ .../EndpointDataReferenceReceiverTest.java | 70 ++ .../cp/adapter/service/ResultServiceTest.java | 119 +++ edc-extensions/cx-oauth2/pom.xml | 43 +- .../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 | 35 - .../oauth2/jwt/decorator/ExpJwtDecorator.java | 41 - .../oauth2/jwt/decorator/IatJwtDecorator.java | 37 - .../jwt/decorator/IdsAudJwtDecorator.java | 33 - .../oauth2/jwt/decorator/IssJwtDecorator.java | 35 - .../oauth2/jwt/decorator/JWTClaimNames.java | 24 - .../oauth2/jwt/decorator/JtiJwtDecorator.java | 31 - .../jwt/decorator/JwtDecoratorExtension.java | 136 --- ...auth2JwtDecoratorRegistryRegistryImpl.java | 42 - .../oauth2/jwt/decorator/SubJwtDecorator.java | 34 - .../oauth2/jwt/decorator/X5tJwtDecorator.java | 51 -- .../generator/JwtTokenGenerationService.java | 102 --- .../JwtTokenGenerationServiceExtension.java | 48 -- .../jwt/validation/AudValidationRule.java | 68 -- .../jwt/validation/ExpValidationRule.java | 71 -- .../jwt/validation/IatValidationRule.java | 76 -- .../jwt/validation/IdsValidationRule.java | 79 -- .../validation/JwtValidationExtension.java | 167 ---- .../jwt/validation/NbfValidationRule.java | 75 -- .../Oauth2ValidationRulesRegistryImpl.java | 40 - .../TokenValidationServiceImpl.java | 107 --- .../edc/oauth2/CXOAuth2Extension.java | 39 + ...spaceconnector.spi.system.ServiceExtension | 20 +- .../oauth2/jwk/JwkPublicKeyResolverTest.java | 204 ----- .../oauth2/jwk/RsaPublicKeyReaderTest.java | 84 -- .../jwt/decorator/DapsJwtDecoratorTest.java | 32 - .../jwt/decorator/ExpJwtDecoratorTest.java | 44 - .../jwt/decorator/IatJwtDecoratorTest.java | 41 - .../jwt/decorator/IdsAudJwtDecoratorTest.java | 32 - .../jwt/decorator/IssJwtDecoratorTest.java | 35 - .../jwt/decorator/JtiJwtDecoratorTest.java | 27 - ...2JwtDecoratorRegistryRegistryImplTest.java | 107 --- .../jwt/decorator/SubJwtDecoratorTest.java | 35 - edc-extensions/data-encryption/pom.xml | 18 +- .../algorithms/CryptoAlgorithm.java | 34 - .../edc/data/encryption/data/CryptoData.java | 20 - .../encryption/data/CryptoDataFactory.java | 29 - .../data/encryption/data/DecryptedData.java | 16 - .../data/encryption/data/EncryptedData.java | 16 - .../AesDataEncrypterConfiguration.java | 26 - .../encrypter/DataEncrypterFactory.java | 68 -- .../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 -- .../data/encryption/provider/KeyProvider.java | 23 - .../edc/data/encryption/util/ArrayUtil.java | 42 - .../encryption/DataEncryptionExtension.java | 60 +- .../algorithms/CryptoAlgorithm.java | 40 + .../algorithms/aes/AesAlgorithm.java | 34 +- .../aes/AesInitializationVectorIterator.java | 24 +- .../algorithms/aes/ByteCounter.java | 22 +- .../edc/data/encryption/data/CryptoData.java | 26 + .../encryption/data/CryptoDataFactory.java | 35 + .../data/CryptoDataFactoryImpl.java | 22 +- .../data/encryption/data/DecryptedData.java | 22 + .../data/encryption/data/EncryptedData.java | 22 + .../AesDataEncrypterConfiguration.java | 32 + .../encrypter/AesDataEncrypterImpl.java | 40 +- .../encrypter/DataEncrypterFactory.java | 74 ++ .../edc/data/encryption/key/AesKey.java | 26 + .../edc/data/encryption/key/CryptoKey.java | 22 + .../data/encryption/key/CryptoKeyFactory.java | 26 + .../encryption/key/CryptoKeyFactoryImpl.java | 45 + .../encryption/provider/AesKeyProvider.java | 69 ++ .../provider/CachingKeyProvider.java | 24 +- .../data/encryption/provider/KeyProvider.java | 29 + .../edc/data/encryption/util/ArrayUtil.java | 48 ++ ...spaceconnector.spi.system.ServiceExtension | 16 +- .../DataEncryptionExtensionTest.java | 26 +- .../algorithms/aes/AesAlgorithmTest.java | 32 +- .../AesInitializationVectorIteratorTest.java | 24 +- .../algorithms/aes/ByteCounterTest.java | 24 +- .../DataEncrypterAesComponentTest.java | 44 +- .../encrypter/DataEncrypterFactoryTest.java | 24 +- .../key/CryptoKeyFactoryImplTest.java | 22 +- .../provider/AesKeyProviderTest.java | 26 +- .../provider/CachingKeyProviderTest.java | 24 +- .../data/encryption/util/ArrayUtilTest.java | 22 +- .../dataplane-selector-configuration/pom.xml | 36 +- ...SelectorConfigurationServiceExtension.java | 35 +- ...spaceconnector.spi.system.ServiceExtension | 16 +- ...ationServiceExtensionEdcExtensionTest.java | 26 +- ...ctorConfigurationServiceExtensionTest.java | 22 +- edc-extensions/hashicorp-vault/pom.xml | 37 +- .../HashicorpVaultClientConfig.java | 34 - ...shicorpVaultCreateEntryRequestPayload.java | 47 -- ...hicorpVaultCreateEntryResponsePayload.java | 33 - .../HashicorpVaultEntryMetadata.java | 40 - .../HashicorpVaultException.java | 28 - ...HashicorpVaultGetEntryResponsePayload.java | 48 -- .../AbstractHashicorpVaultExtension.java | 29 +- .../HashicorpCertificateResolver.java | 22 +- .../edc/hashicorpvault/HashicorpVault.java | 22 +- .../hashicorpvault/HashicorpVaultClient.java | 36 +- .../HashicorpVaultClientConfig.java | 40 + ...shicorpVaultCreateEntryRequestPayload.java | 53 ++ ...hicorpVaultCreateEntryResponsePayload.java | 39 + .../HashicorpVaultEntryMetadata.java | 46 + .../HashicorpVaultException.java | 34 + ...HashicorpVaultGetEntryResponsePayload.java | 54 ++ .../HashicorpVaultHealthCheck.java | 2 +- .../HashicorpVaultHealthExtension.java | 24 +- .../HashicorpVaultHealthResponse.java | 2 +- .../HashicorpVaultHealthResponsePayload.java | 2 +- .../HashicorpVaultVaultExtension.java | 24 +- .../edc/hashicorpvault/PathUtil.java | 2 +- .../tractusx}/edc/hashicorpvault/PemUtil.java | 22 +- ...spaceconnector.spi.system.ServiceExtension | 14 +- .../hashicorpvault/AbstractHashicorpIT.java | 28 +- .../HashicorpCertificateResolverIT.java | 22 +- .../HashicorpCertificateResolverTest.java | 22 +- .../HashicorpVaultClientTest.java | 22 +- .../HashicorpVaultExtensionTest.java | 22 +- ...ashicorpVaultHealthCheckExtensionTest.java | 22 +- .../HashicorpVaultHealthCheckTest.java | 22 +- .../edc/hashicorpvault/HashicorpVaultIT.java | 48 +- .../hashicorpvault/HashicorpVaultTest.java | 22 +- .../edc/hashicorpvault/PathUtilTest.java | 2 +- .../X509CertificateTestUtil.java | 22 +- .../src/test/resources/logback.xml | 11 +- edc-extensions/pom.xml | 21 +- edc-extensions/postgresql-migration/pom.xml | 18 +- .../AssetPostgresqlMigrationExtension.java | 29 - ...efinitionPostgresqlMigrationExtension.java | 33 - ...gotiationPostgresqlMigrationExtension.java | 33 - .../PolicyPostgresqlMigrationExtension.java | 31 - ...erProcessPostgresqlMigrationExtension.java | 33 - .../AbstractPostgresqlMigrationExtension.java | 27 +- .../AssetPostgresqlMigrationExtension.java | 35 + ...efinitionPostgresqlMigrationExtension.java | 36 + ...gotiationPostgresqlMigrationExtension.java | 36 + .../DriverManagerConnectionFactory.java | 22 +- .../PolicyPostgresqlMigrationExtension.java | 35 + ...erProcessPostgresqlMigrationExtension.java | 36 + ...spaceconnector.spi.system.ServiceExtension | 24 +- .../V0_0_1__Init_Asset_Database_Schema.sql | 0 .../asset/V0_0_2__Milestone5_Update.sql | 0 .../V0_0_3__Snapshot_20220815_Update.sql | 0 ...nit_ContractDefinition_Database_Schema.sql | 0 ...ition_Access_Contract_Policy_Id_Schema.sql | 0 .../V0_0_3__Snapshot_20220815_Update.sql | 0 ...it_ContractNegotiation_Database_Schema.sql | 0 ...egotation_Contract_Agreement_Id_Schema.sql | 0 .../V0_0_3__Milestone5_Update.sql | 0 .../V0_0_4__Snapshot_20220815_Update.sql | 0 .../V0_0_1__Init_Policy_Database_Schema.sql | 0 .../policy/V0_0_2__Milestone5_Update.sql | 0 .../V0_0_3__Snapshot_20220815_Update.sql | 0 ...__Init_TransferProcess_Database_Schema.sql | 0 ..._Alter_TransferProcess_Add_DataAddress.sql | 0 .../V0_0_3__Alter_Rename_Id.sql | 0 .../V0_0_4__Milestone5_Update.sql | 0 .../V0_0_5__Snapshot_20220815_Update.sql | 0 edc-tests/README.md | 2 +- edc-tests/pom.xml | 67 +- .../diagrams/deployed_components.puml | 47 -- .../helm/all-in-one/templates/_helpers.tpl | 62 -- .../helm/all-in-one/templates/secret.yaml | 91 -- .../deployment/helm/all-in-one/values.yaml | 565 ------------- .../.gitignore | 0 .../.helmignore | 0 .../Chart.yaml | 69 +- .../README.md | 0 .../diagrams/deployed_components.png | Bin .../diagrams/deployed_components.puml | 47 ++ .../supporting-infrastructure/values.yaml | 305 +++++++ .../catenax/edc/tests/ConnectorFactory.java | 37 - .../net/catenax/edc/tests/ConnectorSteps.java | 26 - .../java/net/catenax/edc/tests/Constants.java | 29 - .../net/catenax/edc/tests/Environment.java | 59 -- .../net/catenax/edc/tests/data/Asset.java | 24 - .../catenax/edc/tests/data/Constraint.java | 3 - .../edc/tests/data/ContractDefinition.java | 29 - .../edc/tests/data/ContractNegotiation.java | 25 - .../tests/data/ContractNegotiationState.java | 23 - .../catenax/edc/tests/data/ContractOffer.java | 24 - .../edc/tests/data/PayMeConstraint.java | 26 - .../catenax/edc/tests/data/Permission.java | 26 - .../net/catenax/edc/tests/data/Policy.java | 25 - .../edc/tests/features/RunCucumberTest.java | 22 - .../net/catenax/edc/tests/util/Timeouts.java | 23 - .../tractusx}/edc/tests/AssetStepDefs.java | 24 +- .../edc/tests/BackendServiceBackendAPI.java | 24 +- .../edc/tests/BackendServiceSteps.java | 2 +- .../tractusx}/edc/tests/CatalogStepDefs.java | 24 +- .../tractusx}/edc/tests/Connector.java | 32 +- .../tractusx/edc/tests/ConnectorFactory.java | 43 + .../tractusx/edc/tests/ConnectorSteps.java | 32 + .../eclipse/tractusx/edc/tests/Constants.java | 38 + .../edc/tests/ContractDefinitionStepDefs.java | 24 +- .../edc/tests/DataManagementAPI.java | 170 +++- .../tractusx/edc/tests/Environment.java | 76 ++ .../tractusx}/edc/tests/NegotiationSteps.java | 34 +- .../tractusx}/edc/tests/PolicyStepDefs.java | 32 +- .../edc/tests/S3FileTransferStepsDefs.java | 198 +++++ .../tractusx/edc/tests/data/Asset.java | 30 + .../edc/tests/data/AssetWithDataAddress.java | 31 + .../data/BusinessPartnerNumberConstraint.java | 2 +- .../tractusx/edc/tests/data/Constraint.java | 3 + .../edc/tests/data/ContractDefinition.java | 35 + .../edc/tests/data/ContractNegotiation.java | 31 + .../tests/data/ContractNegotiationState.java | 29 + .../edc/tests/data/ContractOffer.java | 30 + .../tractusx/edc/tests/data/DataAddress.java | 30 + .../edc/tests/data/PayMeConstraint.java | 32 + .../tractusx/edc/tests/data/Permission.java | 32 + .../tractusx/edc/tests/data/Policy.java | 31 + .../edc/tests/data/TransferProcess.java | 29 + .../edc/tests/data/TransferProcessState.java | 25 + .../edc/tests/features/ParameterTypes.java | 6 +- .../edc/tests/features/RunCucumberTest.java | 28 + .../edc/tests/util/DatabaseCleaner.java | 22 +- .../tractusx/edc/tests/util/S3Client.java | 124 +++ .../tractusx/edc/tests/util/Timeouts.java | 30 + edc-tests/src/test/resources/logback-test.xml | 11 +- .../features/ContractNegotiation.feature | 13 +- .../edc/tests/features/ContractOffers.feature | 17 +- .../edc/tests/features/S3FileTransfer.feature | 48 ++ misc/NOTICE.md.template | 21 - misc/license-mappings.xml | 761 ----------------- pom.xml | 391 ++++++++- 364 files changed, 11079 insertions(+), 7216 deletions(-) create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 charts/tractusx-connector/.helmignore create mode 100644 charts/tractusx-connector/Chart.yaml create mode 100644 charts/tractusx-connector/README.md create mode 100644 charts/tractusx-connector/README.md.gotmpl create mode 100644 charts/tractusx-connector/templates/NOTES.txt create mode 100644 charts/tractusx-connector/templates/_helpers.tpl create mode 100644 charts/tractusx-connector/templates/configmap-controlplane.yaml create mode 100644 charts/tractusx-connector/templates/configmap-dataplane.yaml create mode 100644 charts/tractusx-connector/templates/deployment-controlplane.yaml create mode 100644 charts/tractusx-connector/templates/deployment-dataplane.yaml create mode 100644 charts/tractusx-connector/templates/hpa-controlplane.yaml create mode 100644 charts/tractusx-connector/templates/hpa-dataplane.yaml create mode 100644 charts/tractusx-connector/templates/ingress-controlplane.yaml create mode 100644 charts/tractusx-connector/templates/ingress-dataplane.yaml create mode 100644 charts/tractusx-connector/templates/service-controlplane.yaml create mode 100644 charts/tractusx-connector/templates/service-dataplane.yaml create mode 100644 charts/tractusx-connector/templates/serviceaccount.yaml create mode 100644 charts/tractusx-connector/values.yaml create mode 100644 docs/development/postman/README.md create mode 100644 docs/development/postman/collection.json create mode 100644 docs/development/postman/images/screenshot.png create mode 100644 docs/migration/Version_0.1.2_0.1.3.md create mode 100644 docs/release-notes/Version 0.1.3.md delete mode 160000 edc create mode 100644 edc-controlplane/edc-controlplane-memory-hashicorp-vault/pom.xml create mode 100644 edc-controlplane/edc-controlplane-memory-hashicorp-vault/src/main/docker/Dockerfile delete mode 100644 edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/BusinessPartnerDutyFunction.java rename edc-extensions/business-partner-validation/src/main/java/{net/catenax => org/eclipse/tractusx}/edc/validation/businesspartner/BusinessPartnerValidationExtension.java (59%) rename edc-extensions/business-partner-validation/src/main/java/{net/catenax => org/eclipse/tractusx}/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java (63%) create mode 100644 edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerDutyFunction.java rename edc-extensions/business-partner-validation/src/main/java/{net/catenax => org/eclipse/tractusx}/edc/validation/businesspartner/functions/BusinessPartnerPermissionFunction.java (50%) rename edc-extensions/business-partner-validation/src/main/java/{net/catenax => org/eclipse/tractusx}/edc/validation/businesspartner/functions/BusinessPartnerProhibitionFunction.java (50%) rename edc-extensions/business-partner-validation/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/validation/businesspartner/BusinessPartnerValidationExtensionTest.java (68%) rename edc-extensions/business-partner-validation/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidationTest.java (76%) create mode 100644 edc-extensions/control-plane-adapter/README.md 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/DataReferenceRetrievalDto.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/ExternalRequestException.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/InMemoryMessageBus.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/MessageBus.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 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/contractnotification/ContractInMemorySyncService.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractInfo.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractNegotiationListenerImpl.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractNotificationHandler.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractNotificationSyncService.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/DataTransferInitializer.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataRefInMemorySyncService.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataRefNotificationSyncService.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/EndpointDataReferenceReceiverImpl.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/service/ErrorResultService.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/java/net/catenax/edc/cp/adapter/util/ExpiringMap.java create mode 100644 edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/util/LockMap.java create mode 100644 edc-extensions/control-plane-adapter/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension create mode 100644 edc-extensions/control-plane-adapter/src/main/resources/control-plane-adapter.jpg 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/InMemoryMessageBusTest.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/contractnotification/ContractNegotiationListenerTest.java create mode 100644 edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractNotificationHandlerTest.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/process/datareference/EndpointDataReferenceReceiverTest.java create mode 100644 edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/service/ResultServiceTest.java delete mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/OAuth2Extension.java delete mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/OAuth2IdentityService.java delete mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/JsonWebKey.java delete mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/JsonWebKeySet.java delete mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/JwkPublicKeyResolver.java delete mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/PublicKeyHolder.java delete mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/PublicKeyReader.java delete mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/RsaPublicKeyReader.java delete mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/DapsJwtDecorator.java delete mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/ExpJwtDecorator.java delete mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/IatJwtDecorator.java delete mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/IdsAudJwtDecorator.java delete mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/IssJwtDecorator.java delete mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/JWTClaimNames.java delete mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/JtiJwtDecorator.java delete mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/JwtDecoratorExtension.java delete mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/Oauth2JwtDecoratorRegistryRegistryImpl.java delete mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/SubJwtDecorator.java delete mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/X5tJwtDecorator.java delete mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/generator/JwtTokenGenerationService.java delete mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/generator/JwtTokenGenerationServiceExtension.java delete mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/AudValidationRule.java delete mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/ExpValidationRule.java delete mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/IatValidationRule.java delete mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/IdsValidationRule.java delete mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/JwtValidationExtension.java delete mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/NbfValidationRule.java delete mode 100644 edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/Oauth2ValidationRulesRegistryImpl.java delete 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/java/org/eclipse/tractusx/edc/oauth2/CXOAuth2Extension.java delete mode 100644 edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwk/JwkPublicKeyResolverTest.java delete mode 100644 edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwk/RsaPublicKeyReaderTest.java delete mode 100644 edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/DapsJwtDecoratorTest.java delete mode 100644 edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/ExpJwtDecoratorTest.java delete mode 100644 edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/IatJwtDecoratorTest.java delete mode 100644 edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/IdsAudJwtDecoratorTest.java delete mode 100644 edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/IssJwtDecoratorTest.java delete mode 100644 edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/JtiJwtDecoratorTest.java delete mode 100644 edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/Oauth2JwtDecoratorRegistryRegistryImplTest.java delete mode 100644 edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/SubJwtDecoratorTest.java delete mode 100644 edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/algorithms/CryptoAlgorithm.java delete mode 100644 edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/data/CryptoData.java delete mode 100644 edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/data/CryptoDataFactory.java delete mode 100644 edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/data/DecryptedData.java delete mode 100644 edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/data/EncryptedData.java delete mode 100644 edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/encrypter/AesDataEncrypterConfiguration.java delete mode 100644 edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/encrypter/DataEncrypterFactory.java delete mode 100644 edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/key/AesKey.java delete mode 100644 edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/key/CryptoKey.java delete mode 100644 edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/key/CryptoKeyFactory.java delete mode 100644 edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/key/CryptoKeyFactoryImpl.java delete mode 100644 edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/provider/AesKeyProvider.java delete mode 100644 edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/provider/KeyProvider.java delete mode 100644 edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/util/ArrayUtil.java rename edc-extensions/data-encryption/src/main/java/{net/catenax => org/eclipse/tractusx}/edc/data/encryption/DataEncryptionExtension.java (63%) create mode 100644 edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/CryptoAlgorithm.java rename edc-extensions/data-encryption/src/main/java/{net/catenax => org/eclipse/tractusx}/edc/data/encryption/algorithms/aes/AesAlgorithm.java (71%) rename edc-extensions/data-encryption/src/main/java/{net/catenax => org/eclipse/tractusx}/edc/data/encryption/algorithms/aes/AesInitializationVectorIterator.java (56%) rename edc-extensions/data-encryption/src/main/java/{net/catenax => org/eclipse/tractusx}/edc/data/encryption/algorithms/aes/ByteCounter.java (62%) create mode 100644 edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/CryptoData.java create mode 100644 edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/CryptoDataFactory.java rename edc-extensions/data-encryption/src/main/java/{net/catenax => org/eclipse/tractusx}/edc/data/encryption/data/CryptoDataFactoryImpl.java (67%) create mode 100644 edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/DecryptedData.java create mode 100644 edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/EncryptedData.java create mode 100644 edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/AesDataEncrypterConfiguration.java rename edc-extensions/data-encryption/src/main/java/{net/catenax => org/eclipse/tractusx}/edc/data/encryption/encrypter/AesDataEncrypterImpl.java (66%) create mode 100644 edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterFactory.java create mode 100644 edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/AesKey.java create mode 100644 edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/CryptoKey.java create mode 100644 edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/CryptoKeyFactory.java create mode 100644 edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/CryptoKeyFactoryImpl.java create mode 100644 edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/provider/AesKeyProvider.java rename edc-extensions/data-encryption/src/main/java/{net/catenax => org/eclipse/tractusx}/edc/data/encryption/provider/CachingKeyProvider.java (64%) create mode 100644 edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/provider/KeyProvider.java create mode 100644 edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/util/ArrayUtil.java rename edc-extensions/data-encryption/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/data/encryption/DataEncryptionExtensionTest.java (77%) rename edc-extensions/data-encryption/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/data/encryption/algorithms/aes/AesAlgorithmTest.java (61%) rename edc-extensions/data-encryption/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/data/encryption/algorithms/aes/AesInitializationVectorIteratorTest.java (65%) rename edc-extensions/data-encryption/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/data/encryption/algorithms/aes/ByteCounterTest.java (76%) rename edc-extensions/data-encryption/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/data/encryption/encrypter/DataEncrypterAesComponentTest.java (60%) rename edc-extensions/data-encryption/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/data/encryption/encrypter/DataEncrypterFactoryTest.java (70%) rename edc-extensions/data-encryption/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/data/encryption/key/CryptoKeyFactoryImplTest.java (50%) rename edc-extensions/data-encryption/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/data/encryption/provider/AesKeyProviderTest.java (62%) rename edc-extensions/data-encryption/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/data/encryption/provider/CachingKeyProviderTest.java (72%) rename edc-extensions/data-encryption/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/data/encryption/util/ArrayUtilTest.java (71%) rename edc-extensions/dataplane-selector-configuration/src/main/java/{net/catenax => org/eclipse/tractusx}/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtension.java (83%) rename edc-extensions/dataplane-selector-configuration/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtensionEdcExtensionTest.java (85%) rename edc-extensions/dataplane-selector-configuration/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtensionTest.java (90%) delete mode 100644 edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultClientConfig.java delete mode 100644 edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultCreateEntryRequestPayload.java delete mode 100644 edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultCreateEntryResponsePayload.java delete mode 100644 edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultEntryMetadata.java delete mode 100644 edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultException.java delete mode 100644 edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultGetEntryResponsePayload.java rename edc-extensions/hashicorp-vault/src/main/java/{net/catenax => org/eclipse/tractusx}/edc/hashicorpvault/AbstractHashicorpVaultExtension.java (77%) rename edc-extensions/hashicorp-vault/src/main/java/{net/catenax => org/eclipse/tractusx}/edc/hashicorpvault/HashicorpCertificateResolver.java (63%) rename edc-extensions/hashicorp-vault/src/main/java/{net/catenax => org/eclipse/tractusx}/edc/hashicorpvault/HashicorpVault.java (60%) rename edc-extensions/hashicorp-vault/src/main/java/{net/catenax => org/eclipse/tractusx}/edc/hashicorpvault/HashicorpVaultClient.java (87%) create mode 100644 edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultClientConfig.java create mode 100644 edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultCreateEntryRequestPayload.java create mode 100644 edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultCreateEntryResponsePayload.java create mode 100644 edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultEntryMetadata.java create mode 100644 edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultException.java create mode 100644 edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultGetEntryResponsePayload.java rename edc-extensions/hashicorp-vault/src/main/java/{net/catenax => org/eclipse/tractusx}/edc/hashicorpvault/HashicorpVaultHealthCheck.java (98%) rename edc-extensions/hashicorp-vault/src/main/java/{net/catenax => org/eclipse/tractusx}/edc/hashicorpvault/HashicorpVaultHealthExtension.java (69%) rename edc-extensions/hashicorp-vault/src/main/java/{net/catenax => org/eclipse/tractusx}/edc/hashicorpvault/HashicorpVaultHealthResponse.java (97%) rename edc-extensions/hashicorp-vault/src/main/java/{net/catenax => org/eclipse/tractusx}/edc/hashicorpvault/HashicorpVaultHealthResponsePayload.java (96%) rename edc-extensions/hashicorp-vault/src/main/java/{net/catenax => org/eclipse/tractusx}/edc/hashicorpvault/HashicorpVaultVaultExtension.java (68%) rename edc-extensions/hashicorp-vault/src/main/java/{net/catenax => org/eclipse/tractusx}/edc/hashicorpvault/PathUtil.java (93%) rename edc-extensions/hashicorp-vault/src/main/java/{net/catenax => org/eclipse/tractusx}/edc/hashicorpvault/PemUtil.java (63%) rename edc-extensions/hashicorp-vault/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/hashicorpvault/AbstractHashicorpIT.java (81%) rename edc-extensions/hashicorp-vault/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/hashicorpvault/HashicorpCertificateResolverIT.java (61%) rename edc-extensions/hashicorp-vault/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/hashicorpvault/HashicorpCertificateResolverTest.java (63%) rename edc-extensions/hashicorp-vault/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/hashicorpvault/HashicorpVaultClientTest.java (91%) rename edc-extensions/hashicorp-vault/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/hashicorpvault/HashicorpVaultExtensionTest.java (77%) rename edc-extensions/hashicorp-vault/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/hashicorpvault/HashicorpVaultHealthCheckExtensionTest.java (83%) rename edc-extensions/hashicorp-vault/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/hashicorpvault/HashicorpVaultHealthCheckTest.java (66%) rename edc-extensions/hashicorp-vault/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/hashicorpvault/HashicorpVaultIT.java (55%) rename edc-extensions/hashicorp-vault/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/hashicorpvault/HashicorpVaultTest.java (82%) rename edc-extensions/hashicorp-vault/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/hashicorpvault/PathUtilTest.java (96%) rename edc-extensions/hashicorp-vault/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/hashicorpvault/X509CertificateTestUtil.java (86%) delete mode 100644 edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/AssetPostgresqlMigrationExtension.java delete mode 100644 edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/ContractDefinitionPostgresqlMigrationExtension.java delete mode 100644 edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/ContractNegotiationPostgresqlMigrationExtension.java delete mode 100644 edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/PolicyPostgresqlMigrationExtension.java delete mode 100644 edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/TransferProcessPostgresqlMigrationExtension.java rename edc-extensions/postgresql-migration/src/main/java/{net/catenax => org/eclipse/tractusx}/edc/postgresql/migration/AbstractPostgresqlMigrationExtension.java (76%) create mode 100644 edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/AssetPostgresqlMigrationExtension.java create mode 100644 edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/ContractDefinitionPostgresqlMigrationExtension.java create mode 100644 edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/ContractNegotiationPostgresqlMigrationExtension.java rename edc-extensions/postgresql-migration/src/main/java/{net/catenax => org/eclipse/tractusx}/edc/postgresql/migration/DriverManagerConnectionFactory.java (50%) create mode 100644 edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/PolicyPostgresqlMigrationExtension.java create mode 100644 edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/TransferProcessPostgresqlMigrationExtension.java rename edc-extensions/postgresql-migration/src/main/resources/{net/catenax => org/eclipse/tractusx}/edc/postgresql/migration/asset/V0_0_1__Init_Asset_Database_Schema.sql (100%) rename edc-extensions/postgresql-migration/src/main/resources/{net/catenax => org/eclipse/tractusx}/edc/postgresql/migration/asset/V0_0_2__Milestone5_Update.sql (100%) rename edc-extensions/postgresql-migration/src/main/resources/{net/catenax => org/eclipse/tractusx}/edc/postgresql/migration/asset/V0_0_3__Snapshot_20220815_Update.sql (100%) rename edc-extensions/postgresql-migration/src/main/resources/{net/catenax => org/eclipse/tractusx}/edc/postgresql/migration/contractdefinition/V0_0_1__Init_ContractDefinition_Database_Schema.sql (100%) rename edc-extensions/postgresql-migration/src/main/resources/{net/catenax => org/eclipse/tractusx}/edc/postgresql/migration/contractdefinition/V0_0_2__Alter_ContractDefinition_Access_Contract_Policy_Id_Schema.sql (100%) rename edc-extensions/postgresql-migration/src/main/resources/{net/catenax => org/eclipse/tractusx}/edc/postgresql/migration/contractdefinition/V0_0_3__Snapshot_20220815_Update.sql (100%) rename edc-extensions/postgresql-migration/src/main/resources/{net/catenax => org/eclipse/tractusx}/edc/postgresql/migration/contractnegotiation/V0_0_1__Init_ContractNegotiation_Database_Schema.sql (100%) rename edc-extensions/postgresql-migration/src/main/resources/{net/catenax => org/eclipse/tractusx}/edc/postgresql/migration/contractnegotiation/V0_0_2__Alter_ContractNegotation_Contract_Agreement_Id_Schema.sql (100%) rename edc-extensions/postgresql-migration/src/main/resources/{net/catenax => org/eclipse/tractusx}/edc/postgresql/migration/contractnegotiation/V0_0_3__Milestone5_Update.sql (100%) rename edc-extensions/postgresql-migration/src/main/resources/{net/catenax => org/eclipse/tractusx}/edc/postgresql/migration/contractnegotiation/V0_0_4__Snapshot_20220815_Update.sql (100%) rename edc-extensions/postgresql-migration/src/main/resources/{net/catenax => org/eclipse/tractusx}/edc/postgresql/migration/policy/V0_0_1__Init_Policy_Database_Schema.sql (100%) rename edc-extensions/postgresql-migration/src/main/resources/{net/catenax => org/eclipse/tractusx}/edc/postgresql/migration/policy/V0_0_2__Milestone5_Update.sql (100%) rename edc-extensions/postgresql-migration/src/main/resources/{net/catenax => org/eclipse/tractusx}/edc/postgresql/migration/policy/V0_0_3__Snapshot_20220815_Update.sql (100%) rename edc-extensions/postgresql-migration/src/main/resources/{net/catenax => org/eclipse/tractusx}/edc/postgresql/migration/transferprocess/V0_0_1__Init_TransferProcess_Database_Schema.sql (100%) rename edc-extensions/postgresql-migration/src/main/resources/{net/catenax => org/eclipse/tractusx}/edc/postgresql/migration/transferprocess/V0_0_2__Alter_TransferProcess_Add_DataAddress.sql (100%) rename edc-extensions/postgresql-migration/src/main/resources/{net/catenax => org/eclipse/tractusx}/edc/postgresql/migration/transferprocess/V0_0_3__Alter_Rename_Id.sql (100%) rename edc-extensions/postgresql-migration/src/main/resources/{net/catenax => org/eclipse/tractusx}/edc/postgresql/migration/transferprocess/V0_0_4__Milestone5_Update.sql (100%) rename edc-extensions/postgresql-migration/src/main/resources/{net/catenax => org/eclipse/tractusx}/edc/postgresql/migration/transferprocess/V0_0_5__Snapshot_20220815_Update.sql (100%) delete mode 100644 edc-tests/src/main/resources/deployment/helm/all-in-one/diagrams/deployed_components.puml delete mode 100644 edc-tests/src/main/resources/deployment/helm/all-in-one/templates/_helpers.tpl delete mode 100644 edc-tests/src/main/resources/deployment/helm/all-in-one/templates/secret.yaml delete mode 100644 edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml rename edc-tests/src/main/resources/deployment/helm/{all-in-one => supporting-infrastructure}/.gitignore (100%) rename edc-tests/src/main/resources/deployment/helm/{all-in-one => supporting-infrastructure}/.helmignore (100%) rename edc-tests/src/main/resources/deployment/helm/{all-in-one => supporting-infrastructure}/Chart.yaml (51%) rename edc-tests/src/main/resources/deployment/helm/{all-in-one => supporting-infrastructure}/README.md (100%) rename edc-tests/src/main/resources/deployment/helm/{all-in-one => supporting-infrastructure}/diagrams/deployed_components.png (100%) create mode 100644 edc-tests/src/main/resources/deployment/helm/supporting-infrastructure/diagrams/deployed_components.puml create mode 100644 edc-tests/src/main/resources/deployment/helm/supporting-infrastructure/values.yaml delete mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/ConnectorFactory.java delete mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/ConnectorSteps.java delete mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/Constants.java delete mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/Environment.java delete mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/data/Asset.java delete mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/data/Constraint.java delete mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/data/ContractDefinition.java delete mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/data/ContractNegotiation.java delete mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/data/ContractNegotiationState.java delete mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/data/ContractOffer.java delete mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/data/PayMeConstraint.java delete mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/data/Permission.java delete mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/data/Policy.java delete mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/features/RunCucumberTest.java delete mode 100644 edc-tests/src/test/java/net/catenax/edc/tests/util/Timeouts.java rename edc-tests/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/tests/AssetStepDefs.java (57%) rename edc-tests/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/tests/BackendServiceBackendAPI.java (90%) rename edc-tests/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/tests/BackendServiceSteps.java (90%) rename edc-tests/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/tests/CatalogStepDefs.java (73%) rename edc-tests/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/tests/Connector.java (50%) create mode 100644 edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/ConnectorFactory.java create mode 100644 edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/ConnectorSteps.java create mode 100644 edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/Constants.java rename edc-tests/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/tests/ContractDefinitionStepDefs.java (58%) rename edc-tests/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/tests/DataManagementAPI.java (72%) create mode 100644 edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/Environment.java rename edc-tests/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/tests/NegotiationSteps.java (64%) rename edc-tests/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/tests/PolicyStepDefs.java (55%) create mode 100644 edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/S3FileTransferStepsDefs.java create mode 100644 edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/Asset.java create mode 100644 edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/AssetWithDataAddress.java rename edc-tests/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/tests/data/BusinessPartnerNumberConstraint.java (78%) create mode 100644 edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/Constraint.java create mode 100644 edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractDefinition.java create mode 100644 edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractNegotiation.java create mode 100644 edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractNegotiationState.java create mode 100644 edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractOffer.java create mode 100644 edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/DataAddress.java create mode 100644 edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/PayMeConstraint.java create mode 100644 edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/Permission.java create mode 100644 edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/Policy.java create mode 100644 edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/TransferProcess.java create mode 100644 edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/TransferProcessState.java rename edc-tests/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/tests/features/ParameterTypes.java (56%) create mode 100644 edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/features/RunCucumberTest.java rename edc-tests/src/test/java/{net/catenax => org/eclipse/tractusx}/edc/tests/util/DatabaseCleaner.java (51%) create mode 100644 edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/util/S3Client.java create mode 100644 edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/util/Timeouts.java rename edc-tests/src/test/resources/{net/catenax => org/eclipse/tractusx}/edc/tests/features/ContractNegotiation.feature (69%) rename edc-tests/src/test/resources/{net/catenax => org/eclipse/tractusx}/edc/tests/features/ContractOffers.feature (83%) create mode 100644 edc-tests/src/test/resources/org/eclipse/tractusx/edc/tests/features/S3FileTransfer.feature delete mode 100644 misc/NOTICE.md.template delete mode 100644 misc/license-mappings.xml diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 4f51b1f25..ac8a05390 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -10,20 +10,19 @@ on: - '[0-9]+.[0-9]+.[0-9]+' release: types: - - released + - published pull_request: paths-ignore: - 'charts/**' branches: - '*' + workflow_dispatch: jobs: secret-presence: runs-on: ubuntu-latest outputs: 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: - @@ -31,8 +30,6 @@ jobs: 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 @@ -41,12 +38,12 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v3.1.0 with: fetch-depth: 0 - name: Set up JDK 11 - uses: actions/setup-java@v3.5.1 + uses: actions/setup-java@v3.6.0 with: java-version: '11' distribution: 'adopt' @@ -64,23 +61,16 @@ jobs: # Set-Up - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v3.1.0 with: fetch-depth: 0 - name: Set up JDK 11 - uses: actions/setup-java@v3.5.1 + uses: actions/setup-java@v3.6.0 with: 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-20220922-SNAPSHOT -xjavadoc - working-directory: edc - name: Cache SonarCloud packages uses: actions/cache@v3 @@ -110,22 +100,15 @@ jobs: # Set-Up - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v3.1.0 - name: Set up JDK 11 - uses: actions/setup-java@v3.5.1 + uses: actions/setup-java@v3.6.0 with: java-version: '11' 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-20220922-SNAPSHOT -xjavadoc - working-directory: edc - name: Build Extensions run: |- @@ -142,13 +125,14 @@ jobs: matrix: name: - edc-controlplane-memory + - edc-controlplane-memory-hashicorp-vault - edc-controlplane-postgresql - edc-controlplane-postgresql-hashicorp-vault steps: # Set-Up - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v3.1.0 - name: Login to GitHub Container Registry if: | @@ -160,19 +144,12 @@ jobs: password: ${{ secrets.CXNG_GHCR_PAT }} - name: Set up JDK 11 - uses: actions/setup-java@v3.5.1 + uses: actions/setup-java@v3.6.0 with: java-version: '11' 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-20220922-SNAPSHOT -xjavadoc - working-directory: edc - name: Build Controlplane run: |- @@ -206,23 +183,6 @@ jobs: ${{ (needs.secret-presence.outputs.CXNG_GHCR_PAT && github.event_name != 'pull_request' && 'true') || 'false' }} 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 - 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/${{ matrix.name }} - createprofile: true - version: ${{ github.ref }}-${{ github.sha }} - filepath: edc-controlplane/${{ matrix.name }}/target/${{ matrix.name }}.jar - vid: ${{ secrets.ORG_VERACODE_API_ID }} - vkey: ${{ secrets.ORG_VERACODE_API_KEY }} build-dataplane: runs-on: ubuntu-latest @@ -237,7 +197,7 @@ jobs: # Set-Up - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v3.1.0 - name: Login to GitHub Container Registry if: | @@ -249,19 +209,12 @@ jobs: password: ${{ secrets.CXNG_GHCR_PAT }} - name: Set up JDK 11 - uses: actions/setup-java@v3.5.1 + uses: actions/setup-java@v3.6.0 with: java-version: '11' 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-20220922-SNAPSHOT -xjavadoc - working-directory: edc - name: Build Dataplane run: |- @@ -295,20 +248,3 @@ jobs: ${{ (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 }} - - - 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/${{ matrix.name }} - createprofile: true - 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 }} diff --git a/.github/workflows/business-tests.yaml b/.github/workflows/business-tests.yaml index 71a6f63d9..45814d29a 100644 --- a/.github/workflows/business-tests.yaml +++ b/.github/workflows/business-tests.yaml @@ -20,10 +20,10 @@ jobs: ############## - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v3.1.0 - name: Set-Up JDK 11 - uses: actions/setup-java@v3.5.1 + uses: actions/setup-java@v3.6.0 with: java-version: '11' distribution: 'adopt' @@ -39,7 +39,7 @@ jobs: uses: azure/setup-kubectl@v3.0 - name: Helm Set-Up - uses: azure/setup-helm@v3.3 + uses: azure/setup-helm@v3.4 with: version: v3.8.1 - @@ -69,13 +69,6 @@ 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-20220922-SNAPSHOT -xjavadoc - working-directory: edc - name: Build edc-controlplane-postgresql-hashicorp-vault run: |- @@ -105,52 +98,32 @@ jobs: 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_DATA_MANAGEMENT_URL=http://sokrates-controlplane:8081/data" | tee -a ${GITHUB_ENV} + echo "SOKRATES_IDS_URL=http://sokrates-controlplane:8084/api/v1/ids" | tee -a ${GITHUB_ENV} + echo "SOKRATES_DATA_PLANE_URL=http://sokrates-dataplane:8081/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 "SOKRATES_DATABASE_USER=user" | tee -a ${GITHUB_ENV} + echo "SOKRATES_DATABASE_PASSWORD=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_MANAGEMENT_URL=http://plato-controlplane:8081/data" | tee -a ${GITHUB_ENV} + echo "PLATO_IDS_URL=http://plato-controlplane:8084/api/v1/ids" | tee -a ${GITHUB_ENV} + echo "PLATO_DATA_PLANE_URL=http://plato-dataplane:8081/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} + echo "PLATO_DATABASE_USER=user" | tee -a ${GITHUB_ENV} + echo "PLATO_DATABASE_PASSWORD=password" | tee -a ${GITHUB_ENV} + echo "EDC_AWS_ENDPOINT_OVERRIDE=http://minio:9000" | tee -a ${GITHUB_ENV} + echo "PLATO_AWS_SECRET_ACCESS_KEY=platoqwerty123" | tee -a ${GITHUB_ENV} + echo "PLATO_AWS_ACCESS_KEY_ID=platoqwerty123" | tee -a ${GITHUB_ENV} + echo "SOKRATES_AWS_SECRET_ACCESS_KEY=sokratesqwerty123" | tee -a ${GITHUB_ENV} + echo "SOKRATES_AWS_ACCESS_KEY_ID=sokratesqwerty123" | tee -a ${GITHUB_ENV} - - name: Install test environment via Helm + name: Install infrastructure components via Helm run: |- # Update helm dependencies - helm dependency update edc-tests/src/main/resources/deployment/helm/all-in-one + helm dependency update edc-tests/src/main/resources/deployment/helm/supporting-infrastructure - # 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.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 \ - --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 \ + # Install the all-in-one supporting infrastructure environment (daps, vault, pgsql, minio) + helm install infrastructure edc-tests/src/main/resources/deployment/helm/supporting-infrastructure \ --wait-for-jobs --timeout=120s # GH pipelines constrained by cpu, so give helm some time to register all resources \w k8s @@ -158,50 +131,85 @@ jobs: # 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 --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 ) + kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=vault --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=vault --tail 500 && exit 1 ) + kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=sokrates-postgresql --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=sokrates-postgresql --tail 500 && exit 1 ) + kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=plato-postgresql --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=plato-postgresql --tail 500 && exit 1 ) + kubectl wait --for=condition=ready pod -l app=minio --timeout=120s || ( kubectl logs -l app=minio --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 \ - --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 \ - --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 \ + # Install Plato + helm install plato charts/tractusx-connector \ + --set fullnameOverride=plato \ + --set controlplane.service.type=NodePort \ + --set controlplane.endpoints.data.authKey=password \ + --set controlplane.image.tag=business-test \ + --set controlplane.image.pullPolicy=Never \ + --set controlplane.image.repository=docker.io/library/edc-controlplane-postgresql-hashicorp-vault \ + --set dataplane.image.tag=business-test \ + --set dataplane.image.pullPolicy=Never \ + --set dataplane.image.repository=docker.io/library/edc-dataplane-hashicorp-vault \ + --set controlplane.debug.enabled=true \ + --set controlplane.suspendOnStart=false \ + --set postgresql.enabled=true \ + --set postgresql.username=user \ + --set postgresql.password=password \ + --set postgresql.jdbcUrl=jdbc:postgresql://plato-postgresql:5432/edc \ + --set vault.hashicorp.url=http://vault:8200 \ + --set vault.hashicorp.token=root \ + --set vault.secretNames.transferProxyTokenSignerPublicKey=plato/daps/my-plato-daps-crt \ + --set vault.secretNames.transferProxyTokenSignerPrivateKey=plato/daps/my-plato-daps-key \ + --set vault.secretNames.transferProxyTokenEncryptionAesKey=plato/data-encryption-aes-keys \ + --set vault.secretNames.dapsPrivateKey=plato/daps/my-plato-daps-key \ + --set vault.secretNames.dapsPublicKey=plato/daps/my-plato-daps-crt \ + --set daps.url=http://ids-daps:4567 \ + --set daps.clientId=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 \ + --set dataplane.aws.endpointOverride=http://minio:9000 \ + --set dataplane.aws.secretAccessKey=platoqwerty123 \ + --set dataplane.aws.accessKeyId=platoqwerty123 \ + --set backendService.httpProxyTokenReceiverUrl=http://example.com \ + --wait-for-jobs --timeout=120s + + # Install Sokrates + helm install sokrates charts/tractusx-connector \ + --set fullnameOverride=sokrates \ + --set controlplane.service.type=NodePort \ + --set controlplane.endpoints.data.authKey=password \ + --set controlplane.image.tag=business-test \ + --set controlplane.image.pullPolicy=Never \ + --set controlplane.image.repository=docker.io/library/edc-controlplane-postgresql-hashicorp-vault \ + --set dataplane.image.tag=business-test \ + --set dataplane.image.pullPolicy=Never \ + --set dataplane.image.repository=docker.io/library/edc-dataplane-hashicorp-vault \ + --set controlplane.debug.enabled=true \ + --set controlplane.suspendOnStart=false \ + --set postgresql.enabled=true \ + --set postgresql.username=user \ + --set postgresql.password=password \ + --set postgresql.jdbcUrl=jdbc:postgresql://sokrates-postgresql:5432/edc \ + --set vault.hashicorp.url=http://vault:8200 \ + --set vault.hashicorp.token=root \ + --set vault.secretNames.transferProxyTokenSignerPublicKey=sokrates/daps/my-sokrates-daps-crt \ + --set vault.secretNames.transferProxyTokenSignerPrivateKey=sokrates/daps/my-sokrates-daps-key \ + --set vault.secretNames.transferProxyTokenEncryptionAesKey=sokrates/data-encryption-aes-keys \ + --set vault.secretNames.dapsPrivateKey=sokrates/daps/my-sokrates-daps-key \ + --set vault.secretNames.dapsPublicKey=sokrates/daps/my-sokrates-daps-crt \ + --set daps.url=http://ids-daps:4567 \ + --set daps.clientId=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 \ + --set dataplane.aws.endpointOverride=http://minio:9000 \ + --set dataplane.aws.secretAccessKey=sokratesqwerty123 \ + --set dataplane.aws.accessKeyId=sokratesqwerty123 \ + --set backendService.httpProxyTokenReceiverUrl=http://example.com \ --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 -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 ) + #kubectl wait --for=condition=ready pod -l app.kubernetes.io/instance=sokratesbackendapplication --timeout=120s || ( kubectl logs -l app.kubernetes.io/instance=sokratesbackendapplication --tail 500 && exit 1 ) + #kubectl wait --for=condition=ready pod -l app.kubernetes.io/instance=platobackendapplication --timeout=120s || ( kubectl logs -l app.kubernetes.io/instance=platobackendapplication --tail 500 && exit 1 ) + kubectl wait --for=condition=ready pod -l app.kubernetes.io/instance=sokrates-controlplane --timeout=120s || ( kubectl logs -l app.kubernetes.io/instance=sokrates-controlplane --tail 500 && exit 1 ) + kubectl wait --for=condition=ready pod -l app.kubernetes.io/instance=sokrates-dataplane --timeout=120s || ( kubectl logs -l app.kubernetes.io/instance=sokrates-dataplane --tail 500 && exit 1 ) + kubectl wait --for=condition=ready pod -l app.kubernetes.io/instance=plato-controlplane --timeout=600s || ( kubectl logs -l app.kubernetes.io/instance=plato-controlplane --tail 500 && exit 1 ) + kubectl wait --for=condition=ready pod -l app.kubernetes.io/instance=plato-dataplane --timeout=600s || ( kubectl logs -l app.kubernetes.io/instance=plato-dataplane --tail 500 && exit 1 ) ############################################## ### Run Business Tests inside kind cluster ### @@ -218,7 +226,7 @@ jobs: { "args": [ "-c", - "cd /product-edc && ./mvnw -s settings.xml -B -Pbusiness-tests -pl edc-tests test -Dtest=net.catenax.edc.tests.features.RunCucumberTest" + "cd /product-edc && ./mvnw -s settings.xml -B -Pbusiness-tests -pl edc-tests test -Dtest=org.eclipse.tractusx.edc.tests.features.RunCucumberTest" ], "command": [ "/bin/sh" @@ -243,7 +251,12 @@ jobs: {"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": "PLATO_DATABASE_PASSWORD", "value": "${PLATO_DATABASE_PASSWORD}"}, + {"name": "EDC_AWS_ENDPOINT_OVERRIDE", "value": "${EDC_AWS_ENDPOINT_OVERRIDE}"}, + {"name": "PLATO_AWS_SECRET_ACCESS_KEY", "value": "${PLATO_AWS_SECRET_ACCESS_KEY}"}, + {"name": "PLATO_AWS_ACCESS_KEY_ID", "value": "${PLATO_AWS_ACCESS_KEY_ID}"}, + {"name": "SOKRATES_AWS_SECRET_ACCESS_KEY", "value": "${SOKRATES_AWS_SECRET_ACCESS_KEY}"}, + {"name": "SOKRATES_AWS_ACCESS_KEY_ID", "value": "${SOKRATES_AWS_ACCESS_KEY_ID}"} ], EOF diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index 051192bf1..03ba5ace4 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@v3 + - uses: actions/checkout@v3.1.0 - name: Create release branch run: git checkout -b release/${{ github.event.inputs.version }} @@ -29,7 +29,7 @@ jobs: git config user.email noreply@github.com - name: Set up JDK 11 - uses: actions/setup-java@v3.5.1 + uses: actions/setup-java@v3.6.0 with: java-version: '11' distribution: 'adopt' @@ -43,7 +43,7 @@ jobs: GITHUB_PACKAGE_PASSWORD: ${{ secrets.CXNG_GHCR_PAT }} - name: Bump version in /charts - uses: mikefarah/yq@v4.27.5 + uses: mikefarah/yq@v4.30.5 with: cmd: |- find charts -name Chart.yaml | xargs -n1 yq -i '.appVersion = "${{ github.event.inputs.version }}" | .version = "${{ github.event.inputs.version }}"' diff --git a/.github/workflows/helm-lint.yaml b/.github/workflows/helm-lint.yaml index bf341b5d6..9318fd528 100644 --- a/.github/workflows/helm-lint.yaml +++ b/.github/workflows/helm-lint.yaml @@ -27,12 +27,12 @@ jobs: ############## - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v3.1.0 with: fetch-depth: 0 - name: helm (setup) - uses: azure/setup-helm@v3.3 + uses: azure/setup-helm@v3.4 with: version: v3.8.1 - diff --git a/.github/workflows/kics.yml b/.github/workflows/kics.yml index 4430f4512..8cf5e4ce5 100644 --- a/.github/workflows/kics.yml +++ b/.github/workflows/kics.yml @@ -20,7 +20,7 @@ jobs: security-events: write steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.1.0 - name: KICS scan uses: checkmarx/kics-github-action@master @@ -30,10 +30,6 @@ jobs: disable_secrets: true output_path: kicsResults/ output_formats: "json,sarif" - 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/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index d10185c72..efdfc6124 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -64,21 +64,14 @@ jobs: echo "RELEASE_VERSION=${{ needs.release-version.outputs.RELEASE_VERSION }}" >> $GITHUB_ENV - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v3.1.0 - name: Set up JDK 11 - uses: actions/setup-java@v3.5.1 + uses: actions/setup-java@v3.6.0 with: 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-20220922-SNAPSHOT -xjavadoc - working-directory: edc - name: Deploy run: |- @@ -103,12 +96,12 @@ jobs: echo "RELEASE_VERSION=${{ needs.release-version.outputs.RELEASE_VERSION }}" >> $GITHUB_ENV - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v3.1.0 with: fetch-depth: 0 - name: Install Helm - uses: azure/setup-helm@v3.3 + uses: azure/setup-helm@v3.4 with: version: v3.8.1 - @@ -146,7 +139,7 @@ jobs: echo "RELEASE_VERSION=${{ needs.release-version.outputs.RELEASE_VERSION }}" >> $GITHUB_ENV - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v3.1.0 with: # 0 to fetch the full history due to upcoming merge of main into develop branch fetch-depth: 0 @@ -179,7 +172,7 @@ jobs: prerelease: false - name: Set up JDK 11 - uses: actions/setup-java@v3.5.1 + uses: actions/setup-java@v3.6.0 with: java-version: '11' distribution: 'adopt' diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml index a8cf21ec3..e1af5ae69 100644 --- a/.github/workflows/trivy.yml +++ b/.github/workflows/trivy.yml @@ -39,7 +39,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3.1.0 - name: Run Trivy vulnerability scanner in repo mode uses: aquasecurity/trivy-action@master @@ -70,6 +70,7 @@ jobs: matrix: image: - edc-controlplane-memory + - edc-controlplane-memory-hashicorp-vault - edc-controlplane-postgresql - edc-controlplane-postgresql-hashicorp-vault - edc-dataplane-azure-vault @@ -77,7 +78,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v3.1.0 - name: Run Trivy vulnerability scanner if: always() diff --git a/.github/workflows/veracode.yaml b/.github/workflows/veracode.yaml index f3230f5a2..e67376dbf 100644 --- a/.github/workflows/veracode.yaml +++ b/.github/workflows/veracode.yaml @@ -1,2 +1,138 @@ -# 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 +--- +name: "Veracode" + +on: + schedule: + - cron: '0 2 * * *' + workflow_dispatch: + +jobs: + secret-presence: + runs-on: ubuntu-latest + outputs: + 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.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 + + verify-formatting: + runs-on: ubuntu-latest + steps: + - + name: Checkout + uses: actions/checkout@v3.1.0 + with: + fetch-depth: 0 + - + name: Set up JDK 11 + uses: actions/setup-java@v3.6.0 + with: + java-version: '11' + distribution: 'adopt' + cache: 'maven' + - + name: Verify proper formatting + run: ./mvnw -s settings.xml -B spotless:check + + build-controlplane: + runs-on: ubuntu-latest + needs: [ secret-presence, verify-formatting ] + strategy: + fail-fast: false + matrix: + name: + - edc-controlplane-memory + - edc-controlplane-memory-hashicorp-vault + - edc-controlplane-postgresql + - edc-controlplane-postgresql-hashicorp-vault + steps: + # Set-Up + - + name: Checkout + uses: actions/checkout@v3.1.0 + - + name: Set up JDK 11 + uses: actions/setup-java@v3.6.0 + with: + java-version: '11' + distribution: 'adopt' + cache: 'maven' + # Build + - + name: Build Controlplane + run: |- + ./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: Tar gzip files for veracode upload + run: |- + tar -czvf edc-controlplane/${{ matrix.name }}/target/${{ matrix.name }}.tar.gz edc-controlplane/${{ matrix.name }}/target/${{ matrix.name }}.jar edc-controlplane/${{ matrix.name }}/target/lib/*.jar + - + 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 + continue-on-error: true + with: + appname: product-edc/${{ matrix.name }} + createprofile: true + version: ${{ matrix.name }}-${{ github.sha }} + filepath: edc-controlplane/${{ matrix.name }}/target/${{ matrix.name }}.tar.gz + vid: ${{ secrets.ORG_VERACODE_API_ID }} + vkey: ${{ secrets.ORG_VERACODE_API_KEY }} + + build-dataplane: + runs-on: ubuntu-latest + needs: [ secret-presence, verify-formatting ] + strategy: + fail-fast: false + matrix: + name: + - edc-dataplane-azure-vault + - edc-dataplane-hashicorp-vault + steps: + # Set-Up + - + name: Checkout + uses: actions/checkout@v3.1.0 + - + name: Set up JDK 11 + uses: actions/setup-java@v3.6.0 + with: + java-version: '11' + distribution: 'adopt' + cache: 'maven' + # Build + - + name: Build Dataplane + run: |- + ./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: Tar gzip files for veracode upload + run: |- + tar -czvf edc-dataplane/${{ matrix.name }}/target/${{ matrix.name }}.tar.gz edc-dataplane/${{ matrix.name }}/target/${{ matrix.name }}.jar edc-dataplane/${{ matrix.name }}/target/lib/*.jar + - + 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 + continue-on-error: true + with: + appname: product-edc/${{ matrix.name }} + createprofile: true + version: ${{ matrix.name }}-${{ github.sha }} + filepath: edc-dataplane/${{ matrix.name }}/target/${{ matrix.name }}.tar.gz + vid: ${{ secrets.ORG_VERACODE_API_ID }} + vkey: ${{ secrets.ORG_VERACODE_API_KEY }} + diff --git a/CHANGELOG.md b/CHANGELOG.md index 83c1881f6..f7b295f8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.1.3] - 2022-11-30 + +### Added + +- New Postman collection for developers `/docs/development/postman` +- New EDC Image with HashiCorp Vault and InMemory Storage +- (Experimental) Simplified deployment of the EDC in `/charts/tractusx-connector` + +### Changed + +- Set EDC version to `0.0.1-20221006-SNAPSHOT` +- Business Partner Number Extension no longer supports the 'IN' constraint operator +- HashiCorp Vault Extension now allows sub directories for secrets +- Update package structure/namespace from `net.catenax` to `org.eclipse.tractusx` + +### Fixed + +- S3 Data Transfer + ## [0.1.2] - 2022-09-30 ### Added @@ -16,8 +35,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Moved helm charts from `deployment/helm` to `charts` -- Replaced distroless image with alpine in all docker images -- Update EDC commit to `740c100ac162bc41b1968c232ad81f7d739aefa9` ## [0.1.1] - 2022-09-04 @@ -108,7 +125,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.2...HEAD +[Unreleased]: https://github.com/catenax-ng/product-edc/compare/0.1.3...HEAD + +[0.1.3]: https://github.com/catenax-ng/product-edc/compare/0.1.2...0.1.3 [0.1.2]: https://github.com/catenax-ng/product-edc/compare/0.1.1...0.1.2 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..14db7e6fa --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Community Code of Conduct + +**Version 1.2 +August 19, 2020** + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as community members, contributors, committers, and project leaders pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +With the support of the Eclipse Foundation staff (the “Staff”), project committers and leaders are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project committers and leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies within all project spaces, and it also applies when an individual is representing the Eclipse Foundation project or its community in public spaces. Examples of representing a project or community include posting via an official social media account, or acting as a project representative at an online or offline event. Representation of a project may be further defined and clarified by project committers, leaders, or the EMO. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the Staff at codeofconduct@eclipse.org. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The Staff is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project committers or leaders who do not follow the Code of Conduct in good faith may face temporary or permanent repercussions as determined by the Staff. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org) , version 1.4, available at [https://www.contributor-covenant.org/version/1/4/code-of-conduct.html](https://www.contributor-covenant.org/version/1/4/code-of-conduct/) \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..39dd5bdba --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,58 @@ +# Contributing to Eclipse Tractus-X + +Thanks for your interest in this project. + +## Project description + +The companies involved want to increase the automotive industry's +competitiveness, improve efficiency through industry-specific cooperation and +accelerate company processes through standardization and access to information +and data. A special focus is also on SMEs, whose active participation is of +central importance for the network's success. That is why Catena-X has been +conceived from the outset as an open network with solutions ready for SMEs, +where these companies will be able to participate quickly and with little IT +infrastructure investment. Tractus-X is meant to be the PoC project of the +Catena-X alliance focusing on parts traceability. + +* https://projects.eclipse.org/projects/automotive.tractusx + +## Developer resources + +Information regarding source code management, builds, coding standards, and +more. + +* https://projects.eclipse.org/projects/automotive.tractusx/developer + +The project maintains the source code repositories in the following GitHub organization: + +* https://github.com/eclipse-tractusx/ + +## Eclipse Development Process + +This Eclipse Foundation open project is governed by the Eclipse Foundation +Development Process and operates under the terms of the Eclipse IP Policy. + +* https://eclipse.org/projects/dev_process +* https://www.eclipse.org/org/documents/Eclipse_IP_Policy.pdf + +## Eclipse Contributor Agreement + +In order to be able to contribute to Eclipse Foundation projects you must +electronically sign the Eclipse Contributor Agreement (ECA). + +* http://www.eclipse.org/legal/ECA.php + +The ECA provides the Eclipse Foundation with a permanent record that you agree +that each of your contributions will comply with the commitments documented in +the Developer Certificate of Origin (DCO). Having an ECA on file associated with +the email address matching the "Author" field of your contribution's Git commits +fulfills the DCO's requirement that you sign-off on your contributions. + +For more information, please see the Eclipse Committer Handbook: +https://www.eclipse.org/projects/handbook/#resources-commit + +## Contact + +Contact the project developers via the project's "dev" list. + +* https://accounts.eclipse.org/mailing-list/tractusx-dev \ No newline at end of file diff --git a/DEPENDENCIES b/DEPENDENCIES index c6b2f625b..5e00bd93f 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -1,169 +1,172 @@ -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.electronwill.night-config/core/3.6.6, NOASSERTION, restricted, clearlydefined -maven/mavencentral/com.electronwill.night-config/toml/3.6.6, NOASSERTION, restricted, 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.azure/azure-core-http-netty/1.12.5, MIT, approved, clearlydefined +maven/mavencentral/com.azure/azure-core/1.32.0, MIT, approved, clearlydefined +maven/mavencentral/com.azure/azure-identity/1.6.0, MIT, approved, clearlydefined +maven/mavencentral/com.azure/azure-security-keyvault-secrets/4.5.0, MIT, approved, clearlydefined +maven/mavencentral/com.electronwill.night-config/core/3.6.6, LGPL-3.0-only, approved, #3767 +maven/mavencentral/com.electronwill.night-config/toml/3.6.6, LGPL-3.0-only, approved, #3766 +maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.14.0-rc2, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.fasterxml.jackson.core/jackson-core/2.14.0-rc2, Apache-2.0 AND MIT, approved, #4303 +maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.14.0-rc2, Apache-2.0, approved, #4105 +maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-xml/2.14.0-rc2, Apache-2.0, approved, #4300 +maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jsr310/2.14.0-rc2, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.fasterxml.woodstox/woodstox-core/6.3.1, Apache-2.0, approved, #4302 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.microsoft.azure/msal4j/1.13.1, 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.nimbusds/nimbus-jose-jwt/8.23, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.nimbusds/oauth2-oidc-sdk/9.35, Apache-2.0, approved, clearlydefined maven/mavencentral/com.squareup.okhttp3/okhttp/4.9.3, Apache-2.0 AND MPL-2.0, approved, #3225 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/de.fraunhofer.iais.eis.ids.infomodel/java/4.1.3, Apache-2.0, approved, #3779 +maven/mavencentral/de.fraunhofer.iais.eis.infomodel/util/4.1.3, Apache-2.0, approved, #3780 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/io.netty/netty-buffer/4.1.79.Final, Apache-2.0, approved, CQ21842 +maven/mavencentral/io.netty/netty-codec-dns/4.1.79.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-codec-http/4.1.79.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-codec-http2/4.1.79.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-codec-socks/4.1.79.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-codec/4.1.79.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-common/4.1.79.Final, Apache-2.0 AND MIT AND CC0-1.0, approved, CQ21843 +maven/mavencentral/io.netty/netty-handler-proxy/4.1.79.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-handler/4.1.79.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-resolver-dns-classes-macos/4.1.79.Final, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.netty/netty-resolver-dns-native-macos/4.1.79.Final, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.netty/netty-resolver-dns/4.1.79.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-resolver/4.1.79.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-tcnative-boringssl-static/2.0.53.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.53.Final, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.netty/netty-transport-classes-epoll/4.1.79.Final, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.netty/netty-transport-classes-kqueue/4.1.79.Final, Apache-2.0, approved, #4107 +maven/mavencentral/io.netty/netty-transport-native-epoll/4.1.79.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-transport-native-kqueue/4.1.79.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-transport-native-unix-common/4.1.79.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-transport/4.1.79.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.projectreactor.netty/reactor-netty-core/1.0.22, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.projectreactor.netty/reactor-netty-http/1.0.22, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.projectreactor/reactor-core/3.4.22, 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, #3288 +maven/mavencentral/net.minidev/accessors-smart/2.4.7, Apache-2.0, approved, clearlydefined +maven/mavencentral/net.minidev/json-smart/2.4.7, Apache-2.0, approved, #3288 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-core/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-spi/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-model/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/runtime-metamodel/0.0.1-20220929.123028-13, 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.3.1, NOASSERTION, restricted, clearlydefined +maven/mavencentral/org.eclipse.dataspaceconnector/apache-commons-pool-sql/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/api-configuration/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/api-core/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/asset-api/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/asset-index-sql/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/auth-spi/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/auth-tokenbased/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/azure-vault/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/catalog-api/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/catalog-spi/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/common-sql/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/common-util/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/contract-spi/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/contract/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/contractagreement-api/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/contractdefinition-api/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/contractdefinition-store-sql/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/contractnegotiation-api/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/contractnegotiation-store-sql/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/control-plane-core/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/control-plane-spi/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/core-base/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/core-boot/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/core-micrometer/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/core-spi/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/data-management-api/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/data-plane-api/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/data-plane-framework/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/data-plane-http/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/data-plane-s3/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/data-plane-selector-client/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/data-plane-selector-core/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/data-plane-selector-spi/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/data-plane-spi/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/data-plane-transfer-spi/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/data-plane-transfer-sync/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/filesystem-configuration/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/http-receiver/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/http/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/iam-daps/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/ids-api-configuration/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/ids-api-multipart-dispatcher-v1/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/ids-api-multipart-endpoint-v1/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/ids-api-transform-v1/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/ids-core/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/ids-spi/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/jdk-logger-monitor/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/jersey-micrometer/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/jersey/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/jetty-micrometer/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/jetty/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/jwt-spi/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/oauth2-core/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/oauth2-spi/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/observability-api/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/policy-engine-spi/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/policy-engine/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/policy-evaluator/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/policy-model/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/policy-spi/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/policy-store-sql/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/policydefinition-api/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/runtime-metamodel/0.0.1-20221025.153030-25, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/s3-core/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/transaction-datasource-spi/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/transaction-local/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/transaction-spi/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/transfer-process-store-sql/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/transfer-spi/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/transfer/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/transferprocess-api/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/transport-spi/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.dataspaceconnector/web-spi/0.0.1-20221006-20221006.060025-1, Apache-2.0, approved, technology.dataspaceconnector +maven/mavencentral/org.eclipse.tractusx.edc.extensions/business-partner-validation/0.1.3-SNAPSHOT, Apache-2.0, approved, automotive.tractusx +maven/mavencentral/org.eclipse.tractusx.edc.extensions/cx-oauth2/0.1.3-SNAPSHOT, Apache-2.0, approved, automotive.tractusx +maven/mavencentral/org.eclipse.tractusx.edc.extensions/data-encryption/0.1.3-SNAPSHOT, Apache-2.0, approved, automotive.tractusx +maven/mavencentral/org.eclipse.tractusx.edc.extensions/dataplane-selector-configuration/0.1.3-SNAPSHOT, Apache-2.0, approved, automotive.tractusx +maven/mavencentral/org.eclipse.tractusx.edc.extensions/hashicorp-vault/0.1.3-SNAPSHOT, Apache-2.0, approved, automotive.tractusx +maven/mavencentral/org.eclipse.tractusx.edc.extensions/postgresql-migration/0.1.3-SNAPSHOT, Apache-2.0, approved, automotive.tractusx +maven/mavencentral/org.eclipse.tractusx.edc/edc-controlplane-base/0.1.3-SNAPSHOT, Apache-2.0, approved, automotive.tractusx +maven/mavencentral/org.eclipse.tractusx.edc/edc-controlplane-postgresql/0.1.3-SNAPSHOT, Apache-2.0, approved, automotive.tractusx +maven/mavencentral/org.eclipse.tractusx.edc/edc-dataplane-base/0.1.3-SNAPSHOT, Apache-2.0, approved, automotive.tractusx +maven/mavencentral/org.flywaydb/flyway-core/9.6.0, Apache-2.0, approved, #4301 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.1, BSD-3-Clause, approved, CQ23029 maven/mavencentral/org.ow2.asm/asm/9.3, BSD-3-Clause, approved, CQ24052 maven/mavencentral/org.postgresql/postgresql/42.5.0, BSD-2-Clause, approved, #3416 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.17.278, , restricted, clearlydefined -maven/mavencentral/software.amazon.awssdk/arns/2.17.278, , restricted, clearlydefined -maven/mavencentral/software.amazon.awssdk/auth/2.17.278, , restricted, clearlydefined -maven/mavencentral/software.amazon.awssdk/aws-core/2.17.278, , restricted, clearlydefined -maven/mavencentral/software.amazon.awssdk/aws-query-protocol/2.17.278, , restricted, clearlydefined -maven/mavencentral/software.amazon.awssdk/aws-xml-protocol/2.17.278, , restricted, clearlydefined -maven/mavencentral/software.amazon.awssdk/http-client-spi/2.17.278, , restricted, clearlydefined -maven/mavencentral/software.amazon.awssdk/iam/2.17.278, , restricted, clearlydefined -maven/mavencentral/software.amazon.awssdk/json-utils/2.17.278, , restricted, clearlydefined -maven/mavencentral/software.amazon.awssdk/metrics-spi/2.17.278, , restricted, clearlydefined -maven/mavencentral/software.amazon.awssdk/profiles/2.17.278, , restricted, clearlydefined -maven/mavencentral/software.amazon.awssdk/protocol-core/2.17.278, , restricted, clearlydefined -maven/mavencentral/software.amazon.awssdk/regions/2.17.278, , restricted, clearlydefined -maven/mavencentral/software.amazon.awssdk/s3/2.17.278, , restricted, clearlydefined +maven/mavencentral/org.reactivestreams/reactive-streams/1.0.4, CC0-1.0, approved, CQ16332 +maven/mavencentral/org.slf4j/slf4j-api/2.0.3, MIT, approved, CQ24150 +maven/mavencentral/software.amazon.awssdk/annotations/2.17.278, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/arns/2.17.278, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/auth/2.17.278, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/aws-core/2.17.278, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/aws-query-protocol/2.17.278, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/aws-xml-protocol/2.17.278, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/http-client-spi/2.17.278, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/iam/2.17.278, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/json-utils/2.17.278, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/metrics-spi/2.17.278, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/profiles/2.17.278, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/protocol-core/2.17.278, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/regions/2.17.278, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/s3/2.17.278, Apache-2.0, approved, clearlydefined maven/mavencentral/software.amazon.awssdk/sdk-core/2.17.278, Apache-2.0, approved, #3167 -maven/mavencentral/software.amazon.awssdk/sts/2.17.278, , restricted, clearlydefined +maven/mavencentral/software.amazon.awssdk/sts/2.17.278, Apache-2.0, approved, clearlydefined maven/mavencentral/software.amazon.awssdk/third-party-jackson-core/2.17.278, Apache-2.0, approved, #3166 -maven/mavencentral/software.amazon.awssdk/utils/2.17.278, , restricted, clearlydefined +maven/mavencentral/software.amazon.awssdk/utils/2.17.278, Apache-2.0, approved, clearlydefined maven/mavencentral/software.amazon.eventstream/eventstream/1.0.1, Apache-2.0, approved, clearlydefined diff --git a/NOTICE.md b/NOTICE.md index e0508c539..d9fce018c 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -1,330 +1,49 @@ -# Notices for Catena-X NG Product EDC +# Notices for Eclipse Tractus-X + +This content is produced and maintained by the Eclipse Tractus-X project. + +* Project home: https://projects.eclipse.org/projects/automotive.tractusx + +See the AUTHORS file(s) distributed with this work for additional information regarding authorship. + +## Trademarks + +Eclipse Tractus-X is a trademark of the Eclipse Foundation. ## 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. +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. +This program and the accompanying materials are made available under the terms +of 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: +The project maintains the following source code repositories +in the GitHub organization https://github.com/eclipse-tractusx: + +* https://github.com/eclipse-tractusx/ +* https://github.com/eclipse-tractusx/ + + +## Third-party Content -* https://github.com/catenax-ng/product-edc +This project leverages the following third party content. -## Third-party Content (Overarching All Modules) +See DEPENDENCIES file. -* 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 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 -* 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 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) -* 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-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 -* 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 The Apache License, Version 2.0 -* HTTP functionality for the Reactor Netty library under The Apache Software 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 -* 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 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 -* 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 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 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 -* 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 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 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 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 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) -* Microsoft Azure Java Core Library under The MIT License (MIT) -* Microsoft Azure Netty HTTP Client Library under The MIT License (MIT) -* MIME streaming extension under Eclipse Distribution License - v 1.0 -* mockito-core under The MIT License -* 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 -* 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 The Apache License, Version 2.0 -* oauth2-spi under The Apache License, Version 2.0 -* Objenesis 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 -* 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 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 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 -* 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 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 +## Cryptography +Content may contain encryption software. The country in which you are currently +may have restrictions on the import, possession, and use, and/or re-export to +another country, of encryption software. BEFORE using any encryption software, +please check the country's laws, regulations and policies concerning the import, +possession, or use, and re-export of encryption software, to see if this is +permitted. \ No newline at end of file diff --git a/README.md b/README.md index 4a7d0fe38..e2fbce7d9 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,11 @@ Derivatives of the Data-Plane can be found here 1. Build EDC Submodule Dependencies ```shell git submodule update --init + +#Unix cd edc && ./gradlew publishToMavenLocal -Pskip.signing=true -PedcVersion=0.0.1-20220902-SNAPSHOT -xjavadoc && cd .. +#windows +cd edc && ./gradlew.bat publishToMavenLocal --project-prop skip.signing=true --project-prop edcVersion=0.0.1-20220902-SNAPSHOT && cd.. ``` 2. Build Product-EDC Container Images diff --git a/SECURITY.md b/SECURITY.md index ebfd8b30b..7d8fced73 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,63 +1,6 @@ # 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. + +## Reporting a Vulnerability + +Please report a found vulnerability here: +[https://www.eclipse.org/security/](https://www.eclipse.org/security/) \ No newline at end of file diff --git a/charts/edc-controlplane/Chart.yaml b/charts/edc-controlplane/Chart.yaml index a0e964e0d..c295e77f0 100644 --- a/charts/edc-controlplane/Chart.yaml +++ b/charts/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/charts/edc-controlplane type: application -appVersion: "0.1.2" -version: 0.1.2 +appVersion: "0.1.3" +version: 0.1.3 maintainers: [] diff --git a/charts/edc-controlplane/README.md b/charts/edc-controlplane/README.md index 2ea1be08a..2defe7ac4 100644 --- a/charts/edc-controlplane/README.md +++ b/charts/edc-controlplane/README.md @@ -1,6 +1,6 @@ # edc-controlplane -![Version: 0.1.2](https://img.shields.io/badge/Version-0.1.2-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.1.2](https://img.shields.io/badge/AppVersion-0.1.2-informational?style=flat-square) +![Version: 0.1.3](https://img.shields.io/badge/Version-0.1.3-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.1.3](https://img.shields.io/badge/AppVersion-0.1.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 --version 0.1.2 +$ helm install my-release catenax-ng-product-edc/edc-controlplane --version 0.1.3 ``` ## Values @@ -24,6 +24,7 @@ $ helm install my-release catenax-ng-product-edc/edc-controlplane --version 0.1. | 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.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) | +| customLabels | object | `{}` | Additional custom Labels to add | | 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 | @@ -91,6 +92,8 @@ $ helm install my-release catenax-ng-product-edc/edc-controlplane --version 0.1. | 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. | +| volumeMounts | list | `[]` | Additional volumeMounts to the controlplane main container | +| volumes | list | `[]` | Additional volumes to the controlplane pod | ---------------------------------------------- Autogenerated from chart metadata using [helm-docs v1.10.0](https://github.com/norwoodj/helm-docs/releases/v1.10.0) diff --git a/charts/edc-controlplane/templates/_helpers.tpl b/charts/edc-controlplane/templates/_helpers.tpl index b1d1c7d15..272a0f27d 100644 --- a/charts/edc-controlplane/templates/_helpers.tpl +++ b/charts/edc-controlplane/templates/_helpers.tpl @@ -36,6 +36,7 @@ Common labels {{- define "edc-controlplane.labels" -}} helm.sh/chart: {{ include "edc-controlplane.chart" . }} {{ include "edc-controlplane.selectorLabels" . }} +{{ include "edc-controlplane.customLabels" . }} {{- if .Chart.AppVersion }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} @@ -50,6 +51,15 @@ app.kubernetes.io/name: {{ include "edc-controlplane.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end }} +{{/* +Custom labels +*/}} +{{- define "edc-controlplane.customLabels" -}} +{{- with .Values.customLabels }} +{{ toYaml . }} +{{- end }} +{{- end }} + {{/* Create the name of the service account to use */}} diff --git a/charts/edc-controlplane/templates/deployment.yaml b/charts/edc-controlplane/templates/deployment.yaml index 266ae9cab..6739c67c0 100644 --- a/charts/edc-controlplane/templates/deployment.yaml +++ b/charts/edc-controlplane/templates/deployment.yaml @@ -101,6 +101,9 @@ spec: - name: configuration mountPath: /app/logging.properties subPath: logging.properties + {{- with .Values.volumeMounts }} + {{- toYaml . | nindent 12 }} + {{- end }} volumes: - name: configuration configMap: @@ -112,6 +115,9 @@ spec: path: opentelemetry.properties - key: logging.properties path: logging.properties + {{- with .Values.volumes }} + {{- toYaml . | nindent 8 }} + {{- end }} {{- with .Values.nodeSelector }} nodeSelector: {{- toYaml . | nindent 8 }} diff --git a/charts/edc-controlplane/values.yaml b/charts/edc-controlplane/values.yaml index 249e3d4dd..395a47358 100644 --- a/charts/edc-controlplane/values.yaml +++ b/charts/edc-controlplane/values.yaml @@ -31,6 +31,9 @@ nameOverride: "" # -- Overrides the releases full name fullnameOverride: "" +# -- Additional custom Labels to add +customLabels: {} + serviceAccount: # -- Specifies whether a [service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) should be created per release create: true @@ -90,6 +93,12 @@ startupProbe: # -- Number of seconds after the container has started before liveness probes are initiated. initialDelaySeconds: 10 +# -- Additional volumeMounts to the controlplane main container +volumeMounts: [] + +# -- Additional volumes to the controlplane pod +volumes: [] + ## EDC endpoints exposed by the control-plane edc: endpoints: diff --git a/charts/edc-dataplane/Chart.yaml b/charts/edc-dataplane/Chart.yaml index 4610c99e8..9c721b909 100644 --- a/charts/edc-dataplane/Chart.yaml +++ b/charts/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/charts/edc-dataplane type: application -appVersion: "0.1.2" -version: 0.1.2 +appVersion: "0.1.3" +version: 0.1.3 maintainers: [] diff --git a/charts/edc-dataplane/README.md b/charts/edc-dataplane/README.md index de73cdb84..acee7c8d7 100644 --- a/charts/edc-dataplane/README.md +++ b/charts/edc-dataplane/README.md @@ -1,6 +1,6 @@ # edc-dataplane -![Version: 0.1.2](https://img.shields.io/badge/Version-0.1.2-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.1.2](https://img.shields.io/badge/AppVersion-0.1.2-informational?style=flat-square) +![Version: 0.1.3](https://img.shields.io/badge/Version-0.1.3-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.1.3](https://img.shields.io/badge/AppVersion-0.1.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 --version 0.1.2 +$ helm install my-release catenax-ng-product-edc/edc-dataplane --version 0.1.3 ``` ## Values @@ -24,6 +24,7 @@ $ helm install my-release catenax-ng-product-edc/edc-dataplane --version 0.1.2 | 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.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) | +| customLabels | object | `{}` | Additional custom Labels to add | | 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/charts/edc-dataplane/templates/_helpers.tpl b/charts/edc-dataplane/templates/_helpers.tpl index c6e3bc131..3615298cd 100644 --- a/charts/edc-dataplane/templates/_helpers.tpl +++ b/charts/edc-dataplane/templates/_helpers.tpl @@ -36,6 +36,7 @@ Common labels {{- define "edc-dataplane.labels" -}} helm.sh/chart: {{ include "edc-dataplane.chart" . }} {{ include "edc-dataplane.selectorLabels" . }} +{{ include "edc-dataplane.customLabels" . }} {{- if .Chart.AppVersion }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} @@ -50,6 +51,15 @@ app.kubernetes.io/name: {{ include "edc-dataplane.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end }} +{{/* +Custom labels +*/}} +{{- define "edc-dataplane.customLabels" -}} +{{- with .Values.customLabels }} +{{ toYaml . }} +{{- end }} +{{- end }} + {{/* Create the name of the service account to use */}} diff --git a/charts/edc-dataplane/values.yaml b/charts/edc-dataplane/values.yaml index 03e8ea4e8..725f10413 100644 --- a/charts/edc-dataplane/values.yaml +++ b/charts/edc-dataplane/values.yaml @@ -31,6 +31,9 @@ nameOverride: "" # -- Overrides the releases full name fullnameOverride: "" +# -- Additional custom Labels to add +customLabels: {} + serviceAccount: # -- Specifies whether a [service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) should be created per release create: true diff --git a/charts/tractusx-connector/.helmignore b/charts/tractusx-connector/.helmignore new file mode 100644 index 000000000..0e8a0eb36 --- /dev/null +++ b/charts/tractusx-connector/.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/charts/tractusx-connector/Chart.yaml b/charts/tractusx-connector/Chart.yaml new file mode 100644 index 000000000..029a3da7b --- /dev/null +++ b/charts/tractusx-connector/Chart.yaml @@ -0,0 +1,21 @@ +apiVersion: v2 +name: tractusx-connector +description: A Helm chart for Tractus-X Eclipse Data Space Connector +# 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.3 +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "0.1.3" diff --git a/charts/tractusx-connector/README.md b/charts/tractusx-connector/README.md new file mode 100644 index 000000000..9848314c5 --- /dev/null +++ b/charts/tractusx-connector/README.md @@ -0,0 +1,230 @@ +# tractusx-connector + +![Version: 0.1.3](https://img.shields.io/badge/Version-0.1.3-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.1.3](https://img.shields.io/badge/AppVersion-0.1.3-informational?style=flat-square) + +A Helm chart for Tractus-X Eclipse Data Space Connector + +## TL;DR +```shell +$ helm repo add catenax-ng-product-edc https://catenax-ng.github.io/product-edc +$ helm install tractus-x-connector catenax-ng-product-edc/tractus-x-connector --version 0.1.3 +``` + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| backendService.httpProxyTokenReceiverUrl | string | `""` | | +| controlplane.affinity | object | `{}` | | +| controlplane.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/) | +| controlplane.autoscaling.maxReplicas | int | `100` | Maximum replicas if resource consumption exceeds resource threshholds | +| controlplane.autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | +| controlplane.autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | +| controlplane.autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | +| controlplane.debug.enabled | bool | `false` | | +| controlplane.debug.port | int | `1044` | | +| controlplane.debug.suspendOnStart | bool | `false` | | +| controlplane.endpoints | object | `{"control":{"path":"/control","port":8083},"data":{"authKey":"","path":"/data","port":8081},"default":{"path":"/api","port":8080},"ids":{"path":"/api/v1/ids","port":8084},"metrics":{"path":"/metrics","port":8085},"validation":{"path":"/validation","port":8082}}` | endpoints of the control plane | +| controlplane.endpoints.control | object | `{"path":"/control","port":8083}` | control api, used for internal control calls. can be added to the internal ingress, but should probably not | +| controlplane.endpoints.control.path | string | `"/control"` | path for incoming api calls | +| controlplane.endpoints.control.port | int | `8083` | port for incoming api calls | +| controlplane.endpoints.data | object | `{"authKey":"","path":"/data","port":8081}` | data management api, used by internal users, can be added to an ingress and must not be internet facing | +| controlplane.endpoints.data.authKey | string | `""` | authentication key, must be attached to each 'X-Api-Key' request header | +| controlplane.endpoints.data.path | string | `"/data"` | path for incoming api calls | +| controlplane.endpoints.data.port | int | `8081` | port for incoming api calls | +| controlplane.endpoints.default | object | `{"path":"/api","port":8080}` | default api for health checks, should not be added to any ingress | +| controlplane.endpoints.default.path | string | `"/api"` | path for incoming api calls | +| controlplane.endpoints.default.port | int | `8080` | port for incoming api calls | +| controlplane.endpoints.ids | object | `{"path":"/api/v1/ids","port":8084}` | ids api, used for inter connector communication and must be internet facing | +| controlplane.endpoints.ids.path | string | `"/api/v1/ids"` | path for incoming api calls | +| controlplane.endpoints.ids.port | int | `8084` | port for incoming api calls | +| controlplane.endpoints.metrics | object | `{"path":"/metrics","port":8085}` | metrics api, used for application metrics, must not be internet facing | +| controlplane.endpoints.metrics.path | string | `"/metrics"` | path for incoming api calls | +| controlplane.endpoints.metrics.port | int | `8085` | port for incoming api calls | +| controlplane.endpoints.validation | object | `{"path":"/validation","port":8082}` | validation api, only used by the data plane and should not be added to any ingress | +| controlplane.endpoints.validation.path | string | `"/validation"` | path for incoming api calls | +| controlplane.endpoints.validation.port | int | `8082` | port for incoming api calls | +| controlplane.env | object | `{}` | | +| controlplane.envConfigMapNames | list | `[]` | | +| controlplane.envSecretNames | list | `[]` | | +| controlplane.envValueFrom | object | `{}` | | +| controlplane.image.pullPolicy | string | `"IfNotPresent"` | [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use | +| controlplane.image.repository | string | `""` | Which derivate of the control plane to use. when left empty the deployment will select the correct image automatically | +| controlplane.image.tag | string | `"0.1.2"` | Overrides the image tag whose default is the chart appVersion | +| controlplane.ingresses[0].annotations | object | `{}` | Additional ingress annotations to add | +| controlplane.ingresses[0].certManager.clusterIssuer | string | `""` | If preset enables certificate generation via cert-manager cluster-wide issuer | +| controlplane.ingresses[0].certManager.issuer | string | `""` | If preset enables certificate generation via cert-manager namespace scoped issuer | +| controlplane.ingresses[0].className | string | `""` | Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use | +| controlplane.ingresses[0].enabled | bool | `false` | | +| controlplane.ingresses[0].endpoints | list | `["ids"]` | EDC endpoints exposed by this ingress resource | +| controlplane.ingresses[0].hostname | string | `"edc-control.local"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | +| controlplane.ingresses[0].tls | object | `{"enabled":false,"secretName":""}` | TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource | +| controlplane.ingresses[0].tls.enabled | bool | `false` | Enables TLS on the ingress resource | +| controlplane.ingresses[0].tls.secretName | string | `""` | If present overwrites the default secret name | +| controlplane.ingresses[1].annotations | object | `{}` | Additional ingress annotations to add | +| controlplane.ingresses[1].certManager.clusterIssuer | string | `""` | If preset enables certificate generation via cert-manager cluster-wide issuer | +| controlplane.ingresses[1].certManager.issuer | string | `""` | If preset enables certificate generation via cert-manager namespace scoped issuer | +| controlplane.ingresses[1].className | string | `""` | Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use | +| controlplane.ingresses[1].enabled | bool | `false` | | +| controlplane.ingresses[1].endpoints | list | `["data","control"]` | EDC endpoints exposed by this ingress resource | +| controlplane.ingresses[1].hostname | string | `"edc-control.intranet"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | +| controlplane.ingresses[1].tls | object | `{"enabled":false,"secretName":""}` | TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource | +| controlplane.ingresses[1].tls.enabled | bool | `false` | Enables TLS on the ingress resource | +| controlplane.ingresses[1].tls.secretName | string | `""` | If present overwrites the default secret name | +| controlplane.initContainers | list | `[]` | | +| controlplane.internationalDataSpaces.catalogId | string | `"TXDC-Catalog"` | | +| controlplane.internationalDataSpaces.curator | string | `""` | | +| controlplane.internationalDataSpaces.description | string | `"Tractus-X Eclipse IDS Data Space Connector"` | | +| controlplane.internationalDataSpaces.id | string | `"TXDC"` | | +| controlplane.internationalDataSpaces.maintainer | string | `""` | | +| controlplane.internationalDataSpaces.title | string | `""` | | +| controlplane.livenessProbe.enabled | bool | `true` | Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) | +| controlplane.livenessProbe.failureThreshold | int | `6` | when a probe fails kubernetes will try 6 times before giving up | +| controlplane.livenessProbe.initialDelaySeconds | int | `30` | seconds to wait before performing the first liveness check | +| controlplane.livenessProbe.periodSeconds | int | `10` | this fields specifies that kubernetes should perform a liveness check every 10 seconds | +| controlplane.livenessProbe.successThreshold | int | `1` | number of consecutive successes for the probe to be considered successful after having failed | +| controlplane.livenessProbe.timeoutSeconds | int | `5` | number of seconds after which the probe times out | +| controlplane.logging | string | `".level=INFO\nhandlers=java.util.logging.ConsoleHandler\njava.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter\njava.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n"` | configuration of the [Java Util Logging Facade](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html) | +| controlplane.nodeSelector | object | `{}` | | +| controlplane.opentelemetry | string | `"otel.javaagent.enabled=false\notel.javaagent.debug=false"` | configuration of the [Open Telemetry Agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) to collect and expose metrics | +| controlplane.podAnnotations | object | `{}` | additional annotations for the pod | +| controlplane.podLabels | object | `{}` | additional labels for the pod | +| controlplane.podSecurityContext | object | `{"fsGroup":10001,"runAsGroup":10001,"runAsUser":10001,"seccompProfile":{"type":"RuntimeDefault"}}` | 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 | +| controlplane.podSecurityContext.fsGroup | int | `10001` | The owner for volumes and any files created within volumes will belong to this guid | +| controlplane.podSecurityContext.runAsGroup | int | `10001` | Processes within a pod will belong to this guid | +| controlplane.podSecurityContext.runAsUser | int | `10001` | Runs all processes within a pod with a special uid | +| controlplane.podSecurityContext.seccompProfile.type | string | `"RuntimeDefault"` | Restrict a Container's Syscalls with seccomp | +| controlplane.readinessProbe.enabled | bool | `true` | Whether to enable kubernetes [readiness-probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) | +| controlplane.readinessProbe.failureThreshold | int | `6` | when a probe fails kubernetes will try 6 times before giving up | +| controlplane.readinessProbe.initialDelaySeconds | int | `30` | seconds to wait before performing the first readiness check | +| controlplane.readinessProbe.periodSeconds | int | `10` | this fields specifies that kubernetes should perform a readiness check every 10 seconds | +| controlplane.readinessProbe.successThreshold | int | `1` | number of consecutive successes for the probe to be considered successful after having failed | +| controlplane.readinessProbe.timeoutSeconds | int | `5` | number of seconds after which the probe times out | +| controlplane.replicaCount | int | `1` | | +| controlplane.resources | object | `{}` | [resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) for the container | +| controlplane.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 | +| controlplane.securityContext.capabilities.add | list | `[]` | Specifies which capabilities to add to issue specialized syscalls | +| controlplane.securityContext.capabilities.drop | list | `["ALL"]` | Specifies which capabilities to drop to reduce syscall attack surface | +| controlplane.securityContext.readOnlyRootFilesystem | bool | `true` | Whether the root filesystem is mounted in read-only mode | +| controlplane.securityContext.runAsNonRoot | bool | `true` | Requires the container to run without root privileges | +| controlplane.securityContext.runAsUser | int | `10001` | The container's process will run with the specified uid | +| controlplane.service.annotations | object | `{}` | | +| controlplane.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. | +| controlplane.tolerations | list | `[]` | | +| controlplane.url.ids | string | `""` | Explicitly declared url for reaching the ids api (e.g. if ingresses not used) | +| controlplane.volumeMounts | list | `[]` | declare where to mount [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) into the container | +| controlplane.volumes | list | `[]` | [volume](https://kubernetes.io/docs/concepts/storage/volumes/) directories | +| customLabels | object | `{}` | | +| daps.clientId | string | `""` | | +| daps.paths.jwks | string | `"/jwks.json"` | | +| daps.paths.token | string | `"/token"` | | +| daps.url | string | `""` | | +| dataplane.affinity | object | `{}` | | +| dataplane.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/) | +| dataplane.autoscaling.maxReplicas | int | `100` | Maximum replicas if resource consumption exceeds resource threshholds | +| dataplane.autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | +| dataplane.autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | +| dataplane.autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | +| dataplane.aws.accessKeyId | string | `""` | | +| dataplane.aws.endpointOverride | string | `""` | | +| dataplane.aws.secretAccessKey | string | `""` | | +| dataplane.debug.enabled | bool | `false` | | +| dataplane.debug.port | int | `1044` | | +| dataplane.debug.suspendOnStart | bool | `false` | | +| dataplane.endpoints.control.path | string | `"/api/dataplane/control"` | | +| dataplane.endpoints.control.port | int | `8083` | | +| dataplane.endpoints.default.path | string | `"/api"` | | +| dataplane.endpoints.default.port | int | `8080` | | +| dataplane.endpoints.metrics.path | string | `"/metrics"` | | +| dataplane.endpoints.metrics.port | int | `8084` | | +| dataplane.endpoints.public.path | string | `"/api/public"` | | +| dataplane.endpoints.public.port | int | `8081` | | +| dataplane.endpoints.validation.path | string | `"/validation"` | | +| dataplane.endpoints.validation.port | int | `8082` | | +| dataplane.env | object | `{}` | | +| dataplane.envConfigMapNames | list | `[]` | | +| dataplane.envSecretNames | list | `[]` | | +| dataplane.envValueFrom | object | `{}` | | +| dataplane.image.pullPolicy | string | `"IfNotPresent"` | [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use | +| dataplane.image.repository | string | `""` | Which derivate of the data plane to use. when left empty the deployment will select the correct image automatically | +| dataplane.image.tag | string | `"0.1.2"` | Overrides the image tag whose default is the chart appVersion | +| dataplane.ingresses[0].annotations | object | `{}` | Additional ingress annotations to add | +| dataplane.ingresses[0].certManager.clusterIssuer | string | `""` | If preset enables certificate generation via cert-manager cluster-wide issuer | +| dataplane.ingresses[0].certManager.issuer | string | `""` | If preset enables certificate generation via cert-manager namespace scoped issuer | +| dataplane.ingresses[0].className | string | `""` | Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use | +| dataplane.ingresses[0].enabled | bool | `false` | | +| dataplane.ingresses[0].endpoints | list | `["public"]` | EDC endpoints exposed by this ingress resource | +| dataplane.ingresses[0].hostname | string | `"edc-data.local"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | +| dataplane.ingresses[0].tls | object | `{"enabled":false,"secretName":""}` | TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource | +| dataplane.ingresses[0].tls.enabled | bool | `false` | Enables TLS on the ingress resource | +| dataplane.ingresses[0].tls.secretName | string | `""` | If present overwrites the default secret name | +| dataplane.initContainers | list | `[]` | | +| dataplane.livenessProbe.enabled | bool | `true` | Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) | +| dataplane.livenessProbe.failureThreshold | int | `6` | when a probe fails kubernetes will try 6 times before giving up | +| dataplane.livenessProbe.initialDelaySeconds | int | `30` | seconds to wait before performing the first liveness check | +| dataplane.livenessProbe.periodSeconds | int | `10` | this fields specifies that kubernetes should perform a liveness check every 10 seconds | +| dataplane.livenessProbe.successThreshold | int | `1` | number of consecutive successes for the probe to be considered successful after having failed | +| dataplane.livenessProbe.timeoutSeconds | int | `5` | number of seconds after which the probe times out | +| dataplane.logging | string | `".level=INFO\nhandlers=java.util.logging.ConsoleHandler\njava.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter\njava.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n"` | configuration of the [Java Util Logging Facade](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html) | +| dataplane.nodeSelector | object | `{}` | | +| dataplane.opentelemetry | string | `"otel.javaagent.enabled=false\notel.javaagent.debug=false"` | configuration of the [Open Telemetry Agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) to collect and expose metrics | +| dataplane.podAnnotations | object | `{}` | additional annotations for the pod | +| dataplane.podLabels | object | `{}` | additional labels for the pod | +| dataplane.podSecurityContext | object | `{"fsGroup":10001,"runAsGroup":10001,"runAsUser":10001,"seccompProfile":{"type":"RuntimeDefault"}}` | 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 | +| dataplane.podSecurityContext.fsGroup | int | `10001` | The owner for volumes and any files created within volumes will belong to this guid | +| dataplane.podSecurityContext.runAsGroup | int | `10001` | Processes within a pod will belong to this guid | +| dataplane.podSecurityContext.runAsUser | int | `10001` | Runs all processes within a pod with a special uid | +| dataplane.podSecurityContext.seccompProfile.type | string | `"RuntimeDefault"` | Restrict a Container's Syscalls with seccomp | +| dataplane.readinessProbe.enabled | bool | `true` | Whether to enable kubernetes [readiness-probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) | +| dataplane.readinessProbe.failureThreshold | int | `6` | when a probe fails kubernetes will try 6 times before giving up | +| dataplane.readinessProbe.initialDelaySeconds | int | `30` | seconds to wait before performing the first readiness check | +| dataplane.readinessProbe.periodSeconds | int | `10` | this fields specifies that kubernetes should perform a liveness check every 10 seconds | +| dataplane.readinessProbe.successThreshold | int | `1` | number of consecutive successes for the probe to be considered successful after having failed | +| dataplane.readinessProbe.timeoutSeconds | int | `5` | number of seconds after which the probe times out | +| dataplane.replicaCount | int | `1` | | +| dataplane.resources | object | `{}` | [resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) for the container | +| dataplane.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 | +| dataplane.securityContext.capabilities.add | list | `[]` | Specifies which capabilities to add to issue specialized syscalls | +| dataplane.securityContext.capabilities.drop | list | `["ALL"]` | Specifies which capabilities to drop to reduce syscall attack surface | +| dataplane.securityContext.readOnlyRootFilesystem | bool | `true` | Whether the root filesystem is mounted in read-only mode | +| dataplane.securityContext.runAsNonRoot | bool | `true` | Requires the container to run without root privileges | +| dataplane.securityContext.runAsUser | int | `10001` | The container's process will run with the specified uid | +| dataplane.service.port | int | `80` | | +| dataplane.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. | +| dataplane.tolerations | list | `[]` | | +| dataplane.url.public | string | `""` | Explicitly declared url for reaching the public api (e.g. if ingresses not used) | +| dataplane.volumeMounts | list | `[]` | declare where to mount [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) into the container | +| dataplane.volumes | list | `[]` | [volume](https://kubernetes.io/docs/concepts/storage/volumes/) directories | +| fullnameOverride | string | `""` | | +| imagePullSecrets | list | `[]` | Existing image pull secret to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) | +| nameOverride | string | `""` | | +| postgresql.enabled | bool | `false` | | +| postgresql.jdbcUrl | string | `""` | | +| postgresql.password | string | `""` | | +| postgresql.username | string | `""` | | +| serviceAccount.annotations | object | `{}` | | +| serviceAccount.create | bool | `true` | | +| serviceAccount.imagePullSecrets | list | `[]` | Existing image pull secret bound to the servic eaccount to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) | +| serviceAccount.name | string | `""` | | +| vault.azure.certificate | string | `""` | | +| vault.azure.client | string | `""` | | +| vault.azure.enabled | bool | `false` | | +| vault.azure.name | string | `""` | | +| vault.azure.secret | string | `""` | | +| vault.azure.tenant | string | `""` | | +| vault.hashicorp.enabled | bool | `true` | | +| vault.hashicorp.healthCheck.enabled | bool | `true` | | +| vault.hashicorp.healthCheck.standbyOk | bool | `true` | | +| vault.hashicorp.paths.health | string | `"/v1/sys/health"` | | +| vault.hashicorp.paths.secret | string | `"/v1/secret"` | | +| vault.hashicorp.timeout | int | `30` | | +| vault.hashicorp.token | string | `""` | | +| vault.hashicorp.url | string | `""` | | +| vault.secretNames.dapsPrivateKey | string | `"daps-private-key"` | | +| vault.secretNames.dapsPublicKey | string | `"daps-public-key"` | | +| vault.secretNames.transferProxyTokenEncryptionAesKey | string | `"transfer-proxy-token-encryption-aes-key"` | | +| vault.secretNames.transferProxyTokenSignerPrivateKey | string | `"transfer-proxy-token-signer-private-key"` | | +| vault.secretNames.transferProxyTokenSignerPublicKey | string | `"transfer-proxy-token-signer-public-key"` | | + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.10.0](https://github.com/norwoodj/helm-docs/releases/v1.10.0) diff --git a/charts/tractusx-connector/README.md.gotmpl b/charts/tractusx-connector/README.md.gotmpl new file mode 100644 index 000000000..51eecb90b --- /dev/null +++ b/charts/tractusx-connector/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 tractus-x-connector catenax-ng-product-edc/tractus-x-connector --version {{ .Version }} +``` + +{{ template "chart.maintainersSection" . }} + +{{ template "chart.sourcesSection" . }} + +{{ template "chart.requirementsSection" . }} + +{{ template "chart.valuesSection" . }} + +{{ template "helm-docs.versionFooter" . }} diff --git a/charts/tractusx-connector/templates/NOTES.txt b/charts/tractusx-connector/templates/NOTES.txt new file mode 100644 index 000000000..254cf9c67 --- /dev/null +++ b/charts/tractusx-connector/templates/NOTES.txt @@ -0,0 +1,45 @@ +1. Get the control plane URL by running these commands: +{{ with index .Values.controlplane.ingresses 0}} +{{- if .enabled }} +{{- range .paths }} + http{{ if .tls }}s{{ end }}://{{ .hostname }}{{ .path }} +{{- end }} +{{- else if contains "NodePort" $.Values.controlplane.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ $.Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "txdc.fullname" $ }}) + 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.controlplane.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 "txdc.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "txdc.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ $.Values.controlplane.service.port }} +{{- else if contains "ClusterIP" $.Values.controlplane.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ $.Release.Namespace }} -l "app.kubernetes.io/name={{ include "txdc.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 +{{- end }} +{{- end }} + +2. Get the data plane URL by running these commands: +{{ with index .Values.controlplane.ingresses 0}} +{{- if .enabled }} +{{- range .paths }} + http{{ if .tls }}s{{ end }}://{{ .hostname }}{{ .path }} +{{- end }} +{{- else if contains "NodePort" $.Values.dataplane.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ $.Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "txdc.fullname" $ }}) + 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.dataplane.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 "txdc.fullname" $ }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "txdc.fullname" $ }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" $.Values.dataplane.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ $.Release.Namespace }} -l "app.kubernetes.io/name={{ include "txdc.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 +{{- end }} +{{- end }} diff --git a/charts/tractusx-connector/templates/_helpers.tpl b/charts/tractusx-connector/templates/_helpers.tpl new file mode 100644 index 000000000..6d821a6cf --- /dev/null +++ b/charts/tractusx-connector/templates/_helpers.tpl @@ -0,0 +1,175 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "txdc.name" -}} +{{- default .Chart.Name .Values.nameOverride | replace "+" "_" | 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 "txdc.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 "txdc.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Control Common labels +*/}} +{{- define "txdc.labels" -}} +helm.sh/chart: {{ include "txdc.chart" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Control Common labels +*/}} +{{- define "txdc.controlplane.labels" -}} +helm.sh/chart: {{ include "txdc.chart" . }} +{{ include "txdc.controlplane.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +app.kubernetes.io/component: edc-controlplane +app.kubernetes.io/part-of: edc +{{- end }} + +{{/* +Data Common labels +*/}} +{{- define "txdc.dataplane.labels" -}} +helm.sh/chart: {{ include "txdc.chart" . }} +{{ include "txdc.dataplane.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +app.kubernetes.io/component: edc-dataplane +app.kubernetes.io/part-of: edc +{{- end }} + +{{/* +Control Selector labels +*/}} +{{- define "txdc.controlplane.selectorLabels" -}} +app.kubernetes.io/name: {{ include "txdc.name" . }}-controlplane +app.kubernetes.io/instance: {{ .Release.Name }}-controlplane +{{- end }} + +{{/* +Data Selector labels +*/}} +{{- define "txdc.dataplane.selectorLabels" -}} +app.kubernetes.io/name: {{ include "txdc.name" . }}-dataplane +app.kubernetes.io/instance: {{ .Release.Name }}-dataplane +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "txdc.controlplane.serviceaccount.name" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "txdc.fullname" . ) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "txdc.dataplane.serviceaccount.name" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "txdc.fullname" . ) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{/* +Control IDS URL +*/}} +{{- define "txdc.controlplane.url.ids" -}} +{{- if .Values.controlplane.url.ids }}{{/* if ids api url has been specified explicitly */}} +{{- .Values.controlplane.url.ids }} +{{- else }}{{/* else when ids api url has not been specified explicitly */}} +{{- with (index .Values.controlplane.ingresses 0) }} +{{- if .enabled }}{{/* if ingress enabled */}} +{{- if .tls.enabled }}{{/* if TLS enabled */}} +{{- printf "https://%s" .hostname -}} +{{- else }}{{/* else when TLS not enabled */}} +{{- printf "http://%s" .hostname -}} +{{- end }}{{/* end if tls */}} +{{- else }}{{/* else when ingress not enabled */}} +{{- printf "http://%s-controlplane:%v" ( include "txdc.fullname" $ ) $.Values.controlplane.endpoints.ids.port -}} +{{- end }}{{/* end if ingress */}} +{{- end }}{{/* end with ingress */}} +{{- end }}{{/* end if .Values.controlplane.url.ids */}} +{{- end }} + +{{/* +Control IDS URL +*/}} +{{- define "txdc.controlplane.url.validation" -}} +{{- printf "http://%s-controlplane:%v%s" ( include "txdc.fullname" $ ) $.Values.controlplane.endpoints.validation.port $.Values.controlplane.endpoints.validation.path -}} +{{- end }} + +{{/* +Data Control URL +*/}} +{{- define "txdc.dataplane.url.control" -}} +{{- printf "http://%s-dataplane:%v%s" (include "txdc.fullname" . ) .Values.dataplane.endpoints.control.port .Values.dataplane.endpoints.control.path -}} +{{- end }} + +{{/* +Data Public URL +*/}} +{{- define "txdc.dataplane.url.public" -}} +{{- if .Values.dataplane.url.public }}{{/* if public api url has been specified explicitly */}} +{{- .Values.dataplane.url.public }} +{{- else }}{{/* else when public api url has not been specified explicitly */}} +{{- with (index .Values.dataplane.ingresses 0) }} +{{- if .enabled }}{{/* if ingress enabled */}} +{{- if .tls.enabled }}{{/* if TLS enabled */}} +{{- printf "https://%s%s" .hostname $.Values.dataplane.endpoints.public.path -}} +{{- else }}{{/* else when TLS not enabled */}} +{{- printf "http://%s%s" .hostname $.Values.dataplane.endpoints.public.path -}} +{{- end }}{{/* end if tls */}} +{{- else }}{{/* else when ingress not enabled */}} +{{- printf "http://%s-dataplane:%v%s" (include "txdc.fullname" $ ) $.Values.dataplane.endpoints.public.port $.Values.dataplane.endpoints.public.path -}} +{{- end }}{{/* end if ingress */}} +{{- end }}{{/* end with ingress */}} +{{- end }}{{/* end if .Values.dataplane.url.public */}} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "txdc.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "txdc.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/charts/tractusx-connector/templates/configmap-controlplane.yaml b/charts/tractusx-connector/templates/configmap-controlplane.yaml new file mode 100644 index 000000000..493426aa2 --- /dev/null +++ b/charts/tractusx-connector/templates/configmap-controlplane.yaml @@ -0,0 +1,14 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "txdc.fullname" . }}-controlplane + namespace: {{ .Release.Namespace | default "default" | quote }} + labels: + {{- include "txdc.controlplane.labels" . | nindent 4 }} +data: + opentelemetry.properties: |- + {{- .Values.controlplane.opentelemetry | nindent 4 }} + + logging.properties: |- + {{- .Values.controlplane.logging | nindent 4 }} diff --git a/charts/tractusx-connector/templates/configmap-dataplane.yaml b/charts/tractusx-connector/templates/configmap-dataplane.yaml new file mode 100644 index 000000000..4f4c1a456 --- /dev/null +++ b/charts/tractusx-connector/templates/configmap-dataplane.yaml @@ -0,0 +1,14 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "txdc.fullname" . }}-dataplane + namespace: {{ .Release.Namespace | default "default" | quote }} + labels: + {{- include "txdc.dataplane.labels" . | nindent 4 }} +data: + opentelemetry.properties: |- + {{- .Values.dataplane.opentelemetry | nindent 4 }} + + logging.properties: |- + {{- .Values.dataplane.logging | nindent 4 }} diff --git a/charts/tractusx-connector/templates/deployment-controlplane.yaml b/charts/tractusx-connector/templates/deployment-controlplane.yaml new file mode 100644 index 000000000..27ac5d7e2 --- /dev/null +++ b/charts/tractusx-connector/templates/deployment-controlplane.yaml @@ -0,0 +1,345 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "txdc.fullname" . }}-controlplane + labels: + {{- include "txdc.controlplane.labels" . | nindent 4 }} +spec: + {{- if not .Values.controlplane.autoscaling.enabled }} + replicas: {{ .Values.controlplane.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "txdc.controlplane.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.controlplane.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "txdc.controlplane.selectorLabels" . | nindent 8 }} + {{- with .Values.controlplane.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "txdc.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.controlplane.podSecurityContext | nindent 8 }} + initContainers: + {{- toYaml .Values.controlplane.initContainers | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.controlplane.securityContext | nindent 12 }} + {{- if .Values.controlplane.image.repository }} + image: "{{ .Values.controlplane.image.repository }}:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" + {{- else if and .Values.postgresql.enabled (not .Values.vault.azure.enabled) }} + image: "ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql-hashicorp-vault:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" + {{- else if and .Values.postgresql.enabled .Values.vault.azure.enabled }} + image: "ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql-vault:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" + {{- else if .Values.vault.hashicorp.enabled }} + image: "ghcr.io/catenax-ng/product-edc/edc-controlplane-memory-hashicorp-vault:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" + {{- else if .Values.vault.azure.enabled }} + image: "ghcr.io/catenax-ng/product-edc/edc-controlplane-memory:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" + {{- else }} + {{- fail "cannot choose control-plane image automatically based on configuration" }} + {{- end }} + imagePullPolicy: {{ .Values.controlplane.image.pullPolicy }} + ports: + {{- range $key,$value := .Values.controlplane.endpoints }} + - name: {{ $key }} + containerPort: {{ $value.port }} + protocol: TCP + {{- end }} + {{- if .Values.controlplane.livenessProbe.enabled }} + livenessProbe: + httpGet: + path: {{ .Values.controlplane.endpoints.default.path }}/check/liveness + port: {{ .Values.controlplane.endpoints.default.port }} + initialDelaySeconds: {{ .Values.controlplane.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.controlplane.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.controlplane.livenessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.controlplane.livenessProbe.failureThreshold }} + successThreshold: {{ .Values.controlplane.livenessProbe.successThreshold }} + {{- end }} + {{- if .Values.controlplane.readinessProbe.enabled }} + readinessProbe: + httpGet: + path: {{ .Values.controlplane.endpoints.default.path }}/check/readiness + port: {{ .Values.controlplane.endpoints.default.port }} + initialDelaySeconds: {{ .Values.controlplane.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.controlplane.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.controlplane.readinessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.controlplane.readinessProbe.failureThreshold }} + successThreshold: {{ .Values.controlplane.readinessProbe.successThreshold }} + {{- end }} + resources: + {{- toYaml .Values.controlplane.resources | nindent 12 }} + env: + {{- if .Values.controlplane.debug.enabled }} + - name: "JAVA_TOOL_OPTIONS" + {{- if and .Values.controlplane.debug.enabled .Values.controlplane.debug.suspendOnStart }} + value: >- + {{ printf "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=%v" .Values.controlplane.debug.port }} + {{- else }} + value: >- + {{ printf "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=%v" .Values.controlplane.debug.port }} + {{- end }} + {{- end }} + + ######################## + ## DAPS CONFIGURATION ## + ######################## + + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/iam/oauth2/oauth2-core + - name: EDC_OAUTH_CLIENT_ID + value: {{ .Values.daps.clientId | required ".Values.daps.clientId is required" | quote }} + - name: EDC_OAUTH_PROVIDER_JWKS_URL + value: {{ printf "%s%s" .Values.daps.url .Values.daps.paths.jwks }} + - name: EDC_OAUTH_TOKEN_URL + value: {{ printf "%s%s" .Values.daps.url .Values.daps.paths.token }} + - name: EDC_OAUTH_PRIVATE_KEY_ALIAS + value: {{ .Values.vault.secretNames.dapsPrivateKey | required ".Values.vault.secretNames.dapsPrivateKey is required" | quote }} + - name: EDC_OAUTH_PUBLIC_KEY_ALIAS + value: {{ .Values.vault.secretNames.dapsPublicKey | required ".Values.vault.secretNames.dapsPublicKey is required" | quote }} + + ####### + # API # + ####### + - name: "EDC_API_AUTH_KEY" + value: {{ .Values.controlplane.endpoints.data.authKey | required ".Values.controlplane.endpoints.data.authKey is required" | quote }} + - name: "WEB_HTTP_DEFAULT_PORT" + value: {{ .Values.controlplane.endpoints.default.port | quote }} + - name: "WEB_HTTP_DEFAULT_PATH" + value: {{ .Values.controlplane.endpoints.default.path | quote }} + - name: "WEB_HTTP_DATA_PORT" + value: {{ .Values.controlplane.endpoints.data.port | quote }} + - name: "WEB_HTTP_DATA_PATH" + value: {{ .Values.controlplane.endpoints.data.path | quote }} + - name: "WEB_HTTP_VALIDATION_PORT" + value: {{ .Values.controlplane.endpoints.validation.port | quote }} + - name: "WEB_HTTP_VALIDATION_PATH" + value: {{ .Values.controlplane.endpoints.validation.path | quote }} + - name: "WEB_HTTP_CONTROL_PORT" + value: {{ .Values.controlplane.endpoints.control.port | quote }} + - name: "WEB_HTTP_CONTROL_PATH" + value: {{ .Values.controlplane.endpoints.control.path | quote }} + - name: "WEB_HTTP_IDS_PORT" + value: {{ .Values.controlplane.endpoints.ids.port | quote }} + - name: "WEB_HTTP_IDS_PATH" + value: {{ .Values.controlplane.endpoints.ids.path | quote }} + + ######### + ## IDS ## + ######### + - name: "IDS_WEBHOOK_ADDRESS" + value: {{ include "txdc.controlplane.url.ids" . | quote }} + - name: "EDC_IDS_ENDPOINT" + value: {{ printf "%s%s" (include "txdc.controlplane.url.ids" .) .Values.controlplane.endpoints.ids.path | quote }} + - name: "EDC_IDS_ID" + value: {{ printf "urn:connector:%s" (lower .Values.controlplane.internationalDataSpaces.id) | quote }} + - name: "EDC_IDS_DESCRIPTION" + value: {{ .Values.controlplane.internationalDataSpaces.description | quote }} + - name: "EDC_IDS_TITLE" + value: {{ .Values.controlplane.internationalDataSpaces.title | quote }} + - name: "EDC_IDS_MAINTAINER" + value: {{ .Values.controlplane.internationalDataSpaces.maintainer | quote }} + - name: "EDC_IDS_CURATOR" + value: {{ .Values.controlplane.internationalDataSpaces.curator | quote }} + - name: "EDC_IDS_CATALOG_ID" + value: {{ printf "urn:catalog:%s" (lower .Values.controlplane.internationalDataSpaces.catalogId) | quote }} + - name: "EDC_OAUTH_PROVIDER_AUDIENCE" + value: "idsc:IDS_CONNECTORS_ALL" + - name: "EDC_OAUTH_ENDPOINT_AUDIENCE" + value: {{ printf "%s%s%s" (include "txdc.controlplane.url.ids" . ) .Values.controlplane.endpoints.ids.path "/data" | quote }} + # this is the old setting name for 'EDC_OAUTH_ENDPOINT_AUDIENCE' and is mandatory for Produce EDC v0.1.2 and older + - name: "EDC_IDS_ENDPOINT_AUDIENCE" + value: {{ printf "%s%s%s" (include "txdc.controlplane.url.ids" . ) .Values.controlplane.endpoints.ids.path "/data" | quote }} + + {{- if .Values.postgresql.enabled }} + + ################ + ## POSTGRESQL ## + ################ + + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/control-plane/store/sql/asset-index-sql + - name: "EDC_DATASOURCE_ASSET_NAME" + value: "asset" + - name: "EDC_DATASOURCE_ASSET_USER" + value: {{ .Values.postgresql.username | required ".Values.postgresql.username is required" | quote }} + - name: "EDC_DATASOURCE_ASSET_PASSWORD" + value: {{ .Values.postgresql.password | required ".Values.postgresql.password is required" | quote }} + - name: "EDC_DATASOURCE_ASSET_URL" + value: {{ .Values.postgresql.jdbcUrl | required ".Values.postgresql.jdbcUrl is required" | quote }} + + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/control-plane/store/sql/contract-definition-store-sql + - name: "EDC_DATASOURCE_CONTRACTDEFINITION_NAME" + value: "contractdefinition" + - name: "EDC_DATASOURCE_CONTRACTDEFINITION_USER" + value: {{ .Values.postgresql.username | required ".Values.postgresql.username is required" | quote }} + - name: "EDC_DATASOURCE_CONTRACTDEFINITION_PASSWORD" + value: {{ .Values.postgresql.password | required ".Values.postgresql.password is required" | quote }} + - name: "EDC_DATASOURCE_CONTRACTDEFINITION_URL" + value: {{ .Values.postgresql.jdbcUrl | required ".Values.postgresql.jdbcUrl is required" | quote }} + + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/control-plane/store/sql/contract-negotiation-store-sql + - name: "EDC_DATASOURCE_CONTRACTNEGOTIATION_NAME" + value: "contractnegotiation" + - name: "EDC_DATASOURCE_CONTRACTNEGOTIATION_USER" + value: {{ .Values.postgresql.username | required ".Values.postgresql.username is required" | quote }} + - name: "EDC_DATASOURCE_CONTRACTNEGOTIATION_PASSWORD" + value: {{ .Values.postgresql.password | required ".Values.postgresql.password is required" | quote }} + - name: "EDC_DATASOURCE_CONTRACTNEGOTIATION_URL" + value: {{ .Values.postgresql.jdbcUrl | required ".Values.postgresql.jdbcUrl is required" | quote }} + + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/control-plane/store/sql/policy-store-sql + - name: "EDC_DATASOURCE_POLICY_NAME" + value: "policy" + - name: "EDC_DATASOURCE_POLICY_USER" + value: {{ .Values.postgresql.username | required ".Values.postgresql.username is required" | quote }} + - name: "EDC_DATASOURCE_POLICY_PASSWORD" + value: {{ .Values.postgresql.password | required ".Values.postgresql.password is required" | quote }} + - name: "EDC_DATASOURCE_POLICY_URL" + value: {{ .Values.postgresql.jdbcUrl | required ".Values.postgresql.jdbcUrl is required" | quote }} + + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/control-plane/store/sql/transfer-process-store-sql + - name: "EDC_DATASOURCE_TRANSFERPROCESS_NAME" + value: "transferprocess" + - name: "EDC_DATASOURCE_TRANSFERPROCESS_USER" + value: {{ .Values.postgresql.username | required ".Values.postgresql.username is required" | quote }} + - name: "EDC_DATASOURCE_TRANSFERPROCESS_PASSWORD" + value: {{ .Values.postgresql.password | required ".Values.postgresql.password is required" | quote }} + - name: "EDC_DATASOURCE_TRANSFERPROCESS_URL" + value: {{ .Values.postgresql.jdbcUrl | required ".Values.postgresql.jdbcUrl is required" | quote }} + {{- end }} + + ################ + ## DATA PLANE ## + ################ + + # see extension https://github.com/catenax-ng/product-edc/tree/develop/edc-extensions/dataplane-selector-configuration + - name: "EDC_DATAPLANE_SELECTOR_DEFAULTPLANE_URL" + value: {{ include "txdc.dataplane.url.control" . }}/transfer + - name: "EDC_DATAPLANE_SELECTOR_DEFAULTPLANE_SOURCETYPES" + value: "HttpData,AmazonS3" + - name: "EDC_DATAPLANE_SELECTOR_DEFAULTPLANE_DESTINATIONTYPES" + value: "HttpProxy,AmazonS3" + - name: "EDC_DATAPLANE_SELECTOR_DEFAULTPLANE_PROPERTIES" + value: |- + {{ printf "{ \"publicApiUrl\": \"%s/\" }" (include "txdc.dataplane.url.public" . ) }} + + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/control-plane/data-plane-transfer + - name: "EDC_TRANSFER_PROXY_ENDPOINT" + value: {{ include "txdc.dataplane.url.public" . }}/ + - name: "EDC_TRANSFER_PROXY_TOKEN_SIGNER_PRIVATEKEY_ALIAS" + value: {{ .Values.vault.secretNames.transferProxyTokenSignerPrivateKey | quote }} + - name: "EDC_TRANSFER_PROXY_TOKEN_VERIFIER_PUBLICKEY_ALIAS" + value: {{ .Values.vault.secretNames.transferProxyTokenSignerPublicKey | quote }} + + # see extension https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/tree/main/extensions/control-plane/http-receiver + + - name: "EDC_RECEIVER_HTTP_ENDPOINT" + value: {{ .Values.backendService.httpProxyTokenReceiverUrl | required ".Values.backendService.httpProxyTokenReceiverUrl is required" | quote }} + + ########### + ## VAULT ## + ########### + + {{- if .Values.vault.hashicorp.enabled }} + # see extension https://github.com/catenax-ng/product-edc/tree/develop/edc-extensions/hashicorp-vault + - name: "EDC_VAULT_HASHICORP_URL" + value: {{ .Values.vault.hashicorp.url | required ".Values.vault.hashicorp.url is required" | quote }} + - name: "EDC_VAULT_HASHICORP_TOKEN" + value: {{ .Values.vault.hashicorp.token | required ".Values.vault.hashicorp.token is required" | quote }} + - name: "EDC_VAULT_HASHICORP_TIMEOUT_SECONDS" + value: {{ .Values.vault.hashicorp.timeout | quote }} + - name: "EDC_VAULT_HASHICORP_HEALTH_CHECK_ENABLED" + value: {{ .Values.vault.hashicorp.healthCheck.enabled | quote }} + - name: "EDC_VAULT_HASHICORP_HEALTH_CHECK_STANDBY_OK" + value: {{ .Values.vault.hashicorp.healthCheck.standbyOk | quote }} + - name: "EDC_VAULT_HASHICORP_API_SECRET_PATH" + value: {{ .Values.vault.hashicorp.paths.secret | quote }} + - name: "EDC_VAULT_HASHICORP_API_HEALTH_CHECK_PATH" + value: {{ .Values.vault.hashicorp.paths.health | quote }} + {{- end }} + + {{- if .Values.vault.azure.enabled }} + - name: "EDC_VAULT_CLIENTID" + value: {{ .Values.vault.azure.client | required ".Values.vault.azure.client is required" | quote }} + - name: "EDC_VAULT_TENANTID" + value: {{ .Values.vault.azure.tenant | required ".Values.vault.azure.tenant is required" | quote }} + - name: "EDC_VAULT_NAME" + value: {{ .Values.vault.azure.name | required ".Values.vault.azure.name is required" | quote }} + - name: "EDC_VAULT_CLIENTSECRET" + value: {{ .Values.vault.azure.secret | required ".Values.vault.azure.secret is required" | quote }} + - name: "EDC_VAULT_CERTIFICATE" + value: {{ .Values.vault.azure.certificate | required ".Values.vault.azure.certificate is required" | quote }} + {{- end }} + + ##################### + ## DATA ENCRYPTION ## + ##################### + + # see extension https://github.com/catenax-ng/product-edc/tree/develop/edc-extensions/data-encryption + - name: "EDC_DATA_ENCRYPTION_KEYS_ALIAS" + value: {{ .Values.vault.secretNames.transferProxyTokenEncryptionAesKey | quote }} + - name: "EDC_DATA_ENCRYPTION_ALGORITHM" + value: "AES" + + ###################################### + ## Additional environment variables ## + ###################################### + {{- range $key, $value := .Values.controlplane.envValueFrom }} + - name: {{ $key | quote }} + valueFrom: + {{- tpl (toYaml $value) $ | nindent 16 }} + {{- end }} + {{- range $key, $value := .Values.controlplane.env }} + - name: {{ $key | quote }} + value: {{ $value | quote }} + {{- end }} + {{- if and (or .Values.controlplane.envSecretNames .Values.controlplane.envConfigMapNames) (or (gt (len .Values.controlplane.envSecretNames) 0) (gt (len .Values.controlplane.envConfigMapNames) 0)) }} + envFrom: + {{- with .Values.controlplane.envSecretNames }} + - secretRef: + name: {{ . | quote }} + {{- end }} + {{- with .Values.controlplane.envConfigMapNames }} + - configMapRef: + name: {{ . | quote }} + {{- end }} + {{- end }} + volumeMounts: + - name: "configuration" + mountPath: "/app/opentelemetry.properties" + subPath: "opentelemetry.properties" + - name: "configuration" + mountPath: "/app/logging.properties" + subPath: "logging.properties" + volumes: + - name: "configuration" + configMap: + name: {{ include "txdc.fullname" . }}-controlplane + items: + - key: "opentelemetry.properties" + path: "opentelemetry.properties" + - key: "logging.properties" + path: "logging.properties" + {{- with .Values.controlplane.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.controlplane.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.controlplane.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/charts/tractusx-connector/templates/deployment-dataplane.yaml b/charts/tractusx-connector/templates/deployment-dataplane.yaml new file mode 100644 index 000000000..4221b5a35 --- /dev/null +++ b/charts/tractusx-connector/templates/deployment-dataplane.yaml @@ -0,0 +1,219 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "txdc.fullname" . }}-dataplane + labels: + {{- include "txdc.dataplane.labels" . | nindent 4 }} +spec: + {{- if not .Values.dataplane.autoscaling.enabled }} + replicas: {{ .Values.dataplane.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "txdc.dataplane.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.dataplane.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "txdc.dataplane.selectorLabels" . | nindent 8 }} + {{- with .Values.dataplane.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "txdc.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.dataplane.podSecurityContext | nindent 8 }} + initContainers: + {{- toYaml .Values.dataplane.initContainers | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.dataplane.securityContext | nindent 12 }} + {{- if .Values.dataplane.image.repository }} + image: "{{ .Values.dataplane.image.repository }}:{{ .Values.dataplane.image.tag | default .Chart.AppVersion }}" + {{- else if and .Values.vault.hashicorp }} + image: "ghcr.io/catenax-ng/product-edc/edc-dataplane-hashicorp-vault:{{ .Values.dataplane.image.tag | default .Chart.AppVersion }}" + {{- else if .Values.vault.azure }} + image: "ghcr.io/catenax-ng/product-edc/edc-dataplane-azure-vault:{{ .Values.dataplane.image.tag | default .Chart.AppVersion }}" + {{- else }} + {{- fail "cannot choose data-plane image automatically based on configuration" }} + {{- end }} + imagePullPolicy: {{ .Values.dataplane.image.pullPolicy }} + ports: + {{- range $key,$value := .Values.dataplane.endpoints }} + - name: {{ $key }} + containerPort: {{ $value.port }} + protocol: TCP + {{- end }} + {{- if .Values.dataplane.livenessProbe.enabled }} + livenessProbe: + httpGet: + path: {{ .Values.dataplane.endpoints.default.path }}/check/liveness + port: {{ .Values.dataplane.endpoints.default.port }} + initialDelaySeconds: {{ .Values.dataplane.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.dataplane.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.dataplane.livenessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.dataplane.livenessProbe.failureThreshold }} + successThreshold: {{ .Values.dataplane.livenessProbe.successThreshold }} + {{- end }} + {{- if .Values.dataplane.readinessProbe.enabled }} + readinessProbe: + httpGet: + path: {{ .Values.dataplane.endpoints.default.path }}/check/readiness + port: {{ .Values.dataplane.endpoints.default.port }} + initialDelaySeconds: {{ .Values.dataplane.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.dataplane.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.dataplane.readinessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.dataplane.readinessProbe.failureThreshold }} + successThreshold: {{ .Values.dataplane.readinessProbe.successThreshold }} + {{- end }} + resources: + {{- toYaml .Values.dataplane.resources | nindent 12 }} + env: + {{- if .Values.dataplane.debug.enabled }} + - name: "JAVA_TOOL_OPTIONS" + {{- if and .Values.dataplane.debug.enabled .Values.dataplane.debug.suspendOnStart }} + value: >- + {{ printf "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=%v" .Values.dataplane.debug.port }} + {{- else }} + value: >- + {{ printf "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=%v" .Values.dataplane.debug.port }} + {{- end }} + {{- end }} + + ####### + # API # + ####### + - name: "WEB_HTTP_DEFAULT_PORT" + value: {{ .Values.dataplane.endpoints.default.port | quote }} + - name: "WEB_HTTP_DEFAULT_PATH" + value: {{ .Values.dataplane.endpoints.default.path | quote }} + - name: "WEB_HTTP_CONTROL_PORT" + value: {{ .Values.dataplane.endpoints.control.port | quote }} + - name: "WEB_HTTP_CONTROL_PATH" + value: {{ .Values.dataplane.endpoints.control.path | quote }} + - name: "WEB_HTTP_VALIDATION_PORT" + value: {{ .Values.dataplane.endpoints.validation.port | quote }} + - name: "WEB_HTTP_VALIDATION_PATH" + value: {{ .Values.dataplane.endpoints.validation.path | quote }} + - name: "WEB_HTTP_METRICS_PORT" + value: {{ .Values.dataplane.endpoints.metrics.port | quote }} + - name: "WEB_HTTP_METRICS_PATH" + value: {{ .Values.dataplane.endpoints.metrics.path | quote }} + - name: "WEB_HTTP_PUBLIC_PORT" + value: {{ .Values.dataplane.endpoints.public.port | quote }} + - name: "WEB_HTTP_PUBLIC_PATH" + value: {{ .Values.dataplane.endpoints.public.path | quote }} + - name: "EDC_DATAPLANE_TOKEN_VALIDATION_ENDPOINT" + value: {{ include "txdc.controlplane.url.validation" .}} + + ####### + # AWS # + ####### + {{- if .Values.dataplane.aws.endpointOverride }} + - name: "EDC_AWS_ENDPOINT_OVERRIDE" + value: {{ .Values.dataplane.aws.endpointOverride | quote }} + {{- end }} + {{- if .Values.dataplane.aws.secretAccessKey }} + - name: "AWS_SECRET_ACCESS_KEY" + value: {{ .Values.dataplane.aws.secretAccessKey | quote }} + {{- end }} + {{- if .Values.dataplane.aws.accessKeyId }} + - name: "AWS_ACCESS_KEY_ID" + value: {{ .Values.dataplane.aws.accessKeyId | quote }} + {{- end }} + + ########### + ## VAULT ## + ########### + + {{- if .Values.vault.hashicorp.enabled }} + # see extension https://github.com/catenax-ng/product-edc/tree/develop/edc-extensions/hashicorp-vault + - name: "EDC_VAULT_HASHICORP_URL" + value: {{ .Values.vault.hashicorp.url | required ".Values.vault.hashicorp.url is required" | quote }} + - name: "EDC_VAULT_HASHICORP_TOKEN" + value: {{ .Values.vault.hashicorp.token | required ".Values.vault.hashicorp.token is required" | quote }} + - name: "EDC_VAULT_HASHICORP_TIMEOUT_SECONDS" + value: {{ .Values.vault.hashicorp.timeout | quote }} + - name: "EDC_VAULT_HASHICORP_HEALTH_CHECK_ENABLED" + value: {{ .Values.vault.hashicorp.healthCheck.enabled | quote }} + - name: "EDC_VAULT_HASHICORP_HEALTH_CHECK_STANDBY_OK" + value: {{ .Values.vault.hashicorp.healthCheck.standbyOk | quote }} + - name: "EDC_VAULT_HASHICORP_API_SECRET_PATH" + value: {{ .Values.vault.hashicorp.paths.secret | quote }} + - name: "EDC_VAULT_HASHICORP_API_HEALTH_CHECK_PATH" + value: {{ .Values.vault.hashicorp.paths.health | quote }} + {{- end }} + + {{- if .Values.vault.azure.enabled }} + - name: "EDC_VAULT_CLIENTID" + value: {{ .Values.vault.azure.client | quote }} + - name: "EDC_VAULT_TENANTID" + value: {{ .Values.vault.azure.tenant | quote }} + - name: "EDC_VAULT_NAME" + value: {{ .Values.vault.azure.name | quote }} + - name: "EDC_VAULT_CLIENTSECRET" + value: {{ .Values.vault.azure.secret | quote }} + - name: "EDC_VAULT_CERTIFICATE" + value: {{ .Values.vault.azure.certificate | quote }} + {{- end }} + + ###################################### + ## Additional environment variables ## + ###################################### + {{- range $key, $value := .Values.controlplane.envValueFrom }} + - name: {{ $key | quote }} + valueFrom: + {{- tpl (toYaml $value) $ | nindent 16 }} + {{- end }} + {{- range $key, $value := .Values.controlplane.env }} + - name: {{ $key | quote }} + value: {{ $value | quote }} + {{- end }} + {{- if and (or .Values.dataplane.envSecretNames .Values.dataplane.envConfigMapNames) (or (gt (len .Values.dataplane.envSecretNames) 0) (gt (len .Values.dataplane.envConfigMapNames) 0)) }} + envFrom: + {{- with .Values.controlplane.envSecretNames }} + - secretRef: + name: {{ . | quote }} + {{- end }} + {{- with .Values.controlplane.envConfigMapNames }} + - configMapRef: + name: {{ . | quote }} + {{- end }} + {{- end }} + volumeMounts: + - name: "configuration" + mountPath: "/app/opentelemetry.properties" + subPath: "opentelemetry.properties" + - name: "configuration" + mountPath: "/app/logging.properties" + subPath: "logging.properties" + volumes: + - name: "configuration" + configMap: + name: {{ include "txdc.fullname" . }}-dataplane + items: + - key: "opentelemetry.properties" + path: "opentelemetry.properties" + - key: "logging.properties" + path: "logging.properties" + {{- with .Values.dataplane.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.dataplane.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.dataplane.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/charts/tractusx-connector/templates/hpa-controlplane.yaml b/charts/tractusx-connector/templates/hpa-controlplane.yaml new file mode 100644 index 000000000..36fe8fae0 --- /dev/null +++ b/charts/tractusx-connector/templates/hpa-controlplane.yaml @@ -0,0 +1,29 @@ +{{- if .Values.controlplane.autoscaling.enabled }} +--- +apiVersion: autoscaling/v2beta1 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "txdc.fullname" . }}-controlplane + labels: + {{- include "txdc.controlplane.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "txdc.fullname" . }}-controlplane + minReplicas: {{ .Values.controlplane.autoscaling.minReplicas }} + maxReplicas: {{ .Values.controlplane.autoscaling.maxReplicas }} + metrics: + {{- if .Values.controlplane.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + targetAverageUtilization: {{ .Values.controlplane.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.controlplane.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + targetAverageUtilization: {{ .Values.controlplane.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/charts/tractusx-connector/templates/hpa-dataplane.yaml b/charts/tractusx-connector/templates/hpa-dataplane.yaml new file mode 100644 index 000000000..abad34fcc --- /dev/null +++ b/charts/tractusx-connector/templates/hpa-dataplane.yaml @@ -0,0 +1,29 @@ +{{- if .Values.controlplane.autoscaling.enabled }} +--- +apiVersion: autoscaling/v2beta1 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "txdc.fullname" . }}-dataplane + labels: + {{- include "txdc.dataplane.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "txdc.fullname" . }}-dataplane + minReplicas: {{ .Values.dataplane.autoscaling.minReplicas }} + maxReplicas: {{ .Values.dataplane.autoscaling.maxReplicas }} + metrics: + {{- if .Values.dataplane.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + targetAverageUtilization: {{ .Values.dataplane.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.dataplane.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + targetAverageUtilization: {{ .Values.dataplane.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/charts/tractusx-connector/templates/ingress-controlplane.yaml b/charts/tractusx-connector/templates/ingress-controlplane.yaml new file mode 100644 index 000000000..a2325b17c --- /dev/null +++ b/charts/tractusx-connector/templates/ingress-controlplane.yaml @@ -0,0 +1,77 @@ +{{- $fullName := include "txdc.fullname" . }} +{{- $controlLabels := include "txdc.controlplane.labels" . | nindent 4 }} +{{- $controlEdcEndpoints := .Values.controlplane.endpoints }} +{{- $gitVersion := .Capabilities.KubeVersion.GitVersion }} +{{- $namespace := .Release.Namespace }} + +{{- range .Values.controlplane.ingresses }} +{{- if and .enabled .endpoints }} +{{- $controlIngressName := printf "%s-controlplane-%s" $fullName .hostname }} +--- +{{- if semverCompare ">=1.19-0" $gitVersion }} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" $gitVersion }} +apiVersion: networking.k8s.io/v1beta1 +{{- else }} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $controlIngressName }} + namespace: {{ $namespace | default "default" | quote }} + labels: + {{- $controlLabels | nindent 2 }} + annotations: + {{- 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 }} + {{- end }} + {{- 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 .className (semverCompare ">=1.18-0" $gitVersion) }} + ingressClassName: {{ .className }} + {{- end }} + {{- if .hostname }} + {{- if .tls.enabled }} + tls: + - hosts: + - {{ .hostname }} + {{- if .tls.secretName }} + secretName: {{ .tls.secretName }} + {{- else }} + secretName: {{ $controlIngressName }}-tls + {{- end }} + {{- end }} + rules: + - host: {{ .hostname }} + http: + paths: + {{- $ingressEdcEndpoints := .endpoints }} + {{- range $name, $mapping := $controlEdcEndpoints }} + {{- if (has $name $ingressEdcEndpoints) }} + - path: {{ $mapping.path }} + pathType: Prefix + backend: + {{- if semverCompare ">=1.19-0" $gitVersion }} + service: + name: {{ $fullName }}-controlplane + port: + number: {{ $mapping.port }} + {{- else }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} +{{- end }}{{- /* end: if .enabled */}} +{{- end }}{{- /* end: range .Values.ingresses */}} diff --git a/charts/tractusx-connector/templates/ingress-dataplane.yaml b/charts/tractusx-connector/templates/ingress-dataplane.yaml new file mode 100644 index 000000000..1f79d09c1 --- /dev/null +++ b/charts/tractusx-connector/templates/ingress-dataplane.yaml @@ -0,0 +1,77 @@ +{{- $fullName := include "txdc.fullname" . }} +{{- $dataLabels := include "txdc.dataplane.labels" . | nindent 4 }} +{{- $dataEdcEndpoints := .Values.dataplane.endpoints }} +{{- $gitVersion := .Capabilities.KubeVersion.GitVersion }} +{{- $namespace := .Release.Namespace }} + +{{- range .Values.dataplane.ingresses }} +{{- if and .enabled .endpoints }} +{{- $dataIngressName := printf "%s-dataplane-%s" $fullName .hostname }} +--- +{{- if semverCompare ">=1.19-0" $gitVersion }} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" $gitVersion }} +apiVersion: networking.k8s.io/v1beta1 +{{- else }} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $dataIngressName }} + namespace: {{ $namespace | default "default" | quote }} + labels: + {{- $dataLabels | nindent 2 }} + annotations: + {{- 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 }} + {{- end }} + {{- 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 .className (semverCompare ">=1.18-0" $gitVersion) }} + ingressClassName: {{ .className }} + {{- end }} + {{- if .hostname }} + {{- if .tls.enabled }} + tls: + - hosts: + - {{ .hostname }} + {{- if .tls.secretName }} + secretName: {{ .tls.secretName }} + {{- else }} + secretName: {{ $dataIngressName }}-tls + {{- end }} + {{- end }} + rules: + - host: {{ .hostname }} + http: + paths: + {{- $ingressEdcEndpoints := .endpoints }} + {{- range $name, $mapping := $dataEdcEndpoints }} + {{- if (has $name $ingressEdcEndpoints) }} + - path: {{ $mapping.path }} + pathType: Prefix + backend: + {{- if semverCompare ">=1.19-0" $gitVersion }} + service: + name: {{ $fullName }}-dataplane + port: + number: {{ $mapping.port }} + {{- else }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} +{{- end }}{{- /* end: if .enabled */}} +{{- end }}{{- /* end: range .Values.ingresses */}} diff --git a/charts/tractusx-connector/templates/service-controlplane.yaml b/charts/tractusx-connector/templates/service-controlplane.yaml new file mode 100644 index 000000000..3c4f70888 --- /dev/null +++ b/charts/tractusx-connector/templates/service-controlplane.yaml @@ -0,0 +1,37 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ include "txdc.fullname" . }}-controlplane + namespace: {{ .Release.Namespace | default "default" | quote }} + labels: + {{- include "txdc.controlplane.labels" . | nindent 4 }} +spec: + type: {{ .Values.controlplane.service.type }} + ports: + - port: {{ .Values.controlplane.endpoints.default.port }} + targetPort: default + protocol: TCP + name: default + - port: {{ .Values.controlplane.endpoints.control.port }} + targetPort: control + protocol: TCP + name: control + - port: {{ .Values.controlplane.endpoints.data.port }} + targetPort: data + protocol: TCP + name: data + - port: {{ .Values.controlplane.endpoints.validation.port }} + targetPort: validation + protocol: TCP + name: validation + - port: {{ .Values.controlplane.endpoints.ids.port }} + targetPort: ids + protocol: TCP + name: ids + - port: {{ .Values.controlplane.endpoints.metrics.port }} + targetPort: metrics + protocol: TCP + name: metrics + selector: + {{- include "txdc.controlplane.selectorLabels" . | nindent 4 }} diff --git a/charts/tractusx-connector/templates/service-dataplane.yaml b/charts/tractusx-connector/templates/service-dataplane.yaml new file mode 100644 index 000000000..26fa9c203 --- /dev/null +++ b/charts/tractusx-connector/templates/service-dataplane.yaml @@ -0,0 +1,29 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ include "txdc.fullname" . }}-dataplane + namespace: {{ .Release.Namespace | default "default" | quote }} + labels: + {{- include "txdc.dataplane.labels" . | nindent 4 }} +spec: + type: {{ .Values.dataplane.service.type }} + ports: + - port: {{ .Values.dataplane.endpoints.default.port }} + targetPort: default + protocol: TCP + name: default + - port: {{ .Values.dataplane.endpoints.control.port }} + targetPort: control + protocol: TCP + name: control + - port: {{ .Values.dataplane.endpoints.public.port }} + targetPort: public + protocol: TCP + name: public + - port: {{ .Values.dataplane.endpoints.metrics.port }} + targetPort: metrics + protocol: TCP + name: metrics + selector: + {{- include "txdc.dataplane.selectorLabels" . | nindent 4 }} diff --git a/charts/tractusx-connector/templates/serviceaccount.yaml b/charts/tractusx-connector/templates/serviceaccount.yaml new file mode 100644 index 000000000..c650bcd68 --- /dev/null +++ b/charts/tractusx-connector/templates/serviceaccount.yaml @@ -0,0 +1,16 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "txdc.serviceAccountName" . }} + labels: + {{- include "txdc.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- with .Values.serviceAccount.imagePullSecrets }} +imagePullSecrets: + {{- toYaml . | nindent 2 }} +{{- end }} +{{- end }} diff --git a/charts/tractusx-connector/values.yaml b/charts/tractusx-connector/values.yaml new file mode 100644 index 000000000..d64cc257c --- /dev/null +++ b/charts/tractusx-connector/values.yaml @@ -0,0 +1,504 @@ +# Default values for eclipse-dataspace-connector. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +fullnameOverride: "" +nameOverride: "" + +# -- 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: [ ] + +customLabels: { } + +controlplane: + image: + # -- Which derivate of the control plane to use. when left empty the deployment will select the correct image automatically + repository: "" + # -- [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: "0.1.2" + initContainers: [ ] + debug: + enabled: false + port: 1044 + suspendOnStart: false + internationalDataSpaces: + id: TXDC + description: Tractus-X Eclipse IDS Data Space Connector + title: "" + maintainer: "" + curator: "" + catalogId: TXDC-Catalog + livenessProbe: + # -- Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) + enabled: true + # -- seconds to wait before performing the first liveness check + initialDelaySeconds: 30 + # -- this fields specifies that kubernetes should perform a liveness check every 10 seconds + periodSeconds: 10 + # -- number of seconds after which the probe times out + timeoutSeconds: 5 + # -- when a probe fails kubernetes will try 6 times before giving up + failureThreshold: 6 + # -- number of consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + readinessProbe: + # -- Whether to enable kubernetes [readiness-probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) + enabled: true + # -- seconds to wait before performing the first readiness check + initialDelaySeconds: 30 + # -- this fields specifies that kubernetes should perform a readiness check every 10 seconds + periodSeconds: 10 + # -- number of seconds after which the probe times out + timeoutSeconds: 5 + # -- when a probe fails kubernetes will try 6 times before giving up + failureThreshold: 6 + # -- number of consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + # -- endpoints of the control plane + endpoints: + # -- default api for health checks, should not be added to any ingress + default: + # -- port for incoming api calls + port: 8080 + # -- path for incoming api calls + path: /api + # -- data management api, used by internal users, can be added to an ingress and must not be internet facing + data: + # -- port for incoming api calls + port: 8081 + # -- path for incoming api calls + path: /data + # -- authentication key, must be attached to each 'X-Api-Key' request header + authKey: "" + # -- validation api, only used by the data plane and should not be added to any ingress + validation: + # -- port for incoming api calls + port: 8082 + # -- path for incoming api calls + path: /validation + # -- control api, used for internal control calls. can be added to the internal ingress, but should probably not + control: + # -- port for incoming api calls + port: 8083 + # -- path for incoming api calls + path: /control + # -- ids api, used for inter connector communication and must be internet facing + ids: + # -- port for incoming api calls + port: 8084 + # -- path for incoming api calls + path: /api/v1/ids + # -- metrics api, used for application metrics, must not be internet facing + metrics: + # -- port for incoming api calls + port: 8085 + # -- path for incoming api calls + path: /metrics + 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 + annotations: { } + # -- additional labels for the pod + podLabels: { } + # -- additional annotations for the pod + 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: + 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: + # -- 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: 10001 + # Extra environment variables that will be pass onto deployment pods + env: {} + # ENV_NAME: value + + # "valueFrom" environment variable references that will be added to deployment pods. Name is templated. + # ref: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#envvarsource-v1-core + envValueFrom: {} + # ENV_NAME: + # configMapKeyRef: + # name: configmap-name + # key: value_key + # secretKeyRef: + # name: secret-name + # key: value_key + + # [Kubernetes Secret Resource](https://kubernetes.io/docs/concepts/configuration/secret/) names to load environment variables from + envSecretNames: [] + # - first-secret + # - second-secret + + # [Kubernetes ConfigMap Resource](https://kubernetes.io/docs/concepts/configuration/configmap/) names to load environment variables from + envConfigMapNames: [] + # - first-config-map + # - second-config-map + + ## Ingress declaration to expose the network service. + ingresses: + ## Public / Internet facing Ingress + - enabled: false + # -- The hostname to be used to precisely map incoming traffic onto the underlying network service + hostname: "edc-control.local" + # -- Additional ingress annotations to add + annotations: { } + # -- EDC endpoints exposed by this ingress resource + endpoints: + - ids + # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use + className: "" + # -- TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource + 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 + 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 + hostname: "edc-control.intranet" + # -- Additional ingress annotations to add + annotations: { } + # -- EDC endpoints exposed by this ingress resource + endpoints: + - data + - control + # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use + className: "" + # -- TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource + 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 + issuer: "" + # -- If preset enables certificate generation via cert-manager cluster-wide issuer + clusterIssuer: "" + # -- declare where to mount [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) into the container + volumeMounts: [ ] + # -- [volume](https://kubernetes.io/docs/concepts/storage/volumes/) directories + volumes: [ ] + # -- [resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) for the container + resources: + { } + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + replicaCount: 1 + 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 + # -- configuration of the [Open Telemetry Agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) to collect and expose metrics + opentelemetry: |- + otel.javaagent.enabled=false + otel.javaagent.debug=false + # -- configuration of the [Java Util Logging Facade](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html) + logging: |- + .level=INFO + handlers=java.util.logging.ConsoleHandler + java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter + java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n + + # [node selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) to constrain pods to nodes + nodeSelector: { } + # [tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) to configure preferred nodes + tolerations: [ ] + # [affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) to configure which nodes the pods can be scheduled on + affinity: { } + + url: + # -- Explicitly declared url for reaching the ids api (e.g. if ingresses not used) + ids: "" + +dataplane: + image: + # -- Which derivate of the data plane to use. when left empty the deployment will select the correct image automatically + repository: "" + # -- [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: "0.1.2" + initContainers: [ ] + debug: + enabled: false + port: 1044 + suspendOnStart: false + livenessProbe: + # -- Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) + enabled: true + # -- seconds to wait before performing the first liveness check + initialDelaySeconds: 30 + # -- this fields specifies that kubernetes should perform a liveness check every 10 seconds + periodSeconds: 10 + # -- number of seconds after which the probe times out + timeoutSeconds: 5 + # -- when a probe fails kubernetes will try 6 times before giving up + failureThreshold: 6 + # -- number of consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + readinessProbe: + # -- Whether to enable kubernetes [readiness-probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) + enabled: true + # -- seconds to wait before performing the first readiness check + initialDelaySeconds: 30 + # -- this fields specifies that kubernetes should perform a liveness check every 10 seconds + periodSeconds: 10 + # -- number of seconds after which the probe times out + timeoutSeconds: 5 + # -- when a probe fails kubernetes will try 6 times before giving up + failureThreshold: 6 + # -- number of consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + 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 + port: 80 + endpoints: + default: + port: 8080 + path: /api + public: + port: 8081 + path: /api/public + validation: + port: 8082 + path: /validation + control: + port: 8083 + path: /api/dataplane/control + metrics: + port: 8084 + path: /metrics + aws: + endpointOverride: "" + accessKeyId: "" + secretAccessKey: "" + # -- additional labels for the pod + podLabels: { } + # -- additional annotations for the pod + 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: + 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: + # -- 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: 10001 + # Extra environment variables that will be pass onto deployment pods + env: {} + # ENV_NAME: value + + # "valueFrom" environment variable references that will be added to deployment pods. Name is templated. + # ref: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#envvarsource-v1-core + envValueFrom: {} + # ENV_NAME: + # configMapKeyRef: + # name: configmap-name + # key: value_key + # secretKeyRef: + # name: secret-name + # key: value_key + + # [Kubernetes Secret Resource](https://kubernetes.io/docs/concepts/configuration/secret/) names to load environment variables from + envSecretNames: [] + # - first-secret + # - second-secret + + # [Kubernetes ConfigMap Resource](https://kubernetes.io/docs/concepts/configuration/configmap/) names to load environment variables from + envConfigMapNames: [] + # - first-config-map + # - second-config-map + + ## Ingress declaration to expose the network service. + ingresses: + ## Public / Internet facing Ingress + - enabled: false + # -- The hostname to be used to precisely map incoming traffic onto the underlying network service + hostname: "edc-data.local" + # -- Additional ingress annotations to add + annotations: { } + # -- EDC endpoints exposed by this ingress resource + endpoints: + - public + # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use + className: "" + # -- TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource + 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 + issuer: "" + # -- If preset enables certificate generation via cert-manager cluster-wide issuer + clusterIssuer: "" + # -- declare where to mount [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) into the container + volumeMounts: [ ] + # -- [volume](https://kubernetes.io/docs/concepts/storage/volumes/) directories + volumes: [ ] + # -- [resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) for the container + resources: + { } + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + replicaCount: 1 + 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 + # -- configuration of the [Open Telemetry Agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) to collect and expose metrics + opentelemetry: |- + otel.javaagent.enabled=false + otel.javaagent.debug=false + # -- configuration of the [Java Util Logging Facade](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html) + logging: |- + .level=INFO + handlers=java.util.logging.ConsoleHandler + java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter + java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n + # [node selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) to constrain pods to nodes + nodeSelector: { } + # [tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) to configure preferred nodes + tolerations: [ ] + # [affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) to configure which nodes the pods can be scheduled on + affinity: { } + + url: + # -- Explicitly declared url for reaching the public api (e.g. if ingresses not used) + public: "" + +postgresql: + enabled: false + jdbcUrl: "" + username: "" + password: "" + +vault: + hashicorp: + enabled: true + url: "" + token: "" + timeout: 30 + healthCheck: + enabled: true + standbyOk: true + paths: + secret: /v1/secret + health: /v1/sys/health + azure: + enabled: false + name: "" + client: "" + tenant: "" + secret: "" + certificate: "" + secretNames: + transferProxyTokenSignerPrivateKey: transfer-proxy-token-signer-private-key + transferProxyTokenSignerPublicKey: transfer-proxy-token-signer-public-key + transferProxyTokenEncryptionAesKey: transfer-proxy-token-encryption-aes-key + dapsPrivateKey: daps-private-key + dapsPublicKey: daps-public-key + +daps: + url: "" + clientId: "" + paths: + jwks: /jwks.json + token: /token + +backendService: + httpProxyTokenReceiverUrl: "" + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: { } + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + # -- Existing image pull secret bound to the servic eaccount to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) + imagePullSecrets: [] diff --git a/ct.yaml b/ct.yaml index 4c716ff7d..1b81ec6ce 100644 --- a/ct.yaml +++ b/ct.yaml @@ -1,4 +1,4 @@ --- validate-maintainers: false chart-dirs: - - deployment/helm + - charts diff --git a/docs/data-transfer/Transfer Data.md b/docs/data-transfer/Transfer Data.md index 5de4b3111..43ca66ebe 100644 --- a/docs/data-transfer/Transfer Data.md +++ b/docs/data-transfer/Transfer Data.md @@ -1,61 +1,23 @@ # 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. +This document will showcase a data transfer between two connectors. ---- +For this transfer connector **Bob** will act as data provider, and connector **Alice** will act as data +consumer. But the roles could be inverse as well. -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. +> Please note: Before running the examples the corresponding environment variables must be set. **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="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. +Set up a data offer in **Bob**, so that **Alice** 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/developer/architecture/domain-model.md)). @@ -70,7 +32,7 @@ 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" \ +curl -X POST "$BOB_DATAMGMT_URL/data/assets" \ --header 'X-Api-Key: password' \ --header 'Content-Type: application/json' \ --data '{ @@ -91,7 +53,7 @@ curl -X POST "$PLATO_DATAMGMT_URL/data/assets" \ ``` ```bash -curl -X POST "${PLATO_DATAMGMT_URL}/data/policydefinitions" \ +curl -X POST "${BOB_DATAMGMT_URL}/data/policydefinitions" \ --header 'X-Api-Key: password' \ --header 'Content-Type: application/json' \ --data '{ @@ -113,7 +75,7 @@ curl -X POST "${PLATO_DATAMGMT_URL}/data/policydefinitions" \ ```bash -curl -X POST "${PLATO_DATAMGMT_URL}/data/contractdefinitions" \ +curl -X POST "${BOB_DATAMGMT_URL}/data/contractdefinitions" \ --header 'X-Api-Key: password' \ --header 'Content-Type: application/json' \ --data '{ @@ -133,7 +95,7 @@ curl -X POST "${PLATO_DATAMGMT_URL}/data/contractdefinitions" \ ## 2. Request Contract Offer Catalog -In this step Sokrates gets told to request contract offers from another connector (in this case Plato). Sokrates will +In this step Alice gets told to request contract offers from another connector (in this case Bob). Alice 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 @@ -144,8 +106,8 @@ connectors, that intent to send messages to each other, have the same DAPS insta **Run** ```bash -curl -G -X GET "${SOKRATES_DATAMGMT_URL}/data/catalog" \ - --data-urlencode "providerUrl=${PLATO_IDS_URL}/api/v1/ids/data" \ +curl -G -X GET "${ALICE_DATAMGMT_URL}/data/catalog" \ + --data-urlencode "providerUrl=${BOB_IDS_URL}/api/v1/ids/data" \ --header 'X-Api-Key: password' \ --header 'Content-Type: application/json' \ -s | jq @@ -168,12 +130,12 @@ and checking whether the `contractAgreementId` is set. This might take a few sec ```bash export NEGOTIATION_ID=$( \ - curl -X POST "${SOKRATES_DATAMGMT_URL}/data/contractnegotiations" \ + curl -X POST "${ALICE_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\", + \"connectorAddress\": \"${BOB_IDS_URL}/api/v1/ids/data\", \"offer\": { \"offerId\": \"1:foo\", \"assetId\": \"1\", @@ -197,7 +159,7 @@ export NEGOTIATION_ID=$( \ ```bash -curl -X GET "${SOKRATES_DATAMGMT_URL}/data/contractnegotiations/${NEGOTIATION_ID}" \ +curl -X GET "${ALICE_DATAMGMT_URL}/data/contractnegotiations/${NEGOTIATION_ID}" \ --header 'X-Api-Key: password' \ --header 'Content-Type: application/json' \ -s | jq @@ -214,7 +176,7 @@ the transfer process is `COMPLETED`. ```bash export CONTRACT_AGREEMENT_ID=$( \ - curl -X GET "$SOKRATES_DATAMGMT_URL/data/contractnegotiations/$NEGOTIATION_ID" \ + curl -X GET "$ALICE_DATAMGMT_URL/data/contractnegotiations/$NEGOTIATION_ID" \ --header 'X-Api-Key: password' \ --header 'Content-Type: application/json' \ -s | jq -r '.contractAgreementId') @@ -223,13 +185,13 @@ export CONTRACT_AGREEMENT_ID=$( \ ```bash export TRANSFER_PROCESS_ID=$(tr -dc '[:alnum:]' < /dev/urandom | head -c20) export TRANSFER_ID=$( \ - curl -X POST "${SOKRATES_DATAMGMT_URL}/data/transferprocess" \ + curl -X POST "${ALICE_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\", + \"connectorAddress\": \"${BOB_IDS_URL}/api/v1/ids/data\", \"contractId\": \"${CONTRACT_AGREEMENT_ID}\", \"assetId\": \"1\", \"managedResources\": \"false\", @@ -239,7 +201,7 @@ export TRANSFER_ID=$( \ ``` ```bash -curl -X GET "$SOKRATES_DATAMGMT_URL/data/transferprocess/$TRANSFER_ID" \ +curl -X GET "$ALICE_DATAMGMT_URL/data/transferprocess/$TRANSFER_ID" \ --header 'X-Api-Key: password' \ --header 'Content-Type: application/json' \ -s | jq @@ -253,7 +215,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}" \ +curl -X GET "${ALICE_BACKEND_URL}/${TRANSFER_PROCESS_ID}" \ --header 'Accept: application/octet-stream' \ -s | jq ``` diff --git a/docs/data-transfer/diagrams/transfer_sequence_1.png b/docs/data-transfer/diagrams/transfer_sequence_1.png index f52ecbb70a0aadca9e555a2481581d11c86ca1d6..22500abc3cf28c375dc580e0abedb524989ed12d 100644 GIT binary patch literal 28233 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 literal 29595 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 S*d-L>t**AAR=(!-d;bAdoUX6{ diff --git a/docs/data-transfer/diagrams/transfer_sequence_1.puml b/docs/data-transfer/diagrams/transfer_sequence_1.puml index a159447ee..b49733a01 100644 --- a/docs/data-transfer/diagrams/transfer_sequence_1.puml +++ b/docs/data-transfer/diagrams/transfer_sequence_1.puml @@ -1,33 +1,33 @@ @startuml -!define sokratesColor 66CCFF -!define platoColor CCFF99 +!define aliceColor 66CCFF +!define bobColor 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 +box Alice + participant AliceControlPlane as "Control Plane" #aliceColor + participant AliceBackendService as "Backend Application" #aliceColor + participant AliceDataPlane as "Data Plane" #aliceColor end box -box Plato - participant PlatoControlPlane as "Control Plane" #platoColor - participant PlatoDataPlane as "Data Plane" #platoColor +box Bob + participant BobControlPlane as "Control Plane" #bobColor + participant BobDataPlane as "Data Plane" #bobColor end box participant JsonPlaceHolder as "JsonPlaceHolder" -User -> PlatoControlPlane ++ : Create Asset +User -> BobControlPlane ++ : Create Asset return 204 -User -> PlatoControlPlane ++ : Create Policy +User -> BobControlPlane ++ : Create Policy return 204 -User -> PlatoControlPlane ++ : Create Contract Definition +User -> BobControlPlane ++ : Create Contract Definition return 204 diff --git a/docs/data-transfer/diagrams/transfer_sequence_2.png b/docs/data-transfer/diagrams/transfer_sequence_2.png index ed2f4dd90855fddd2db0b7841d109524f5628246..95b6eeced3a681dc04de023023d69ba0e2adae49 100644 GIT binary patch literal 27684 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 literal 29371 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 diff --git a/docs/data-transfer/diagrams/transfer_sequence_2.puml b/docs/data-transfer/diagrams/transfer_sequence_2.puml index 805bcbeec..2d078607d 100644 --- a/docs/data-transfer/diagrams/transfer_sequence_2.puml +++ b/docs/data-transfer/diagrams/transfer_sequence_2.puml @@ -1,27 +1,27 @@ @startuml -!define sokratesColor 66CCFF -!define platoColor CCFF99 +!define aliceColor 66CCFF +!define bobColor 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 +box Alice + participant AliceControlPlane as "Control Plane" #aliceColor + participant AliceBackendService as "Backend Application" #aliceColor + participant AliceDataPlane as "Data Plane" #aliceColor end box -box Plato - participant PlatoControlPlane as "Control Plane" #platoColor - participant PlatoDataPlane as "Data Plane" #platoColor +box Bob + participant BobControlPlane as "Control Plane" #bobColor + participant BobDataPlane as "Data Plane" #bobColor end box participant JsonPlaceHolder as "JsonPlaceHolder" -User -> SokratesControlPlane ++ : Request Contract Offers from Plato - SokratesControlPlane -> PlatoControlPlane ++ : IDS Description Request Message +User -> AliceControlPlane ++ : Request Contract Offers from Bob + AliceControlPlane -> BobControlPlane ++ : IDS Description Request Message return Description return Contract Offers diff --git a/docs/data-transfer/diagrams/transfer_sequence_3.png b/docs/data-transfer/diagrams/transfer_sequence_3.png index b1d56ec6c24a1c7ec2913088ff82c1cda4350404..14a30c9a9155109ee92a4baa939cc8d2f9c3e8c2 100644 GIT binary patch literal 32718 zcmc$`1yt1A`UZ?3sHC)%v@|Fo(g@Ps2n;A45)wm$iilFuNK3~MGL$fcfJg~QGo&av z45@T|dpw?d&%OV1|FORHtub19%3f`%SUA6#O|5;8Vj7>4 zk>$hl+Z3u4VMXILQniL6hz-A})Zn@MAvR=}HAW3l+5S8IpZ$N-479hoIrn%i-3-g< zcysyH2O-kz3c>vBx&71C>c^qn)8eJRH|^G61f)q( z)8iXB`gK)uMp8X=IFxwwNtAv=)!`$pl<^X$UA$J!h4Mv3d-nB<6X=J77k|!qb+$dB zyi{}g?g6>FPYR9PJ#H$&-3MMW>&Qxl5zd ztE<+HQ11;@pYEfi+^_3UH#J{(O*vr{6N?i1eDectX3Ft#P=DvZw2)nYx^I7zq{Ryl zr7&R-gWwl$cnBO5+~5}s3yJ&dH%Yi~f?vPkC9tOc^&8`)^d1-0p8Ds{#o{?y6l{7D zihnHiNd%uY!byL90PFDXp3OdLbS+oub(T!<>gw=#x%sIA#iV(o&!JOA)7gyo92W_e z?NXHJ>@^%LtV5zx-Zm|^5BS@e7t^~7(AV#{-H{Foa6}QQcq}dsZ(nn8+G(>Et{Q z32FbFB1AcbwQ#Y^#U(_`$?z$9hocvM{Ppf+k&ac(#%fP*B9Ev=@Tu40_J?$)8Z5rd zz`grno~a^EH!6|~KjRV6N**r6_a^bhi()a+$wG;Av*mENTiMLa%;ee@ABy6x@lA^tKOLZ#Q&*XLS-?d^+TFdl}9=0N|! zv#wZHDgQk}Vq$*%B5fa^gNaJ3RU?~XQLKwGkdVvBz@-$sk7|sJjD9rK)Ews5r32p7 zhCF*VJw2_O%s*4-I`_Tvbp+(wu(;*NmxWnDsnU5r9|kOEcz@)NVUB^ES>K0psJ|&q zGAY}cJ3qT^^@-4i`onXknkR(CX7z%L9sL;+@hwAmT&2}K@<NF)> z1|=gN15(RtYid(JRV0JY0#LbHu&OGL_!}BUakUxg>C&e^^%k#uVEtqCq8VjW_-afR zCu=O25Mx-yxJAfX=QMe7r?FBb+$Lgz3Mp(L51k9#hYX^>zm}ouQ}$T3(9sg}TJFyX zBW5sZ^r@8hYmj>U?PG4$g{`aLp%$E=xRgAG)0I}2*2%I2EC({Be#S{hep_ttTGM3= zG(mW8YEFR{2A9O1`HVI7pF~0W|G{xk_uJFeL$)esSE<%J-#>4SthM zF%QLnX)-c0%v}rBQ&{#sef;>Pw?HH9Y*;`WvpSM&sGXqe{nH9 z=>Eoxn!sv1r+K(c?@KLM^-JYafwl|?!ylvk&4KXOi`{e@ zE?Otc+)si?+Ko%!b-aM@I9j4`|0XxLsyQ-Gg}fy4p{8zJ8vQwRc@C1`_c^!?GFB~$U};%5?F z0<0%EF^~^LUHrlB+z2wC%<>vAELd(>aDw+BG)SzeuVEMCVtu&K#s3u#3|$Q5!R0(E zEUZgRh$nPlS<6D7GXM34I7r+Vvj3M0So0&+#>&zjK7p=Ja=`?j`RQe1y}9t$TYyjC zr972DB;oyk`XK+W-{k-Q3rr|NH)orsDy@``jOuZWasN7s6SC0rv*XRj-#=xDdxf9n zJ1@FCxboMTv%*1U+_L&q@3Cwja@Jvq2g_0Bi#KwOxGr6Dwnf8<;T63@2IjZB|){<*-M(#%0>t@G=j>s-$rr{9Zy zib{JP*J<* zY6FlKLI#+(6aL*=w^p=WF=tzT(hTlqUuu<2S9Z0xHXH&y6|_UsRVZVgsyBkg%n=D` zIcs0+9fvd>5vVpN=oOatzqzF*%_R^cg&>1KW`JxLPBkw z{ID_#ZH|CyNFm(q1!{E0u!pgcHNHN&xH6KL1eu@ZV}8N<@Yly z_^f!EIb=_lt^@nw19K+CZGkTg{=c5O%jG&nmrD}9b+T`FF`Bl{m{pVTGq&~k5^jUl z0B%)QJ~py11pMeeMGFduCC>1cX?1zl;M^pLfuvEdL`zYB4gy~mB>_KbxbWV_1!ZW} zcArqjX5|I)3xEG$YI`=KQv(#eTUTs8n7z@5%A;h+x2GR?H5qn09;Z`CH;`*rZO1>( z9ofCjHP6?O5L{5#AT1gmaP&wbv39+Tu@E@`t@Ks*47ahaI}}S>+)r=QKYf%(s0cN!Z>GI;yn*9nma#?87IsZ9E z@pX?}Yg6yMR`PO%h}e=(<#&o)Y#SGH(Y2<5c~5e4T@%Go5G3c$Don^-vwhsCWa=7k zxHp8@UoSa*K)T6mCR}R%T)+LivEiN8js+?S71#8dtd2y@(8jw{OGttwf|$_1tz#6) z@c{;{dYQtwS~F06!cpK}$Q(7gs~DaXRTB>5dxl;>U%P6OqZIHXF!=MhLlzzT6r6`K zppqk8a`fFai~zxQUZXvu3xQLsyUh2*p=({|iVe#eP)GDsD8r8*kw`I4PU|mkA1)7^ zyBQeqco>gR@63n25`I$AEdJJ|lv6ynM+;fgeU8I>TG93)@>;?Sf0p?^C(XV5**6`| z$(ybSbXsc29IyWL_yfWIcPrK_(sR>#Mm-UR(gi1{_c~|?oF#{Y*6Xxh2X%ILL|udw z2D&7quS8#yOdN~wJ|4GUSX#|+;Ph0L?s4hZ!(q+zZ_RfbT)v%dxY_4eIsbFDaqW?= zyYw-`SXK_QV%9Y$BFwJ4t1$ElDj5x-0K=7Jy+6FBr-nyaC}{h5eDU~ua-fjoR+@9f zF_lP>B@GpW%<eC)!A!HHL&{N(HpCG} zayx2JJw4YVo+@61sLS*<@G}>DUN#-8P;?0u=BKxTW&7bR`h{>Ld(HaxsI#xlK7-7z z#KBvOhBU*~+e0&mJgd9O9Nf~Qj`NzuM2A%Q^Z4sW2ulBFem?MJ*te^P5cOw*{55$L zYP;4xXQOm0-mt{it8OihyZOnUvn*%%l$o+IWk}a^){N7xx4UoM zEn^-Yc724PBq%cuKGE$c&W|IXi6vuqFh>!leKPbv;2n4?NWrkVw`1s1!Qbvt+-T1h-%E)A}TccT; zxj{p8%x2D6cZf>DjM_mx6`y&{F;5`LNyaLFpA8``bvY%^E<$rQt^ZhqgE!m~hAiAc zr&@spr6Zt0DG+6mV5oK~v~(QCt_GjFc{^@o4>omWr-wnNPx9cwV78~|`fYfzcXtjh zB%Mc^ve9k%X0b-!r$v>v$=wPg^edJT`2i!-m-2c3;n+KTjw;U-b~9%~VWOO{xw53F z!TC{?jHDrjct!9kS@_|(1m++wP z$xx$o6K)U}hhK|J#cua^Be(O5*FUP2#lk|0M|IC553iR$>fH#Zq~kFtK`kvARu|dr zqIBj0Jia+LH0*Ake3CSWX}4A^4nMPnw!}#5&Ec< zFbXY2ef>IQJpfwdEa|{zPvrtQ%!7GHCqw33zEE>UN>VbMdOv7Bl+<|&8e?s=jHbXx zJ^ds*u8Y_UUTD9{-U~-OLc&2a!R=IqyDQ(s<@A*1CJRDp|3p)z?$cWp6eK6cTD#3j z{0FOQbz)}lV0VXsYIp)E*Sv_ImBAd&zC@{v%KfVD>L|?_vMGyn&ErG*HBg8mwvF3XAd$o_8KN0Cym*|b{Vv~5r^n9VZ0Jgu5Xl5ULk&1 zr;qX^q@d@jw)ZDE-tIS_lZ%MAmyh}L64lI3cspzgZLY(Pdn9!Py7?K@;wB{@_R6f+ zR48y@CrXgyKd4PVKlEVCWM|JOuCY!|Xz)qlt{y~XRM_JkqtEpwcioaQ(iz3~TQ4na zUvJKLBuYzJ<>y0=Hn?RNbIp3~%zDncia*q$Nc-HZ|18sGkz7ab5nT&sM$%5(db_0= zbpJ#;Il<{mp4|xI-s+jV-cUvgBN0oV-&w~C{__4fvRIPXjmFB7@u$ZG<5}sl{ld;x z!If%{h2l)asXb}y@+8FgGjv)^L*P@s#-3MXv=sbl33uxcPw4XE1{q3{%V>SRZU$3m z*i{F1*0Fe{HSOZk;uuhEyJt1bXFG7WbmyV_Wc$Q~Lw*U@1B*A=N zjtnsIQ)$1EJ>Y5d0=(Y*PR<)k5KF-=4xtJVcfn&vWd3UI9T?r3MS`Z9@BH4wTl@${ zdPXJ2nAJ4AB5xh!erLeYmjxd4f@F@<5b1)0DO^N>jKXN6&Ci0 z5AirRHFg_D$D>$Y6?u11*Zh4bk3T#u!839;%OWU+ch3LpgoG`WE#uo_B2Qg!BJ2xn z9BVs{u;TIeCEf3aWtIr&dj3k1q|c9~fe7k-m#NxzgLPaSF0MHVF|oky4pGarYEv?^ z;&h39`E0pRW%>Nnhwb0iMxEGd^9|bYpQBM0FNL|qaaWH^?-M9UF(?G_S{t2%?8808 z@AVY@4E^99Gz;=$D;LI&z*O|6fj}R_lHxDr!4LGwD>Y~;RqJAh_*HY5iL5$mO`wYc zyNJ=A&Tm=`32_8>#E0-Qa*nA*Db_*up)sov5xba@e?TajRi%L*qIE`$D&>(h-UCQ( z7wc$I-8)YN;hY?(SFRzjgTXB1{xvUVTt**G_PaUoLNp&{$-3X6ZP70d<})FETP4vo zrmxLqP^j<%KK1yAZ+P(dz!{IZ;vOxeMAO5;^+S6Hw}7(16faV3W^dvYc2-VkW3=2> zV>zm|TORK>{FnJJn>j?Zw>zR}!uQs+G>gU?@M)}{ zD&5VjT34u4bE<~Djy&an3u3otYLk48DYL0N=IIMRK4C?Ovl;==lHce?V6KSMR$o}A{VWeYr_P5gM2v)t@G&T#q28Q{ zIgb<}2Xm>*rTGyOQ1t6)7tPyT9oC0CyZYy=g$M@yNsYy-fxQ#Dmr55eB`5V8*YCt< z1=bpI3)%6z`LH35s9B=IU)<}1Zc6itI8D5J6$3e~ilAf|(m`P(agnVr(R^6<_dYH;cH$*aZ8a3usQp&lxho<~j?_Lev12i1wgkMDI9PF6_V9UJ0Vjn+SR zJqGJh6)(|ss&JSi`3QYpwfVG#A%oy{M&*6sVGsz3VfHs^X0Pi}o30W^_0`yKgNz^< zP%QQ6RxM%1YkrQryQYFmstgu!+`APDJlJwj3DuxN6okqK%O!pOb96NAPwk38UvHHDh4}C0Z_;}%c7)BS#;f| z{y`gZ1LkiCW-vNtrewxqwNT zk3XTV_v)iAh*?waK%nEbc7rugEj~Ur#ix2W=H9_=GQ$v>p7OEKwU4Oz`PVILd9y#~ zTumP8P@DDzwS)!hh^3z4);OHP-F1%m78L?N1^e|Dl0-a4<`2DHkM9T!=WKpk`(ueW zj=6GH_6RS|*!7F=N=xrG!Jpaks}_F#8=)_T_!ZPnUA(M~)+2WIfVvQo(*O#6j(O>q zu8~>gix2`zdg1!A20O3HY;?Jp_})DANP(0;XH{%(Nb#Jm`G_3_drMm)IWkezdO$G!G`aZ|``OVPVqbP!*8(bpHXQ0%SQw^wF6AHn@(8e&spj~|QIk(hC<|Yk^oA)I zO@77`wR0<&Yt{X%u4bf%3Vl%`X|evQ;K{3Sk)mHsjFg)N zn8m3_y)Id&!3|V=hM<~GMj!9kofpzlxp{sT4&L{Fzv1?*D$PG5d0D!JYY{2Zc}Cvy zw!cdZMZVjcvT=fle0X@~?(8=>G1-VHw6JZDMvENH?^l1~TRj*IouIsQCWLa<5r9Ou zd3%)uz~|}nYG?E#>m5g;6!Q;*DtrhHyP$hPds}r(_ig&9=W@KG4=l-dExt4(!lwwod*HwH|G~*qoYQJ4e`;ngT>d&>OCxcd>1i~lR zs`b-jM=@;(VjX2F{j}rcSyLzP#!=7?thbqFw=KbVb2(8$OM6l?o3lbk;q^U5s5J z4CqRFUFDi}NC16q5j1mBnZ)w9!aPGixdweTD|#1uq7iBqPMp{J-Z$}W65ld=iPngK zUBmaS%kFowUZ(DN4T-nkIF9slm~1b8%x^rwA=_~Aa4|-Xl6!-7revgnD&z9cqa*%9 zQJObO-ETp29jfT9-RlB6!7(GXbfjV=+~a#$sLaQ zbRnA^zv0d>);YPJ?_hF!N4e`p(~Q?Wax%SLp*GI|``$&-?giyn^w*{Q)R__DGq$GC zV%`HwdwYeJpVy9?4)12kg+ihx=n{2Yy>oCJZkn@87A-b@OdjFzo8Y|!=b4R<){OSV z7gTe6Sg^pDQqj|!jyovQIS52ywk9y z#weBKjLe7I_9=lTa(|bP=DNAd*-8G!#zq`^s95QBkQ689M`Znj?zo_xs+fZz9pN|M zI+`4?c#MLIycBnHJFEHcNtbvm2l<>GmMer)5M92Eo~~C?S2sQMfY$V9uECFyc3D7# zi!z()?S*NAMnb@%pyv|Fb@eQ1DPG>seSKw?9pP%p{E5-d)9<&xwuXHF_N}q0iB8y_ zX%xrfp&3+%FzL}+v=qf|g|H$tr3k|uyR#rT@ziQ<#-+i3>NGR_77!>uim4>}?v2fs zbInvBbdJD`Dhl%QN{uU@-7ac9*#6$_2~-@v?Qb1XbfR&WTqoY$rB{QSzI)Khu9h^7 z@P194>`kD4@fY8jtLGVMXqWs7AJDJ3@U%|(sa$@B900uR(%q+M+hS{ZsLl=?C?6WB!oGV*yG0R?<$f;@57uEyf%y1%4$XAq zTtyEMCkNRtI5G;x=z1}Y>RSm&s8RE>@ z*4gv{U-lEP{89%DF+6)>XFJd!RH_t%&t^PpqTgNmxfkmy=DP%L5iZ zE85|cAGCK0%1t-Yr6O{X zMn$MiC~hnsvPQ%3hqAc9{YLP`Rysvjxm3GQ+&=_G+45&xR+k47y!oXwT zj(zfjJ4rDiK3x5)s`?a-Ef1U>Sa*P}W%>zv=MtQN@abRFS;n;P^4%mM95HYq$(D~; zo*xo!JzvgyqXYH*H3WIP*5iJPc0$OS*%0n0^d9+CnXd~kh(GAL*5;x6nG+$&>YxY) z3v=t-kYD$DVut%`Xn^Kp=c7ml?_~tDc~O_3%ixdUje+>Tuw!FJoc!@<|Mlbl+b6P7 z(AOV9Y&OG6Yh;1|0Yt@TLcwEL3MAK_!24|NBkm^$+ef=={(gRnii#x`*jP{cZsnq} zovfsgHP`F;5q+s59&T>R>goxOB}Ns}fk&SA$?0SR`Auup{pugKztAhxJVi*bQiNS& z6BDb~NV`2dJL}@&ve=va`t@tjWo9Kr5ymuC*}c>>sNXH9^eH@p6@Mn2fPCdDkZ{8Z7wetVZ}E8{ z6dgy4$zQtVk}(ig>+dlG(#Mg{UAtnKt%uH!=g#}n#e^J3ibi2ZJX18X%V-edg>Y5s zjB@)Tkp7IuP&s#wL4IC|QxR8+y!QBD>tX60kW{nf!@{+EUSF3I_d>A+eg&jDyCYQU~Xp8;7@py?;sIv+4J zsjGhmL!ofGyEblGZ9l)ddcXD&GG8rO{LY=1bVV{}2dt|J6&0@CaqOUO27zT*?U~QY z{9}9j3IT!BTyx;$_ugwupHuXj1AKL>KYaKQ%c_K*qQ{scLiP$2;pfkv7tO??hK2DI z^~+3lM~y6^04RLlJ)_TCljkvDZjG{&q2gor&oKKn5TZJYTK=HVOaqXg60Z=V@P>sMe7$GgD>3{9r!F z+0m(7d)WURvD@*Gvc=DAh&*Xf=x>BDXRG_0L4nJqn>YAiJ`7~Lj?PXcMa8qz{W*OL zQ2D{zfTkyzK3P&F4nhV8GdJ5Dc)Zk?HsS%CR|+!Wh%|R31bh(qEV`z^a0MqN3l2Me(aP@wO;T?aE0e>vQ*16fRyGUq)P$YJgng$dy( zS`ik~LWtc!X8wS2Y6*{6$R(1bsrr7*$i!H{6HGvq8Ug4bIu{UMi||^z(UJ)BK~_8H zr6Lv8go&&TGk>3ZM4eUGEUF#mTg=#QK}`0JHI2 z>aYd8#9Mr31e+ZQGkQ@Mvs86x1q7kU@3o6E7~DBKJ-&YZdgp6K4)z?s)#mS@jz32a znTFpkH>+nsX=O^P2!!UWc9o~!ud=OinK60Pix~nRuB+`alU;)}*L>kR;;0HNNKL_u`JZx!{j4NsGA=i7k1r z@fp$*64=b+SP%$=>r^eD2n1>obZE26DZ;80ooD5-^D-r!o?r)8okreAGn~dI*^aI6 z@_@357PKa^zi}ZJJ@g4oq>6XBMGJrT2AEcS3Qld!7q4GySz5A)QPmfWw1egG9Ie|_ zRaK>b4%%DoKzDcdRMmq8I2h_NV7I%N|9yQO0yE^(5s^4qL62qnP;2r8ZM^|Vf@in6luTS!jS$S|C*oFvg<+N$?eX{pVgccXK7#84856k=Ush>4rqO|Q5cYJ9+s$+mps zhvsM^WJe!=72wPKCNeg}$Se5m4DfGXTxmn_h;r8LvI3L%%}N8hxFv&`uo?cXlyvJM z*&jdl@&bA3eYI^J0H@BA)uc{1E;9`vP#Qp~kEjJ>kuje-9 z_LcEiNRCSU4PleYxOreu1SptNi+wnD#=ObTphREAY_e`H$h1VCt3lX?73c4!rxUEC zn)Lhph>u^g`k}cB_i5;_{t${Toe&kDInc|dW*eVf^6~^-9^V~ zc7#(pJR0IKvw(IR9)pye3)&Hq%a>n9MU5syXmM&zzC{S+(H&bXZJ&c<03hd!5PW}aUe%Ry(5K6kWSiWk~1yn}{(!9K-rgbi9qOOJ; zI9L-jza3+5dNzO-DlaD|DJ6x20~iZ>&as5{_!GT$caFe;GfU8> zr^t1Y`w2RgHgis*(&OK9XBh@wLmoKq@YGy8XB=TdU`^nFw|d}Q3P3InK&!1XHNYYG z?yugwgF>nX*L;=<6Qbb7 zGI=PP{%;}uv)`Bbn{nIH;){XkwF`wI#Uwd!;Z6fvk!-&mg=fX!&yGGzHzmTB^Zt=p zF+q^C_*q9sXS4aZ9B!EWh~DxR=7q|O=MS66F`xYx@O@9w?kb4*J^QiJl#md4OUoR6 zJVL@{zLR64qT;9j2HWz;XqX>AgC%2)fqS zqIMU557W(m;NU|61c0>w#WVx-TGS03w{o|MZ#|QPTKKKzgS@#0G9D-<^x~dH@$thT zKU|l2MsT8svBdlTwo_`I^=`Z*Wd&&`Ujf@4c?y7y$|VO^KxkN5S^_ne7gb@=GMl47 zprthcRuDg=VWhQn9=NG@|{rK@?ne!7NgE+H>h6a$Lo`;6kxXwwh zZ;M>kwls)^j|$SS3gUXXsO z`BsoscCxY`0Ki#DRR4;ZVh~xs*Z7VFv@?>X%5T@vom6+Ky3L$_uiZ4 zpd+`oUH~Q;ja^Wd*##Q?_xZ(K+FuZ{Bg-sWf3zmXdFCu4Qw6{xRrmek>2@HF?T-fg&vj zT_sYY167)I7fe-NGB9*!tmyTcEq+cFfq^%$G+n!zQ5A!}!N&IT2xL4RDTkw6;B+={1o;;{9MzSM6i4TXiKEA#d;fwebqTK?5KO ztj-DX^WVS6zj>n!RU6~kbz2=`XGZ96qS3sg=i8l(`dNU2EpRUX2+}Gb&#op!*VTE0 zG;^EiKRHz67iT5`v zGb|P~ULf|U1kBA$s=zEy0jlg}A|Zg@Cg2C`lTetdaT@P;>vI7MP~3B6t5={U2ER^O z>8E_g$Ii}(b>WnH&=?I44r2PwY7ilPFJE3(fz}YK3`?uswlt_BeQZf~!$87-Fs<0O0`d)!}|Q^FKTjh)wlmArSihVA-KIA1%g!4IWfHhU)hT z9uZ+Oi-?G1i?=1FTnP2>4=AUSX2w=$qHP^L3)%r-kq2NG>>LTjr`%hgDx6N{BW4g! z0MD3o_F=}P#*v6tSQ&{uL5oS71W%+M{DabP*MWldCI!}2Pa@Cp;bG|W=Mz1i!$XYO zvJ8HHet39z#PoNxOCMs@{M} zqMV#X*5Z^yPghN_em}x*$^>L=iQu!7n3${c@5%J50J#S!pf9xt?u@k>4H}F~z+Vjh zB*iCFqOEs4C zzYShE0%F*f1m|()U}wklJe;og?p+KR$NmpfRL_FIURA8f%CcA;%AW^C?e8mBw*oQ| z@D!-&dZFfGTgTt!C#4-z|NhC_fU;B2o8qw=tc~M~^FPinLFum;07dwp)dgd$|JT99 z@K679MgNlm`eR(SPG(~0 zFfeIdE@o!*TwUGq9OUHWfU8(tSf~b4|A?mw%V{8B{`c3XypMi5=*EM!DLtkmZXLzX z&p*N}atH7(hmQeyLrzAPuuk~as)&n=tA`un*^r}lLqNc&Je|$lmkFWR^@&;W?Ufu0 zef!&q67HAEOlyANn^DFs%aEqtlbb z2RulVBc3!Y4tslhH-wx9b3*OM0Kx&{HP{T}`(r7JQ6}^D^vuj^1K^){RyV=kg6?sL zYt<3rCtK|#qmb@wwT@B$1cE2{edR91^T$4fQd|YRA7D{bhtv@(TL2(}jYRpNe*4pST*c5e7ghcX=oW z8cP?u{W4iN68Hqe6&0)qh>5R@x}+Dqj*bS;eVc}C{RlLNV89bgP^!dH)>~g&uUxsp z7M*v$_;s%fz@sYflz)OvXc7-TJPD`d$&?AsL;>1Ovjq>};qU;^oSVe?I6!hOvE1v1 z8?I)^SNG>os)zyEkG++03OI+DLi2wV6!0v8)p4&$4yu;Ot6rb>{dT*ZW9+GT1MUZCTKV+>x>dF2@jG;YZ)_9Oy=rKk>way_PGU{hZ9ClwWy zaKC4H@zC`dgi)iqx_Vx6n^|{!9wn(#G<~)oS%!Y%x=s}!FYfzs*?oR<3kVklM5ruc z4sU(oE`4aXNH_K>O4kOimSumqm@eVnKQj{ukj%o7zQBwYrIE(Be2*ycCGNW|Y|7Me zC4n#d?^m;Maj>!7LnSrj6tfM=K+$g+qvW7UQMBx}08y$0#;t}Y1F)9!MWycr2_t{4 z-tZ+E-<^e#(%y2z&kmZ4$yJse_@o}&?f~zDO?H-3wf3h=NiF}v(}MeRf8T<0+hUU zcU(~s=ecKNBhZa>OY)RYmlB93f99$h79Tal%2HXAd@#gjK2XEL!1TmN7ub|Z=Z zC~shRK6OuZ+;Q=KI^KtjSg$IaRXyYc8)=@t^zYc zw+eJBaa*zJuVmaXdlVmW?G9!@D*s^3;?QKE`NuF=fElBEm3%PVNPjAUpfLx59XHi2 z@Z9~4C$o6iO1c71oV$2_ZjmHtnZWYHr{X;T%us=?5%dz_HKuiZ0)pPz3`p4ZU)}|5ocm|il#H4mDb};r2|V6or}RHwuhSc@DFx&sXx?sJ z=>!>^lNM0(3%;*+cUK$;!K`Mt1Fn`Um{uMuV_Hh|Ir;Vg8oLL_iL%b; zR&7qz35WzfRscDbP3aNX06Ej(1vJ;LcOWY56Qq_0O0`8sTYLN=5-b3-HbOXar; z6j%i*65i!L_VU)&)-Rl(HRU?Duh1Ej`iwC^rzv_pNc7Z88FHpxLU-T6xE z?*DO2#+ey*GJ$3R8U$qv6LCp_F_k2@3{;kxYz4SFV7qFD9T605*+HjD_m~=E%n<1SfK%^x)88sEb_I?nGXL_MF))G)~^m)u5p$?8a!j0@7UCV z3zHGU)zmY@M+XK303Qf&%}>`Pm!H@XzxRo<%;~8W{NkV9bh)5d=kl}oE?l_q?R=m; z%KZ4^Z*enhBuA?Nb6P;Xs=YnuDc$w!&9;5Ci_(-mj1NCl19I?BKB^NN4wJ-kd$zo< ziGkW}%l-T`&H#PfGztz57Mv+DfHfa3rG4w?4`NxwOP`B3Z2L>1H%N#`hR#UZ5216d@5glrNOhGnVl7Pi)2 zZ1rJhBG^n902%!*6Kz6*F40wO4?oT71m)*p}{YDv70-P{h)RReiP;G?4eM=Qs& z0GTXkt*p+z;=MxIwJTy$8Mm02o_Jz3dC*IchB=K6&x5_|h3yas#33z+=Z$orxX6#K zt(D0d*vrJTdBrx|Cscm{|9deeT~l3`YtGaa{lVb1kY9x2+o=Ki4eKQk5&Df*2ci^cVQ#I6h|xA zmY2u27(JpNr#V>z7`bi1Teo7(@h#Z==_H_~MKbOSmq+90^GncCsZA$4y+dkZR-YnC zYgJE&=h<4r@x)o)18$XrqYxtv?Sbh507#WsbqYXub}#msAgbgW-Q~h@6)k5bLEWI) z@bLDY14l;~Hr)vTr8*}FK-ZB-b)&{dqa~wY?DT37lwovNsIg4`4fR@)6f1xv*Uci9 z|8obp(MH?>W-ngdyX~>0?kn}mwxnmaDCLB}8pOe?@bDe2&fZ=gLBTlgKhRw@nXH`B z#V{herS)}<+bUCewV+MRVJCWXqU%`ezwc6yR%N~aC*0F^1?}8$33Zs-xLRdg)?)o~ z=GtIxm}Xsg`{F!)2ldE7?7SWG+}~vee`P+)K9^*E$jC0py@6*6QC}=q`(0k_8Rn}} zhN4ybh_6d>8Y076&;Xh3b#r8?2YT8W@j_y9S5%)7a~Sa(zkBdXNxQBsZFdtqQnpZB zVz>gXdo@Zgs=K@u+&+h(dyDq2blc%At-USsD@F4snbl;#!h>0Y8F^Gdq66AuOn;`N zukM-f3xM>1m0|~M!M2Xs9VQL4N1A$i{shwdb*~d+p`rUqN}XV>n0N?XnyQfP=&M4 zI^sfrHu)JS{?i;|C88{d;!niMVKA`sj>ep|OX*HTXvWDsZ2UVd9A(e{fVK~tM%;g{ z=)jFDAL#dkfTk67;mxd~qoq}a^U%lY!$DsK)C7RAfVP)-y%}cX3R+DP0sPG zP*Bj_yLXdBXa0dkv>v^Tj6{RP1IOjym0_?2-YJaK^ScZKiVFCRP$O~a>FWL=AtAkh zj@{g3q@w!tgQLLtKHOU6!Gi}t7G9Y|c&@UH-NbqT#khdR0LKK<^oyHTj`d)szjZ|9 z=HzreRx7ZQDIj=a@LO@jWP_Uuj@=1w-!e7C{9+-Ek3*dwSxuQu0r@-I`N*h{C~Q40 zB^JLF`%g^@4!gm{RSeD)VtcO(1vGH6b~ZLR^5lW}XLmd52@>E{`%}IH<};lYG&n&B zZ27zq00{7gEy#-pihtw#`jd!@uGft?5X$*YKq@O=9$-v`s=d&|+oAqjS*=aA*akR& zZr!>C-A#v_t!UA7QTu2DQ)(s7=kg5H^l$dt??(f_Bj!pDPs7q{rD4jfD8R6y?QqipjfA-O9oiK z40eoKIDc=j!=Nf~!bm;-FQ?yMbi|o}8{FI|aBh^r7%DDGUN8@w?ZRVJu0Q7ZWCYut z^4Ht_bHv;sP_TBEVl;tW2~?Gx)=P|>rVD()DrNz21N=6_t&xu(?-jqG)yk5FjD5u8 z#C!KoqZ|D3OH$xp2DJ;5H>>#yCPqhVFh|cw{`joK7|7+ z2MGHJTQcDhVE)jS8DhCmk9N#h1kw(lN%h+*PQ_zzW)c8S-Q(kRfSXu&2INZ*6Gfbu z3YE7PfSG)@y`A;uO*f$S0EeDkL725qQnhC+P%Djcb8}f)Shx*Js_h1@>j;ER0$*_@ zSYW`W{wgw3T~#%w2b@R*hF-zZ1DvpxD%fBgh)k@-71pFBW8kT{i1j4h>L2pRjy@C! z2^#!93tgn!fTvjpKc0x;4H9ReC3Xm*C zt>Dj^8RvcQWkIw<1hJ~c=t?{zKfxYl8*3jP0VlOyZE3cis zhzPO@@Eeda&p|Q=yL}TB&p_Nz@tYldc#4xlL?h&tR-npv1V=cIa)-BrNmxt-C!cxz z(;wLY+}1}&|4(yo9Tnvo{e7bzvBd%uB&0(?x#sXArP^y19CQE`w`WWcLDx4G~=`FOXpd2HGY+k*W%)3WIWy51B4h zRO`K?n>lT8Saj5Hz$eay!W5OUD*mPV%C*3070rI-k#%R%m1`n!Jo8zr7dV0PH$Hb) zM%<;M7jfAp@}dr1b>P#dC;h#~UVEp=1dM=VPMDfRa?6<g5TkHM)9y<8sSv| z*7P!p-?A5!%BP^|*4Ni7pmLnZK*=&0PuT^z;dt;u1sgGK^uHBv!P*0~CPl6^QM5Fa z=V3Y}x=coE8@R~ynkXPe9j8NTt&lGNpWIM>|0A|>lu{L74dc0=?j9g;Vm;|WHz@7l z)j|XX5V4`fOwKI@zIwJn!13f!px8xu?re4u^S`vYfY#jsln4^8c(GtM$?Y#!wY3M} zy><$e1O^93y{DJsM5jJ+lm{U+jURn3!RIG(rjNMnPy>Ork4CwS2+#sV4EO$qnx!Vd zYoiKH_aJ+Onwy%Es}<;U>|ERj+re{d&yR0?0MCFBa}3^zbWjZrWS$Tk0@n_qfs=!S zD=2ak^DAfngUcp>my89b>oE2!YinfrvcnMb9X)nz^~;xo$B!?JRHH*~&1?`ulg)F< z(J`p7j@N*IMVApL?pDfxKDTyn``g`6sabG*Fb2E2Zk{JF1Tgk)E-o^1a!M~xHJW}e zRL4t&Vvlx6t)|uj9!suvo7Ob->N}2ckj!f!*pQHr0LFu$@GWSv6VnO~K=RwgIFNOn znTg5l>vUHrZkC`?JwmaI2YrP2I$W~Qo70;!EQOGBDvcnnf@LA>5*&Tww=FT{cj7VdBQI6}ZQx>BlR@U6mzX#Tu{svQYf$fY{u2)qj0)tWf$G9utGWw7okc2Vz@7#T zmts3sUu3z6Uh{wi?_E+z$n-&=dv$_nIWQ?HiC|(V0CkvwT%DI6@}xFa4iVjw`g37> zmwXJ-6#$o*Q>W;fa%yY6055{6YkJo_$};>>Bm||eDSoDS1biPPrsuJo7PipFat2Su z{R>6?O>pA- zL+|<(1gdmMyRtu$VxP#(ww!kO<^u zc$jpol;@Hl&4EPvdcWC`_ioP+Iuwl62*dvW)V&INg7Qwv13?)Y5~^{@jdtH;*o zLmB^@N)U+TmAwCjmVqG^l&@LW&2?a@aZvs(DB!YCym|8jtb&VcAf^AKZ6Gt$F*+5~ z(7+5*FYKNv81{lH1VpT`p@H(`32=S!$B!RRo;@q%FfOm)MX+#-GH`wRiqe4*tw$x z>f%HjF^`XON#jF~c7&#%RN(meeE$Od2~x^7oCo?sK*(wKjKwHOQ7=C{Kkj1rCwYcw zh2Cj@S41(ilE5+is1)K&>N95;K)n+91Nr}nq@{NKfuxmY+JXM^QZ}ej(P9AjxEcM} zbV6PAv1ifEYFGZI(^)n{pHY;QluFOG>PRG^`;ZH^ZXTXx*vHd1!Q&H}S5_9V=<#wO z4#Q2f=O{?`g~aCc|6XD+JNT2p`4a)zU`2yiPdATtUCnlo)1|0(yE6bg=(`p`VY-!! z@>B1?R-Up8k&K3#Lv%Vy+)@C@+L^SUkbjIork6S+qcl)GT(4^dh!p9$X^}By$B3PrCM+J6b zFgmUq1P;Gt8~_DJ8NKf$nl+D#savl#P|Mvdd3t36cRFcVsO;<(LC8~|fQJX0)pyy4 zgnx!z16b^jxN8AZlyTX6?}Q1^)eKBb-6bKbisCmb9Tgh$+xab{tzzC2M(IE9hrJ(1 zfSB|XV>~67AvZ~py;JBY$#YdENB!j!DP)=%5QMZJ=~d1I90t$R#{E6iSd5B_VI{2` z6v`C-jHZ;Tfz8(3PQMoh&%OOCNlQTb%Hnjyf{dUCB10{)=HAq^ zZHuFX{I4^)j;nTCDrS(5>aJg8N{K}elZ3>sdP6Wj`k#6Jz@(D z3j-oUrFj~>t@3XQhw5K=2*jobl!!cl-}LYxG_Q!eYb$mALBYu^XWx-k{yPIV?w9%i zuy9Dm!l8BtoXoq?byEE%D2#GDPG6&V@SVRK2I%za)vH-$V`F150|J=CpYICTjogH> z16;!KV2N^1jch&9_>a-P6AxwBAEOOGCG%kd8vIwFQb441JRS#qJ_M3Z#Lydc8oBU6 zC-(-oDTmv?Z+nS1qp*-bcf<-p!weEqs=+e5M29jD`{~Z)@bK{BuLlfTdmC4cRhx91-_1Ym_Q*?9Q) z!o%N=jv9tyAxwev7wCpZrSre$oyO%V4VutrAd11UX;~O$Nru ze|o`!sI%%|1`HhpKhUG%KG6Guz2wXGsePS${@L+a(iKew!PSjV(H9^lg&4DRY6_=I z1YMGaA>=O({!@E$3T#h>2EW5!Br~Ha2nDGhjg)@`ong+XBg)7AH#AE^d+1Gg%v+k! zv-x`Hh8i;O!2i?*_m3jx%`Bg(sIw1_|7ddk74-z(4kF!tw`==DZs+&DU0LG$843TT zT?z!aAC9~Kc>JG|+dpM6;0R?bqYKTH0zO`k33~Cqi0}T=F(<~vQ!C58-nW^kBt36Z zBlue%Sr5N;Z??Xn%(r!(Nrnnskeu5hou8=9NEqu1CQQ@|CTMFfvvR4;bDMR4Q63FD z|FHE!=&Zdd3z!JG^BcWQJ#?BWw(wm;u(ozgTGiT6xJ8fIHi%lwGp~MPJoIWDJ!G6V zyyi-^S1njDoYl^F9`_G(-+QFVzT1_`fXOP_eg-G3_vE~y+X2R5({-+U4b{|KJ{rd! zUgYw(&oXtcdqM&Sw)5&N7t01hHNH~h-sHuNK-iGIOP*&$maE%ko<60hb;L*ga2CaH zm?476DIE#)U&~c<-^in9(cw5F%bIF=!bIk|uM!6u%;?huE`?WJA)1#KE;m4FkRb38=#_erwEcJL1@oKxpRv-Ev?r#?-)p4V?&X z@gjr+U{kMw&miW(`%c#Sd4<0CeLRc`wpj{#uNUO8rMr>3WaY}en@!^_20kd*WQw+45=tj{2i1nwq0?RA&^HH^)d4@znz_citNNtK3& zPu+FQutn#0992O?4KoJgo#cC>p5(OP0~@&#J-E_5+>}nt%<560I}R~N$RZQnvg#jb zqcdAdj4j;s5Qj|GjZ~r8ho`{fCt|)r$K+GrN(P);_!>bgp_%z;!ahqAqwRs%N*uudkB+Sc3g( zzCt!dg@hqi*^4>_CtK}Ynf>NOXVuHfX8%Sm!-giMx=HJ6WnJg$(e3X?dJA`Ed-MkK z%<@ER-ioLgp7Z)7=%nP7yQO|=-nSls!FsW?ixlcrzc0CYGicYzN^JP^OfHGbe2Yqk z!G%Z6j+#+(Pa}g?sW&ginlQgiwxzGtPG1#U8?{#5lB5UwF!wL~RxTxntav;oLQdD_ zX_RR+R5qQlsk|79_1qzLp8i7aR1uVHi-|iM^nP=dGVom34aD+i(tNF$c z<6pt)wv5orY#|H>CnwU$3EHG39L5`=FBniJxqD@vJFeNVy(n8+T6%i!LjS_r;^-on z5kl_^o0@{c(w8qcpbHJ!k-)!0Rka;579G0~Y_qp2hH80+B*A-?HJMjiF1Ib<#=R z-^`V`nK8d&+vm{|e^?a;op_yc=j($)&c3PxRbHz4Ru;JX8$ydB=yz*5qh7;0m795| z5&B{|2TF0RzlGDxsVWxJkOhE?E)CD#xvgtv`OkD>1#Q}vQnjb#zWsK8PJ_GX<2THb zp?2nxiSHh|W2MI=gc3_9VlliUfp_oTWBPeBIMf?E(b&q(FXNAUB@+6udPvWQhgc-z zn#QGzXHH2Yp^IH6Wn*Ki0rrzmv$JG0l9PwE*=+(hrKuxvp7^!25i&)#^_3O+H`pR`5_Vsl z)^W#X+efeD5yRTYcNn+=VG3QSZQU%8KijPW?@^|N% zam;>lH%kd?&E}3U4iL` zzy>#2t0vnIUBe8PI|5$V@%r*f?yzyqUP+frPf@;xHrjTHWQEt|;;OW5BPMN*QZVQ9 zYtK5zR|&Rn1@UtD56;bA-?U}p6FN5>+;NL*H4%JvcTdu6k_5=0D9JG3v)id=*LY-o zw7Ho}U-Q~zI+xn<^XtqER@XycWj;@arKC<$myVB^r}Ic}q#aF( zWVoSWfQ1p_R9i;(p}2>t`|hQC=uDS8QEajff%73->eZJh;e4{8jE(#wGYGf5F2tSN zZ&Pa0NlLMCl=}w~c2sjebt0?@CcCV$hr{9npDdkI$EEc*_Jw8jg?hd7mC)B}S=d~q z;j`+0+tvm)RjzaW^FaLHOH;*e?8I48-li$zTBCfJiU&Wah0hdygJcgo;ZpN%zPEpwje#C2oJ(D5nV<#Mqlc*f>FQz1^Vd?>7yuv{Tc3 zq`musyxS)*=Auo$Z&H+yd#)wPA$=!yeaWz;w{Zo+{?pQ&on0h~kVl z*AgEgBSVz=X8T9io5&nS!S^!fWRbq{D}I--WKz7CeTH zBcivqoKsVKlOu7Vh%K?MI04sM7r*>l?juxL&5jZAflO2FT#Ui%NAKreHP))o)P*v` zr??74)!caX-4VMu3-x!L+J=fd?bSPpwhg7LiDxp>#8P%%O-Err;4jioQ1C7?I*}^j z!tJ}`b4eOP%#NKWpZsJQ9`5Jv=V-q0zC6rX+jdL9C(h#I0eYQ?Pj&K^7LG_oPtnNp zQO_C|wK8zB%*d4-ef_Qk;;SAD7o(NfFnL~c%zC||E{ioOcV$J5k#>kO#>Buh?rX`~ z$ASab7qwZ97txr8K&DSk4=;Bav-#+Vwf6Y!} z$v7{Mot#V??(K0tx$d9dF*vl9Se133 zoxZ^^0opgO+dFnP%tV}@yGEUFaC$PpfuB2*w;LPxm{f=29G+6<9%F`( zI12r_uZYD_!*(kJUu8)Qi?8ZOTwC*|vDkB{VJCDOk@&tXH7!a@TK84j`=CmagA1bm zHaVtIDMiB$rcq>LQ6u8BT6K#{30-k(cjY{@EbV8@R*$-6@eh^1EDN%w9(O@#&OPpR zrFz>(#Wa{Ms+p~oR+QfP*zFs!Y`$ccZRD4+R~Ez5vz#soV#-KSRb8plF7wroTU!o< zIb(HYGTOBT_FP7wTE<=4g3LvLm5uUT8je8}(t# zWYCq#r@EJ7zH1m`OU;RKW0u!El;t7v_&|>6#aCsx<0`XP-WS;Bo)w-FAeK>bk&fk@ zGg)tZF6HV)Ep}hZKo7A@uW&(u+0A8RSxqHIjFeo^znYvwJSOjA;F(0t@AFnywM%a(0ueRR#{ z+8r8)gFH%WydUn(*lMNm4`r^miaqH-8{A$cI%VBMHIsn#<8O$qIxANrI^L6$t7SE) zPN~*aiqX405m!=-IB9r;$ZlUQ?)Z?7)C$cChJOu3IJ!jE z!Y$`wY_-m@SGzCGb7jGijBhRAVMoWE9*D{Ltq-j_8-(}li6L{cuGqB1*qjvY!!{(^ z%t^9>ce3=BQ5P3F_OJ^WF;7!=s(YzeDNV?$)VYK*i7f9U0? zZQBy-z#XZ?$E~+6snAjxkG3hxY}t&aKVGv?CDvJ8S1_n_JD<}{G4GaPj!o1#w@NX9 zAW3>Xf}#{H@eRg%U?u`@az_&;oPd6R6X%9uM?!Ve6m*5jq`1s#oFt_d3em0~575~OLXaTJis?6Ul5taCH>TX&SNlfu#aPg=K|uHtW{rpmgN3fX zk&#H9pVDpHY@Rr=%YCx;^qJmkBF6HKypZLkh3DqRvDLP4;-yp7yFxNCAL}ww&5Vq& z(YWQ{_J!$?HAF)@N)LeujgPO+3~X&x9!fuDN0i&QaC=P zy=^OSr%Izyl05I+EfnR>5@VKsb6Yd#yun0`RN+~cE_8wXPLESK#kzq@l0Ve6vs+v8 z`=45MXg-|P&j1R3J+{9%^L9Z_ht$heyZ({lJbY?72dUhdSzno2+~lrXR#?|1*Zhs) zvo9hRSX^H#rUfrG`aWS=4asiwl}{Zc_(OASU>!^Z%T+r2d09=G_ge2 z(1xjc*Riai)QItZLcA&_v^H31$9c2dX{Nh2PnB=P&^K<`cDGT6S0tqgMfn59**&)(!~ zBd1SX6Sl!{V}Hp$e9}b7&hC2fVp90dt{{Ueom*B3UCidufN-zSj62`oxaIATGMeQX z%}s8~;-be9qqSFGR;|YFysw&n;po&sc(pHZrGD`-uc-v4$~JJ0@&_ROU^w?FF<|oO z3)|cF5eu7XEg$s@3U!g6)wX4Av9`FmaS?=jqI-HDRza9a>yb3YFO;>GkyE{azvFTQ4UC7 z*@`t;y6M>kF(z>-Y>@~u9IIm3#^Z6t#%T26vMgt!^7jjxxZLP54ZtZ{U{ES>TCWXz z&BWfomEqy6HRIYqS(WN3u&c!h$%Tc)y2V5+V%@?Gc);&D$U8zUJ|=fz&A)=a*%bEY z<>9lL80fl5HM?@<7-4l9TtT^Qe$_5AON;UNun*v+#K|@#=ycFnAU;nPkD{#JQsl?G z!qKN3Os(hq_Um+EVFA$P>|3T*mxJrkCZo8DMr5zYzF!uDOtx}hp2uJ>Jo2JY;F$yG*Wfw4xL30lj;k0iGB#y7*_L$zKx%s8H@X%QxG zKiawAsiIdXXYcspRc7`ZaMk^&Llc@9y-Ui#ZY^lLr~2L?d2FVaIY|Me8h0|&3ph1b zYP_7e%9?X=On2ghyz;+$>c*{8n(Ll{lztsfCr*)_mCL)}UCS&pFZ^Rw*VAFJ9;V)2 z)iEWH+|41)bT;0M`K)l;FgToA+-y_R?ZNsd{es;k6;!|q<{4P(o?F%}gJd&vVpmUx zc`;w3c-+~l1UHFoTFRf{wrC<6lK9q9Z}T?h>`~2@5W4=mYAZfOD?*q zU&qt-^&Mb)rGiY83$Bh3buk&*7fk&*_@+d3Z$i)S3m#jxcpuignuKt8A|5;0Mgyq< zbfF{I;H%OeCMM$1i?+{e2A~H-(ArSXWc{s=9xPFL75-zmSUXN6ZV+?p(j`n>4{6|~g_!ATE9f|F zHHyqQ4j-vC?=Y};a0snQdN1rM)3^Xif?2D)x-&;d5G7tgO|4-H9YGwAveJ!|z>Cg2 zbH>WzxYaDi$t-r$FjCNt#X4#JCQpf8}H5o?8N(?#BSCTza zw{E5#-JEt4pg$ZJNg3 zR$`3lqQavv@8eg@6rRafvht7phzR`Y5A*AjHQ8$PUS4ZsT?Ayl$Ye3yt4-2F3^sXvgVuypz2Jqp$Bc z9zUAPA$j;I*SyRl_-@U#J=TM-^ymC$<$^8ZA^xwgmpp#{?B|IIFr}-Wp4N6<^$(FW z*Pfduy@2k?V7Br|XNNf<*%r+!SHWwq{I!KMg;#5?{pE>ONsF%qf zsI5Ei!54jyJKSAc|Jk5mX)_d2r|voQXKwMwg6v63PR@=dI)2b(V8MIA{w=qzYQ(<% z+M_9#k?gb~`T@p1I7nvN8*iBLQH`pQ^B zu!rH!;b!-%`7|$(NWxu%wF&w_n%WgSFzB8Vm2NUlfF?gI+&iPVPga|yA4;T9SRE*0Rc4Lcgr~n16$6V-@ z$ty@22N;)8Pqd(Ev$A*xrrQ3anWbfngV)%L#;C}p6|#nf>U@Kc6GuV~Mcgg3`O+-m z;|Q|y#G4sJ%R*Is&$@wOYX3=TuVp1|6%{IGl`INnl$byK&=$GY{O}=N3Hb%mu$Cw< zMKv#dPnnC6`YD;y#)xifr$ztPg!m`;D|y|thPpN4hQ0h34h0l)Jk_bWz0$v)#;W;9 zA^#m=v^SKjH7iE7ZDq|I$1tGKF!z%b#!no!JT?-F$>#CKMM;#{%F2?I>=IYiMEFd`Ei8H zWy9q-)it)UA6lb(y5G}lnRK@nVlXWeD;8wRJOQqiy(lH9{KVsM5i3*Xw4`xh=xc*P zY2Fpi4cAX8|JL1`)r7$;Hr5u%4zp`jg~i4BcQ~@2^G|~>52ikBW=j6R*G~ttU&^Q& zcglG&l8h{&t?@=&XsE<}du83B?c7|(Q4e3cR86T@#Y9BX;G`tI55+Da(3F2(Os&k} z5qu2o-Vl9SiV*!J0Il?IS?nS51HUUjViRq5TJ&Y|8^Es9fk8>i9t z{}0dif9E4`^k}I5P=n{uPF5~a#Quxm)q4^kl&%Lj|6nSUb+C(svM~U^X_6LtI>*@d z|2p6_qK&p@W@dNqa@%w!mPaR}9{&193pJ6zTRTHD&J3f?^{2Vi8mIm?lL!u!@NIhe zEUBm1w9o97JD_xg=<;tcclc=0pKe#<$e=9O#|`ygMS$+sFbbp*0)L|9~C0|%STnCtUlw( zlE2k5KdAf5gzsWRBR9Mrq~SHs3eo-gUtk)qLtzAl9m<8*wQd|-y}WbHuKDoT!M`lA z<|r&!ltR~^o{lAHFUe~9#r@BSetozMcg+4VJYmS+{l0iZ^plSKdfV_dv(tNj9aN}p zSHAAvOLPLvzD^u#%>M?~g*xmA}j~=yuBQ=+AE;*C4o< r5q>3ggWcWtKSdC%@Cd&$i?=10J*BuQFAoY4{;nXSDxEKN>*4aiX9D#_&EKBZ zGjDJ-+`JiZd(g_3e@*@MWeTEKBJo`oFYVJ5n(*pPC?2F|Y;Zzuk`FDKI%rS( z5pQzJv94by$oMKuF#X`=6Q5J56Laey2bRA_drC|+8OoGtUgWbj@a{2)p7c7?SALP0 z_Ekamgzyz1_RRQ5=PNbJT%;xYk56#xPNQEZ4HoX(h>PPfR2ka}^1X7jEZdO-9t9)wrxKm&LCCSkpe2 zcb6$R@Fs3S!PEJQLzl+VT01eb3WR33{5|D-TjqlA=PWe}O02sEMysndyQfD=Hh-|E znsXGtK4_+VSB0!Dm!w-doZ53wxIfwIDCJK%+e(&rdQ<7~hjFj|=?T&9 zKDQW&$Uf8JyBs(;J~+3o$*4axoKGUw?$+F`kqP2q2qEEQlf&g+l+=BjZ7c6^?ESsm z;&yTSR2OxJv7YL*qSwWm>Xu0wDg?N6i4=`}-+R1rHM94vH4c_OObb$yKHaR-7&^bS zBo?mhxH>g4A-U=36>$FLUzQEUPU6P~|Dwstcz%6GkgDE< zB>nXnNrtLAs;#|Ur`&mape$J?UfR1hgpNDU@BnUS$sB-*gJ4+M1fE7q1%=R%MsL7#k_4F?Ey#cW=MIj=kYWxX<;lQ0o-b ztzwfTj+uRaC8uQ`gV)B)G%=T7eOk)nVTtV^5f!tj&G+}7yBnnER@Dm4?udw#6j^;E z6FGU?($Z2^wyv+xJn$6B82bc9pfy3nZb0$u*|VDYhMqsZvKKeM$iLu;OHN$ISZyEL4s@p4wTlcffEq~W&oM3Zl11Ieh1AFkXRCGsaF zBlDvtqBzMmaFv?x3@WB}c-UyU$i>kyBs3H&G(UICsHRQ^5z3lou(7v|&DOG`Pq6Zx zT>D(z+T46xSZ2iEj4JnTp1xRIcU^t`F;=Nc_szvJr)jEF!q562ug`XGZEdkgxF(7^ zjD0V%%88WLk*&EX$;imq+}w<||M4}0oB`3$(2$;)Syyt<6mbr{i!$;eT|f`2sj9vd zwjtvU2ne`(WxjDXbw410pm$~FOIl74Ztzf*=dPBKXG~mNQ~rf6%Ca1aR^%xm%l9Vr zL|g5#yn6JSbKTjfgu}T9T@2_NK8f03eR}EZ71ruq0tfYmtL!41Z@RO!$P?I>WdwsW zo7&qEcV82AnOfBXJ-mBvXE!bDm}IZx{ONgaV=A-9&~0pO}FQ&y`kc%C@VuEkp;~s zs=f?XxCIl&;lU(28Y#WLijC!kW%=-EZEAUWIgp&ecCg%~F@#Rzox1Ox@sD8%VosUC z!NffE*CZS#nn-Cl^eZ1mEReq=b1#tgsoLM@?=P_v=?oQ6&oin%y%A_UN=nTYsIN&h zmBb?Md{bGO_{Yzc6<>`lk)yZSB8=X~kRQvj`Tag|mu?)rPkR#a1SZ4>c8kN#&mT%j z;(fin{FHcQ+LahDs|b-&XManq`A%>sE-L!$ za%gj(jIes(p{gQUWDGa>>(^=5{{{3YwEv>f~wepx- z)2yx1u7^3wt6h|cjoaVdlzWzu!LHQ_F$HzwoI2I7UCOPQ(~ChWc66fA{2L=$h|kEd zeLVUtq`44*|F1pDGsu^%U@_If1dBF)5J}<`7Z^y`Y69QGNkXpasH&c@-r4B4>zmzJ z=(QVF~Qi>M{TxhRmBdE#GH`{sivn7Tb{XIjGp%A6^AX_unxXf<2iYJl<`~AE4 zrAsTVib}7p#79Np>zQo!_V&VZqp3_yP0=4nT;vek{v%#GQUl*QdihdxWQ<%5>j}zo z@c6$Ye@GI&3xL(bT_r0tfT79B%J#~gVONYLcmvxJ6B3kx zI|;%Cp_zi_?X7fWJPB;6`?sPvWXM}1`_#oBE*lH(%Uv+2a3!P*Dl2xJY*FciWqbSf zZBtXz;$Vdq1h>zxBzbk;`}n||KR=!RmbRe)e)D!_Uu@mA-aG?| zVu{v>J*;ep(*Dl6%*(c)$3kT8czAf&+w(IAHlnyR-oeZTQ?WR@x|#`@?Y!xn7=C%{ zEvcgpBZY%bT9uEgk*nXazasCI9q;NclB2*5aqhu`2W-zTUc5NAsbE<7@c5M2qBosD zYNfWFjoH~7gDMs6+J328DS?A$lI zcp)ocg9r`!%TCk;zPOG$c&8>XTNf*_0+>RlScHL;+oJjY?yXZ%Xx@D+|c~e(F!S+2-2%HB(q@s*bbRLFJpDok%j0)AY<&KA6r8N^8i<}{FkSm zL9sjk-@S?90!G4ZQQs5?f*6A)9l?8!D>%M5FU3gwYH-SUd?{giD!(HsO^?Hg75IPs zwEs_r{Qsw$e7cO-ooJ34`ruw)T}K%poAT#|3MbXy*j}6Ne4|_qIcAR>Th|tF;-6Qz zet9h~CMJf)IsBSLL?+Mg)H+yP-Cas(7p`|LgF2%v;O*wQsaMk8uaZ?SMVo5SGkKme*SQJTRD1y zQOvH^Pu_cPh1gp(WEPX`eXyOS_NG)rJBVq>RhDtt{P}jah>QMd1}~0qg;YiDJ^xFc z69}HWksA6IKf}2EoSz!Fu4l4i$FC)pucU_Yo`eUPj)p~@8$*~)kH=Z)EB+KOyJ(oPg9PkU&QJ~?gwz8&2JhBZXPKcs;0Yg2|g1-p*Q){nC&dF|Wd7`MfN z#4Zt8S#r+M_mnT;y3=1|fKg^Pd+1;{G@2`w*0>%-oAsku z_mhm-V+C#PSl6|8g6Hp9d^HkS$xoyGZdTfk&#ow;X*-a)i!yIC2}0d-?py3{k4JHi zs+MFtR69fFz4cN_`}Ae^Y|)Q3XScfO3WOXk?#$HR_?DR%WwoC&rPfoKyc#a5s!z75 zQMZA-)PoE^C@)>=)r|R&l%uGt>CD~#-XM&9H{|ELt|=DF4aWVv(xDe;+4We6g-mW0 zqj^TBG)!(bxZm>5jehFQlF@;{E_sN(QC;_cr>xV-H<>IncGKaazCb|+mJ_w`B~5w! zV_UiWhWp#aIYRUWPn0JT?5Jdkl@diSP|?%I8`8bOzMPPL zGkK7$hj)gk+`4+n%l_@e;>g$+$Jw1cJteon!~y$>D|$D~9nweT&G5{e-rbIPX>0P> zmCS;=oV)PTbMm$e`3MuFfz`-`Uz65orahhNtjoIeovk<1I&f3@koWmzG;JD|=;z55 z&HV5jmX@F2lX!|ym-Ua`@!;< z=qWf`ny-}m)i7YXnuxR}fGmRFU-rYD_|?4ip$I!o%kscMgW0OK+&TwEt(D^qC5Bsf z8efLV(2iU^%`36q=av+Tn$68i|6p-N{N4KmRAA8^v3wu5vn3={Z85KVDXQ04zMb}O zg$b_LM3z%}S_#I`kQif-mC|Q~ewgJH(OmOFP5G)}>T&-0 za=ACnr99XC8vV{_EHQJucwoVL`P&-^Un00ge9Q`h;krPEZtNF$wTcCft-fjP}CvDfGn}VN0GHN`D?^q^m+*@EAqLU$-p}poO zE#n!#y0Jc##W9={ZC$(QGTgI(C1WlLQI?n4tFx1O+t;g@e1A7K32)ON8QZA1PEW@3 z>^Ygk+B;*L`;&AdC(4lMkq-TfA=p)kW5~JqCnxCHKl04hYGyn}C}par?;Q`7GzfI< z!S>8OCm|NmYA5dew-1I2w1#K*3j$qk*|z(W9@Ero8zwb8v9nGWN2|v-&?rob;E*vTRm?6i zwNpVAFhSoC|iTqYcPFE#751fM45qG9S|mMW!N>&1&`M*hwcBM0=- zLBGvwS3MLLdDs&PYTL63jL!3J{PMdYwsKJiSWDA#N;6UePxp_RQoFX!yJw5GC%SW9 zIqCE=Fw;73c&~h&J{yI55^qL%S0h-ix_R|MKQU9ZqmHQlwq;S>x;WLjrbD?h+h|@M z7lot^yvu{NWeQOaibiF(T96q^`W#w*e{96_X`f?S?tB*~7Di%&lA~{*uQrAVvWU5G zT3$~(_M}jC!b2KveCaQY)2sWejfVU0H$T<(u=9$I;|}_2c;a>?nnaIQ0`KMbM-FE8o8sMb>b_M4cLYAL z2&bKUI27W~x>UWd)3ta^A*RUxdIO8-eN|N8*L0h(n#>_d;7fw zwCJxzR@Ky(v1FY233`+L+qn*Xn4(j{*8V-unQwV5eNYmzB#@P`rn!0keX+%tcj@Wm z4i2BD+6`J~KRt^lP^xs96X|GPpQ|RA4x%T!GG-I;*PcUXsj{PN`tLy~oco$Ve3N$? zBXA>FU0YXIUOt!LewE`Sv6qN8L_$(t<0sgSUa!+qpH$sGQQg@g&wTG@W_w_C=X>tv?6OsWH`igm5pOoe{G}ZBg)kiOa&L!3|o)}v&DPE-3{*=Q?&0&p$Pw5AkiQ`ifLb z&h3j6EqI^ixr-Zy9D{1Jt<4SDeh=PLa@{l|ENT5+{r%EAqY+6ji@X&FMi|v9tPfXm zxl5yu6O%tiIEDMN!Lu{6?GXuaak$CKDyj!P*;?L2(|VN;h4enS%vI&*55G9Wiq@bm z)y)0wPz$u%__G&Q8y~}lU6-V^i>=ER2i=Eb53HoNSSKd7R@AlI+Yd-{9&gMC`o$$C zCfnH9)C!cnEVzGxn7G6QJ5Oa0x@V8z_CBkhKSE7i?6LiL@yTS1m;fq9T09+(tmKw{ z6Y?n@e*;qPp7ZeSi>QU|ZlvH~a^2wv-mm#{yD_JV%&`2Ugj8`gHhz8W*&52lJgzzi z?>q`sJ9(9f6>IRA{RT;D!hbKx+0SUm+c ze5ra~oqV!*p3uM6-^d5Maf-P5D;in<$MVNfh@tjp9nV`2CP^k_|JdCot3Q zLuylcXGA>ranF71J=A1=iwHNn-hBQ7JC8y%rwJ9gJ&Ef%0%@<|utx?#!fRPSh@Q{P z=rdh2olN~=9i?tpl9ccfyLh<#c9CbX=4cC4JLD$nV=>71SLBfwX`1fbgHzW4*r;&rmo)y9w*F7>x0V%w|OL6yTnH&QhDny#0a{m8Wg)0yRs zjmhUHPijv$MVRLC=oR%#UwFPj zF5aCYjv&A*s5$I z++#$e&R2$ikjd_8E`^sVY&%7?;5hYp4e!II8|Cgmnz$9Bv{3OER=VlGZ!SqCk}sA{ zxeLC+D%f!KA~Tc$vy8r0PO3p>NZF}AIL@FJCdIAsvUq&)68~Ph>Dg_{(j+VHLCsPTjF*ZfQpQC6}*msd2xC?o-)Hh(M$1%9;lCFk{q!JubhPB;m zPPY)M9QwkXYkl3Otc}4JU1BW{h_?lFH2Db?U16 z`lKB$d7#!;S8+E3&%KiJWbEqu`qgr6ibbW`+q*J;hU}8oF&na3orvmBR}ZccAh5Q1 z#)LeHWjT9a2(zde9Ccpf0I|g>&i-yhKy>t&*JMpvbx`%*_(?1xm6=yl zNMGE(>2;iS#XFa-^Jwh?SCzhfr_*mazn5AO7Q=nR+rVRLvJFR2n94Nf0bkGK9gSIu zsbO~tvaTe-o#|%T8-xxC1QDxkEu}fy#M>)SX5e7TC8pIhKR?@!aU^=B^|fcVUd*6@ zH#C&gU;|sI1`nNZZ&%LrIA8rFyh*_!BbyU}jS+X5tv);mo>yMGdzV{hGFNZ&!-s52 z7IDAamI`a%Hpd>4Dw10_Z;E1P`+)jE`D|xIjMZjKD?H}X#U>1 zh~2^BlxE=38r>PegfR&*i92&vo6Ys)+T$JWtsSe5RH1Jt!~v z&CF1&voQ;ahhej_y<^ixXbSCy)v{cdlR`FSJuVZMKkl5Yziw`(j9&{nEs2 z`K0rDyIiYc^i1XGwXN)O1M`~!MV>!mI}h_WUs;vkNa4u6w?6QL#94Dn-&6pnLT;1D z>yUN1GKkfa=q0;rpK$1AG#cI2xXg9xd7@T1M#Q~|<$DRn>8;Q*&5Y$-U`0y*o_Te; zFr?55H`kzQe)mr~32Dx9TWvnPnwJy|U%o1NIZD5q{@h?;MqfyLR51 z1&(4`pI1ADDYmX4Tc##%rA@hw!>rif)6+J@*iGcM>PSj^+}M246ZXhDDta;T%i;Q! z6yFQi{Ak`t>79ELZCBiZNZ50Zkz?$C%Kf;0wO|-kCj(jA=%Tqwow+N4^r>ndXSa>{ zxMx42G}leSo+#ec-xzjtWWP;a8Kqb?y=VIBF-Dd{)&83L53WS)X|c#FuJaRi)snAb zW9euL-{^~d`YfeKwSMmUvpkj5vCKOX&smjOM3+5PBo~#JorPI}SHE;@qWNcO>3yduo_p&Za?~wNO`pfcKQmv6BUw*e4GHIbxS=?8 zZA&XOm|{CcGq-&Dvk~!dk^uYHx;j61_|wc=gN^*^gA(wy6cfv!573tG%Pdflu_ zxCfqYL#~Q??sA#a_Jf@_xlI^mp55sqv!&EqNwytDz3zS1r&x2JtH+|3!&0O~BeOr{ zX_%DZ+fz)6+)xn=TwGQrrf7yD*e&($ENm10i7uKBt2tF93s)I&fQO1uX+Rh4I^~HYN&2b@>H3frFrrU`xg0g3Uk73(L5q;vN^!;*Fx!ZpHO)z7KwY&GjeMUY zza*6=tX=K0p3iABtcp?`!3Z3klGGg0Ee8#62G8dPQ#RhiJyq#v9Ep%)EmCN5=S*wD z;Go>S?a#t1uo+@KITW>{edM~1y_0(6GTiB%KZR%d7;!W6cKXPZw_lObSl6YYP|5cM z_ZXN5h^#-iw3I8*g*%LAq8Y4xor8kI@2!uyInu!+tp|6)!j)ajrxli$T4GKu%+HIW z0_AR8Yjt(oeE010hj3cp*@Ljg7k9|GfRK!N zUCKj;ub66}nE=cb`3WL%?z}hmz5_|<`E%yD_Pie$a{V!C{`(&( zSXq<(iI2(dgZ?AqbpQVSzN!(7rl#g85nB&X4C{lBZMU8HO)f@U6sY4Wye0D|nVCV2 za35je~CGEX?9fj zxagT*jOBm0>2AA3E|-Sm_K1IYzR_X$LvFgx*t2Exp$vTGt_zk{R{fxM({XDKd~jzd zekGw*<4ypR-M&E>!6Nzia4({se5;S*SI$rs3<*Z$}H!dpa@`Fey=!yOV3%hEG+Mh`azalA_Eb0ES z`{&YdH3+ShFAl%Fy_;5W|C5y0UT;s2D@e?BZI7Ffbh!WdK5fm-7j-bE!&RO%G&CT^ zkw;oSc)&pG9>k#e-H}~^ENJMbx(nM?Z0$tvHv?b0iVQ6j@-o86w z5F5$Exr&f5rz*3)#NZDC7Smq*{no@r{9}W3pGDN4aX#P+x|7)ai)n8=`f8-K!97$` zq3Br0AZLM%%B9Nm584tAQ=fiu{@d~|5CvG+OjE7#)&<6Q9|=wZ!83umh6`#0cf8I* zkaBuiF(gw8%NSKz{ny&gpz_BhmhFfcOO^5B@%E~=dTlX8Q zcE5Nvv*&4WjY-Q%eCep@iHn38iu0J7Vei8akJn#9s<}Byi4t>~S{x`-<7eh@>Qj58 zM4C-9$rdN}qR{Fa!i3KE6q7)}2y--^0lk>0VAeHzGt5<##^qTW;i$6t*YcqO8Uc!hkH>>Jyj% zs{bM(f`Te;F~vrLhd+TK5I*uM2%t2Ea~odD>If3IPves?{x#0%8O&cZq3VgqmNF5H z$G%&lJNDOAo5j=u9WTxhOs8;uK{}YJJ{@UI-_h6fth)|N2dde_+`s<%0&B^b7=Iqs z2wL&cxWt(ZTu||I^$ZA-EQ0knK0?TYuh-o zM1M`{Kc@Wl6h;+HG&R#Bwf@BVZ6Cuq7bcPgFx29wdI0 z6Cm(`CE)-fMO|Iph(5&1^UwHw>R5E2-IfSvl|C)*{5Cn+C_&gJpx=-%QA{j;E`e$v1LBg zCXlgUgwdwjc1>eu$BQbg`-^SbT3f-uRQeL^LGtqQE3zR3?~5O;nc2DwRX$3&acJ8f z&elr}-l!v=#DxnN?E8)^0H-CHVj(F6I^@5m4n|PNyS`3KgRtWb5#w-gTDs0A|K4m@ z7Su|sKYubyd+ot+f&0=fxYsBbOjTgF+8Qrt)sw@>%Idu_uXEsduv8&K!3su)Zw%@5 zvH1oSJ?|f=ZLQ0lyha_M30w7-3@$Q?x%2XJK}pFWVI+iAB8BM0M4r+f5~BFH zI1*hjMXiBaT~nQN07v-8j~`%d@;=-b$vFUzhOe(L7*>4az7qmhICT?RfwQ>@JtCgziHWO{^V-G|%p>`7K@>l}<+EDvKsa7a-~A}fNlZcZWFA~Ty;=-B zh2X+~2@Q$y z$vfTD+6uOwZAi~1k9u-+j9aJMlg5du2~v@8#E%RC(|1beyRy{uiY$d3CvIximsx9S z#^&ee!{%9=YD-E?1YcVXUd?;3qkMye)_d7w%U)2h(8znAZnidLU~te1MwWJ+($3C~ zI{(QN6381b_${24M{21%Az@LT60X6^fjBQDDtfZGC*O#bM}?WE8~3^N;a=zW?~gk( z@Ay3qDwS3t%}%LClSvj76zG&Wva5$2qclN}%+Bh<%z=d<)SdiNUtb?M-!_()Nyy0F zsb_tCCF$OF!@Z+sx*18`a7pV_#5l?OecD_fWwb917hyCsoO~;$AG+AhC$ECe0 z8G20a#XA~n{`nQ%h9Q~qkj?>7MLSzHlh#1 zRX+Su>M#!brV#zXebaB}ir3y2=-jy3 z_c7#`@QEml5xet!g8b=EX)a*4j!A9d1W@1+csot&#~L)Xb{c)3ANsHxeNk<7_0ZSw z9lmu;L}Vo14fC^{SKmpGgtDc88!w;*JT@_08X*SebU$h8YEt71r|7!$EXb_AW;#-> z-9(uyrnRR|<8RsEc1bH2+wJ)~9$E1o__dH%)lgAU!5sS&o(w#)Ln+v9gR{kDwu@27 zk~Z6@#MU5?j1FQ)bheugwSIg8b&8^W10YFKk&{IKA%1eRTemctBDmK>h^H`U>Julv zR(l^-q7dYUPRg!mB7Pea0#+rB#R2aV)kQ zKD~K@deg3In?q{}6Lmp(s_6Xr%a>_42-E0n`6&{YY|fab{K{E(Ut`Bw(@#LiUN6gu zMqfmUOk>!zdwO~p5cX}eSphIow-;l#v5&0SDuYyWyR2PppcS^#EFJ!Ot& zjnz`IOLgDd5CiH)Zs%ig(o}nr#P;ye{Jb?eIr+&feNq%*Xdppx>#^NjCVkhHvBt`e ztGN3mg@u6Y89h#KEDmxHSmTI#4;}xV&Hk0IJ|{To7{f+kvuQ=z4OKQ96i(F7md!<1NkYq_ef`YgQor6CW%j<_PSu*jXPOR#sMyCwS>Q z=O!_JUaR$F^SpDX_pl3f)bHzqonXiuDf(rO5XZ;8-A%66B9)WGW4K2#DO(*pz0%%% zLC>(@B^hdLM1Fk6h-N;0+VI0;mklNSw3_X;=P!JND9(rygBcM!(s~{oNJDS2LuF2% zK7Pzp&r+Rt?Hw5zfx~_P@%zz^G=>$tgJ7mbGt<)2GBNG++g1BE3t9C%E90pUqSvRS zXUhTS*^%pP1UB}wQzFmZHT6FCx@9@^&} zWU^nl@Md$^JJ~ae+*v0D+;K-Gi5BTzk|0aw(f&tTUc^q|7WK%Fu^`M zmfi1ijjD$R2AI>SXY=*TP5`|NX%dc;f41+j$sgf!)6?cQHk3UHpDI4#P?=g;k*g9G zUp8w!X%>9+?j#W8*4NhqZPnkYlWqhWjMOJx_`-S-MaQh05<|EF=IZ{Gb)I8@)g?T( z(4{O=iP$N|4iVgC0|VG^iN^5)NMk;T^(wb##CACp%d8z81@@$HYN6VML{CLR0(RW? z7}q{tz2d9OE90Nam|jc)0QuP1xboVN@_!F4dKRZ8A4kW>Z-e=TTx6yXq60*~Eg`lP zwu(Kbi?AZZmh4qsTvw~`nUCUvrY8NS3RCdte#DJ;C7Vmb!Q*6xXk)gZ&9FA+l**Nr z6{vVG%`P24VkY>E7_hnrj#{wLy;M3}VvwPJ4B#-J{4T)W@G&=?OOWQnLPEedtykqy z78RxD?EIeJk6)-!)TxGVY-nf*9HHT1VRW|VNM!H-Ht-$0j`{#*z_?5IWx?lNU%$D~ ze_`NuB~ggm6ri8b?Ck8&P(`qzg1tZ)^?+2i@ZaBKAG*>g<>b>|URDM^-Yfu9r%#`D zj*n$~Px{*>_cwT~;dinZC--UJ+1XJr$ZXv9z&V5e3ulI$FrYW*h#_ge{SH9$1#8$y znQR0mvRqnu6o)%oTYxD^N}f_5Y@OlvUDJ?YBL1&HH+HC*{ppTs+C~0WW+?USU0jg& zbPLVen1n1ZTXsFEK#<4ODXp=gaj3?7Arghm1j+<$@8qbkwY zd+MmsfcMYpVNVdM3b^pMpYo`j8A%ocC?re%dr`uh0sEJnup&FU^nZJF_htz3onOz; zxGZW^l;&Vh_0Od^s#kvBMoj(zbE5yhkNf|#KS8-yH>ki26?}3e@v+nPLXd=RD=AF{ z(msV^K+ymrjJF+BaXM`K|2)okTd`)R(tLi^S>{xm3De@ zv`pd77BH|s3$ngC(o<$ojRdCI^cS0%nH@iVT>lOfpRZrPet1S6Pq)AFkzk29WD>}x z2Nhz{*9lMIB@Tk8zfU$-x9B>sOV+*UloU2@?rdLt;)C^XMs+M9$NrxAe=SkkmO(Lu zO9(Xp1qHK5E9MK$KexYI;<3dKKIfhFxzEYc)zFKuhn-E9@_hN~)vJhz z6^OX?vv$?{^e0c2nl_(!Qs%aFx~rxd3Lmpt5xb#h;o(Qt$9@AT?GAxuEMIj^4@}qj z^XCW3T|za;Mk;vpjB8|SKU{xya>dTV!T%pSrEv)ZRkcm`yKZP^(1BJE054Twlj$a?DPzO1NlhnWC#`{=@gCjgee zV6n2Y^8bd5uW52<4+yB&&4Mr9n63`IDw=uv&E=nNv><%}_v-5DNk3W*Fw%MUj}3c_ z3`M9zP*CBu|9D6d<+(B@56u8?l#|=*029p3&+kFO0k%tG=ICZb1I0;xpR%Kg-rL{5 zpp@7&CcQP{U+yv|>oZb}tE9X%P*!hwl&As8UR2ARc)EDWxK@?dAKQ~>usd2GtWpLR ze$@;G2UM__ss0938XarUxUj+I-volhgNpz{T;{GG2$|Lpb~3)sR2bz2(pju=UJ!ae%Qr;=(BAXfSn)Ijc}-schFD zleXU#Fx8JuG9HcG5U=@5X(Ty__dg19 z9^KFX8M=5A(I!NpSUjj{2?0sFL!E_}?~K`jBaG>W4=K9!ltnKaX zp^yaO>GPF+UR=N#?i=%rJj8^Afk8n>HSb7K+e@MQpe%+#_z>l}stvc-eQ$wV3+x^v zPy%ijxH~)Hy=x62rv@-ltNg-LghYx();UubA$EZ`N*x|(4bB&y;QPKM5=H|)@ zqo5fD0IU%_J>ec?+s|ko9Sk5Se<-Gz1M4rKP2lSd@gAnEZ_!@8@%G61seuVaKZQY29a z@bM>ShOyGNtb;M~`mchkI-b$S=k!;diC<%+qx>gWdBlsrjvzW;Zb%X}%;6li${?~W z?3L~wp`m{lN>M}(k<^;U{@2hu2ul{3Ij_}d100ztayMyCbYsS$a01p1$H1RJu_@86lFFsY%Gyh@7 zUn?)~|3ARK-~8s^A@d*c;$MULpK;uO_}1U#E8b!2T~QR&34PPa-tF`se>{Ei|skbyqnI@*E zML;~*DiY=84I}r@ym#-Ob{o)J#*B}fmWX&DHg2jMlc4XVVioNJ3q@~VpTmdA3TvRC zq2beY;X8Y24mswaTg%8dyS5-jXK}_)jfR!!AW+S?)~_8)d@AlT?u#`bkxb3Z9ED!Q z0}7Q<%VAlh&M5+{A%lvHjMQERFfuw&HmF1KljdH^8ehWRH>8dgbq63DDEe!jLSNJ? zZ+g{j)B9xG6BYB2N1&ZEhGJ zOT5}&eWXWsJPo~p-rnBO8S3V?xm2}F#brYGlR>jKL;)HZ85v1qSiQDVb8~GV7Rn%? zrB3@K53q^fppg!;D4OEqK>973Cs{g$9GtWb%%CT-vDLJ-7*xxEY){67WxEEI)P5W<_t)~kL!;NPr zaqsws03^W-j@r*(*WPxgs6oomkEV}}k2@Ka^IX<=qr_q_LrmvxBOQsoPbHGMXD^V)TIz5;@TiHoa67Ih)&xC2KVPbt4%PVQwk1qT*SzD8 z-h%rw8hugh@$vCcf6t51Ji+ZliWGvrJ^1x&B=YtM28~mF(kBLkU!aiXA)9P+`%~B{ zRQ6|cfKJ|6W1{HyhAL6H<&rWnh-gc6KBb_z{&p=vjQh>t%oYHbqxx z@=0vBQM~`10oEOzLTb_~0T9tO<6pg^ikbgB0EV>TX0q8rIIg|NZ@-1v>R14*R9^+b zQJ-Qw20>14e-_B$HqRfJ96Muc2fm9M@4BGr318RF*4Ar#68R+Th6;X&uEk7=qWq^EJF%%ZWV`bgkT6tF|fZ&mdq~|ki z%?GQEc3zXrwn4K^tz}Wq{&-rC}07*Zf_2**27+| z4eK@Pby^n*@)Nv7GbSC|0G#TZNktHZ6;E*jozoM&rEnp88nN%kBCr`LE>xRZ78?EQ zt=@p=Q8!kBDY<0W%e^)3l3z^A(UBbQl9A=d+2{`t0ZAvD$oL z;M^eu$`Ul%IAJSCuY}4qUew`ig#=CFgO=!vU%q^qKe&r#)^2KU{?U;t2Qb@lEtJ*> z3n@~_c}8VXSjDRsgn={9KAgh!<5quLNtn`)Iw}k+2=Zqw{rW z=+$6z2Q(H#^eB|}mJ0@A_2Xiu#X#w$pUkSbUeJ#x`D>*`FVje5wHJ;+we5vg$Ew~M z7c{9OSe(Rk^aE(eOoCI2iHJ(6O&jF`oxoCH{LpQp(-Uq$I%TZsK+888jn@BQ>hG~; zPz>;Msrpb#4`KM9rza==w3XEy(+k*T^n6efd%Vg$t-I18MSjH7;P| z0J9m$Xix-#}8$p9IjD#jQ+Rwwnpa2br z4bw#3S=-QX&Hj!`0c^Y2i)ulSic_E>gPl=Adczz+ewt!1D6_J%61rMgsHtm)?z@1( z3)GA26Y#UtiD6Jn^ball@GiU3|4ORyPx!$NpA5Cy0m6;u9Uh>Ul zi3K15M$P@~{dxXU*pGy8*t6?{3ON@;tfu%A>&kLQ^{YIN$XscMv4D{*zDrQ+03|#6 zDtZhj7xx^kI=vs*pJ0tbDsb1hnNjX$)to*gpdH6aR`PL(i+^uy3^cmGm37O-a5jRf zUT>-sa0K9>0;DK?FE4ZSZZh99zil93I7)@wDP_-a@+YU{c5obe8)3Vrk2t2im-nt& zs)V4%T>A7qbw{+)Wc-O5KYj%FyU)KWvS0QHo>YqvhbjIS-xe@T<^VE*;Ti%u1~f)y z7M8M4hYlxLS%wGFCJ;IZ5~@0Zej^mK@GE0NmM%7;Uw;HON2->bRQU-0H(TtZjdBE|i3~Z$k}s z)f9AHA-ka~t_HDQXFpK!dvkOL>;$x#BC@Nru2_&!2nJud?8M2GnE(9?6t{#=v5sd! zL)HL<1mqF6l$@^}5uU&`|&=NC0jekhxRv-9+) zc+FnCIt{D?K(iYw?aV<}1%qGL{zaUs&Lb+o+yWx=N}x9GPnsVtKHGv>99M(0`6w@2QZyLmP9D+|hl1zjossCP+>?!jL#lI?7Vk zvko#AJ1ynE`r(8AM$-SqWpY#={g-~~-&aZd~o|6=^qsu zw7%!l{hq`>{(kCjkBQ%3uk?TYPjV81p8(i{k}YUIQUg&N6tG2bA=HuJ-&9hndbsi$ z-Sg0}uN?@q)F^N!)KMzoWS>1b+o017jR@|3u^xF1#t93`Ob zqbnhLnhaz=oXahtN9c3r2))uO?o5>w}>Yo!9pBot{a{{gp3@x`n z=XP{-EPc$snqRfUB_(y5DZsPuOoOM70pt~8C;ySJcE0PNrJtRD6&>Bcbp&!u01oQM zwN~Y~o{%|-ccjOFGQ0!DxwD1I6wW>dgV9AB``Y`1Hn~w+UJgEj-JPBCiVANqS^ni` z&;=|Gs@L`F*JUT6+_SK<(gVsEoCw_K&j$q7`V|&BffpQn8TeS^R&c5K#=$Sq_;t&) z!jF^SXP$AX*Z6Es#!2%-ov_S6EX%;)mNvyTis=f zWcTBTp7CG5E^(xWz?=1ca2JVVjZ()Q36+wP8XXIJw(8)z_6ccupDBhc4pwNN%$nnJa&mo(_N73W>fe>XGoE|@pwrl- z>BSjPV%xiyNMf%%S~b-U60UHW`{<^VcMP2c(m4dQdka3q{*?aV3f+Z+G8KK@J>Yr# z-rI{l^$mD8$tzbz7;v^dDVzUZlHcL+J--tOT|V3F92{gKGx?%*9E-}MApP;2;IRS8 zIY$7}Ggsg6Ftn}gMdE%hIYQJ91Emf}WqTiH5)?|4;fV=TM9`W0?jr+$PP4MI(0dCd z!@9bv8cetU%x5i?=7(73@ps zofp2lY%P!eV~T+mf48K0?@nr9{5T{;;7FM zpZwPfq)YLSat}()ZAP`T>OY9SXT7&^8?88@%w1rl5HO*<0=Yi zGVsIxX7@DH2?&pU=nM131A&6v&s* zYV-moZ@HcLu%kVe1WWnTu*XH^18EeunRYts*xy3nugwBS2r7&;zln*du$oocpD#KM zS^xUl8W%6lC4%^Hf`lXhGP!Ly#I3EZFjzp48;%y#2hz2L73M=iIxskR&gN(or3XIb zn-za1eiEvxr!D&-^J`ZytNkPMn_6uXrYWkyFQ4npK8HC1Gpob^>&1b_l$5mt5w~m+ z?~dJnBtlmi7EgX`b!CMalzLE59XsAWD8Ga-YN$|BtNz=A{B_S?gG(Bp62ZX3V^isd z59)eI$vQI=(=5mjU+z4hjWfFF|FTImC_LO|ZANilM(51qJ~K@l<>HyU5)we!95b?q z8B^auivg&NGk2KmL=pRMy6fK||7SAz;JT&T)mHHG056KO{r2q}2Yu$#-fRtC`;`fx z#Grbq>F9XlxP@oY%})!8>Q7zvR{{aRCWW7P2d9Lz^afS$>`2HR0s~1~>3pt^ zL5-oKql0rkhsrj=G4zZ&E;@!Q#udQTCxvvvo5CPqXLHHDnfqCHLtESX;0rzetRiVJ zr01`|cTE@aD>y1MdZ;@*kv1b#Y+W2|EJlz>x8@QbG}S&<%~@l%I9_lYs8)E(qb?|_ zkkHWXB2(?E*8s8!@2sM5B_uiN+p#-IQj z66A_u{qFUKjw3;}@<04{SgV)T4_wG$4!|Apk2=3Is<;&MXRgAfhdZjjZ2LboT^vw5 zBoKG=-*{TMJ@5(VUkS4x7Iaa<?r!b?!1nab!qKHA;m4J3kbDDp-`ClR zvn6398~L|o@&C$L!If4jqHt#bCO47|>^paM9CS0d4Y3d;&#R1#L5WbNAy?RA_6OjeX=K*v#yd@j~!azu9 z9~4<*+pWhU*h+TH?ja320Xr~{&E|XARG7fTcHLj4Yhb1gP!{Tnq^PE=s`A-KYpf8)jtV{R4}08c(cjDW## z7sYXU`1(Z~WK$@9Sk|NRZHRRuuC9CQ57TVHPOz9b=8xaz?vQ&U-KLTVlwVCPEx7C; z$SuO?y1RGpg7j_g;2?^PLuQ>PnPJ=&?}KO#w;p}A6-i`FhsVwew+Q5s$EPr{Fm4RO zPh22`DCpO%FdKXMqaBjNfm|H|)=DrdmEGOlb&Mc@h#-(J>)57e1+DvXb#hPW@bK_} zY^FE#mEgu`boda_J$v&zsDJVi99QJK+oH=gilLZ5qq$=S>F21wS)TG4( zUp$s@?Uyv$7Gr=yXZ+fOKHo8AwN1ulFEt+aR>kM=o` z?0kHD#wdX|3@bVywO+b(>9MsvJDl$xXqORDQ8*xDk%XdoJ0dKMkm!pb@iMnt52S@O zX{q)Oq+ah>wJT^V*x zpw@Qq2*VTbSP~~aeFFw26dp92i&L;S!o~<)IFJ+!07)0qK)#g^a&g?Ni&a0#R+b*2 z?$sUpub)o#%FkeMvknhFYrBd#OG`_?a5zB}&2OCnJ0i>$w6d~-lp|i~*PS^iL8#cT z&nY8b;omsebUmogwpr*iAILYfcesR5Lr=gn+X=h7ygbgDKVDJ8fs49NQi`1ZaW21* zY7_fStdQMu9Ig$iHyi87ls?YlGpeJ|CKz%!qqf8tS^}UbN59X;xml(eQ#O2%!`ilw0ZSY_q)MLX%HfdN@6`*euWJ)4T6&f*EVD#|El^5Ep zpZ;xMO7@T|0e3d}(f3N*eu2IDWMJc~*md}B-Yka76&L$r_T?A>-rF`L}m*48#KaJD;Sp2B?zT?v~~ zyz>;AurM~&RYDaW`M3(G<3N!H0rO~>$2P=6sEq*)fnlO6k7IGS^wjytUm)!K($dn` zuLMWhG*y_3E5lD(^+QBl$1)D3$7NSvE&<6SFeJeGsaia`Ug^d2O^%D_WSF8-maQ>)+YQ zX@b3L+)2po7x?{8GDfP;k30nAucrW{#(fA|>jp#~{7WbixRyECM94Bk{>m_~M_@UK zE*-nCh-KijxvSqKwN13h4;lq_z*$Z5&V{ z)EWO@Fi@Nph)Y)~zc%9wgIfQwmPVKGCw%!w4g8Pu|D}K8bjkyaHcZ<+GEJEbZ~AvT z87@`-6Fd3e|J8qhBU@4NzPkATM1g?``9uEv#hkcNXoV~A;yUz1{`cUNzmDuTi0IeV z0tfI99PSTE@P9%VbB=ZHDSv_`|Mj{Qj@$NwX$vXGPGX?Y2_hnQ`DS<+Jjjoj0XGIq zL~Q;1kd4rfpY5>(4hN4iJlx1 za;kU%o^ss6!$MC|b@9W$vUA*rfHn{r5g~T>t{FEOEfrNA5fjDBJ8e_fXZm#Fk#1_x1IK)ZY7v$cHZ-gsE4BxORzBD_;^uqeywK z8y@Sk&{u15 zXX5!@4Js!9j2oZbM71Z;N3N8t3k05w*{du#PSiQFkFD}W_xjz90q#iy=~ zMjfXvddK*N3;!p71#HwC5*&{Bt3z}jQ_hM1epY>__!YPFBIjfDvZ?U5@rGRcs7a{i1PmUIbRX5(G%8B@a`kV%w!h zdU_Mk5LSyNXAlnxqo$+`3V-7U+qeITSvhE$CNxi1`W}6Yo57i~RoVxe<0(d?o0yoG zDi~>3SmQ;B6_Rb@Wi`FxSYrROrL;%2`QSO@440@uVy9ZVW##s_3n+Kl1wE+(U zV30!<&Ke8%C|EUB=DT0b8OG;T(PVsLI%~9^6~@K4`;erV6)yl!liG{`fsO67>i_sr zU?l^#U!>TZsnXI?X?B2L@2~+Jeows+lyv(sDEWz$pEH_@TS2x7)eR)hFb#Via-9ge z^wf{8$M0)~hjt7N?E!Xh3L&Ef8mp~CDD0IACE(2F@4#dQPwcbkNJT+5E}kEvJvYUk zWXBW)U-dp{!bcHSlEW2yUWwegABm8GDiB&!HPzHrujNENDzlOMh?}|3UXY&!4(7_v z^U0Q(*OEC*6*`RYpf~ISy>xe{6Vr(}BqnTe8FfYX$rGs?o>;$}?uYzhC04VW{_GWG zf7~b{L2mwk|LiB6sDcHRae1Mw+Ljg*DBs5oeq1(z8W36rC^rLxr#e5uJ%Pe!6)0dD zA?9=oHW-gzU+a4+dGPjR3qrd>wx#w1d78O`LQs1ZuAG&S@OQ+UDHi3Y+G}`Iwg5Yy zfPerDox1qk+XON!h|347u?LjYLln8DBSL36O~9zF3_$Ic44SPKxdGYfUFwI zYiZN-%)*Wv#x-6i2?z)P8B?<+8Ck5drfIi#c?%kR~N8r9Q5?M5KTpo zuCA^&azg?FDk{#2feImW=M95xe|3V;>`XPX(cQ4GTN!#Cz}8>uzAx`Pt%K{h0xgx? zTeaXBRW!rv5{0kbl-}VmTZ-Q<`;)k1uXH5sKFd+f-khYJ7@Z690v}S5$ zUegqlxQkz$#Zbc;Vy3n)AdV7r-nNpJm4%T0M@!RrIXxo(itlv7E>0j)0q_MXe@@oa z;e#*&gwf|^%>-K7n2rRZ*|bNv%AS827<^H)JY;DY(s;joLsVrx!k!yfg*r3?+uRcoZm)`+2m2~@gBu{ zJW`3o=N(1wLYSqd7CiMB{t%i2A!ioR)4Mz?CMhWi^|85DxUec89v%5LOi+6D#bsza#4zK@Q{Qa?~z?BZqs`BSi{^Q}m#^mdZ$+uoU`rGEB> z)gVx5@JRKSI4)`2o3V(-z?*?`pv|W06HFX=XzSH!^L_w*AU{)igoYFoYVN)7^>izu z3gJQTX$kV$2k4K?4G;%w7UGVyp$plJ98t*p=Q|d&)?AZAm0L~+ppPx%A&getT5eiA z-e@^dY<&E%W^a}omV?wtYXkIEQ%FN0zlBOoz0lZxvWbeB_1ZN_DXHV6q?^mjK7M}S zm1pNK!S;3&8mAx!05`qJuuGizD7?vlz+=t;+PBUh4k0uS;iI@&M$yeR%&0x+Eo;;T zr6wpgHa041XMbl&8~Sk0UcP!&{O(=oA@py8@d472Wv~(Dv6o~;MGZn)g5#5+I&CZ? zbo$qKPL@i34KjF>5jfwAlamF&vZh~VVoC=yNOz}>K8d;VRB=`d0ngWl7yZ0&G*`$# zS3HEk3S?RIgY^j+tAm3B%!LwUV_O76>cLJ6??~usEsZA+<#M%HP1Ub=4Fidl@cXX6thfF9Oye42eV7}5R#(UtjhJ1w4 zfa-xX1p%dWN9`rl?#Of%C<7M=?6BG^n!q_ z)}zvR&Wz_n2|F9-(-6G8ezyJ4W&#Ja3WbPLqZ>W}fC{WeD{io$Y^ptuLuwh7nmYZC z&oZ<}40ftRU0OEn!>&U8t}jgs1`yvS%L(pe7TKkDB`nVAr%0$~B2I3q2T1ceseLd!HB7RmtaTwZ6R|7icF)F;Ag3=wxMB&{}js1K{ zQbyKcgqLC~s7(zoq8z~>g1({D@?DIS+Y0I(a`?8J)9hnhMCQU%yju^k_CAPiSATA3pF`2Z zs&Cnvc@AU5&0W*qQr;2Q$iGM@q56bVcbRfX<$T&eRx48cdkUt!PNvFFYZ-*(x@~GC{RwzlFOz0BOFi#?uY_hjwf~=XX~fu^M9sdY1!KRdgRKzmYJog zZ~RX!B%(J08M4gPZu|&ry`0T|oe7a1RrFrs>-0Qj)NA7*y}83hRyWN1#a~A*jJWD< zeq*qlMp#)|(%sNCuE^n5KQniVi48^7o8vRCRhT=2cu8`eA7n!4sD||Rfe))WG{{1L z1U0p!tn71!vJp&(A|xbCU=|b<lU2DI+TH@anTsZ%8ZVo18-uqFypv&1kpjJ(^J7TQZ~fxM6JY^84=NXKX_HimWxm)r0GTFZImtmx`vO zyLNu@L2Ww{dW3GSC&rW9GRO2Ww>T1W-P2@wIAYh5KPG*0q&m+|0#6qW& zQ-+=4wv=H(x3F%yEABQIzUSH?-57GU%=>88_WD7d`P$|r!N`K-NlCAS$X?a&lZVq= zIN5ubI8#hEFR|?%VhP$I30MkM&gIas5mUI$FUHmwoAxmQv)N@bH)or9MI+xagob#> zFYc>K;IRk!^1~eCjZhY)vd1=vER#3X6O-d!n=_S*))0IV{8F7L8r-`tX8x23 z7R=TtQ!i59*2ero>~@A-I=6*2M}hhrU0!z=G7syrTs3v%c01K>B)FU48|MMx~oBUfj;SRsPlz0AXh4@0jrxRgCPf6wdp3pb4HUV~E|1+;a-3tlVg3kMquDqSk*aEUu`LbL780UNwTh1t z(fsQ)Z0eU5m9nsN?|hsd*xS1@+OGq;w7LqDF3?|))&cj@<1RcUwnDTCewG#@Ccj$3 zYQ`mN-v61?)!N3~S%2-;95eX@nI0ZH+fcvl;MNi6`qE+)s#idLc?P*-%ku_R>*N$p zT}RRwB^EZWOHp8DW9@j!oSUg`KB8!?jJpGAn7w|Sv8wtlbR%dbd!u~nRh({7|q&u{oee4fNU%5MoVB;{Ys^4;~I~A)hxAaE?oVv6JRp^ zXjqx!Lg#r0HLkfZx>z~-E7HRPnh%%|SCnKP@}g>c+$D85P1$;udZC4?>jcT_{ruSV zs`>Omaixb*_u8J)3>826I>*JMn9Mn#|@%gbv(4m9GbGt z+95&5e?V8mV8TytEqpVPZj4 zv|A;xd5y5w$CIFCRGmm^?0ZJ*tWO5(5DIK`2lpusdo{$^CTU)x4CSg zUE%og0Trq{8##Ssj^u;MBAVc;^|52=Y0tAxm$V8V@e5xW4M$)#H`?Pv*7h%73Hux) zd|+!);Yq|~$*qA=IWj}mF_x%8uC2GK1Ke4lO4rF2ma9Za)0%udpMaW$w&>p5$)w*4MU z<@_P*ThP7wTe)YM&c4=WhTEP^ytY5%_)PpsP{vclD;Sn+GR<^AAcVR4C|K58a6~!d zB=;974s{2qRH-47tvTUeH^0DwD%Ih$RQW7sB)>C(#7KX{2V24OW*;SP9Od%&-G6>F zeHu*{EtN2kUGgC*IOdLq;NT@Cd68y%-N)q2cfD(T3EDyIzLMKCUvcLwygOy0W<#ft_$bH$ufNcy|MS(t_eur9 zeZ6N&=p%58k4-DQgmqRc^`4d_{VglI2my{;JG4|TTa!2VXI>EWEA6=K9T7Zm(#VtB z;BoqKI^EAIR|XZMR5a<=+*vXMaak)Bq})pfiYP&giCdku+v|yk2;3oAVPb^ukxO1_ z6S^f?cIP$V${ppUHZj$2SkkM0df{;kXbAPM{ zNqu83;{`NsYz_R}yBGgcXkk|5Ika%24yoPtF&VzkAjs`4+jJ6z`fdjI(zU|6WRY|~ z>W}J+4!*dRp#dx7D_ZoZVf`g@fhS461U6c>Hr62(PM^=3tnt@; zoleuJ8++xIZLHttyEEy$7fCEb$zI^a_KAA+yPCDP6+idj+T}~n+ikNRq`4y0&yf90 zZj+fKg%S}mWhh5GYc;!IO_TJ4hM6&?uQ@Cgtc;dw+00)Nna<VjR}$P3{_{td?rzu`Z>y)gI}9`h4Hm7|weV zt5CVO^C9WTWgBouP7Pi*1KAmsA$b(=DugvlbMQt>%bwIT&@5pPo`p95c-|8O$!IOD zA(;9rIeE+W!nM>=QP!~gv3?fh+nc9nVtuNSbuCo%4b`65IXbO3C#*r4c@GlllTXdI zBU&LLmSR@UlT8>PcT!u_nJ+$zeZ!V@s zhb4*}G5EAS^yZMxyi*V2p0{yZ7rUgz`8~^y=Gz^DN7&hSG()@kpi)WGvYGiVEEzrP z>R8)@B;U7=eYWvQGu;*A!~0x6|g?PilHZQd3ZpQlBa-OHH`= z;D_+ijq4e=`o%De{%3|%GL-VWmsIMJJ^a^95BhruLwmCAKZuoFpVFOtCGh@Z>fkv` zhcrhO?~__Kd!J3EFngVJB?1xKY?JXq2|k?w4I@}!6pI{`ad(`!V}pYcCxHJ|10S-l z@6C8}1@`Qs3R-+cOuD+tUEAMcEV3F|@a+cKWC$ZB`y06wq91yhxER%H#C%*Ko(cEE?w!@pCnS`H0q2%Ic*dT5F5;4?aY8 z2fzDKTTX>r-|7o!30LiJ_d6`5)wKtPH&=D}(_lhao`pC$Zz9W9f6|p38kPnoHzUX} zA7f|kp6@MQh=n8ikun~kxy^uLTwPw|ipCw2Gzy6*Gid8Lekx^u>EK&i6MvCT4PzL~ zPD?B%v071F;wztL5E-{ zG@w5OdRlDi1Dq|VlKd>rUOJ9c>fD7i({_>n?qu*vMqt{HMO3SA!+UDdGaLzuiC1B- zxi}Kd!`4_=C$X4~Mg09cwyiIKVdF97!sz%a>VtylbD001&4K{@v^qub1)U5_W zEqZnDUBmMAMEsBjf>%P#l15eYn6DIUJvkR1OQe3;b9y;3FOCe;uVTq`y zs>(flSTs6Hwxy`wb}xQ|>$I@&0eq*Br0^#tI>kL%MD{5~fsjo3Sp13#DyS|_p1wXY zi7IQ1WzV!;3pVRF?4roeX=wVoy&eU_Ih4K;t3r-UFH7ui@`+q@&y_XhLBAqS={3@% z*3ng6KnI+mE@PECJ)2cZt23C}i-i7JMusP^OSKd8T=u-cl10+E(V3}59W@qbwDM3+ zZ3;L9vT`L-=He(?-;)p#6|p5}VF6hG{CFKpPr5rd1@IDujQ|upNT%hKB*>meeP=$4ec`Mc$ID5#C8{4pPcE zB89->F4P4%Fb1YsBRA^SDGS=AB!ckp2;HRaiaap6C>xmdGVH$ot2ek;hkMg&JwwUK zYrlz(&g{K#x3P8-%bRVjM$f*a1sz*cjcS zHU^{5;K<>b;US2M@llhcz^VS@MgOPfS|#!Yeikhr9?36n6X$pRZeQO2h-{4Vsqkme^hlI zjq8?xIX20te7P%VhsSikFOfiHT*Gq?j8DCGBHg2j%F~VS_Z$4A1REn@&%wZOM@{{3 z*!^rY<@F=KyRocR{lLJ^v>|$w&LNpb|Ni9dLV}x*eX;v6&J6ec(2koqXwf6^qG#z@ zc&t^wFWe7Wy;f4(V)pp|JZWPT_x#igT~jeU*}s?Y*&4c4&EA)E*0Z~JiI?$zKayrM zHGjH7s-D4qR^F za9LQIJsjd((s+@CkN!~HB!0iJ~W$$bbs1Ma8t5j?Oz|K&J&fKS8_ WQZ!@|bb$L0DY1KZb3`A%{Qm%Q&K}?Z diff --git a/docs/data-transfer/diagrams/transfer_sequence_3.puml b/docs/data-transfer/diagrams/transfer_sequence_3.puml index 43707af36..7bec9eadb 100644 --- a/docs/data-transfer/diagrams/transfer_sequence_3.puml +++ b/docs/data-transfer/diagrams/transfer_sequence_3.puml @@ -1,33 +1,33 @@ @startuml -!define sokratesColor 66CCFF -!define platoColor CCFF99 +!define aliceColor 66CCFF +!define bobColor 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 +box Alice + participant AliceControlPlane as "Control Plane" #aliceColor + participant AliceBackendService as "Backend Application" #aliceColor + participant AliceDataPlane as "Data Plane" #aliceColor end box -box Plato - participant PlatoControlPlane as "Control Plane" #platoColor - participant PlatoDataPlane as "Data Plane" #platoColor +box Bob + participant BobControlPlane as "Control Plane" #bobColor + participant BobDataPlane as "Data Plane" #bobColor end box participant JsonPlaceHolder as "JsonPlaceHolder" -User -> SokratesControlPlane ++ : Negotiate Contract for Offer X -SokratesControlPlane --> User: Negotiation ID - SokratesControlPlane -> PlatoControlPlane ++ : IDS Contract Negotiation (simplified) +User -> AliceControlPlane ++ : Negotiate Contract for Offer X +AliceControlPlane --> User: Negotiation ID + AliceControlPlane -> BobControlPlane ++ : IDS Contract Negotiation (simplified) return Contract Agreement -deactivate SokratesControlPlane +deactivate AliceControlPlane -User -> SokratesControlPlane ++ : Request Negotiation by ID +User -> AliceControlPlane ++ : 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 index 43701e1ba1486f8dfa8171ba46b782bea61da0ea..e3630e70affabe09a7edeb8df1f48e04716f066a 100644 GIT binary patch literal 56765 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
p8&};4ezXF)nRpvazm85djzNlJ@@| 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 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 +User -> AliceControlPlane ++ : 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) +User -> AliceControlPlane ++ : Initiate Transfer with Agreement ID +AliceControlPlane --> User : Transfer Process ID + AliceControlPlane -> BobControlPlane ++ : 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 +AliceControlPlane -> AliceBackendService ++ : Data Plane Endpoint + Token + AliceBackendService -> AliceDataPlane ++ : Request Data with Token + AliceDataPlane -> BobDataPlane ++ : Request Data + BobDataPlane -> JsonPlaceHolder ++ : Request Data return data return data return data - SokratesBackendService -> SokratesBackendService : Write Data to File + AliceBackendService -> AliceBackendService : Write Data to File return ok -deactivate SokratesControlPlane +deactivate AliceControlPlane -User -> SokratesControlPlane ++ : Request Transfer Process by ID +User -> AliceControlPlane ++ : 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 index 080c26335df9d5ab8a381d22594c99bcd9492f6d..1a18b5be2d981dd319b16057473c0c7da3a7b4b9 100644 GIT binary patch literal 21142 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 WY;x{cJenc=Ei0)gkt6={?f(UPEr_WA literal 21812 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 IbxsLQ0Pl#yT>t<8 diff --git a/docs/data-transfer/diagrams/transfer_sequence_5.puml b/docs/data-transfer/diagrams/transfer_sequence_5.puml index b64f2222b..598b747b6 100644 --- a/docs/data-transfer/diagrams/transfer_sequence_5.puml +++ b/docs/data-transfer/diagrams/transfer_sequence_5.puml @@ -1,26 +1,26 @@ @startuml -!define sokratesColor 66CCFF -!define platoColor CCFF99 +!define aliceColor 66CCFF +!define bobColor 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 +box Alice + participant AliceControlPlane as "Control Plane" #aliceColor + participant AliceBackendService as "Backend Application" #aliceColor + participant AliceDataPlane as "Data Plane" #aliceColor end box -box Plato - participant PlatoControlPlane as "Control Plane" #platoColor - participant PlatoDataPlane as "Data Plane" #platoColor +box Bob + participant BobControlPlane as "Control Plane" #bobColor + participant BobDataPlane as "Data Plane" #bobColor end box participant JsonPlaceHolder as "JsonPlaceHolder" -User -> SokratesBackendService ++ : Get File Content +User -> AliceBackendService ++ : Get File Content return data diff --git a/docs/development/Release.md b/docs/development/Release.md index 2b8e09d07..29a85ce5f 100644 --- a/docs/development/Release.md +++ b/docs/development/Release.md @@ -23,6 +23,7 @@ As alternative check out the repository and build the plugin locally, so that it This call generates the dependencies file. If there is a value set for `dash.iplab.token` it will also automatically create new issues for all unknown dependencies at the Eclipse Intellectual Property board https://gitlab.eclipse.org/eclipsefdn/emo-team/iplab/-/issues +**Update Dependencies File and create Eclipse Issues (Eclipse Commiters only)** ```bash ./mvnw org.eclipse.dash:license-tool-plugin:license-check \ -Ddash.summary=DEPENDENCIES \ @@ -30,11 +31,26 @@ https://gitlab.eclipse.org/eclipsefdn/emo-team/iplab/-/issues -Ddash.iplab.token= ``` +**Update Dependencies File** +```bash +./mvnw org.eclipse.dash:license-tool-plugin:license-check \ + -Ddash.summary=DEPENDENCIES +``` +

* see dash documentation on how to get a token

+### 3. Resolve restricted Dependencies + +If a dependency is `restricted`, it is not approved by the Eclipse Foundation, yet. +The Eclipse Bot is able to approve dependencies automatically, if the license can be resolved by ClearlyDefined. + +1. (optional) Visit [https://clearlydefined.io/harvest](https://clearlydefined.io/harvest) and harvest the dependency from maven central. +2. Create the Eclipse IP Issues or ask an Eclipse Commiter to do this for you. + + [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/docs/development/postman/README.md b/docs/development/postman/README.md new file mode 100644 index 000000000..a6f5005b9 --- /dev/null +++ b/docs/development/postman/README.md @@ -0,0 +1,27 @@ +# Postman Collection + +## Prerequisites + +[![Postman][postman-shield]][postman-url] + +## Postman + +The Postman app can be used to send and receive EDC messages. + +### Install/Download Postman +please visit https://www.postman.com/downloads/ + +### Import Postman collection? +please visit https://learning.postman.com/docs/getting-started/importing-and-exporting-data/ + +## Collection + +The postman collection contains the most common API calls. Please note hat the + +- Policy & Negotiation calls come in pairs for the different kinds of policies +- The 'Data' call only works when using the All-In-One Deployment of this repository + +![screenshot](./images/screenshot.png) + +[postman-shield]: https://img.shields.io/badge/Postman-URL-orange +[postman-url]: https://www.postman.com \ No newline at end of file diff --git a/docs/development/postman/collection.json b/docs/development/postman/collection.json new file mode 100644 index 000000000..1e002e92f --- /dev/null +++ b/docs/development/postman/collection.json @@ -0,0 +1,798 @@ +{ + "info": { + "_postman_id": "0e923447-f09f-4cd9-8d2f-5bd4a09d378e", + "name": "EDC", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Asset", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{PROVIDER_DATAMGMT_URL}}/data/assets/{{ASSET_ID}}", + "host": [ + "{{PROVIDER_DATAMGMT_URL}}" + ], + "path": [ + "data", + "assets", + "{{ASSET_ID}}" + ] + } + }, + "response": [] + }, + { + "name": "Assets", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{PROVIDER_DATAMGMT_URL}}/data/assets", + "host": [ + "{{PROVIDER_DATAMGMT_URL}}" + ], + "path": [ + "data", + "assets" + ] + } + }, + "response": [] + }, + { + "name": "Asset", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 201\", function () {", + " pm.response.to.have.status(201);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"asset\": {\n \"properties\": {\n \"asset:prop:id\": \"{{ASSET_ID}}\",\n \"asset:prop:description\": \"Product EDC Demo Asset\"\n }\n },\n \"dataAddress\": {\n \"properties\": {\n \"type\": \"HttpData\",\n \"baseUrl\": \"https://jsonplaceholder.typicode.com/todos/1\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{PROVIDER_DATAMGMT_URL}}/data/assets", + "host": [ + "{{PROVIDER_DATAMGMT_URL}}" + ], + "path": [ + "data", + "assets" + ] + } + }, + "response": [] + }, + { + "name": "Asset", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{PROVIDER_DATAMGMT_URL}}/data/assets/{{ASSET_ID}}", + "host": [ + "{{PROVIDER_DATAMGMT_URL}}" + ], + "path": [ + "data", + "assets", + "{{ASSET_ID}}" + ] + } + }, + "response": [] + }, + { + "name": "Policy", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"id\": \"{{POLICY_ID}}\",\n \"policy\": {\n \"prohibitions\": [],\n \"obligations\": [],\n \"permissions\": [\n {\n \"edctype\": \"dataspaceconnector:permission\",\n \"action\": {\n \"type\": \"USE\"\n },\n \"constraints\": []\n }\n ]\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{PROVIDER_DATAMGMT_URL}}/data/policydefinitions/{{POLICY_ID}}", + "host": [ + "{{PROVIDER_DATAMGMT_URL}}" + ], + "path": [ + "data", + "policydefinitions", + "{{POLICY_ID}}" + ] + } + }, + "response": [] + }, + { + "name": "Policies", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"id\": \"{{POLICY_ID}}\",\n \"policy\": {\n \"prohibitions\": [],\n \"obligations\": [],\n \"permissions\": [\n {\n \"edctype\": \"dataspaceconnector:permission\",\n \"action\": {\n \"type\": \"USE\"\n },\n \"constraints\": []\n }\n ]\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{PROVIDER_DATAMGMT_URL}}/data/policydefinitions", + "host": [ + "{{PROVIDER_DATAMGMT_URL}}" + ], + "path": [ + "data", + "policydefinitions" + ] + } + }, + "response": [] + }, + { + "name": "Policy (Public)", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"id\": \"{{POLICY_ID}}\",\n \"policy\": {\n \"prohibitions\": [],\n \"obligations\": [],\n \"permissions\": [\n {\n \"edctype\": \"dataspaceconnector:permission\",\n \"action\": {\n \"type\": \"USE\"\n },\n \"constraints\": []\n }\n ]\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{PROVIDER_DATAMGMT_URL}}/data/policydefinitions", + "host": [ + "{{PROVIDER_DATAMGMT_URL}}" + ], + "path": [ + "data", + "policydefinitions" + ] + } + }, + "response": [] + }, + { + "name": "Policy (Properties)", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"id\": \"{{POLICY_ID}}\",\n \"policy\": {\n \"prohibitions\": [],\n \"obligations\": [],\n \"permissions\": [\n {\n \"edctype\": \"dataspaceconnector:permission\",\n \"action\": {\n \"type\": \"USE\"\n },\n \"constraints\": []\n }\n ],\n \"extensibleProperties\": {\n \"foo\": \"bar\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{PROVIDER_DATAMGMT_URL}}/data/policydefinitions", + "host": [ + "{{PROVIDER_DATAMGMT_URL}}" + ], + "path": [ + "data", + "policydefinitions" + ] + } + }, + "response": [] + }, + { + "name": "Policy (BPN)", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"id\": \"{{POLICY_ID}}\",\n \"policy\": {\n \"prohibitions\": [],\n \"obligations\": [],\n \"permissions\": [\n {\n \"edctype\": \"dataspaceconnector:permission\",\n \"action\": {\n \"type\": \"USE\"\n },\n \"constraints\": [\n {\n \"edctype\": \"AtomicConstraint\",\n \"leftExpression\": {\n \"edctype\": \"dataspaceconnector:literalexpression\",\n \"value\": \"BusinessPartnerNumber\"\n },\n \"rightExpression\": {\n \"edctype\": \"dataspaceconnector:literalexpression\",\n \"value\": \"{{POLICY_BPN}}\"\n },\n \"operator\": \"EQ\"\n }\n ]\n }\n ]\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{PROVIDER_DATAMGMT_URL}}/data/policydefinitions", + "host": [ + "{{PROVIDER_DATAMGMT_URL}}" + ], + "path": [ + "data", + "policydefinitions" + ] + } + }, + "response": [] + }, + { + "name": "Policy", + "request": { + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"id\": \"{{POLICY_ID}}\",\n \"policy\": {\n \"prohibitions\": [],\n \"obligations\": [],\n \"permissions\": [\n {\n \"edctype\": \"dataspaceconnector:permission\",\n \"action\": {\n \"type\": \"USE\"\n },\n \"constraints\": []\n }\n ]\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{PROVIDER_DATAMGMT_URL}}/data/policydefinitions/{{POLICY_ID}}", + "host": [ + "{{PROVIDER_DATAMGMT_URL}}" + ], + "path": [ + "data", + "policydefinitions", + "{{POLICY_ID}}" + ] + } + }, + "response": [] + }, + { + "name": "Contract Definition", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"id\": \"{{POLICY_ID}}\",\n \"policy\": {\n \"prohibitions\": [],\n \"obligations\": [],\n \"permissions\": [\n {\n \"edctype\": \"dataspaceconnector:permission\",\n \"action\": {\n \"type\": \"USE\"\n },\n \"constraints\": []\n }\n ]\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{PROVIDER_DATAMGMT_URL}}/data/contractdefinitions/{{POLICY_ID}}", + "host": [ + "{{PROVIDER_DATAMGMT_URL}}" + ], + "path": [ + "data", + "contractdefinitions", + "{{POLICY_ID}}" + ] + } + }, + "response": [] + }, + { + "name": "Contract Definitiions", + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"id\": \"{{POLICY_ID}}\",\n \"policy\": {\n \"prohibitions\": [],\n \"obligations\": [],\n \"permissions\": [\n {\n \"edctype\": \"dataspaceconnector:permission\",\n \"action\": {\n \"type\": \"USE\"\n },\n \"constraints\": []\n }\n ]\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{PROVIDER_DATAMGMT_URL}}/data/contractdefinitions", + "host": [ + "{{PROVIDER_DATAMGMT_URL}}" + ], + "path": [ + "data", + "contractdefinitions" + ] + } + }, + "response": [] + }, + { + "name": "Contract Definitiion", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"id\": \"{{CONTRACT_DEFINITION_ID}}\",\n \"criteria\": [\n {\n \"operandLeft\": \"asset:prop:id\",\n \"operator\": \"=\",\n \"operandRight\": \"{{ASSET_ID}}\"\n }\n ],\n \"accessPolicyId\": \"{{ACCESS_POLICY_ID}}\",\n \"contractPolicyId\": \"{{CONTRACT_POLICY_ID}}\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{PROVIDER_DATAMGMT_URL}}/data/contractdefinitions", + "host": [ + "{{PROVIDER_DATAMGMT_URL}}" + ], + "path": [ + "data", + "contractdefinitions" + ] + } + }, + "response": [] + }, + { + "name": "Contract Definition", + "request": { + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"id\": \"{{POLICY_ID}}\",\n \"policy\": {\n \"prohibitions\": [],\n \"obligations\": [],\n \"permissions\": [\n {\n \"edctype\": \"dataspaceconnector:permission\",\n \"action\": {\n \"type\": \"USE\"\n },\n \"constraints\": []\n }\n ]\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{PROVIDER_DATAMGMT_URL}}/data/contractdefinitions/{{POLICY_ID}}", + "host": [ + "{{PROVIDER_DATAMGMT_URL}}" + ], + "path": [ + "data", + "contractdefinitions", + "{{POLICY_ID}}" + ] + } + }, + "response": [] + }, + { + "name": "Catalog", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{CONSUMER_DATAMGMT_URL}}/data/catalog?providerUrl={{PROVIDER_IDS_URL}}/api/v1/ids/data", + "host": [ + "{{CONSUMER_DATAMGMT_URL}}" + ], + "path": [ + "data", + "catalog" + ], + "query": [ + { + "key": "providerUrl", + "value": "{{PROVIDER_IDS_URL}}/api/v1/ids/data" + } + ] + } + }, + "response": [] + }, + { + "name": "Negotation", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Body matches string\", function () {", + " var jsonData = pm.response.json();", + " pm.collectionVariables.set(\"AGREEMENT_ID\", jsonData.contractAgreementId);", + "});" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"connectorId\": \"foo\",\n \"connectorAddress\": \"{{PROVIDER_IDS_URL}}/api/v1/ids/data\",\n \"offer\": {\n \"offerId\": \"{{POLICY_ID}}:foo\",\n \"assetId\": \"{{ASSET_ID}}\",\n \"policy\": {\n \"prohibitions\": [],\n \"obligations\": [],\n \"permissions\": [\n {\n \"edctype\": \"dataspaceconnector:permission\",\n \"action\": {\n \"type\": \"USE\"\n },\n \"target\": \"{{ASSET_ID}}\",\n \"constraints\": []\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{CONSUMER_DATAMGMT_URL}}/data/contractnegotiations/{{NEGOTIATION_ID}}", + "host": [ + "{{CONSUMER_DATAMGMT_URL}}" + ], + "path": [ + "data", + "contractnegotiations", + "{{NEGOTIATION_ID}}" + ] + } + }, + "response": [] + }, + { + "name": "Negotation (Public)", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Body matches string\", function () {", + " var jsonData = pm.response.json();", + " pm.collectionVariables.set(\"NEGOTIATION_ID\", jsonData.id);", + "", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"connectorId\": \"foo\",\n \"connectorAddress\": \"{{PROVIDER_IDS_URL}}/api/v1/ids/data\",\n \"offer\": {\n \"offerId\": \"{{POLICY_ID}}:foo\",\n \"assetId\": \"{{ASSET_ID}}\",\n \"policy\": {\n \"prohibitions\": [],\n \"obligations\": [],\n \"permissions\": [\n {\n \"edctype\": \"dataspaceconnector:permission\",\n \"action\": {\n \"type\": \"USE\"\n },\n \"target\": \"{{ASSET_ID}}\",\n \"constraints\": []\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{CONSUMER_DATAMGMT_URL}}/data/contractnegotiations", + "host": [ + "{{CONSUMER_DATAMGMT_URL}}" + ], + "path": [ + "data", + "contractnegotiations" + ] + } + }, + "response": [] + }, + { + "name": "Negotation (Properties)", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Body matches string\", function () {", + " var jsonData = pm.response.json();", + " pm.collectionVariables.set(\"NEGOTIATION_ID\", jsonData.id);", + "", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"connectorId\": \"foo\",\n \"connectorAddress\": \"{{PROVIDER_IDS_URL}}/api/v1/ids/data\",\n \"offer\": {\n \"offerId\": \"{{POLICY_ID}}:foo\",\n \"assetId\": \"{{ASSET_ID}}\",\n \"policy\": {\n \"prohibitions\": [],\n \"obligations\": [],\n \"permissions\": [\n {\n \"edctype\": \"dataspaceconnector:permission\",\n \"action\": {\n \"type\": \"USE\"\n },\n \"target\": \"{{ASSET_ID}}\",\n \"constraints\": []\n }\n ],\n \"extensibleProperties\": {\n \"foo\": \"bar\"\n }\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{CONSUMER_DATAMGMT_URL}}/data/contractnegotiations", + "host": [ + "{{CONSUMER_DATAMGMT_URL}}" + ], + "path": [ + "data", + "contractnegotiations" + ] + } + }, + "response": [] + }, + { + "name": "Negotation (BPN)", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Body matches string\", function () {", + " var jsonData = pm.response.json();", + " pm.collectionVariables.set(\"NEGOTIATION_ID\", jsonData.id);", + "", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"connectorId\": \"foo\",\n \"connectorAddress\": \"{{PROVIDER_IDS_URL}}/api/v1/ids/data\",\n \"offer\": {\n \"offerId\": \"{{POLICY_ID}}:foo\",\n \"assetId\": \"{{ASSET_ID}}\",\n \"policy\": {\n \"prohibitions\": [],\n \"obligations\": [],\n \"permissions\": [\n {\n \"edctype\": \"dataspaceconnector:permission\",\n \"action\": {\n \"type\": \"USE\"\n },\n \"target\": \"{{ASSET_ID}}\",\n \"constraints\": [\n {\n \"edctype\": \"AtomicConstraint\",\n \"leftExpression\": {\n \"edctype\": \"dataspaceconnector:literalexpression\",\n \"value\": \"BusinessPartnerNumber\"\n },\n \"rightExpression\": {\n \"edctype\": \"dataspaceconnector:literalexpression\",\n \"value\": \"{{POLICY_BPN}}\"\n },\n \"operator\": \"EQ\"\n }\n ]\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{CONSUMER_DATAMGMT_URL}}/data/contractnegotiations", + "host": [ + "{{CONSUMER_DATAMGMT_URL}}" + ], + "path": [ + "data", + "contractnegotiations" + ] + } + }, + "response": [] + }, + { + "name": "Transfer", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "pm.collectionVariables.set(\"TRANSFER_ID\", Math.random());" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Body matches string\", function () {", + " var jsonData = pm.response.json();", + " pm.collectionVariables.set(\"TRANSFER_PROCESS_ID\", jsonData.id);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{ \"id\": \"{{TRANSFER_ID}}\",\n \"connectorId\": \"foo\", \n \"connectorAddress\": \"{{PROVIDER_IDS_URL}}/api/v1/ids/data\", \n \"contractId\": \"{{AGREEMENT_ID}}\", \n \"assetId\": \"{{ASSET_ID}}\",\n \"managedResources\": \"false\", \n \"dataDestination\": { \"type\": \"HttpProxy\" }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{CONSUMER_DATAMGMT_URL}}/data/transferprocess", + "host": [ + "{{CONSUMER_DATAMGMT_URL}}" + ], + "path": [ + "data", + "transferprocess" + ] + } + }, + "response": [] + }, + { + "name": "Transfer", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{CONSUMER_DATAMGMT_URL}}/data/transferprocess/{{TRANSFER_PROCESS_ID}}", + "host": [ + "{{CONSUMER_DATAMGMT_URL}}" + ], + "path": [ + "data", + "transferprocess", + "{{TRANSFER_PROCESS_ID}}" + ] + } + }, + "response": [] + }, + { + "name": "Data (Test Setup Only)", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/octet-stream", + "type": "default" + } + ], + "url": { + "raw": "{{CONSUMER_BACKEND_URL}}/{{TRANSFER_ID}}", + "host": [ + "{{CONSUMER_BACKEND_URL}}" + ], + "path": [ + "{{TRANSFER_ID}}" + ] + } + }, + "response": [] + } + ], + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "password", + "type": "string" + }, + { + "key": "key", + "value": "X-Api-Key", + "type": "string" + } + ] + }, + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ], + "variable": [ + { + "key": "CONSUMER_DATAMGMT_URL", + "value": "http://192.168.49.2:31639" + }, + { + "key": "PROVIDER_IDS_URL", + "value": "http://plato-edc-controlplane:8282" + }, + { + "key": "PROVIDER_DATAMGMT_URL", + "value": "http://192.168.49.2:31495" + }, + { + "key": "CONSUMER_BACKEND_URL", + "value": "http://192.168.49.2:31612", + "type": "default" + }, + { + "key": "ASSET_ID", + "value": "1", + "type": "default" + }, + { + "key": "POLICY_ID", + "value": "1", + "type": "default" + }, + { + "key": "CONTRACT_POLICY_ID", + "value": "1", + "type": "default" + }, + { + "key": "ACCESS_POLICY_ID", + "value": "1", + "type": "default" + }, + { + "key": "CONTRACT_DEFINITION_ID", + "value": "1", + "type": "default" + }, + { + "key": "POLICY_BPN", + "value": "BPNSOKRATES", + "type": "default" + }, + { + "key": "NEGOTIATION_ID", + "value": "" + }, + { + "key": "AGREEMENT_ID", + "value": "" + }, + { + "key": "TRANSFER_ID", + "value": "" + }, + { + "key": "TRANSFER_PROCESS_ID", + "value": "" + } + ] +} \ No newline at end of file diff --git a/docs/development/postman/images/screenshot.png b/docs/development/postman/images/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..8a9d231c67352b39336f5362a5eb2ea12aa7b75e GIT binary patch literal 77083 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%0DNtR|7lBA}M3~B_qYm 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 literal 0 HcmV?d00001 diff --git a/docs/migration/Version_0.1.2_0.1.3.md b/docs/migration/Version_0.1.2_0.1.3.md new file mode 100644 index 000000000..55c9c4570 --- /dev/null +++ b/docs/migration/Version_0.1.2_0.1.3.md @@ -0,0 +1,20 @@ +# Migration Version 0.1.2 to 0.1.3 + +This document contains a list of breaking changes that are introduced in version 0.1.3. + +## OAuth2 Extension + +As the images now use the official OAuth2 Extension, the audience settings need to the updated. + +**Add the following settings** +- EDC_OAUTH_PROVIDER_AUDIENCE +- EDC_OAUTH_ENDPOINT_AUDIENCE + +**Remove the following setting** +- EDC_IDS_ENDPOINT_AUDIENCE + +Example +``` +EDC_OAUTH_PROVIDER_AUDIENCE: idsc:IDS_CONNECTORS_ALL +EDC_OAUTH_ENDPOINT_AUDIENCE: http://plato-edc-controlplane:8282/api/v1/ids/data +``` \ No newline at end of file diff --git a/docs/release-notes/Version 0.1.3.md b/docs/release-notes/Version 0.1.3.md new file mode 100644 index 000000000..2ee32093f --- /dev/null +++ b/docs/release-notes/Version 0.1.3.md @@ -0,0 +1,91 @@ +# Release Notes Version 0.1.3 + +30.11.2022 + +## 0. Summary + +1. Container Images + - New Image: HashiCorp Vault & In Memory Store +2. Extensions + - Business Partner Extension + - HashiCorp Vault Extension + - OAuth2 Extension +3. Bug Fixes + - S3 Data Transfer + +# 1. Container Images + +## 1.1 New Image: HashiCorp Vault & In Memory Store + +The EDC now releases a fourth image with a combination of HashiCorp Vault and In Memory Store extensions. + +# 2. Extensions + +## 2.1 Business Partner Extension + +**Removed support for Constraint with multiple BPNs** +The possibility to use multiple Business Partner Numbers inside of a single constraint has been removed. It looks like +this was only possible due to a missing feature and may lead to unexpected side +effects (https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/2026) + +Hence, this kind of policy is no longer supported! + +```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 BPN extension will now always decline BPN policies with 'IN' operators, when asked by the EDC to enforce it. + +## 2.2 HashiCorp Vault Extension + +It is now possible to arrange HashiCorp Vault secrets in sub-directories. + +For example by storing the DAPS secrets in their own `/daps` directory: + +``` +EDC_OAUTH_PRIVATE_KEY_ALIAS: daps/my-plato-daps-key +EDC_OAUTH_PUBLIC_KEY_ALIAS: daps/my-plato-daps-crt +``` + +## 2.3 OAuth2 Extension + +The EDC Oauth2 Extension has now the possibility to add the audience to the claim. As the official OAuth2 Extension was +added to the control plane again most of the functionality of the CX Oauth2 Extension was removed. + +> **Breaking Change** The official OAuth2 Extension uses different settings then the EDC OAuth Extension. Please +> consolidate the [Migration Documentation](../migration/Version_0.1.2_0.1.3.md). + +# 3. Bug Fixes + +## 3.1 S3 Data Transfer + +Version 0.1.2 had some issues with the S3 data transfer. This version fixes them. \ No newline at end of file diff --git a/edc b/edc deleted file mode 160000 index 740c100ac..000000000 --- a/edc +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 740c100ac162bc41b1968c232ad81f7d739aefa9 diff --git a/edc-controlplane/edc-controlplane-base/pom.xml b/edc-controlplane/edc-controlplane-base/pom.xml index 61b811404..48d6ff542 100644 --- a/edc-controlplane/edc-controlplane-base/pom.xml +++ b/edc-controlplane/edc-controlplane-base/pom.xml @@ -1,24 +1,30 @@ edc-controlplane - net.catenax.edc - 0.1.2 + org.eclipse.tractusx.edc + 0.1.3 4.0.0 @@ -52,22 +58,28 @@ - net.catenax.edc.extensions + org.eclipse.tractusx.edc.extensions business-partner-validation - net.catenax.edc.extensions + org.eclipse.tractusx.edc.extensions dataplane-selector-configuration - net.catenax.edc.extensions + org.eclipse.tractusx.edc.extensions data-encryption - net.catenax.edc.extensions + org.eclipse.tractusx.edc.extensions cx-oauth2 + + + org.eclipse.dataspaceconnector + control-plane-core + + org.eclipse.dataspaceconnector @@ -79,6 +91,14 @@ org.eclipse.dataspaceconnector auth-tokenbased + + org.eclipse.dataspaceconnector + oauth2-core + + + org.eclipse.dataspaceconnector + iam-daps + @@ -117,32 +137,10 @@ - - org.eclipse.dataspaceconnector - core-base - - - org.eclipse.dataspaceconnector - core-boot - - - org.eclipse.dataspaceconnector - transfer - - - org.eclipse.dataspaceconnector - contract - org.eclipse.dataspaceconnector jwt-spi - @@ -161,6 +159,10 @@ org.eclipse.dataspaceconnector data-plane-selector-spi + + org.eclipse.dataspaceconnector + data-plane-transfer-client + @@ -188,10 +190,27 @@ org.eclipse.dataspaceconnector http - org.eclipse.dataspaceconnector http-receiver + + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + - \ No newline at end of file + diff --git a/edc-controlplane/edc-controlplane-memory-hashicorp-vault/pom.xml b/edc-controlplane/edc-controlplane-memory-hashicorp-vault/pom.xml new file mode 100644 index 000000000..416ce398e --- /dev/null +++ b/edc-controlplane/edc-controlplane-memory-hashicorp-vault/pom.xml @@ -0,0 +1,174 @@ + + + + + edc-controlplane + org.eclipse.tractusx.edc + 0.1.3 + + 4.0.0 + + edc-controlplane-memory-hashicorp-vault + jar + + + ${project.groupId}_${project.artifactId} + + + + ${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 + false + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + package + + copy-dependencies + + + ${project.build.directory}/lib + + + + + + + + + + + + org.eclipse.tractusx.edc + edc-controlplane-base + + + org.eclipse.dataspaceconnector + control-plane-core + + + + + org.eclipse.tractusx.edc.extensions + hashicorp-vault + + + + org.eclipse.dataspaceconnector + data-plane-transfer-client + + + + + + + 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-memory-hashicorp-vault/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-memory-hashicorp-vault/src/main/docker/Dockerfile new file mode 100644 index 000000000..06ee33b61 --- /dev/null +++ b/edc-controlplane/edc-controlplane-memory-hashicorp-vault/src/main/docker/Dockerfile @@ -0,0 +1,69 @@ +# +# Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2021,2022 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# +FROM alpine:3.17.0 as otel + +ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" + +HEALTHCHECK NONE + +RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar + +FROM alpine:3.17.0 + +ARG JAR +ARG LIB + +ARG APP_USER=docker +ARG APP_UID=10100 + +RUN apk update && \ + apk add openjdk11-jre-headless=11.0.17_p8-r3 --no-cache && \ + rm -rf /var/cache/apk/* + +RUN addgroup --system "$APP_USER" + +RUN adduser \ + --shell /sbin/nologin \ + --disabled-password \ + --gecos "" \ + --ingroup "$APP_USER" \ + --no-create-home \ + --uid "$APP_UID" \ + "$APP_USER" + +USER "$APP_USER" +WORKDIR /app + +COPY --from=otel /tmp/opentelemetry-javaagent.jar . +COPY ${JAR} edc-controlplane.jar +COPY ${LIB} ./lib/ + +HEALTHCHECK NONE + +CMD ["java", \ + "-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 7bfadee32..678838be1 100644 --- a/edc-controlplane/edc-controlplane-memory/pom.xml +++ b/edc-controlplane/edc-controlplane-memory/pom.xml @@ -1,22 +1,28 @@ - net.catenax.edc + org.eclipse.tractusx.edc edc-controlplane - 0.1.2 + 0.1.3 4.0.0 @@ -84,7 +90,7 @@ - net.catenax.edc + org.eclipse.tractusx.edc edc-controlplane-base diff --git a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile index aec27cd97..06ee33b61 100644 --- a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile @@ -1,22 +1,31 @@ # # Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2021,2022 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. # # This program and the accompanying materials are made available under the # terms of the Apache License, Version 2.0 which is available at # https://www.apache.org/licenses/LICENSE-2.0 # -# SPDX-License-Identifier: Apache-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. # -# Contributors: -# Mercedes-Benz Tech Innovation GmbH - Initial Dockerfile +# SPDX-License-Identifier: Apache-2.0 # -FROM alpine:3.16.2 as otel +FROM alpine:3.17.0 as otel ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" +HEALTHCHECK NONE + RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM alpine:3.16.2 +FROM alpine:3.17.0 ARG JAR ARG LIB @@ -25,8 +34,8 @@ ARG APP_USER=docker ARG APP_UID=10100 RUN apk update && \ - apk add openjdk11-jre-headless=11.0.16.1_p1-r0 --no-cache && \ - rm -rf /var/cache/apk/* + apk add openjdk11-jre-headless=11.0.17_p8-r3 --no-cache && \ + rm -rf /var/cache/apk/* RUN addgroup --system "$APP_USER" @@ -46,6 +55,8 @@ COPY --from=otel /tmp/opentelemetry-javaagent.jar . COPY ${JAR} edc-controlplane.jar COPY ${LIB} ./lib/ +HEALTHCHECK NONE + CMD ["java", \ "-javaagent:/app/opentelemetry-javaagent.jar", \ "-Dedc.fs.config=/app/configuration.properties", \ diff --git a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/pom.xml b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/pom.xml index 402a1bac2..1e6bd8087 100644 --- a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/pom.xml +++ b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/pom.xml @@ -1,23 +1,29 @@ - net.catenax.edc + org.eclipse.tractusx.edc edc-controlplane - 0.1.2 + 0.1.3 4.0.0 @@ -84,7 +90,7 @@ - net.catenax.edc + org.eclipse.tractusx.edc edc-controlplane-postgresql @@ -96,7 +102,7 @@ - net.catenax.edc.extensions + org.eclipse.tractusx.edc.extensions hashicorp-vault 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 aec27cd97..06ee33b61 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 @@ -1,22 +1,31 @@ # # Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2021,2022 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. # # This program and the accompanying materials are made available under the # terms of the Apache License, Version 2.0 which is available at # https://www.apache.org/licenses/LICENSE-2.0 # -# SPDX-License-Identifier: Apache-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. # -# Contributors: -# Mercedes-Benz Tech Innovation GmbH - Initial Dockerfile +# SPDX-License-Identifier: Apache-2.0 # -FROM alpine:3.16.2 as otel +FROM alpine:3.17.0 as otel ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" +HEALTHCHECK NONE + RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM alpine:3.16.2 +FROM alpine:3.17.0 ARG JAR ARG LIB @@ -25,8 +34,8 @@ ARG APP_USER=docker ARG APP_UID=10100 RUN apk update && \ - apk add openjdk11-jre-headless=11.0.16.1_p1-r0 --no-cache && \ - rm -rf /var/cache/apk/* + apk add openjdk11-jre-headless=11.0.17_p8-r3 --no-cache && \ + rm -rf /var/cache/apk/* RUN addgroup --system "$APP_USER" @@ -46,6 +55,8 @@ COPY --from=otel /tmp/opentelemetry-javaagent.jar . COPY ${JAR} edc-controlplane.jar COPY ${LIB} ./lib/ +HEALTHCHECK NONE + CMD ["java", \ "-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 0ef3fa639..e2d675671 100644 --- a/edc-controlplane/edc-controlplane-postgresql/pom.xml +++ b/edc-controlplane/edc-controlplane-postgresql/pom.xml @@ -1,23 +1,29 @@ - net.catenax.edc + org.eclipse.tractusx.edc edc-controlplane - 0.1.2 + 0.1.3 4.0.0 @@ -85,13 +91,13 @@ - net.catenax.edc + org.eclipse.tractusx.edc edc-controlplane-base - net.catenax.edc.extensions + org.eclipse.tractusx.edc.extensions postgresql-migration diff --git a/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile index aec27cd97..06ee33b61 100644 --- a/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile @@ -1,22 +1,31 @@ # # Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2021,2022 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. # # This program and the accompanying materials are made available under the # terms of the Apache License, Version 2.0 which is available at # https://www.apache.org/licenses/LICENSE-2.0 # -# SPDX-License-Identifier: Apache-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. # -# Contributors: -# Mercedes-Benz Tech Innovation GmbH - Initial Dockerfile +# SPDX-License-Identifier: Apache-2.0 # -FROM alpine:3.16.2 as otel +FROM alpine:3.17.0 as otel ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" +HEALTHCHECK NONE + RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM alpine:3.16.2 +FROM alpine:3.17.0 ARG JAR ARG LIB @@ -25,8 +34,8 @@ ARG APP_USER=docker ARG APP_UID=10100 RUN apk update && \ - apk add openjdk11-jre-headless=11.0.16.1_p1-r0 --no-cache && \ - rm -rf /var/cache/apk/* + apk add openjdk11-jre-headless=11.0.17_p8-r3 --no-cache && \ + rm -rf /var/cache/apk/* RUN addgroup --system "$APP_USER" @@ -46,6 +55,8 @@ COPY --from=otel /tmp/opentelemetry-javaagent.jar . COPY ${JAR} edc-controlplane.jar COPY ${LIB} ./lib/ +HEALTHCHECK NONE + CMD ["java", \ "-javaagent:/app/opentelemetry-javaagent.jar", \ "-Dedc.fs.config=/app/configuration.properties", \ diff --git a/edc-controlplane/pom.xml b/edc-controlplane/pom.xml index 7833a1b49..ddba99e2d 100644 --- a/edc-controlplane/pom.xml +++ b/edc-controlplane/pom.xml @@ -1,23 +1,29 @@ - net.catenax.edc + org.eclipse.tractusx.edc product-edc-parent - 0.1.2 + 0.1.3 4.0.0 @@ -33,5 +39,6 @@ edc-controlplane-memory edc-controlplane-postgresql edc-controlplane-postgresql-hashicorp-vault + edc-controlplane-memory-hashicorp-vault diff --git a/edc-dataplane/edc-dataplane-azure-vault/pom.xml b/edc-dataplane/edc-dataplane-azure-vault/pom.xml index 82c0895d5..2528686a8 100644 --- a/edc-dataplane/edc-dataplane-azure-vault/pom.xml +++ b/edc-dataplane/edc-dataplane-azure-vault/pom.xml @@ -1,23 +1,29 @@ - net.catenax.edc + org.eclipse.tractusx.edc edc-dataplane - 0.1.2 + 0.1.3 4.0.0 @@ -83,7 +89,7 @@ - net.catenax.edc + org.eclipse.tractusx.edc edc-dataplane-base 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 2aec25e1b..5d14913c9 100644 --- a/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile +++ b/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile @@ -1,22 +1,31 @@ # # Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2021,2022 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. # # This program and the accompanying materials are made available under the # terms of the Apache License, Version 2.0 which is available at # https://www.apache.org/licenses/LICENSE-2.0 # -# SPDX-License-Identifier: Apache-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. # -# Contributors: -# Mercedes-Benz Tech Innovation GmbH - Initial Dockerfile +# SPDX-License-Identifier: Apache-2.0 # -FROM alpine:3.16.2 as otel +FROM alpine:3.17.0 as otel ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" +HEALTHCHECK NONE + RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM alpine:3.16.2 +FROM alpine:3.17.0 ARG JAR ARG LIB @@ -25,8 +34,8 @@ ARG APP_USER=docker ARG APP_UID=10100 RUN apk update && \ - apk add openjdk11-jre-headless=11.0.16.1_p1-r0 --no-cache && \ - rm -rf /var/cache/apk/* + apk add openjdk11-jre-headless=11.0.17_p8-r3 --no-cache && \ + rm -rf /var/cache/apk/* RUN addgroup --system "$APP_USER" @@ -46,6 +55,8 @@ COPY --from=otel /tmp/opentelemetry-javaagent.jar . COPY ${JAR} edc-dataplane.jar COPY ${LIB} ./lib/ +HEALTHCHECK NONE + CMD ["java", \ "-javaagent:/app/opentelemetry-javaagent.jar", \ "-Dedc.fs.config=/app/configuration.properties", \ diff --git a/edc-dataplane/edc-dataplane-base/pom.xml b/edc-dataplane/edc-dataplane-base/pom.xml index 4d3604e17..21451ca31 100644 --- a/edc-dataplane/edc-dataplane-base/pom.xml +++ b/edc-dataplane/edc-dataplane-base/pom.xml @@ -1,24 +1,30 @@ edc-dataplane - net.catenax.edc - 0.1.2 + org.eclipse.tractusx.edc + 0.1.3 4.0.0 @@ -120,5 +126,23 @@ org.eclipse.dataspaceconnector http + + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + diff --git a/edc-dataplane/edc-dataplane-hashicorp-vault/pom.xml b/edc-dataplane/edc-dataplane-hashicorp-vault/pom.xml index 25ab47e0d..63b864b87 100644 --- a/edc-dataplane/edc-dataplane-hashicorp-vault/pom.xml +++ b/edc-dataplane/edc-dataplane-hashicorp-vault/pom.xml @@ -1,23 +1,29 @@ - net.catenax.edc + org.eclipse.tractusx.edc edc-dataplane - 0.1.2 + 0.1.3 4.0.0 @@ -83,13 +89,13 @@ - net.catenax.edc + org.eclipse.tractusx.edc edc-dataplane-base - net.catenax.edc.extensions + org.eclipse.tractusx.edc.extensions hashicorp-vault 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 2aec25e1b..5d14913c9 100644 --- a/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile +++ b/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile @@ -1,22 +1,31 @@ # # Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2021,2022 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. # # This program and the accompanying materials are made available under the # terms of the Apache License, Version 2.0 which is available at # https://www.apache.org/licenses/LICENSE-2.0 # -# SPDX-License-Identifier: Apache-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. # -# Contributors: -# Mercedes-Benz Tech Innovation GmbH - Initial Dockerfile +# SPDX-License-Identifier: Apache-2.0 # -FROM alpine:3.16.2 as otel +FROM alpine:3.17.0 as otel ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" +HEALTHCHECK NONE + RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM alpine:3.16.2 +FROM alpine:3.17.0 ARG JAR ARG LIB @@ -25,8 +34,8 @@ ARG APP_USER=docker ARG APP_UID=10100 RUN apk update && \ - apk add openjdk11-jre-headless=11.0.16.1_p1-r0 --no-cache && \ - rm -rf /var/cache/apk/* + apk add openjdk11-jre-headless=11.0.17_p8-r3 --no-cache && \ + rm -rf /var/cache/apk/* RUN addgroup --system "$APP_USER" @@ -46,6 +55,8 @@ COPY --from=otel /tmp/opentelemetry-javaagent.jar . COPY ${JAR} edc-dataplane.jar COPY ${LIB} ./lib/ +HEALTHCHECK NONE + CMD ["java", \ "-javaagent:/app/opentelemetry-javaagent.jar", \ "-Dedc.fs.config=/app/configuration.properties", \ diff --git a/edc-dataplane/pom.xml b/edc-dataplane/pom.xml index 70d806d1b..e439c384e 100644 --- a/edc-dataplane/pom.xml +++ b/edc-dataplane/pom.xml @@ -1,24 +1,30 @@ 4.0.0 - net.catenax.edc + org.eclipse.tractusx.edc product-edc-parent - 0.1.2 + 0.1.3 edc-dataplane diff --git a/edc-extensions/business-partner-validation/README.md b/edc-extensions/business-partner-validation/README.md index daf374e96..42b27bb0c 100644 --- a/edc-extensions/business-partner-validation/README.md +++ b/edc-extensions/business-partner-validation/README.md @@ -1,25 +1,12 @@ -# 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 -Catena-X `Participants` in the `ContractDefinition.AccessPolicy`. +Catena-X `Participants` in the `ContractDefinition.AccessPolicy`. Using a BPN in `ContractDefinition.ContractPolicy` is possible, too, but once the contract is complete there is no policy enforcement in place from the EDC. -**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. - -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. +corresponding documentation can be found in the [EDC GitHub Repository](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector). -The business partner number of another connector is part of the DAPS token. Once a BPN constraint is used in an access +The business partner number of another connector is part of its 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: @@ -42,9 +29,8 @@ 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 -Management API. +Management API. Theoretically it is also possible to combine multiple BPN constraints by wrapping them into an EDC `OrConstraint` (see EDC Repository). -**Example 1 for single BPN:** ```json { "uid": "", @@ -75,44 +61,13 @@ 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" - } - ] - } - ] -} -``` - # 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 3 for accidentially public:** +**Example 1 for accidentially public:** ```json { "uid": "1", @@ -147,7 +102,7 @@ This policy is public available, even though the constraint is described correct --- -**Example 4 for accidentially public:** +**Example 2 for accidentally public:** ```json { @@ -179,4 +134,4 @@ This policy is public available, even though the constraint is described correct } ``` -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. +This policy is public available, too. The cause is a typo in the left-expression of the constraint. This extension only registers the `Constraint.LeftExpression` `BusinessPartnerNumber` within the EDC. Any other term will have the EDC ignore the corresponding constraint, hence interpret the policies as public policy. diff --git a/edc-extensions/business-partner-validation/pom.xml b/edc-extensions/business-partner-validation/pom.xml index fbfe5ba0e..eecbb9567 100644 --- a/edc-extensions/business-partner-validation/pom.xml +++ b/edc-extensions/business-partner-validation/pom.xml @@ -1,23 +1,29 @@ - net.catenax.edc.extensions + org.eclipse.tractusx.edc.extensions edc-extensions - 0.1.2 + 0.1.3 4.0.0 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 deleted file mode 100644 index db7a7282b..000000000 --- a/edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/BusinessPartnerDutyFunction.java +++ /dev/null @@ -1,35 +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.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.engine.AtomicConstraintFunction; -import org.eclipse.dataspaceconnector.spi.policy.engine.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/BusinessPartnerValidationExtension.java b/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtension.java similarity index 59% rename from edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/BusinessPartnerValidationExtension.java rename to edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtension.java index d97953e84..dfc2fda26 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/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtension.java @@ -1,35 +1,40 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.validation.businesspartner; +package org.eclipse.tractusx.edc.validation.businesspartner; import static org.eclipse.dataspaceconnector.spi.policy.engine.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.runtime.metamodel.annotation.Requires; +import org.eclipse.dataspaceconnector.runtime.metamodel.annotation.Inject; import org.eclipse.dataspaceconnector.spi.monitor.Monitor; import org.eclipse.dataspaceconnector.spi.policy.engine.PolicyEngine; import org.eclipse.dataspaceconnector.spi.policy.engine.RuleBindingRegistry; import org.eclipse.dataspaceconnector.spi.system.ServiceExtension; import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; +import org.eclipse.tractusx.edc.validation.businesspartner.functions.BusinessPartnerDutyFunction; +import org.eclipse.tractusx.edc.validation.businesspartner.functions.BusinessPartnerPermissionFunction; +import org.eclipse.tractusx.edc.validation.businesspartner.functions.BusinessPartnerProhibitionFunction; -@Requires({RuleBindingRegistry.class, PolicyEngine.class}) public class BusinessPartnerValidationExtension implements ServiceExtension { /** @@ -50,6 +55,18 @@ public class BusinessPartnerValidationExtension implements ServiceExtension { */ public static final String BUSINESS_PARTNER_CONSTRAINT_KEY = "BusinessPartnerNumber"; + public BusinessPartnerValidationExtension() {} + + public BusinessPartnerValidationExtension( + final RuleBindingRegistry ruleBindingRegistry, final PolicyEngine policyEngine) { + this.ruleBindingRegistry = ruleBindingRegistry; + this.policyEngine = policyEngine; + } + + @Inject private RuleBindingRegistry ruleBindingRegistry; + + @Inject private PolicyEngine policyEngine; + @Override public String name() { return "Business Partner Validation Extension"; @@ -59,8 +76,6 @@ public String name() { 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 = 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/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java similarity index 63% rename from edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java rename to edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java index e5f4eabe0..16997c672 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/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java @@ -1,19 +1,24 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * 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 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.validation.businesspartner.functions; +package org.eclipse.tractusx.edc.validation.businesspartner.functions; import java.util.Map; import java.util.Objects; @@ -32,14 +37,10 @@ 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 = - "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'"; + "Failing evaluation because of invalid BusinessPartnerNumber constraint. As operator only 'EQ' is supported. Unsupported operator: '%s'"; private final Monitor monitor; @@ -98,8 +99,6 @@ protected boolean evaluate( 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, operator); monitor.warning(message); @@ -108,53 +107,6 @@ protected boolean evaluate( } } - /** - * @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 referringConnectorClaim, Object businessPartnerNumbers, PolicyContext policyContext) { - if (businessPartnerNumbers == null) { - final String message = - String.format(FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_ITERABLE, "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, - businessPartnerNumbers.getClass().getName()); - monitor.warning(message); - policyContext.reportProblem(message); - return false; - } - - for (Object businessPartnerNumber : (Iterable) businessPartnerNumbers) { - if (businessPartnerNumber == null) { - final String message = - String.format(SKIP_EVALUATION_BECAUSE_ITERABLE_VALUE_NOT_STRING, "null"); - monitor.warning(message); - policyContext.reportProblem(message); - } 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); - } else if (isCorrectBusinessPartner( - referringConnectorClaim, (String) businessPartnerNumber)) { - return true; // iterable does contain at least one matching value - } - } - - return false; - } - /** * @param referringConnectorClaim of the participant * @param businessPartnerNumber object diff --git a/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerDutyFunction.java b/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerDutyFunction.java new file mode 100644 index 000000000..0282a4465 --- /dev/null +++ b/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerDutyFunction.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.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.engine.AtomicConstraintFunction; +import org.eclipse.dataspaceconnector.spi.policy.engine.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/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerPermissionFunction.java similarity index 50% rename from edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/BusinessPartnerPermissionFunction.java rename to edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerPermissionFunction.java index 101f37054..c5c17d8ec 100644 --- 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/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerPermissionFunction.java @@ -1,18 +1,24 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.validation.businesspartner.functions; +package org.eclipse.tractusx.edc.validation.businesspartner.functions; import org.eclipse.dataspaceconnector.policy.model.Operator; import org.eclipse.dataspaceconnector.policy.model.Permission; 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/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerProhibitionFunction.java similarity index 50% rename from edc-extensions/business-partner-validation/src/main/java/net/catenax/edc/validation/businesspartner/functions/BusinessPartnerProhibitionFunction.java rename to edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerProhibitionFunction.java index 4db6d9629..ad71419f7 100644 --- 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/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerProhibitionFunction.java @@ -1,18 +1,24 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.validation.businesspartner.functions; +package org.eclipse.tractusx.edc.validation.businesspartner.functions; import org.eclipse.dataspaceconnector.policy.model.Operator; import org.eclipse.dataspaceconnector.policy.model.Prohibition; 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 226b41ab3..eaadbabd1 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 @@ -1,14 +1,20 @@ # # Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2021,2022 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. # # This program and the accompanying materials are made available under the # terms of the Apache License, Version 2.0 which is available at # https://www.apache.org/licenses/LICENSE-2.0 # -# SPDX-License-Identifier: Apache-2.0 -# -# Contributors: -# Mercedes-Benz Tech Innovation GmbH - Initial ServiceExtension file +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. # +# SPDX-License-Identifier: Apache-2.0 # -net.catenax.edc.validation.businesspartner.BusinessPartnerValidationExtension +org.eclipse.tractusx.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/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtensionTest.java similarity index 68% rename from edc-extensions/business-partner-validation/src/test/java/net/catenax/edc/validation/businesspartner/BusinessPartnerValidationExtensionTest.java rename to edc-extensions/business-partner-validation/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtensionTest.java index 4ae06eb83..1351564b1 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/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtensionTest.java @@ -1,18 +1,24 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.validation.businesspartner; +package org.eclipse.tractusx.edc.validation.businesspartner; import org.eclipse.dataspaceconnector.policy.model.Duty; import org.eclipse.dataspaceconnector.policy.model.Permission; @@ -32,22 +38,20 @@ class BusinessPartnerValidationExtensionTest { // mocks private ServiceExtensionContext serviceExtensionContext; private PolicyEngine policyEngine; + private RuleBindingRegistry ruleBindingRegistry; @BeforeEach void setup() { policyEngine = Mockito.mock(PolicyEngine.class); - RuleBindingRegistry ruleBindingRegistry = Mockito.mock(RuleBindingRegistry.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(); + extension = new BusinessPartnerValidationExtension(ruleBindingRegistry, policyEngine); } @Test 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/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidationTest.java similarity index 76% rename from edc-extensions/business-partner-validation/src/test/java/net/catenax/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidationTest.java rename to edc-extensions/business-partner-validation/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidationTest.java index b00232d5e..175613f6c 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/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidationTest.java @@ -1,18 +1,24 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.validation.businesspartner.functions; +package org.eclipse.tractusx.edc.validation.businesspartner.functions; import java.util.Collections; import java.util.List; @@ -51,7 +57,7 @@ void BeforeEach() { @EnumSource(Operator.class) void testFailsOnUnsupportedOperations(Operator operator) { - if (operator == Operator.EQ || operator == Operator.IN) { // only allowed operator + if (operator == Operator.EQ) { // only allowed operator return; } @@ -72,7 +78,6 @@ void testFailsOnUnsupportedRightValue() { // invoke & assert Assertions.assertFalse(validation.evaluate(Operator.EQ, 1, policyContext)); - Assertions.assertFalse(validation.evaluate(Operator.IN, "foo", policyContext)); } @Test @@ -135,6 +140,9 @@ void testValidationWhenSingleParticipantIsValid() { Assertions.assertTrue(isContainedTrue); } + // In the past it was possible to use the 'IN' constraint with multiple BPNs as + // a list. This is no longer supported. + // The EDC must now always decline this kind of BPN format. @Test void testValidationForMultipleParticipants() { @@ -143,8 +151,8 @@ void testValidationForMultipleParticipants() { 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("foo", "bar"), policyContext)); + Assertions.assertFalse(validation.evaluate(Operator.IN, List.of(1, "foo"), policyContext)); Assertions.assertFalse(validation.evaluate(Operator.IN, List.of("bar", "bar"), policyContext)); } diff --git a/edc-extensions/control-plane-adapter/README.md b/edc-extensions/control-plane-adapter/README.md new file mode 100644 index 000000000..ddfd2b40e --- /dev/null +++ b/edc-extensions/control-plane-adapter/README.md @@ -0,0 +1,70 @@ +# 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 communication details for 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 yet implemented) +- can retry failed part of the process (no need to start the process from the beginning) + +Configuration: + +| Key | Description | Mandatory | Default | +|:-------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------|----| +| edc.cp.adapter.default.message.retry.number | Number of retries of a message, in case of an error, within the internal process of retrieving DataReference | no | 3 | +| edc.cp.adapter.default.sync.request.timeout | Timeout for synchronous request (in seconds), after witch 'timeout' error will be returned to the requesting client | no | 20 | +| edc.cp.adapter.messagebus.inmemory.thread.number | Number of threads running within the in-memory implementation of MessageBus _ _ | no | 10 | +| edc.cp.adapter.cache.contract.agreement | Turn on/off contract agreement cache for the specific asset. Once the contract is agreed, the second request for the same asset will reuse the agreement. Value 1 = on, 0 = off. | no | 1 | +| edc.cp.adapter.cache.catalog.expire.after | Number of seconds, after witch prevoiusly requested catalog will not be reused, and will be removed from catalog cache | no | 3600 | + + +How to use it: +1. Client sends a GET request with two parameters: assetId and the url of the provider control-plane: + + ``` + /adapter/asset/sync/{assetId}?providerUrl={providerUrl} + ``` + + The example ULR could be: + + ``` + http://localhost:9193/api/v1/data/adapter/asset/sync/123?providerUrl=http://localhost:8182/api/v1/ids/data + ``` + + The controller is registered under the context alias of DataManagment API. The authentication depends on the DataManagement configuration. + To find out more please visit: + + [api-configuration](../../edc/extensions/control-plane/api/data-management/api-configuration/README.md) + + [data-management](../../edc/extensions/control-plane/api/data-management/README.md) + + +2. EndpointDataReference object is returned. Example of the EndpointDataReference response: + ```json + { + "id": "ee8b758a-4b02-4cca-bb37-d0256b4638e7", + "endpoint": "http://consumer-dataplane:9192/publicsubmodel?provider-connector-url=...", + "authKey": "Authorization", + "authCode": "eyJhbGciOiJSUzI1NiJ9.eyJkYWQiOi...", + "properties": { + "cid": "1:b2367617-5f51-48c5-9f25-e30a7299235c" + } + } + ``` + +3. Client, using the DataReference, retrieves the Asset through data-plane. + + Example of the data-plane GET request, to retrieve Asset, with DataReference information: + + ``` + url: http://consumer-dataplane:9192/publicsubmodel?provider-connector-url=... {endpoint} + header: Authorization:eyJhbGciOiJSUzI1NiJ9.eyJkYWQiOi... {authKey:authCode} + ``` + +Internal design of the extension: + +![diagram](src/main/resources/control-plane-adapter.jpg) + + + diff --git a/edc-extensions/control-plane-adapter/pom.xml b/edc-extensions/control-plane-adapter/pom.xml new file mode 100644 index 000000000..b1582c7e3 --- /dev/null +++ b/edc-extensions/control-plane-adapter/pom.xml @@ -0,0 +1,133 @@ + + + + + org.eclipse.tractusx.edc.extensions + edc-extensions + 0.1.3 + + 4.0.0 + + control-plane-adapter + jar + + + ${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 + 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 + + + \ 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..4ddf179ed --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/ApiAdapterConfig.java @@ -0,0 +1,55 @@ +/* + * 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; + +public class ApiAdapterConfig { + private static final String DEFAULT_MESSAGE_RETRY_NUMBER = + "edc.cp.adapter.default.message.retry.number"; + private static final String DEFAULT_SYNC_REQUEST_TIMEOUT = + "edc.cp.adapter.default.sync.request.timeout"; + private static final String IN_MEMORY_MESSAGE_BUS_THREAD_NUMBER = + "edc.cp.adapter.messagebus.inmemory.thread.number"; + private static final String CATALOG_EXPIRE_AFTER_TIME = + "edc.cp.adapter.cache.catalog.expire.after"; + private static final String CONTRACT_AGREEMENT_CACHE = "edc.cp.adapter.cache.contract.agreement"; + + private final ServiceExtensionContext context; + + public ApiAdapterConfig(ServiceExtensionContext context) { + this.context = context; + } + + public int getDefaultMessageRetryNumber() { + return context.getSetting(DEFAULT_MESSAGE_RETRY_NUMBER, 3); + } + + public int getDefaultSyncRequestTimeout() { + return context.getSetting(DEFAULT_SYNC_REQUEST_TIMEOUT, 20); + } + + public int getInMemoryMessageBusThreadNumber() { + return context.getSetting(IN_MEMORY_MESSAGE_BUS_THREAD_NUMBER, 10); + } + + public boolean isContractAgreementCacheOn() { + return context.getSetting(CONTRACT_AGREEMENT_CACHE, 1) != 0; + } + + public int getCatalogExpireAfterTime() { + return context.getSetting(CATALOG_EXPIRE_AFTER_TIME, 3600); + } +} 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..008c61abb --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/ApiAdapterExtension.java @@ -0,0 +1,158 @@ +/* + * 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; + +import net.catenax.edc.cp.adapter.messaging.Channel; +import net.catenax.edc.cp.adapter.messaging.InMemoryMessageBus; +import net.catenax.edc.cp.adapter.messaging.ListenerService; +import net.catenax.edc.cp.adapter.process.contractdatastore.ContractDataStore; +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.*; +import net.catenax.edc.cp.adapter.process.datareference.DataRefInMemorySyncService; +import net.catenax.edc.cp.adapter.process.datareference.DataRefNotificationSyncService; +import net.catenax.edc.cp.adapter.process.datareference.DataReferenceHandler; +import net.catenax.edc.cp.adapter.process.datareference.EndpointDataReferenceReceiverImpl; +import net.catenax.edc.cp.adapter.service.ErrorResultService; +import net.catenax.edc.cp.adapter.service.ResultService; +import net.catenax.edc.cp.adapter.util.ExpiringMap; +import net.catenax.edc.cp.adapter.util.LockMap; +import org.eclipse.dataspaceconnector.api.datamanagement.catalog.service.CatalogServiceImpl; +import org.eclipse.dataspaceconnector.api.datamanagement.configuration.DataManagementApiConfiguration; +import org.eclipse.dataspaceconnector.api.datamanagement.contractnegotiation.service.ContractNegotiationService; +import org.eclipse.dataspaceconnector.api.datamanagement.transferprocess.service.TransferProcessService; +import org.eclipse.dataspaceconnector.runtime.metamodel.annotation.Inject; +import org.eclipse.dataspaceconnector.spi.WebService; +import org.eclipse.dataspaceconnector.spi.contract.negotiation.observe.ContractNegotiationListener; +import org.eclipse.dataspaceconnector.spi.contract.negotiation.observe.ContractNegotiationObservable; +import org.eclipse.dataspaceconnector.spi.message.RemoteMessageDispatcherRegistry; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtension; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; +import org.eclipse.dataspaceconnector.spi.transfer.edr.EndpointDataReferenceReceiver; +import org.eclipse.dataspaceconnector.spi.transfer.edr.EndpointDataReferenceReceiverRegistry; + +public class ApiAdapterExtension implements ServiceExtension { + @Inject private Monitor monitor; + @Inject private ContractNegotiationObservable negotiationObservable; + @Inject private WebService webService; + @Inject private ContractNegotiationService contractNegotiationService; + @Inject private RemoteMessageDispatcherRegistry dispatcher; + @Inject private EndpointDataReferenceReceiverRegistry receiverRegistry; + @Inject private DataManagementApiConfiguration apiConfig; + @Inject private TransferProcessService transferProcessService; + + @Override + public String name() { + return "Control Plane Adapter Extension"; + } + + @Override + public void initialize(ServiceExtensionContext context) { + ApiAdapterConfig config = new ApiAdapterConfig(context); + ListenerService listenerService = new ListenerService(); + InMemoryMessageBus messageBus = + new InMemoryMessageBus( + monitor, listenerService, config.getInMemoryMessageBusThreadNumber()); + + ResultService resultService = new ResultService(config.getDefaultSyncRequestTimeout()); + ErrorResultService errorResultService = new ErrorResultService(monitor, messageBus); + ContractNotificationSyncService contractSyncService = + new ContractInMemorySyncService(new LockMap()); + ContractDataStore contractDataStore = new InMemoryContractDataStore(); + DataTransferInitializer dataTransferInitializer = + new DataTransferInitializer(monitor, transferProcessService); + ContractNotificationHandler contractNotificationHandler = + new ContractNotificationHandler( + monitor, + messageBus, + contractSyncService, + contractNegotiationService, + dataTransferInitializer); + ContractNegotiationHandler contractNegotiationHandler = + getContractNegotiationHandler( + monitor, contractNegotiationService, messageBus, contractDataStore); + DataRefNotificationSyncService dataRefSyncService = + new DataRefInMemorySyncService(new LockMap()); + DataReferenceHandler dataReferenceHandler = + new DataReferenceHandler(monitor, messageBus, dataRefSyncService); + + listenerService.addListener(Channel.INITIAL, contractNegotiationHandler); + listenerService.addListener(Channel.CONTRACT_CONFIRMATION, contractNotificationHandler); + listenerService.addListener(Channel.DATA_REFERENCE, dataReferenceHandler); + listenerService.addListener(Channel.RESULT, resultService); + listenerService.addListener(Channel.DLQ, errorResultService); + + initHttpController(monitor, messageBus, resultService, config); + initContractNegotiationListener( + monitor, + negotiationObservable, + messageBus, + contractSyncService, + contractDataStore, + dataTransferInitializer); + initDataReferenceReciever(monitor, messageBus, dataRefSyncService); + } + + private void initHttpController( + Monitor monitor, + InMemoryMessageBus messageBus, + ResultService resultService, + ApiAdapterConfig config) { + webService.registerResource( + apiConfig.getContextAlias(), + new HttpController(monitor, resultService, messageBus, config)); + } + + private ContractNegotiationHandler getContractNegotiationHandler( + Monitor monitor, + ContractNegotiationService contractNegotiationService, + InMemoryMessageBus messageBus, + ContractDataStore contractDataStore) { + return new ContractNegotiationHandler( + monitor, + messageBus, + contractNegotiationService, + new CatalogServiceImpl(dispatcher), + contractDataStore, + new ExpiringMap<>()); + } + + private void initDataReferenceReciever( + Monitor monitor, + InMemoryMessageBus messageBus, + DataRefNotificationSyncService dataRefSyncService) { + EndpointDataReferenceReceiver dataReferenceReceiver = + new EndpointDataReferenceReceiverImpl(monitor, messageBus, dataRefSyncService); + receiverRegistry.registerReceiver(dataReferenceReceiver); + } + + private void initContractNegotiationListener( + Monitor monitor, + ContractNegotiationObservable negotiationObservable, + InMemoryMessageBus messageBus, + ContractNotificationSyncService contractSyncService, + ContractDataStore contractDataStore, + DataTransferInitializer dataTransferInitializer) { + ContractNegotiationListener contractNegotiationListener = + new ContractNegotiationListenerImpl( + monitor, messageBus, contractSyncService, contractDataStore, dataTransferInitializer); + if (nonNull(negotiationObservable)) { + negotiationObservable.registerListener(contractNegotiationListener); + } + } +} 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..4837d9694 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/HttpController.java @@ -0,0 +1,122 @@ +/* + * 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; + +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.DataReferenceRetrievalDto; +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.MessageBus; +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 MessageBus messageBus; + private final ApiAdapterConfig config; + + @GET + @Path("sync/{assetId}") + public Response getAssetSynchronous( + @PathParam("assetId") String assetId, @QueryParam("providerUrl") String providerUrl) { + + if (invalidParams(assetId, providerUrl)) { + return badRequestResponse(); + } + + String traceId = initiateProcess(assetId, providerUrl); + + try { + ProcessData processData = resultService.pull(traceId); + + if (Objects.isNull(processData)) { + return notFoundResponse(); + } + if (Objects.nonNull(processData.getErrorStatus())) { + return errorResponse(processData); + } + if (Objects.nonNull(processData.getEndpointDataReference())) { + return okResponse(processData); + } + return timeoutResponse(); + } catch (InterruptedException e) { + monitor.severe("InterruptedException", e); + return notFoundResponse(); + } + } + + private Response badRequestResponse() { + return Response.status(Response.Status.BAD_REQUEST) + .entity("AssetId or providerUrl is empty!") + .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 = getProcessData(assetId, providerUrl); + Message message = + new DataReferenceRetrievalDto(processData, config.getDefaultMessageRetryNumber()); + messageBus.send(Channel.INITIAL, message); + return message.getTraceId(); + } + + private Response notFoundResponse() { + return Response.status(Response.Status.NOT_FOUND) + .entity(Response.Status.NOT_FOUND.getReasonPhrase()) + .build(); + } + + private Response errorResponse(ProcessData processData) { + return Response.status(processData.getErrorStatus()) + .entity(processData.getErrorMessage()) + .build(); + } + + private Response okResponse(ProcessData processData) { + return Response.status(Response.Status.OK) + .entity(processData.getEndpointDataReference()) + .build(); + } + + private Response timeoutResponse() { + return Response.status(Response.Status.REQUEST_TIMEOUT) + .entity(Response.Status.REQUEST_TIMEOUT.getReasonPhrase()) + .build(); + } + + private ProcessData getProcessData(String assetId, String providerUrl) { + return ProcessData.builder() + .assetId(assetId) + .provider(providerUrl) + .contractAgreementCacheOn(config.isContractAgreementCacheOn()) + .catalogExpiryTime(config.getCatalogExpireAfterTime()) + .build(); + } +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/dto/DataReferenceRetrievalDto.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/dto/DataReferenceRetrievalDto.java new file mode 100644 index 000000000..de9931616 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/dto/DataReferenceRetrievalDto.java @@ -0,0 +1,23 @@ +/* + * 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 net.catenax.edc.cp.adapter.messaging.Message; + +public class DataReferenceRetrievalDto extends Message { + public DataReferenceRetrievalDto(ProcessData payload, int retryLimit) { + super(payload, retryLimit); + } +} 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..182898eaf --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/dto/ProcessData.java @@ -0,0 +1,48 @@ +/* + * 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; + +import jakarta.ws.rs.core.Response; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import org.eclipse.dataspaceconnector.spi.types.domain.edr.EndpointDataReference; + +@Getter +@ToString +@Builder +public class ProcessData { + private final long timestamp = currentTimeMillis(); + + // request data + private final String assetId; + private final String provider; + private String contractOfferId; + private int catalogExpiryTime; + private boolean contractAgreementCacheOn; + + // 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; +} 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..260000136 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ConfigurationException.java @@ -0,0 +1,21 @@ +/* + * 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 { + public ConfigurationException(String message) { + super(message); + } +} 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..a082a16dd --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ExternalRequestException.java @@ -0,0 +1,25 @@ +/* + * 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 { + 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 new file mode 100644 index 000000000..825e032cd --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/exception/ResourceNotFoundException.java @@ -0,0 +1,25 @@ +/* + * 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 { + 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 new file mode 100644 index 000000000..4ec21b96b --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/Channel.java @@ -0,0 +1,23 @@ +/* + * 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 { + INITIAL, + CONTRACT_CONFIRMATION, + DATA_REFERENCE, + RESULT, + DLQ +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/InMemoryMessageBus.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/InMemoryMessageBus.java new file mode 100644 index 000000000..a0993be5e --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/InMemoryMessageBus.java @@ -0,0 +1,69 @@ +/* + * 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; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; + +public class InMemoryMessageBus implements MessageBus { + private final Monitor monitor; + private final ListenerService listenerService; + private final ScheduledExecutorService executorService; + + public InMemoryMessageBus(Monitor monitor, ListenerService listenerService, int threadPoolSize) { + this.monitor = monitor; + this.listenerService = listenerService; + executorService = Executors.newScheduledThreadPool(threadPoolSize); + } + + @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.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())); + sendMessageToDlq(message, e); + return true; + } + long delayTime = message.unsucceeded(); + 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/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..21bba1b42 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/Listener.java @@ -0,0 +1,19 @@ +/* + * 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

> { + void process(P 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..4418bd661 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/ListenerService.java @@ -0,0 +1,44 @@ +/* + * 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; + +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..ce8bd228b --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/Message.java @@ -0,0 +1,62 @@ +/* + * 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; +import java.util.concurrent.atomic.AtomicInteger; +import lombok.Getter; + +public abstract class Message { + @Getter private final String traceId; + @Getter private final T payload; + private final AtomicInteger errorNumber = new AtomicInteger(); + private final int retryLimit; + @Getter private Exception finalException; + + public Message(String traceId, T payload, int retryLimit) { + this.traceId = traceId; + this.retryLimit = retryLimit; + this.payload = payload; + } + + public Message(T payload, int retryLimit) { + this.traceId = UUID.randomUUID().toString(); + this.retryLimit = retryLimit; + this.payload = payload; + } + + protected long unsucceeded() { + errorNumber.incrementAndGet(); + return getDelayTime(); + } + + protected void clearErrors() { + errorNumber.set(0); + } + + protected boolean canRetry() { + return errorNumber.get() < retryLimit; + } + + protected void setFinalException(Exception e) { + this.finalException = e; + } + + 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/MessageBus.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/MessageBus.java new file mode 100644 index 000000000..a8c17dc42 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/messaging/MessageBus.java @@ -0,0 +1,19 @@ +/* + * 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 MessageBus { + void send(Channel name, Message message); +} 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..5ba7bcb0e --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/ContractAgreementData.java @@ -0,0 +1,42 @@ +/* + * 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; +import lombok.Setter; +import org.eclipse.dataspaceconnector.spi.types.domain.contract.agreement.ContractAgreement; + +@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; + + public static ContractAgreementData from(ContractAgreement agreement) { + ContractAgreementData data = new ContractAgreementData(); + data.setId(agreement.getId()); + data.setAssetId(agreement.getAssetId()); + 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..71e3876b3 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/ContractDataStore.java @@ -0,0 +1,23 @@ +/* + * 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; + +public interface ContractDataStore { + 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 new file mode 100644 index 000000000..be6c692db --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractdatastore/InMemoryContractDataStore.java @@ -0,0 +1,37 @@ +/* + * 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; +import java.util.Map; +import org.eclipse.dataspaceconnector.spi.types.domain.contract.agreement.ContractAgreement; + +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 ContractAgreementData get(String assetId, String provider) { + return contractMap.get(getKey(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 new file mode 100644 index 000000000..4daf8b08a --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnegotiation/ContractNegotiationHandler.java @@ -0,0 +1,141 @@ +/* + * 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; +import java.util.Collections; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.ExecutionException; +import lombok.RequiredArgsConstructor; +import net.catenax.edc.cp.adapter.dto.DataReferenceRetrievalDto; +import net.catenax.edc.cp.adapter.dto.ProcessData; +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.MessageBus; +import net.catenax.edc.cp.adapter.process.contractdatastore.ContractAgreementData; +import net.catenax.edc.cp.adapter.process.contractdatastore.ContractDataStore; +import net.catenax.edc.cp.adapter.util.ExpiringMap; +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.query.QuerySpec; +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; +import org.jetbrains.annotations.Nullable; + +@RequiredArgsConstructor +public class ContractNegotiationHandler implements Listener { + private final Monitor monitor; + private final MessageBus messageBus; + private final ContractNegotiationService contractNegotiationService; + private final CatalogService catalogService; + private final ContractDataStore contractDataStore; + private final ExpiringMap catalogCache; + + @Override + public void process(DataReferenceRetrievalDto dto) { + monitor.info( + String.format( + "[%s] RequestHandler: input request: [%s]", dto.getTraceId(), dto.getPayload())); + ProcessData processData = dto.getPayload(); + + ContractAgreementData contractData = getCachedContractData(dto); + if (Objects.nonNull(contractData) && isContractValid(contractData)) { + monitor.info(String.format("[%s] ContractAgreement taken from cache.", dto.getTraceId())); + dto.getPayload().setContractAgreementId(contractData.getId()); + dto.getPayload().setContractConfirmed(true); + messageBus.send(Channel.CONTRACT_CONFIRMATION, dto); + return; + } + + ContractOffer contractOffer = + findContractOffer( + processData.getAssetId(), + processData.getProvider(), + processData.getCatalogExpiryTime()); + + String contractNegotiationId = + initializeContractNegotiation( + contractOffer, dto.getPayload().getProvider(), dto.getTraceId()); + dto.getPayload().setContractNegotiationId(contractNegotiationId); + + messageBus.send(Channel.CONTRACT_CONFIRMATION, dto); + } + + @Nullable + private ContractAgreementData getCachedContractData(DataReferenceRetrievalDto dto) { + return dto.getPayload().isContractAgreementCacheOn() + ? contractDataStore.get(dto.getPayload().getAssetId(), dto.getPayload().getProvider()) + : null; + } + + 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, int catalogExpiryTime) { + Catalog catalog = getCatalog(providerUrl, catalogExpiryTime); + 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, int catalogExpiryTime) { + Catalog catalog = catalogCache.get(providerUrl, catalogExpiryTime); + if (Objects.nonNull(catalog)) { + return catalog; + } + + try { + catalog = catalogService.getByProviderUrl(providerUrl, QuerySpec.max()).get(); + catalogCache.put(providerUrl, catalog); + return catalog; + } catch (InterruptedException | ExecutionException e) { + throw new ExternalRequestException("Could not retrieve contract offer.", 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/contractnotification/ContractInMemorySyncService.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractInMemorySyncService.java new file mode 100644 index 000000000..3b40911f1 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractInMemorySyncService.java @@ -0,0 +1,94 @@ +/* + * 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; + +import java.util.HashMap; +import java.util.Map; +import net.catenax.edc.cp.adapter.dto.DataReferenceRetrievalDto; +import net.catenax.edc.cp.adapter.util.LockMap; + +public class ContractInMemorySyncService implements ContractNotificationSyncService { + private final Map dtoMap = new HashMap<>(); + private final Map contractInfoMap = new HashMap<>(); + private final LockMap locks; + + public ContractInMemorySyncService(LockMap locks) { + this.locks = locks; + } + + @Override + public DataReferenceRetrievalDto exchangeConfirmedContract( + String negotiationId, String agreementId) { + locks.lock(negotiationId); + DataReferenceRetrievalDto dto = dtoMap.get(negotiationId); + if (isNull(dto)) { + contractInfoMap.put( + negotiationId, new ContractInfo(agreementId, ContractInfo.ContractState.CONFIRMED)); + } + locks.unlock(negotiationId); + return dto; + } + + @Override + public DataReferenceRetrievalDto exchangeDeclinedContract(String negotiationId) { + locks.lock(negotiationId); + DataReferenceRetrievalDto dto = dtoMap.get(negotiationId); + if (isNull(dto)) { + contractInfoMap.put(negotiationId, new ContractInfo(ContractInfo.ContractState.DECLINED)); + } + locks.unlock(negotiationId); + return dto; + } + + @Override + public DataReferenceRetrievalDto exchangeErrorContract(String negotiationId) { + locks.lock(negotiationId); + DataReferenceRetrievalDto dto = dtoMap.get(negotiationId); + if (isNull(dto)) { + contractInfoMap.put(negotiationId, new ContractInfo(ContractInfo.ContractState.ERROR)); + } + + locks.unlock(negotiationId); + return dto; + } + + @Override + public ContractInfo exchangeDto(DataReferenceRetrievalDto dto) { + String negotiationId = dto.getPayload().getContractNegotiationId(); + + locks.lock(negotiationId); + ContractInfo contractInfo = contractInfoMap.get(negotiationId); + if (isNull(contractInfo)) { + dtoMap.put(negotiationId, dto); + } + + locks.unlock(negotiationId); + return contractInfo; + } + + @Override + public void removeContractInfo(String negotiationId) { + contractInfoMap.remove(negotiationId); + locks.removeLock(negotiationId); + } + + @Override + public void removeDto(String negotiationId) { + dtoMap.remove(negotiationId); + locks.removeLock(negotiationId); + } +} 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..46bd038d3 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractInfo.java @@ -0,0 +1,49 @@ +/* + * 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; + +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; + } +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractNegotiationListenerImpl.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractNegotiationListenerImpl.java new file mode 100644 index 000000000..fc3bd55f0 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractNegotiationListenerImpl.java @@ -0,0 +1,79 @@ +package net.catenax.edc.cp.adapter.process.contractnotification; + +import static java.util.Objects.isNull; + +import jakarta.ws.rs.core.Response; +import lombok.RequiredArgsConstructor; +import net.catenax.edc.cp.adapter.dto.DataReferenceRetrievalDto; +import net.catenax.edc.cp.adapter.messaging.Channel; +import net.catenax.edc.cp.adapter.messaging.MessageBus; +import net.catenax.edc.cp.adapter.process.contractdatastore.ContractDataStore; +import org.eclipse.dataspaceconnector.spi.contract.negotiation.observe.ContractNegotiationListener; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.types.domain.contract.negotiation.ContractNegotiation; + +@RequiredArgsConstructor +public class ContractNegotiationListenerImpl implements 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 MessageBus messageBus; + private final ContractNotificationSyncService syncService; + private final ContractDataStore contractDataStore; + private final DataTransferInitializer dataTransfer; + + @Override + public void confirmed(ContractNegotiation negotiation) { + monitor.info("ContractConfirmationHandler: received ContractConfirmation event"); + String negotiationId = negotiation.getId(); + String agreementId = negotiation.getContractAgreement().getId(); + DataReferenceRetrievalDto dto = + syncService.exchangeConfirmedContract(negotiationId, agreementId); + if (isNull(dto)) { + return; + } + dto.getPayload().setContractAgreementId(agreementId); + initiateDataTransfer(dto); + contractDataStore.add( + dto.getPayload().getAssetId(), + dto.getPayload().getProvider(), + negotiation.getContractAgreement()); + syncService.removeDto(negotiationId); + } + + @Override + public void declined(ContractNegotiation negotiation) { + monitor.info("ContractConfirmationHandler: received ContractDeclined event"); + String contractNegotiationId = negotiation.getId(); + DataReferenceRetrievalDto dto = syncService.exchangeDeclinedContract(contractNegotiationId); + if (isNull(dto)) { + return; + } + sendErrorResult(dto, CONTRACT_DECLINED_MESSAGE); + syncService.removeDto(contractNegotiationId); + } + + @Override + public void failed(ContractNegotiation negotiation) { + monitor.info("ContractConfirmationHandler: received ContractError event"); + String contractNegotiationId = negotiation.getId(); + DataReferenceRetrievalDto dto = syncService.exchangeErrorContract(contractNegotiationId); + if (isNull(dto)) { + return; + } + sendErrorResult(dto, CONTRACT_ERROR_MESSAGE); + syncService.removeDto(contractNegotiationId); + } + + public void initiateDataTransfer(DataReferenceRetrievalDto dto) { + dataTransfer.initiate(dto); + dto.getPayload().setContractConfirmed(true); + messageBus.send(Channel.DATA_REFERENCE, dto); + } + + private void sendErrorResult(DataReferenceRetrievalDto dto, String errorMessage) { + dto.getPayload().setErrorMessage(errorMessage); + dto.getPayload().setErrorStatus(Response.Status.BAD_GATEWAY); + messageBus.send(Channel.RESULT, dto); + } +} 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 new file mode 100644 index 000000000..42d6c89cb --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractNotificationHandler.java @@ -0,0 +1,91 @@ +/* + * 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; +import static java.util.Objects.isNull; + +import java.util.Objects; +import lombok.RequiredArgsConstructor; +import net.catenax.edc.cp.adapter.dto.DataReferenceRetrievalDto; +import net.catenax.edc.cp.adapter.messaging.Channel; +import net.catenax.edc.cp.adapter.messaging.Listener; +import net.catenax.edc.cp.adapter.messaging.MessageBus; +import org.eclipse.dataspaceconnector.api.datamanagement.contractnegotiation.service.ContractNegotiationService; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.types.domain.contract.negotiation.ContractNegotiation; +import org.eclipse.dataspaceconnector.spi.types.domain.contract.negotiation.ContractNegotiationStates; + +@RequiredArgsConstructor +public class ContractNotificationHandler implements Listener { + 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 MessageBus messageBus; + private final ContractNotificationSyncService syncService; + private final ContractNegotiationService contractNegotiationService; + private final DataTransferInitializer dataTransfer; + + @Override + public void process(DataReferenceRetrievalDto dto) { + monitor.info( + String.format("[%s] ContractConfirmationHandler: received message.", dto.getTraceId())); + String contractNegotiationId = dto.getPayload().getContractNegotiationId(); + + if (dto.getPayload().isContractConfirmed()) { + initiateDataTransfer(dto); + return; + } + + ContractNegotiation contractNegotiation = + contractNegotiationService.findbyId(contractNegotiationId); + if (isContractConfirmed(contractNegotiation)) { + dto.getPayload().setContractAgreementId(contractNegotiation.getContractAgreement().getId()); + initiateDataTransfer(dto); + return; + } + + ContractInfo contractInfo = syncService.exchangeDto(dto); + if (isNull(contractInfo)) { + return; + } + + if (contractInfo.isConfirmed()) { + dto.getPayload().setContractAgreementId(contractInfo.getContractAgreementId()); + initiateDataTransfer(dto); + } else { + sendErrorResult( + dto, contractInfo.isDeclined() ? CONTRACT_DECLINED_MESSAGE : CONTRACT_ERROR_MESSAGE); + } + syncService.removeContractInfo(contractNegotiationId); + } + + public void initiateDataTransfer(DataReferenceRetrievalDto dto) { + dataTransfer.initiate(dto); + dto.getPayload().setContractConfirmed(true); + messageBus.send(Channel.DATA_REFERENCE, dto); + } + + private void sendErrorResult(DataReferenceRetrievalDto dto, String errorMessage) { + dto.getPayload().setErrorMessage(errorMessage); + dto.getPayload().setErrorStatus(Status.BAD_GATEWAY); + messageBus.send(Channel.RESULT, dto); + } + + 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/contractnotification/ContractNotificationSyncService.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractNotificationSyncService.java new file mode 100644 index 000000000..b3582b9dd --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractNotificationSyncService.java @@ -0,0 +1,32 @@ +/* + * 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.dto.DataReferenceRetrievalDto; + +public interface ContractNotificationSyncService { + DataReferenceRetrievalDto exchangeConfirmedContract( + String contractNegotiationId, String contractAgreementId); + + DataReferenceRetrievalDto exchangeDeclinedContract(String contractNegotiationId); + + DataReferenceRetrievalDto exchangeErrorContract(String contractNegotiationId); + + ContractInfo exchangeDto(DataReferenceRetrievalDto dto); + + void removeContractInfo(String key); + + void removeDto(String key); +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/DataTransferInitializer.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/DataTransferInitializer.java new file mode 100644 index 000000000..29ba2b3bd --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/contractnotification/DataTransferInitializer.java @@ -0,0 +1,56 @@ +package net.catenax.edc.cp.adapter.process.contractnotification; + +import lombok.RequiredArgsConstructor; +import net.catenax.edc.cp.adapter.dto.DataReferenceRetrievalDto; +import net.catenax.edc.cp.adapter.exception.ExternalRequestException; +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.DataAddress; +import org.eclipse.dataspaceconnector.spi.types.domain.transfer.DataRequest; +import org.eclipse.dataspaceconnector.spi.types.domain.transfer.TransferType; + +@RequiredArgsConstructor +public class DataTransferInitializer { + private final Monitor monitor; + private final TransferProcessService transferProcessService; + + public void initiate(DataReferenceRetrievalDto dto) { + monitor.info( + String.format( + "[%s] ContractConfirmationHandler: transfer init - start.", dto.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(dto.getTraceId()) + .assetId(dto.getPayload().getAssetId()) + .contractId(dto.getPayload().getContractAgreementId()) + .connectorId("provider") + .connectorAddress(dto.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", dto.getTraceId())); + if (result.failed()) { + throwDataRefRequestException(dto); + } + } + + private void throwDataRefRequestException(DataReferenceRetrievalDto dto) { + throw new ExternalRequestException( + String.format( + "Data reference initial request failed! AssetId: %s", dto.getPayload().getAssetId())); + } +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataRefInMemorySyncService.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataRefInMemorySyncService.java new file mode 100644 index 000000000..277e96b34 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataRefInMemorySyncService.java @@ -0,0 +1,65 @@ +/* + * 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; + +import java.util.HashMap; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import net.catenax.edc.cp.adapter.dto.DataReferenceRetrievalDto; +import net.catenax.edc.cp.adapter.util.LockMap; +import org.eclipse.dataspaceconnector.spi.types.domain.edr.EndpointDataReference; + +@RequiredArgsConstructor +public class DataRefInMemorySyncService implements DataRefNotificationSyncService { + private final Map dtoMap = new HashMap<>(); + private final Map dataReferenceMap = new HashMap<>(); + private final LockMap locks; + + public EndpointDataReference exchangeDto(DataReferenceRetrievalDto dto, String agreementId) { + locks.lock(agreementId); + EndpointDataReference dataReference = dataReferenceMap.get(agreementId); + if (isNull(dataReference)) { + dtoMap.put(agreementId, dto); + } + locks.unlock(agreementId); + return dataReference; + } + + @Override + public DataReferenceRetrievalDto exchangeDataReference( + EndpointDataReference dataReference, String agreementId) { + locks.lock(agreementId); + DataReferenceRetrievalDto dto = dtoMap.get(agreementId); + if (isNull(dto)) { + dataReferenceMap.put(agreementId, dataReference); + } + locks.unlock(agreementId); + return dto; + } + + @Override + public void removeDataReference(String agreementId) { + dataReferenceMap.remove(agreementId); + locks.removeLock(agreementId); + } + + @Override + public void removeDto(String agreementId) { + dtoMap.remove(agreementId); + locks.removeLock(agreementId); + } +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataRefNotificationSyncService.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataRefNotificationSyncService.java new file mode 100644 index 000000000..f74bb9b58 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataRefNotificationSyncService.java @@ -0,0 +1,29 @@ +/* + * 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.dto.DataReferenceRetrievalDto; +import org.eclipse.dataspaceconnector.spi.types.domain.edr.EndpointDataReference; + +public interface DataRefNotificationSyncService { + EndpointDataReference exchangeDto(DataReferenceRetrievalDto dto, String contractAgreementId); + + DataReferenceRetrievalDto exchangeDataReference( + EndpointDataReference dataReference, String contractAgreementId); + + void removeDataReference(String contractAgreementId); + + void removeDto(String key); +} 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..590e77881 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/DataReferenceHandler.java @@ -0,0 +1,48 @@ +/* + * 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; + +import lombok.RequiredArgsConstructor; +import net.catenax.edc.cp.adapter.dto.DataReferenceRetrievalDto; +import net.catenax.edc.cp.adapter.messaging.Channel; +import net.catenax.edc.cp.adapter.messaging.Listener; +import net.catenax.edc.cp.adapter.messaging.MessageBus; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.types.domain.edr.EndpointDataReference; + +@RequiredArgsConstructor +public class DataReferenceHandler implements Listener { + private final Monitor monitor; + private final MessageBus messageBus; + private final DataRefNotificationSyncService syncService; + + @Override + public void process(DataReferenceRetrievalDto dto) { + String contractAgreementId = dto.getPayload().getContractAgreementId(); + monitor.info(String.format("[%s] DataReference message received.", dto.getTraceId())); + + EndpointDataReference dataReference = syncService.exchangeDto(dto, contractAgreementId); + if (isNull(dataReference)) { + return; + } + + dto.getPayload().setEndpointDataReference(dataReference); + messageBus.send(Channel.RESULT, dto); + syncService.removeDataReference(contractAgreementId); + monitor.info(String.format("[%s] DataReference message processed.", dto.getTraceId())); + } +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/EndpointDataReferenceReceiverImpl.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/EndpointDataReferenceReceiverImpl.java new file mode 100644 index 000000000..adb96c13b --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/process/datareference/EndpointDataReferenceReceiverImpl.java @@ -0,0 +1,39 @@ +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.dto.DataReferenceRetrievalDto; +import net.catenax.edc.cp.adapter.messaging.Channel; +import net.catenax.edc.cp.adapter.messaging.MessageBus; +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 EndpointDataReferenceReceiverImpl implements EndpointDataReferenceReceiver { + private final Monitor monitor; + private final MessageBus messageBus; + private final DataRefNotificationSyncService syncService; + + @Override + public CompletableFuture> send(@NotNull EndpointDataReference dataReference) { + String contractAgreementId = dataReference.getProperties().get("cid"); + monitor.info(String.format("DataReference received, contractAgr.: %s", contractAgreementId)); + + DataReferenceRetrievalDto dto = + syncService.exchangeDataReference(dataReference, contractAgreementId); + if (isNull(dto)) { + return CompletableFuture.completedFuture(Result.success()); + } + dto.getPayload().setEndpointDataReference(dataReference); + messageBus.send(Channel.RESULT, dto); + syncService.removeDto(contractAgreementId); + + monitor.info(String.format("[%s] DataReference processed.", dto.getTraceId())); + return CompletableFuture.completedFuture(Result.success()); + } +} 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..1a1527a97 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/service/ErrorResultService.java @@ -0,0 +1,67 @@ +/* + * 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; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import lombok.RequiredArgsConstructor; +import net.catenax.edc.cp.adapter.dto.DataReferenceRetrievalDto; +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.MessageBus; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; + +@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 MessageBus messageBus; + + @Override + public void process(DataReferenceRetrievalDto dto) { + dto.getPayload().setErrorMessage(getErrorMessage(dto)); + dto.getPayload() + .setErrorStatus( + statusOfException.getOrDefault( + dto.getFinalException().getClass(), Response.Status.INTERNAL_SERVER_ERROR)); + log(dto); + messageBus.send(Channel.RESULT, dto); + } + + private String getErrorMessage(DataReferenceRetrievalDto dto) { + return Objects.nonNull(dto.getFinalException()) + ? dto.getFinalException().getMessage() + : "Unrecognized Exception."; + } + + private void log(DataReferenceRetrievalDto dto) { + monitor.info( + String.format( + "[%s] Sending ERROR message to RESULT channel: %s / %s ", + dto.getTraceId(), + dto.getPayload().getErrorMessage(), + dto.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 new file mode 100644 index 000000000..63a12a65f --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/service/ResultService.java @@ -0,0 +1,66 @@ +/* + * 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; +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.dto.DataReferenceRetrievalDto; +import net.catenax.edc.cp.adapter.dto.ProcessData; +import net.catenax.edc.cp.adapter.messaging.Listener; + +@RequiredArgsConstructor +public class ResultService implements Listener { + private final int CAPACITY = 1; + private final int DEFAULT_TIMEOUT; + 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 { + if (!results.containsKey(id)) { + initiate(id); + } + ProcessData result = results.get(id).poll(timeout, unit); + results.remove(id); + return result; + } + + @Override + public void process(DataReferenceRetrievalDto dto) { + if (isNull(dto) || isNull(dto.getPayload())) { + throw new IllegalArgumentException(); + } + add(dto.getTraceId(), dto.getPayload()); + } + + private void add(String id, ProcessData ProcessData) { + if (!results.containsKey(id)) { + initiate(id); + } + results.get(id).add(ProcessData); + } + + private void initiate(String id) { + results.put(id, new ArrayBlockingQueue<>(CAPACITY)); + } +} diff --git a/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/util/ExpiringMap.java b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/util/ExpiringMap.java new file mode 100644 index 000000000..40a1a4695 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/util/ExpiringMap.java @@ -0,0 +1,71 @@ +/* + * 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.time.Instant; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +public class ExpiringMap { + private final Map map = new HashMap<>(); + private final Map entryTimeMap = new HashMap<>(); + private long expireAfter = 60 * 60; + + public ExpiringMap() {} + + public ExpiringMap(long expireAfter) { + this.expireAfter = expireAfter; + } + + public void put(K key, V value) { + map.put(key, value); + entryTimeMap.put(key, now()); + } + + public V get(K key) { + return get(key, expireAfter); + } + + public V get(K key, long expireAfter) { + V value = map.get(key); + + if (Objects.isNull(value)) { + return null; + } + + Long entryTime = entryTimeMap.get(key); + if (Objects.isNull(entryTime)) { + map.remove(key); + return null; + } + + if (entryTime + expireAfter < now()) { + remove(key); + return null; + } + + return value; + } + + public void remove(K key) { + map.remove(key); + entryTimeMap.remove(key); + } + + private long now() { + return Instant.now().getEpochSecond(); + } +} 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..f3c020c6e --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/main/java/net/catenax/edc/cp/adapter/util/LockMap.java @@ -0,0 +1,44 @@ +/* + * 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; +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/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..6d37b24d2 --- /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 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 +# + +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..00fd1241bc92df47f31b1bd81b84a84baa6fc880 GIT binary patch literal 205271 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�+M2z3LRL2cG4`^QzFm3dA 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

ji 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 IA@E`3zpWZF@c;k- literal 0 HcmV?d00001 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..8d19ee06e --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/HttpControllerTest.java @@ -0,0 +1,64 @@ +/* + * 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 org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.when; + +import jakarta.ws.rs.core.Response; +import net.catenax.edc.cp.adapter.messaging.MessageBus; +import net.catenax.edc.cp.adapter.service.ResultService; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.Mockito; + +public class HttpControllerTest { + @Mock ApiAdapterConfig config = Mockito.mock(ApiAdapterConfig.class); + Integer RETRY_NUMBER = 3; + + @Test + public void getAssetSynchronous_shouldReturnBadRequestIfNoAssetIdParam() { + // given + Monitor monitor = Mockito.mock(Monitor.class); + ResultService resultService = Mockito.mock(ResultService.class); + MessageBus messageBus = Mockito.mock(MessageBus.class); + when(config.getDefaultMessageRetryNumber()).thenReturn(RETRY_NUMBER); + HttpController httpController = new HttpController(monitor, resultService, messageBus, config); + + // 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); + MessageBus messageBus = Mockito.mock(MessageBus.class); + ApiAdapterConfig config = Mockito.mock(ApiAdapterConfig.class); + when(config.getDefaultMessageRetryNumber()).thenReturn(RETRY_NUMBER); + HttpController httpController = new HttpController(monitor, resultService, messageBus, config); + + // 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/InMemoryMessageBusTest.java b/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/messaging/InMemoryMessageBusTest.java new file mode 100644 index 000000000..21e464ab0 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/messaging/InMemoryMessageBusTest.java @@ -0,0 +1,68 @@ +/* + * 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 org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +import net.catenax.edc.cp.adapter.dto.DataReferenceRetrievalDto; +import net.catenax.edc.cp.adapter.dto.ProcessData; +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 InMemoryMessageBusTest { + @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 DataReferenceRetrievalDto(null, 3); + when(listenerService.getListener(any())).thenReturn(listener); + MessageBus messageBus = new InMemoryMessageBus(monitor, listenerService, 3); + + // when + messageBus.send(Channel.INITIAL, message); + + // then + Thread.sleep(50); + verify(listener, times(1)).process(any(DataReferenceRetrievalDto.class)); + } + + @Test + public void send_shouldCallListenerWithRetryOnException() throws InterruptedException { + // given + Message message = new DataReferenceRetrievalDto(null, 3); + when(listenerService.getListener(any())).thenReturn(listener); + doThrow(new IllegalStateException()).doNothing().when(listener).process(any()); + MessageBus messageBus = new InMemoryMessageBus(monitor, listenerService, 3); + + // when + messageBus.send(Channel.INITIAL, message); + + // then + Thread.sleep(1000); + verify(listener, times(2)).process(any(DataReferenceRetrievalDto.class)); + } +} 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..c9aa12dbd --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/contractnegotiation/ContractNegotiationHandlerTest.java @@ -0,0 +1,182 @@ +/* + * 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 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.DataReferenceRetrievalDto; +import net.catenax.edc.cp.adapter.dto.ProcessData; +import net.catenax.edc.cp.adapter.messaging.Message; +import net.catenax.edc.cp.adapter.messaging.MessageBus; +import net.catenax.edc.cp.adapter.process.contractdatastore.ContractAgreementData; +import net.catenax.edc.cp.adapter.process.contractdatastore.ContractDataStore; +import net.catenax.edc.cp.adapter.util.ExpiringMap; +import org.eclipse.dataspaceconnector.api.datamanagement.catalog.service.CatalogService; +import org.eclipse.dataspaceconnector.api.datamanagement.contractnegotiation.service.ContractNegotiationService; +import org.eclipse.dataspaceconnector.policy.model.Policy; +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 MessageBus messageBus; + @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, + messageBus, + contractNegotiationService, + catalogService, + contractDataStore, + new ExpiringMap<>()); + + when(contractDataStore.get(anyString(), anyString())) + .thenReturn(getValidContractAgreementData()); + + // when + contractNegotiationHandler.process(new DataReferenceRetrievalDto(getProcessData(), 3)); + + // then + verify(contractNegotiationService, times(0)).initiateNegotiation(any()); + verify(messageBus, times(1)).send(any(), any(Message.class)); + } + + @Test + public void process_shouldInitializeContractNegotiationWhenCachedContractExpired() { + // given + ContractNegotiationHandler contractNegotiationHandler = + new ContractNegotiationHandler( + monitor, + messageBus, + contractNegotiationService, + catalogService, + contractDataStore, + new ExpiringMap<>()); + + when(contractDataStore.get(anyString(), anyString())) + .thenReturn(getExpiredContractAgreementData()); + when(catalogService.getByProviderUrl(anyString(), any())) + .thenReturn(CompletableFuture.completedFuture(getCatalog())); + when(contractNegotiationService.initiateNegotiation(any())) + .thenReturn(getContractNegotiation()); + + // when + contractNegotiationHandler.process(new DataReferenceRetrievalDto(getProcessData(), 3)); + + // then + verify(contractNegotiationService, times(1)).initiateNegotiation(any()); + verify(messageBus, times(1)).send(any(), any(Message.class)); + } + + @Test + public void process_shouldInitiateContractNegotiationAndSendDtoFurtherIfCacheEmpty() { + // given + ContractNegotiationHandler contractNegotiationHandler = + new ContractNegotiationHandler( + monitor, + messageBus, + contractNegotiationService, + catalogService, + contractDataStore, + new ExpiringMap<>()); + + when(contractDataStore.get(anyString(), anyString())).thenReturn(null); + when(catalogService.getByProviderUrl(anyString(), any())) + .thenReturn(CompletableFuture.completedFuture(getCatalog())); + when(contractNegotiationService.initiateNegotiation(any())) + .thenReturn(getContractNegotiation()); + + // when + contractNegotiationHandler.process(new DataReferenceRetrievalDto(getProcessData(), 3)); + + // then + verify(contractNegotiationService, times(1)).initiateNegotiation(any()); + verify(messageBus, times(1)).send(any(), any(Message.class)); + } + + private ProcessData getProcessData() { + return ProcessData.builder() + .assetId("assetId") + .provider("provider") + .catalogExpiryTime(30) + .contractAgreementCacheOn(true) + .build(); + } + + 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) + .policy(Policy.Builder.newInstance().build()) + .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/contractnotification/ContractNegotiationListenerTest.java b/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractNegotiationListenerTest.java new file mode 100644 index 000000000..bb1298f60 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractNegotiationListenerTest.java @@ -0,0 +1,140 @@ +package net.catenax.edc.cp.adapter.process.contractnotification; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; +import static org.mockito.Mockito.times; + +import jakarta.ws.rs.core.Response; +import net.catenax.edc.cp.adapter.dto.DataReferenceRetrievalDto; +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.MessageBus; +import net.catenax.edc.cp.adapter.process.contractdatastore.ContractDataStore; +import org.eclipse.dataspaceconnector.policy.model.Policy; +import org.eclipse.dataspaceconnector.spi.contract.negotiation.observe.ContractNegotiationListener; +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.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 ContractNegotiationListenerTest { + @Mock Monitor monitor; + @Mock MessageBus messageBus; + @Mock ContractNotificationSyncService syncService; + @Mock ContractDataStore contractDataStore; + @Mock DataTransferInitializer dataTransfer; + + @BeforeEach + void init() { + MockitoAnnotations.openMocks(this); + } + + @Test + public void confirmed_shouldNotInitiateTransferIfMessageNotAvailable() { + // given + ContractNegotiationListener listener = + new ContractNegotiationListenerImpl( + monitor, messageBus, syncService, contractDataStore, dataTransfer); + ContractNegotiation contractNegotiation = getConfirmedContractNegotiation(); + + // when + listener.confirmed(contractNegotiation); + + // then + verify(syncService, times(1)).exchangeConfirmedContract(any(), any()); + verify(dataTransfer, times(0)).initiate(any()); + verify(messageBus, times(0)).send(eq(Channel.DATA_REFERENCE), any(Message.class)); + } + + @Test + public void confirmed_shouldInitiateTransferIfMessageIsAvailable() { + // given + when(syncService.exchangeConfirmedContract(any(), any())) + .thenReturn(new DataReferenceRetrievalDto(getProcessData(), 3)); + verify(dataTransfer, times(0)).initiate(any()); + + ContractNegotiationListener listener = + new ContractNegotiationListenerImpl( + monitor, messageBus, syncService, contractDataStore, dataTransfer); + ContractNegotiation contractNegotiation = getConfirmedContractNegotiation(); + + // when + listener.confirmed(contractNegotiation); + + // then + verify(dataTransfer, times(1)).initiate(any()); + verify(messageBus, times(1)).send(eq(Channel.DATA_REFERENCE), any(Message.class)); + } + + @Test + public void preDeclined_shouldSendErrorResultIfMessageIsAvailable() { + // given + when(syncService.exchangeDeclinedContract(any())) + .thenReturn(new DataReferenceRetrievalDto(getProcessData(), 3)); + ContractNegotiationListener listener = + new ContractNegotiationListenerImpl( + monitor, messageBus, syncService, contractDataStore, dataTransfer); + ContractNegotiation contractNegotiation = getConfirmedContractNegotiation(); + + // when + listener.declined(contractNegotiation); + + // then + ArgumentCaptor messageArg = + ArgumentCaptor.forClass(DataReferenceRetrievalDto.class); + verify(messageBus, 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(syncService.exchangeErrorContract(any())) + .thenReturn(new DataReferenceRetrievalDto(getProcessData(), 3)); + ContractNegotiationListener listener = + new ContractNegotiationListenerImpl( + monitor, messageBus, syncService, contractDataStore, dataTransfer); + ContractNegotiation contractNegotiation = getConfirmedContractNegotiation(); + + // when + listener.failed(contractNegotiation); + + // then + ArgumentCaptor messageArg = + ArgumentCaptor.forClass(DataReferenceRetrievalDto.class); + verify(messageBus, 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()) + .id("contractNegotiationId") + .counterPartyId("counterPartyId") + .counterPartyAddress("counterPartyAddress") + .protocol("protocol") + .contractAgreement( + ContractAgreement.Builder.newInstance() + .id("contractAgreementId") + .providerAgentId("providerAgentId") + .consumerAgentId("consumerAgentId") + .assetId("assetId") + .policy(Policy.Builder.newInstance().build()) + .build()) + .build(); + } + + private ProcessData getProcessData() { + return ProcessData.builder().assetId("assetId").provider("provider").build(); + } +} 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 new file mode 100644 index 000000000..01809a35a --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/contractnotification/ContractNotificationHandlerTest.java @@ -0,0 +1,141 @@ +/* + * 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 org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +import net.catenax.edc.cp.adapter.dto.DataReferenceRetrievalDto; +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.MessageBus; +import org.eclipse.dataspaceconnector.api.datamanagement.contractnegotiation.service.ContractNegotiationService; +import org.eclipse.dataspaceconnector.policy.model.Policy; +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 ContractNotificationHandlerTest { + @Mock Monitor monitor; + @Mock MessageBus messageBus; + @Mock ContractNegotiationService contractNegotiationService; + @Mock ContractNotificationSyncService syncService; + @Mock DataTransferInitializer dataTransfer; + + @BeforeEach + void init() { + MockitoAnnotations.openMocks(this); + } + + @Test + public void process_shouldNotInitiateTransferWhenNoContractNotification() { + // given + ContractNotificationHandler contractNotificationHandler = + new ContractNotificationHandler( + monitor, messageBus, syncService, contractNegotiationService, dataTransfer); + DataReferenceRetrievalDto dto = new DataReferenceRetrievalDto(getProcessData(), 3); + + // when + contractNotificationHandler.process(dto); + + // then + verify(syncService, times(1)).exchangeDto(any()); + verify(dataTransfer, times(0)).initiate(any()); + verify(messageBus, times(0)).send(eq(Channel.DATA_REFERENCE), any(Message.class)); + } + + @Test + public void process_shouldInitiateTransferWhenContractConfirmedFromCache() { + // given + ContractNotificationHandler contractNotificationHandler = + new ContractNotificationHandler( + monitor, messageBus, syncService, contractNegotiationService, dataTransfer); + DataReferenceRetrievalDto dto = new DataReferenceRetrievalDto(getProcessData(), 3); + dto.getPayload().setContractConfirmed(true); + + // when + contractNotificationHandler.process(dto); + + // then + verify(dataTransfer, times(1)).initiate(any()); + verify(messageBus, times(1)).send(eq(Channel.DATA_REFERENCE), any(Message.class)); + } + + @Test + public void process_shouldInitiateTransferWhenContractAlreadyConfirmedAtProvider() { + // given + when(contractNegotiationService.findbyId(any())).thenReturn(getConfirmedContractNegotiation()); + ContractNotificationHandler contractNotificationHandler = + new ContractNotificationHandler( + monitor, messageBus, syncService, contractNegotiationService, dataTransfer); + DataReferenceRetrievalDto dto = new DataReferenceRetrievalDto(getProcessData(), 3); + + // when + contractNotificationHandler.process(dto); + + // then + verify(syncService, times(0)).exchangeDto(any()); + verify(dataTransfer, times(1)).initiate(any()); + verify(messageBus, times(1)).send(eq(Channel.DATA_REFERENCE), any(Message.class)); + } + + @Test + public void process_shouldInitiateTransferWhenContractConfirmedByNotification() { + // given + when(syncService.exchangeDto(any())) + .thenReturn( + new ContractInfo("confirmedContractAgreementId", ContractInfo.ContractState.CONFIRMED)); + ContractNotificationHandler contractNotificationHandler = + new ContractNotificationHandler( + monitor, messageBus, syncService, contractNegotiationService, dataTransfer); + DataReferenceRetrievalDto dto = new DataReferenceRetrievalDto(getProcessData(), 3); + + // when + contractNotificationHandler.process(dto); + + // then + verify(dataTransfer, times(1)).initiate(any()); + verify(messageBus, times(1)).send(eq(Channel.DATA_REFERENCE), any(Message.class)); + verify(syncService, times(1)).removeContractInfo(any()); + } + + 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") + .policy(Policy.Builder.newInstance().build()) + .build()) + .build(); + } + + private ProcessData getProcessData() { + return ProcessData.builder().assetId("assetId").provider("provider").build(); + } +} 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..1f5eca794 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/datareference/DataReferenceHandlerTest.java @@ -0,0 +1,85 @@ +/* + * 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 org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +import net.catenax.edc.cp.adapter.dto.DataReferenceRetrievalDto; +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.MessageBus; +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 MessageBus messageBus; + @Mock DataRefNotificationSyncService notificationSyncService; + + @BeforeEach + void init() { + MockitoAnnotations.openMocks(this); + } + + @Test + public void process_shouldNotSendResultWhenDataReferenceNotAvailable() { + // given + DataReferenceHandler dataReferenceHandler = + new DataReferenceHandler(monitor, messageBus, notificationSyncService); + DataReferenceRetrievalDto dto = new DataReferenceRetrievalDto(getProcessData(), 3); + + // when + dataReferenceHandler.process(dto); + + // then + verify(notificationSyncService, times(1)).exchangeDto(eq(dto), any()); + verify(messageBus, times(0)).send(eq(Channel.RESULT), any(Message.class)); + } + + @Test + public void process_shouldSendResultWhenDataReferenceIsAvailable() { + // given + when(notificationSyncService.exchangeDto(any(), any())).thenReturn(getEndpointDataReference()); + DataReferenceHandler dataReferenceHandler = + new DataReferenceHandler(monitor, messageBus, notificationSyncService); + DataReferenceRetrievalDto dto = new DataReferenceRetrievalDto(getProcessData(), 3); + + // when + dataReferenceHandler.process(dto); + + // then + verify(messageBus, times(1)).send(eq(Channel.RESULT), any(Message.class)); + verify(notificationSyncService, times(1)).removeDataReference(any()); + } + + private EndpointDataReference getEndpointDataReference() { + return EndpointDataReference.Builder.newInstance() + .endpoint("endpoint") + .authCode("authCode") + .authKey("authKey") + .build(); + } + + private ProcessData getProcessData() { + return ProcessData.builder().assetId("assetId").provider("provider").build(); + } +} diff --git a/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/datareference/EndpointDataReferenceReceiverTest.java b/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/datareference/EndpointDataReferenceReceiverTest.java new file mode 100644 index 000000000..b957d4257 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/process/datareference/EndpointDataReferenceReceiverTest.java @@ -0,0 +1,70 @@ +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.DataReferenceRetrievalDto; +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.MessageBus; +import org.eclipse.dataspaceconnector.spi.monitor.Monitor; +import org.eclipse.dataspaceconnector.spi.transfer.edr.EndpointDataReferenceReceiver; +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 EndpointDataReferenceReceiverTest { + @Mock Monitor monitor; + @Mock MessageBus messageBus; + @Mock DataRefNotificationSyncService syncService; + + @BeforeEach + void init() { + MockitoAnnotations.openMocks(this); + } + + @Test + public void send_shouldNotSendResultWhenMessageNotAvailable() { + // given + EndpointDataReferenceReceiver referenceReceiver = + new EndpointDataReferenceReceiverImpl(monitor, messageBus, syncService); + + // when + referenceReceiver.send(getEndpointDataReference()); + + // then + verify(messageBus, times(0)).send(eq(Channel.RESULT), any(Message.class)); + } + + @Test + public void send_shouldSendResultWhenMessageIsAvailable() { + // given + DataReferenceRetrievalDto dto = new DataReferenceRetrievalDto(getProcessData(), 3); + when(syncService.exchangeDataReference(any(), any())).thenReturn(dto); + EndpointDataReferenceReceiver referenceReceiver = + new EndpointDataReferenceReceiverImpl(monitor, messageBus, syncService); + + // when + referenceReceiver.send(getEndpointDataReference()); + + // then + verify(messageBus, times(1)).send(eq(Channel.RESULT), any(Message.class)); + verify(syncService, times(1)).removeDto(any()); + } + + private EndpointDataReference getEndpointDataReference() { + return EndpointDataReference.Builder.newInstance() + .endpoint("endpoint") + .authCode("authCode") + .authKey("authKey") + .build(); + } + + private ProcessData getProcessData() { + return ProcessData.builder().assetId("assetId").provider("provider").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..282c88a30 --- /dev/null +++ b/edc-extensions/control-plane-adapter/src/test/java/net/catenax/edc/cp/adapter/service/ResultServiceTest.java @@ -0,0 +1,119 @@ +/* + * 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 org.junit.jupiter.api.Assertions.fail; + +import java.util.concurrent.TimeUnit; +import net.catenax.edc.cp.adapter.dto.DataReferenceRetrievalDto; +import net.catenax.edc.cp.adapter.dto.ProcessData; +import org.eclipse.dataspaceconnector.spi.types.domain.edr.EndpointDataReference; +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(20); + String endpointDataRefId = "456"; + DataReferenceRetrievalDto dto = getDto(endpointDataRefId); + ProcessData processData; + + // when + resultService.process(dto); + processData = resultService.pull(dto.getTraceId(), 200, TimeUnit.MILLISECONDS); + + // then + Assertions.assertEquals(endpointDataRefId, processData.getEndpointDataReference().getId()); + } + + @Test + public void pull_shouldReturnDataReferenceWhenMessageOccursSecond() throws InterruptedException { + // given + ResultService resultService = new ResultService(20); + String endpointDataRefId = "456"; + DataReferenceRetrievalDto dto = getDto(endpointDataRefId); + ProcessData processData; + + // when + processMessageWithDelay(resultService, dto); + processData = resultService.pull(dto.getTraceId(), 1000, TimeUnit.MILLISECONDS); + + // then + Assertions.assertEquals(endpointDataRefId, processData.getEndpointDataReference().getId()); + } + + @Test + public void pull_shouldReturnNullOnTimeout() throws InterruptedException { + // given + ResultService resultService = new ResultService(20); + + // when + ProcessData processData = resultService.pull("123", 500, TimeUnit.MILLISECONDS); + + // then + Assertions.assertNull(processData); + } + + @Test + public void process_shouldThrowIllegalArgumentExceptionIfNoDataPayload() { + // given + ResultService resultService = new ResultService(20); + DataReferenceRetrievalDto dto = new DataReferenceRetrievalDto(null, 3); + + // when then + try { + resultService.process(dto); + fail("Method should throw IllegalArgumentException"); + } catch (IllegalArgumentException ignored) { + } + } + + private void processMessageWithDelay(ResultService resultService, DataReferenceRetrievalDto dto) { + new Thread( + () -> { + sleep(400); + resultService.process(dto); + }) + .start(); + } + + private DataReferenceRetrievalDto getDto(String endpointDataRefId) { + DataReferenceRetrievalDto dto = new DataReferenceRetrievalDto(getProcessData(), 3); + dto.getPayload() + .setEndpointDataReference( + EndpointDataReference.Builder.newInstance() + .id(endpointDataRefId) + .endpoint("e") + .authCode("c") + .authKey("k") + .build()); + return dto; + } + + private void sleep(long milisec) { + try { + Thread.sleep(milisec); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + private ProcessData getProcessData() { + return ProcessData.builder().assetId("assetId").provider("provider").build(); + } +} diff --git a/edc-extensions/cx-oauth2/pom.xml b/edc-extensions/cx-oauth2/pom.xml index b82d0b05f..ee5ae5402 100644 --- a/edc-extensions/cx-oauth2/pom.xml +++ b/edc-extensions/cx-oauth2/pom.xml @@ -1,24 +1,30 @@ edc-extensions - net.catenax.edc.extensions - 0.1.2 + org.eclipse.tractusx.edc.extensions + 0.1.3 4.0.0 @@ -97,19 +103,12 @@ org.eclipse.dataspaceconnector oauth2-spi - - - org.eclipse.dataspaceconnector - jwt-spi - - org.eclipse.dataspaceconnector jwt-spi - org.projectlombok @@ -128,6 +127,24 @@ okhttp + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + org.junit.jupiter 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 deleted file mode 100644 index 9fe15001c..000000000 --- a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/OAuth2Extension.java +++ /dev/null @@ -1,73 +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.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.runtime.metamodel.annotation.EdcSetting; -import org.eclipse.dataspaceconnector.runtime.metamodel.annotation.Inject; -import org.eclipse.dataspaceconnector.runtime.metamodel.annotation.Provides; -import org.eclipse.dataspaceconnector.runtime.metamodel.annotation.Requires; -import org.eclipse.dataspaceconnector.spi.EdcException; -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.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 deleted file mode 100644 index b1c4dac0f..000000000 --- a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/OAuth2IdentityService.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * 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 deleted file mode 100644 index 70712fbd7..000000000 --- a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/JsonWebKey.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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 deleted file mode 100644 index a4cc93716..000000000 --- a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/JsonWebKeySet.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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 deleted file mode 100644 index 80b31255e..000000000 --- a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/JwkPublicKeyResolver.java +++ /dev/null @@ -1,189 +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.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 deleted file mode 100644 index 72edfb19f..000000000 --- a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/PublicKeyHolder.java +++ /dev/null @@ -1,25 +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.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 deleted file mode 100644 index de73e6e3b..000000000 --- a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/PublicKeyReader.java +++ /dev/null @@ -1,22 +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.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 deleted file mode 100644 index c6098b70b..000000000 --- a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwk/RsaPublicKeyReader.java +++ /dev/null @@ -1,71 +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.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 deleted file mode 100644 index 3e7f90950..000000000 --- a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/DapsJwtDecorator.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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 java.util.Map; -import org.eclipse.dataspaceconnector.spi.jwt.JwtDecorator; - -public class DapsJwtDecorator implements JwtDecorator { - - @Override - public Map claims() { - return Map.of( - "@context", - "https://w3id.org/idsa/contexts/context.jsonld", - "@type", - "ids:DatRequestToken"); - } - - @Override - public Map headers() { - return Map.of(); - } -} 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 deleted file mode 100644 index 4168f7e63..000000000 --- a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/ExpJwtDecorator.java +++ /dev/null @@ -1,41 +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.oauth2.jwt.decorator; - -import java.time.Clock; -import java.time.Duration; -import java.util.Date; -import java.util.Map; -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 Map claims() { - return Map.of( - JWTClaimNames.EXPIRATION_TIME, - Date.from(clock.instant().plusSeconds(expiration.toSeconds()))); - } - - @Override - public Map headers() { - return Map.of(); - } -} 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 deleted file mode 100644 index d8d34d7bb..000000000 --- a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/IatJwtDecorator.java +++ /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 API and Implementation - * - */ -package net.catenax.edc.oauth2.jwt.decorator; - -import java.time.Clock; -import java.util.Date; -import java.util.Map; -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 Map claims() { - return Map.of(JWTClaimNames.ISSUED_AT, Date.from(clock.instant())); - } - - @Override - public Map headers() { - return Map.of(); - } -} 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 deleted file mode 100644 index 673ca7372..000000000 --- a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/IdsAudJwtDecorator.java +++ /dev/null @@ -1,33 +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.oauth2.jwt.decorator; - -import java.util.Collections; -import java.util.Map; -import lombok.RequiredArgsConstructor; -import org.eclipse.dataspaceconnector.spi.jwt.JwtDecorator; - -@RequiredArgsConstructor -public class IdsAudJwtDecorator implements JwtDecorator { - - @Override - public Map claims() { - return Map.of(JWTClaimNames.AUDIENCE, Collections.singletonList("idsc:IDS_CONNECTORS_ALL")); - } - - @Override - public Map headers() { - return Map.of(); - } -} 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 deleted file mode 100644 index 32303d14a..000000000 --- a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/IssJwtDecorator.java +++ /dev/null @@ -1,35 +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.oauth2.jwt.decorator; - -import java.util.Map; -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 Map claims() { - return Map.of(JWTClaimNames.ISSUER, clientId); - } - - @Override - public Map headers() { - return Map.of(); - } -} diff --git a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/JWTClaimNames.java b/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/JWTClaimNames.java deleted file mode 100644 index f1649b8c0..000000000 --- a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/JWTClaimNames.java +++ /dev/null @@ -1,24 +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.oauth2.jwt.decorator; - -public final class JWTClaimNames { - public static final String ISSUER = "iss"; - public static final String SUBJECT = "sub"; - public static final String AUDIENCE = "aud"; - public static final String EXPIRATION_TIME = "exp"; - public static final String NOT_BEFORE = "nbf"; - public static final String ISSUED_AT = "iat"; - public static final String JWT_ID = "jti"; -} 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 deleted file mode 100644 index 39590d1a6..000000000 --- a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/JtiJwtDecorator.java +++ /dev/null @@ -1,31 +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.oauth2.jwt.decorator; - -import java.util.Map; -import java.util.UUID; -import org.eclipse.dataspaceconnector.spi.jwt.JwtDecorator; - -public class JtiJwtDecorator implements JwtDecorator { - - @Override - public Map claims() { - return Map.of(JWTClaimNames.JWT_ID, UUID.randomUUID().toString()); - } - - @Override - public Map headers() { - return Map.of(); - } -} 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 deleted file mode 100644 index 7f551fcc4..000000000 --- a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/JwtDecoratorExtension.java +++ /dev/null @@ -1,136 +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.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.runtime.metamodel.annotation.EdcSetting; -import org.eclipse.dataspaceconnector.runtime.metamodel.annotation.Inject; -import org.eclipse.dataspaceconnector.runtime.metamodel.annotation.Provides; -import org.eclipse.dataspaceconnector.runtime.metamodel.annotation.Requires; -import org.eclipse.dataspaceconnector.spi.EdcException; -import org.eclipse.dataspaceconnector.spi.security.CertificateResolver; -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 deleted file mode 100644 index b57d9737d..000000000 --- a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/Oauth2JwtDecoratorRegistryRegistryImpl.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2022 Amadeus - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 - * - * SPDX-License-Identifier: Apache-2.0 - * - * Contributors: - * Amadeus - Initial 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 deleted file mode 100644 index 951fd8791..000000000 --- a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/SubJwtDecorator.java +++ /dev/null @@ -1,34 +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.oauth2.jwt.decorator; - -import java.util.Map; -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 Map claims() { - return Map.of(JWTClaimNames.SUBJECT, subject); - } - - @Override - public Map headers() { - return Map.of(); - } -} 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 deleted file mode 100644 index 9b8903936..000000000 --- a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/decorator/X5tJwtDecorator.java +++ /dev/null @@ -1,51 +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.oauth2.jwt.decorator; - -import com.nimbusds.jose.util.Base64URL; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Base64; -import java.util.Map; -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; - - 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); - } - } - - @Override - public Map claims() { - return Map.of(); - } - - @Override - public Map headers() { - return Map.of("x5t", new Base64URL(sha1Base64Fingerprint(encodedCertificate))); - } -} 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 deleted file mode 100644 index b4c4f3a27..000000000 --- a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/generator/JwtTokenGenerationService.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2022 Amadeus - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 - * - * SPDX-License-Identifier: Apache-2.0 - * - * Contributors: - * Amadeus - initial API and implementation - * 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.Map.Entry; -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(); - - for (JwtDecorator decorator : decorators) { - for (Entry claim : decorator.claims().entrySet()) { - claimsBuilder.claim(claim.getKey(), claim.getValue()); - } - headerBuilder.customParams(decorator.headers()); - } - - 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 deleted file mode 100644 index 0fcdf35f6..000000000 --- a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/generator/JwtTokenGenerationServiceExtension.java +++ /dev/null @@ -1,48 +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.oauth2.jwt.generator; - -import java.security.PrivateKey; -import lombok.NonNull; -import lombok.Setter; -import org.eclipse.dataspaceconnector.runtime.metamodel.annotation.EdcSetting; -import org.eclipse.dataspaceconnector.runtime.metamodel.annotation.Inject; -import org.eclipse.dataspaceconnector.runtime.metamodel.annotation.Provides; -import org.eclipse.dataspaceconnector.runtime.metamodel.annotation.Requires; -import org.eclipse.dataspaceconnector.spi.jwt.TokenGenerationService; -import org.eclipse.dataspaceconnector.spi.security.PrivateKeyResolver; -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 deleted file mode 100644 index 3f46930ff..000000000 --- a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/AudValidationRule.java +++ /dev/null @@ -1,68 +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.oauth2.jwt.validation; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; -import net.catenax.edc.oauth2.jwt.decorator.JWTClaimNames; -import org.eclipse.dataspaceconnector.spi.iam.ClaimToken; -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.NotNull; -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( - @NotNull ClaimToken toVerify, @Nullable Map additional) { - final List errors = new ArrayList<>(); - - final Object claim = toVerify.getClaims().get(JWTClaimNames.AUDIENCE); - if (!(claim instanceof List)) { - errors.add("Audience claim is not a list"); - } else { - final List audiences = (List) claim; - 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(); - } else { - return Result.failure(errors); - } - } -} 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 deleted file mode 100644 index 36abd84bf..000000000 --- a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/ExpValidationRule.java +++ /dev/null @@ -1,71 +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.oauth2.jwt.validation; - -import static java.time.ZoneOffset.UTC; - -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 net.catenax.edc.oauth2.jwt.decorator.JWTClaimNames; -import org.eclipse.dataspaceconnector.spi.iam.ClaimToken; -import org.eclipse.dataspaceconnector.spi.jwt.TokenValidationRule; -import org.eclipse.dataspaceconnector.spi.result.Result; -import org.jetbrains.annotations.NotNull; -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( - @NotNull ClaimToken toVerify, @Nullable Map additional) { - final List errors = new ArrayList<>(); - - final Instant now = clock.instant(); - final Object claim = toVerify.getClaims().get(JWTClaimNames.EXPIRATION_TIME); - if (!(claim instanceof Date)) { - errors.add("Required expiration (exp) claim is missing in token"); - } else { - final Date expires = (Date) claim; - if (now.isAfter(convertToUtcTime(expires))) { - errors.add("Token has expired (exp)"); - } - } - - if (errors.isEmpty()) { - return Result.success(); - } else { - return Result.failure(errors); - } - } - - 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 deleted file mode 100644 index a5185e559..000000000 --- a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/IatValidationRule.java +++ /dev/null @@ -1,76 +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.oauth2.jwt.validation; - -import static java.time.ZoneOffset.UTC; - -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 net.catenax.edc.oauth2.jwt.decorator.JWTClaimNames; -import org.eclipse.dataspaceconnector.spi.iam.ClaimToken; -import org.eclipse.dataspaceconnector.spi.jwt.TokenValidationRule; -import org.eclipse.dataspaceconnector.spi.result.Result; -import org.jetbrains.annotations.NotNull; -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( - @NotNull ClaimToken toVerify, @Nullable Map additional) { - List errors = new ArrayList<>(); - - Instant now = clock.instant(); - final Object issuedAtClaim = toVerify.getClaims().get(JWTClaimNames.ISSUED_AT); - if (!(issuedAtClaim instanceof Date)) { - errors.add("Issued at (iat) claim is missing in token"); - } else { - final Object expirationTimeClaim = toVerify.getClaims().get(JWTClaimNames.EXPIRATION_TIME); - if (expirationTimeClaim instanceof Date) { - Date expirationTime = (Date) expirationTimeClaim; - Date issuedAt = (Date) issuedAtClaim; - if (issuedAt.toInstant().isAfter(expirationTime.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(); - } else { - return Result.failure(errors); - } - } - - 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 deleted file mode 100644 index 6a61cd6bf..000000000 --- a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/IdsValidationRule.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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 java.util.Map; -import org.eclipse.dataspaceconnector.spi.EdcException; -import org.eclipse.dataspaceconnector.spi.iam.ClaimToken; -import org.eclipse.dataspaceconnector.spi.jwt.TokenValidationRule; -import org.eclipse.dataspaceconnector.spi.result.Result; -import org.jetbrains.annotations.NotNull; -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( - @NotNull ClaimToken toVerify, @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(additional, issuerConnector.toString(), securityProfile); - - } else { - throw new EdcException("Missing required additional information for IDS token validation"); - } - } - - private Result verifyTokenIds( - Map claims, String issuerConnector, @Nullable String securityProfile) { - - // 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 - } - - return Result.success(); - } -} 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 deleted file mode 100644 index c4e5d9ff4..000000000 --- a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/JwtValidationExtension.java +++ /dev/null @@ -1,167 +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.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.runtime.metamodel.annotation.EdcSetting; -import org.eclipse.dataspaceconnector.runtime.metamodel.annotation.Inject; -import org.eclipse.dataspaceconnector.runtime.metamodel.annotation.Provides; -import org.eclipse.dataspaceconnector.runtime.metamodel.annotation.Requires; -import org.eclipse.dataspaceconnector.spi.jwt.TokenValidationService; -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 deleted file mode 100644 index 88672b8cb..000000000 --- a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/NbfValidationRule.java +++ /dev/null @@ -1,75 +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.oauth2.jwt.validation; - -import static java.time.ZoneOffset.UTC; - -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 net.catenax.edc.oauth2.jwt.decorator.JWTClaimNames; -import org.eclipse.dataspaceconnector.spi.iam.ClaimToken; -import org.eclipse.dataspaceconnector.spi.jwt.TokenValidationRule; -import org.eclipse.dataspaceconnector.spi.result.Result; -import org.jetbrains.annotations.NotNull; -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( - @NotNull ClaimToken toVerify, @Nullable Map additional) { - final List errors = new ArrayList<>(); - - final Instant now = clock.instant(); - final Instant leewayNow = now.plusSeconds(notBeforeValidationLeeway.toSeconds()); - - final Object claim = toVerify.getClaims().get(JWTClaimNames.NOT_BEFORE); - if (!(claim instanceof Date)) { - errors.add("Required not before (nbf) claim is missing in token"); - } else { - final Date notBefore = (Date) claim; - 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(); - } else { - return Result.failure(errors); - } - } - - 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 deleted file mode 100644 index b779a96cc..000000000 --- a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/Oauth2ValidationRulesRegistryImpl.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2022 Amadeus - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 - * - * SPDX-License-Identifier: Apache-2.0 - * - * Contributors: - * Amadeus - Initial 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 deleted file mode 100644 index 6a7e4e17f..000000000 --- a/edc-extensions/cx-oauth2/src/main/java/net/catenax/edc/oauth2/jwt/validation/TokenValidationServiceImpl.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (c) 2022 Amadeus - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 - * - * SPDX-License-Identifier: Apache-2.0 - * - * Contributors: - * Amadeus - initial API and implementation - * - */ - -package 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(); - var claimToken = ClaimToken.Builder.newInstance().claims(claimsSet.getClaims()).build(); - - final List errors = - rulesRegistry.getRules().stream() - .map(r -> r.checkRule(claimToken, 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/java/org/eclipse/tractusx/edc/oauth2/CXOAuth2Extension.java b/edc-extensions/cx-oauth2/src/main/java/org/eclipse/tractusx/edc/oauth2/CXOAuth2Extension.java new file mode 100644 index 000000000..4acb18f5a --- /dev/null +++ b/edc-extensions/cx-oauth2/src/main/java/org/eclipse/tractusx/edc/oauth2/CXOAuth2Extension.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.eclipse.tractusx.edc.oauth2; + +import java.util.Map; +import org.eclipse.dataspaceconnector.iam.oauth2.spi.CredentialsRequestAdditionalParametersProvider; +import org.eclipse.dataspaceconnector.runtime.metamodel.annotation.Provider; +import org.eclipse.dataspaceconnector.spi.system.ServiceExtension; + +public class CXOAuth2Extension implements ServiceExtension { + + @Override + public String name() { + return "CX OAuth2"; + } + + @Provider + public CredentialsRequestAdditionalParametersProvider + credentialsRequestAdditionalParametersProvider() { + return tokenParameters -> Map.of("resource", tokenParameters.getAudience()); + } +} 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 index 0303255e4..2e60a45f0 100644 --- 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 @@ -1,17 +1,21 @@ # # Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2021,2022 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. # # This program and the accompanying materials are made available under the # terms of the Apache License, Version 2.0 which is available at # https://www.apache.org/licenses/LICENSE-2.0 # -# SPDX-License-Identifier: Apache-2.0 -# -# Contributors: -# Mercedes-Benz Tech Innovation GmbH - Initial ServiceExtension file +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. # +# SPDX-License-Identifier: Apache-2.0 # -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 + +org.eclipse.tractusx.edc.oauth2.CXOAuth2Extension 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 deleted file mode 100644 index 8d90b5000..000000000 --- a/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwk/JwkPublicKeyResolverTest.java +++ /dev/null @@ -1,204 +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.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 deleted file mode 100644 index b530ba980..000000000 --- a/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwk/RsaPublicKeyReaderTest.java +++ /dev/null @@ -1,84 +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.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 deleted file mode 100644 index 99ed5ae93..000000000 --- a/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/DapsJwtDecoratorTest.java +++ /dev/null @@ -1,32 +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 Test - * - */ -package net.catenax.edc.oauth2.jwt.decorator; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -class DapsJwtDecoratorTest { - - @Test - void decorate() { - final DapsJwtDecorator decorator = new DapsJwtDecorator(); - - Assertions.assertTrue(decorator.claims().containsKey("@context")); - Assertions.assertEquals( - "https://w3id.org/idsa/contexts/context.jsonld", decorator.claims().get("@context")); - - Assertions.assertTrue(decorator.claims().containsKey("@type")); - Assertions.assertEquals("ids:DatRequestToken", decorator.claims().get("@type")); - } -} 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 deleted file mode 100644 index fe5a3e903..000000000 --- a/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/ExpJwtDecoratorTest.java +++ /dev/null @@ -1,44 +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 Test - * - */ -package net.catenax.edc.oauth2.jwt.decorator; - -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); - - Mockito.when(clock.instant()).thenReturn(Instant.ofEpochSecond(0)); - - Assertions.assertTrue(decorator.claims().containsKey(JWTClaimNames.EXPIRATION_TIME)); - Assertions.assertEquals( - new Date(100000), decorator.claims().get(JWTClaimNames.EXPIRATION_TIME)); - } - - @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 deleted file mode 100644 index 898903e62..000000000 --- a/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/IatJwtDecoratorTest.java +++ /dev/null @@ -1,41 +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 Test - * - */ -package net.catenax.edc.oauth2.jwt.decorator; - -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); - - Mockito.when(clock.instant()).thenReturn(Instant.ofEpochSecond(0)); - - Assertions.assertTrue(decorator.claims().containsKey(JWTClaimNames.ISSUED_AT)); - Assertions.assertEquals(new Date(0), decorator.claims().get(JWTClaimNames.ISSUED_AT)); - } - - @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 deleted file mode 100644 index b9180e5ec..000000000 --- a/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/IdsAudJwtDecoratorTest.java +++ /dev/null @@ -1,32 +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 Test - * - */ - -package net.catenax.edc.oauth2.jwt.decorator; - -import java.util.List; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -class IdsAudJwtDecoratorTest { - - @Test - void decorate() { - final String expectedAudience = "idsc:IDS_CONNECTORS_ALL"; - final IdsAudJwtDecorator decorator = new IdsAudJwtDecorator(); - - Assertions.assertTrue(decorator.claims().containsKey(JWTClaimNames.AUDIENCE)); - Assertions.assertEquals( - List.of(expectedAudience), decorator.claims().get(JWTClaimNames.AUDIENCE)); - } -} 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 deleted file mode 100644 index fba6a1d52..000000000 --- a/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/IssJwtDecoratorTest.java +++ /dev/null @@ -1,35 +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 Test - * - */ -package net.catenax.edc.oauth2.jwt.decorator; - -import java.util.UUID; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -class IssJwtDecoratorTest { - - @Test - void decorate() { - final String expectedIssuer = UUID.randomUUID().toString(); - final IssJwtDecorator decorator = new IssJwtDecorator(expectedIssuer); - - Assertions.assertTrue(decorator.claims().containsKey(JWTClaimNames.ISSUER)); - Assertions.assertEquals(expectedIssuer, decorator.claims().get(JWTClaimNames.ISSUER)); - } - - @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 deleted file mode 100644 index 681113b38..000000000 --- a/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/JtiJwtDecoratorTest.java +++ /dev/null @@ -1,27 +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 Test - * - */ -package net.catenax.edc.oauth2.jwt.decorator; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -class JtiJwtDecoratorTest { - - @Test - void decorate() { - final JtiJwtDecorator decorator = new JtiJwtDecorator(); - - Assertions.assertTrue(decorator.claims().containsKey(JWTClaimNames.JWT_ID)); - } -} 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 deleted file mode 100644 index e11b78a38..000000000 --- a/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/Oauth2JwtDecoratorRegistryRegistryImplTest.java +++ /dev/null @@ -1,107 +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 Test - * - */ -package net.catenax.edc.oauth2.jwt.decorator; - -import java.util.Arrays; -import java.util.Map; -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 Map claims() { - return null; - } - - @Override - public Map headers() { - return null; - } - } - - private static class B_JwtDecorator implements JwtDecorator { - @Override - public Map claims() { - return null; - } - - @Override - public Map headers() { - return null; - } - } - - private static class C_JwtDecorator implements JwtDecorator { - @Override - public Map claims() { - return null; - } - - @Override - public Map headers() { - return null; - } - } -} 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 deleted file mode 100644 index 4dc4f32eb..000000000 --- a/edc-extensions/cx-oauth2/src/test/java/net/catenax/edc/oauth2/jwt/decorator/SubJwtDecoratorTest.java +++ /dev/null @@ -1,35 +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 Test - * - */ -package net.catenax.edc.oauth2.jwt.decorator; - -import java.util.UUID; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -class SubJwtDecoratorTest { - - @Test - void decorate() { - final String expectedSubject = UUID.randomUUID().toString(); - final SubJwtDecorator decorator = new SubJwtDecorator(expectedSubject); - - Assertions.assertTrue(decorator.claims().containsKey(JWTClaimNames.SUBJECT)); - Assertions.assertEquals(expectedSubject, decorator.claims().get(JWTClaimNames.SUBJECT)); - } - - @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 0d1dad90d..3ab523166 100644 --- a/edc-extensions/data-encryption/pom.xml +++ b/edc-extensions/data-encryption/pom.xml @@ -1,24 +1,30 @@ edc-extensions - net.catenax.edc.extensions - 0.1.2 + org.eclipse.tractusx.edc.extensions + 0.1.3 4.0.0 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 deleted file mode 100644 index 0bc71c673..000000000 --- a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/algorithms/CryptoAlgorithm.java +++ /dev/null @@ -1,34 +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.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/data/CryptoData.java b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/data/CryptoData.java deleted file mode 100644 index 697a22ede..000000000 --- a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/data/CryptoData.java +++ /dev/null @@ -1,20 +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.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 deleted file mode 100644 index c6abab6e6..000000000 --- a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/data/CryptoDataFactory.java +++ /dev/null @@ -1,29 +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.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/DecryptedData.java b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/data/DecryptedData.java deleted file mode 100644 index 903823757..000000000 --- a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/data/DecryptedData.java +++ /dev/null @@ -1,16 +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.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 deleted file mode 100644 index 033f0ceeb..000000000 --- a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/data/EncryptedData.java +++ /dev/null @@ -1,16 +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.data.encryption.data; - -public interface EncryptedData extends CryptoData {} diff --git a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/encrypter/AesDataEncrypterConfiguration.java b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/encrypter/AesDataEncrypterConfiguration.java deleted file mode 100644 index 3357f7eff..000000000 --- a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/encrypter/AesDataEncrypterConfiguration.java +++ /dev/null @@ -1,26 +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.data.encryption.encrypter; - -import java.time.Duration; -import lombok.NonNull; -import lombok.Value; - -@Value -public class AesDataEncrypterConfiguration { - @NonNull String keySetAlias; - boolean cachingEnabled; - @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 deleted file mode 100644 index 382c8086a..000000000 --- a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/encrypter/DataEncrypterFactory.java +++ /dev/null @@ -1,68 +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.data.encryption.encrypter; - -import lombok.RequiredArgsConstructor; -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 createNoneEncrypter() { - return new DataEncrypter() { - @Override - public String encrypt(String data) { - return data; - } - - @Override - public String decrypt(String data) { - return data; - } - }; - } - - public DataEncrypter createAesEncrypter(AesDataEncrypterConfiguration 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); - } -} 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 deleted file mode 100644 index 136f0e360..000000000 --- a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/key/AesKey.java +++ /dev/null @@ -1,20 +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.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 deleted file mode 100644 index cf3ca2da5..000000000 --- a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/key/CryptoKey.java +++ /dev/null @@ -1,16 +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.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 deleted file mode 100644 index b3eadcd50..000000000 --- a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/key/CryptoKeyFactory.java +++ /dev/null @@ -1,20 +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.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 deleted file mode 100644 index 398088a95..000000000 --- a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/key/CryptoKeyFactoryImpl.java +++ /dev/null @@ -1,39 +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.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 deleted file mode 100644 index be9802013..000000000 --- a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/provider/AesKeyProvider.java +++ /dev/null @@ -1,62 +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.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/KeyProvider.java b/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/provider/KeyProvider.java deleted file mode 100644 index 8e9c8006c..000000000 --- a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/provider/KeyProvider.java +++ /dev/null @@ -1,23 +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.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 deleted file mode 100644 index f632c8121..000000000 --- a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/util/ArrayUtil.java +++ /dev/null @@ -1,42 +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.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/java/net/catenax/edc/data/encryption/DataEncryptionExtension.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/DataEncryptionExtension.java similarity index 63% rename from edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/DataEncryptionExtension.java rename to edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/DataEncryptionExtension.java index ac5d2898b..350fd373d 100644 --- a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/DataEncryptionExtension.java +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/DataEncryptionExtension.java @@ -1,28 +1,27 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.data.encryption; +package org.eclipse.tractusx.edc.data.encryption; import java.time.Duration; import java.util.List; import java.util.stream.Collectors; -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; -import net.catenax.edc.data.encryption.key.CryptoKeyFactoryImpl; -import net.catenax.edc.data.encryption.provider.AesKeyProvider; -import org.eclipse.dataspaceconnector.runtime.metamodel.annotation.EdcSetting; import org.eclipse.dataspaceconnector.runtime.metamodel.annotation.Provides; import org.eclipse.dataspaceconnector.runtime.metamodel.annotation.Requires; import org.eclipse.dataspaceconnector.spi.EdcException; @@ -31,22 +30,28 @@ import org.eclipse.dataspaceconnector.spi.system.ServiceExtension; import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; import org.eclipse.dataspaceconnector.transfer.dataplane.spi.security.DataEncrypter; +import org.eclipse.tractusx.edc.data.encryption.encrypter.AesDataEncrypterConfiguration; +import org.eclipse.tractusx.edc.data.encryption.encrypter.DataEncrypterFactory; +import org.eclipse.tractusx.edc.data.encryption.key.AesKey; +import org.eclipse.tractusx.edc.data.encryption.key.CryptoKeyFactory; +import org.eclipse.tractusx.edc.data.encryption.key.CryptoKeyFactoryImpl; +import org.eclipse.tractusx.edc.data.encryption.provider.AesKeyProvider; @Provides({DataEncrypter.class}) @Requires({Vault.class}) public class DataEncryptionExtension implements ServiceExtension { - public static final String NAME = "Data Encryption Extension"; + public static final String EXTENSION_NAME = "Data Encryption Extension"; - @EdcSetting public static final String ENCRYPTION_KEY_SET = "edc.data.encryption.keys.alias"; + 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 = "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 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 String CACHING_SECONDS = "edc.data.encryption.caching.seconds"; public static final int CACHING_SECONDS_DEFAULT = 3600; private static final CryptoKeyFactory cryptoKeyFactory = new CryptoKeyFactoryImpl(); @@ -57,7 +62,7 @@ public class DataEncryptionExtension implements ServiceExtension { @Override public String name() { - return NAME; + return EXTENSION_NAME; } @Override @@ -75,7 +80,8 @@ 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); + throw new EdcException( + EXTENSION_NAME + ": No vault key secret found for alias " + keyAlias); } try { @@ -83,10 +89,12 @@ public void start() { final List keys = aesKeyProvider.getDecryptionKeySet().collect(Collectors.toList()); monitor.debug( String.format( - "Started " + NAME + ": Found %s registered AES keys in vault.", keys.size())); + "Started " + EXTENSION_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); + EXTENSION_NAME + ": AES keys from vault must be comma separated and Base64 encoded.", + e); } } } @@ -108,7 +116,7 @@ public void initialize(ServiceExtensionContext context) { } else { final String msg = String.format( - DataEncryptionExtension.NAME + DataEncryptionExtension.EXTENSION_NAME + ": Unsupported encryption algorithm '%s'. Supported algorithms are '%s', '%s'.", algorithm, DataEncrypterFactory.AES_ALGORITHM, @@ -123,7 +131,7 @@ 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); + throw new EdcException(EXTENSION_NAME + ": Missing setting " + ENCRYPTION_KEY_SET); } final boolean cachingEnabled = context.getSetting(CACHING_ENABLED, CACHING_ENABLED_DEFAULT); diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/CryptoAlgorithm.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/CryptoAlgorithm.java new file mode 100644 index 000000000..a49cce44d --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/CryptoAlgorithm.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.eclipse.tractusx.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 org.eclipse.tractusx.edc.data.encryption.data.DecryptedData; +import org.eclipse.tractusx.edc.data.encryption.data.EncryptedData; +import org.eclipse.tractusx.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/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesAlgorithm.java similarity index 71% rename from edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/algorithms/aes/AesAlgorithm.java rename to edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesAlgorithm.java index 8aaddfb85..0e420a91c 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/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesAlgorithm.java @@ -1,17 +1,23 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.data.encryption.algorithms.aes; +package org.eclipse.tractusx.edc.data.encryption.algorithms.aes; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; @@ -23,13 +29,13 @@ 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; +import org.eclipse.tractusx.edc.data.encryption.algorithms.CryptoAlgorithm; +import org.eclipse.tractusx.edc.data.encryption.data.CryptoDataFactory; +import org.eclipse.tractusx.edc.data.encryption.data.DecryptedData; +import org.eclipse.tractusx.edc.data.encryption.data.EncryptedData; +import org.eclipse.tractusx.edc.data.encryption.key.AesKey; +import org.eclipse.tractusx.edc.data.encryption.util.ArrayUtil; public class AesAlgorithm implements CryptoAlgorithm { 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/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesInitializationVectorIterator.java similarity index 56% rename from edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/algorithms/aes/AesInitializationVectorIterator.java rename to edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesInitializationVectorIterator.java index 03a5cf789..11ca0011a 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/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesInitializationVectorIterator.java @@ -1,23 +1,29 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.data.encryption.algorithms.aes; +package org.eclipse.tractusx.edc.data.encryption.algorithms.aes; import java.security.SecureRandom; import java.util.Iterator; import java.util.NoSuchElementException; import lombok.SneakyThrows; -import net.catenax.edc.data.encryption.util.ArrayUtil; +import org.eclipse.tractusx.edc.data.encryption.util.ArrayUtil; public class AesInitializationVectorIterator implements Iterator { 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/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/ByteCounter.java similarity index 62% rename from edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/algorithms/aes/ByteCounter.java rename to edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/ByteCounter.java index 4208d4ec6..55eec8184 100644 --- 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/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/ByteCounter.java @@ -1,17 +1,23 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.data.encryption.algorithms.aes; +package org.eclipse.tractusx.edc.data.encryption.algorithms.aes; /** Big Endian Byte Counter */ public class ByteCounter { diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/CryptoData.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/CryptoData.java new file mode 100644 index 000000000..edb3b3620 --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/CryptoData.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.eclipse.tractusx.edc.data.encryption.data; + +public interface CryptoData { + byte[] getBytes(); + + String getBase64(); +} diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/CryptoDataFactory.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/CryptoDataFactory.java new file mode 100644 index 000000000..a37460111 --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/CryptoDataFactory.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.eclipse.tractusx.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/org/eclipse/tractusx/edc/data/encryption/data/CryptoDataFactoryImpl.java similarity index 67% rename from edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/data/CryptoDataFactoryImpl.java rename to edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/CryptoDataFactoryImpl.java index 07a1e39fe..b23966170 100644 --- a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/data/CryptoDataFactoryImpl.java +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/CryptoDataFactoryImpl.java @@ -1,17 +1,23 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.data.encryption.data; +package org.eclipse.tractusx.edc.data.encryption.data; import lombok.Value; import org.bouncycastle.util.encoders.Base64; diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/DecryptedData.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/DecryptedData.java new file mode 100644 index 000000000..63c6cf2b1 --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/DecryptedData.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.eclipse.tractusx.edc.data.encryption.data; + +public interface DecryptedData extends CryptoData {} diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/EncryptedData.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/EncryptedData.java new file mode 100644 index 000000000..dd4e8241a --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/EncryptedData.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.eclipse.tractusx.edc.data.encryption.data; + +public interface EncryptedData extends CryptoData {} diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/AesDataEncrypterConfiguration.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/AesDataEncrypterConfiguration.java new file mode 100644 index 000000000..725828acc --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/AesDataEncrypterConfiguration.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.edc.data.encryption.encrypter; + +import java.time.Duration; +import lombok.NonNull; +import lombok.Value; + +@Value +public class AesDataEncrypterConfiguration { + @NonNull String keySetAlias; + boolean cachingEnabled; + @NonNull Duration cachingDuration; +} 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/org/eclipse/tractusx/edc/data/encryption/encrypter/AesDataEncrypterImpl.java similarity index 66% rename from edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/encrypter/AesDataEncrypterImpl.java rename to edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/AesDataEncrypterImpl.java index e3ac3e82b..746fb4073 100644 --- a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/encrypter/AesDataEncrypterImpl.java +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/AesDataEncrypterImpl.java @@ -1,18 +1,24 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.data.encryption.encrypter; +package org.eclipse.tractusx.edc.data.encryption.encrypter; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; @@ -23,16 +29,16 @@ 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; +import org.eclipse.tractusx.edc.data.encryption.DataEncryptionExtension; +import org.eclipse.tractusx.edc.data.encryption.algorithms.CryptoAlgorithm; +import org.eclipse.tractusx.edc.data.encryption.data.CryptoDataFactory; +import org.eclipse.tractusx.edc.data.encryption.data.DecryptedData; +import org.eclipse.tractusx.edc.data.encryption.data.EncryptedData; +import org.eclipse.tractusx.edc.data.encryption.key.AesKey; +import org.eclipse.tractusx.edc.data.encryption.provider.KeyProvider; @RequiredArgsConstructor public class AesDataEncrypterImpl implements DataEncrypter { @@ -76,7 +82,7 @@ public String decrypt(String value) { .orElseThrow( () -> new EdcException( - DataEncryptionExtension.NAME + DataEncryptionExtension.EXTENSION_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.")); } @@ -93,7 +99,7 @@ private Optional decrypt(EncryptedData data, AesKey key) { | InvalidAlgorithmParameterException e) { monitor.warning( String.format( - DataEncryptionExtension.NAME + DataEncryptionExtension.EXTENSION_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/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterFactory.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterFactory.java new file mode 100644 index 000000000..d60d052f5 --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterFactory.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.edc.data.encryption.encrypter; + +import lombok.RequiredArgsConstructor; +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.eclipse.tractusx.edc.data.encryption.algorithms.CryptoAlgorithm; +import org.eclipse.tractusx.edc.data.encryption.algorithms.aes.AesAlgorithm; +import org.eclipse.tractusx.edc.data.encryption.data.CryptoDataFactory; +import org.eclipse.tractusx.edc.data.encryption.data.CryptoDataFactoryImpl; +import org.eclipse.tractusx.edc.data.encryption.key.AesKey; +import org.eclipse.tractusx.edc.data.encryption.key.CryptoKeyFactory; +import org.eclipse.tractusx.edc.data.encryption.provider.AesKeyProvider; +import org.eclipse.tractusx.edc.data.encryption.provider.CachingKeyProvider; +import org.eclipse.tractusx.edc.data.encryption.provider.KeyProvider; + +@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 createNoneEncrypter() { + return new DataEncrypter() { + @Override + public String encrypt(String data) { + return data; + } + + @Override + public String decrypt(String data) { + return data; + } + }; + } + + public DataEncrypter createAesEncrypter(AesDataEncrypterConfiguration 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); + } +} diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/AesKey.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/AesKey.java new file mode 100644 index 000000000..69ef83021 --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/AesKey.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.eclipse.tractusx.edc.data.encryption.key; + +public interface AesKey extends CryptoKey { + byte[] getBytes(); + + String getBase64(); +} diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/CryptoKey.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/CryptoKey.java new file mode 100644 index 000000000..f7d8b3601 --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/CryptoKey.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.eclipse.tractusx.edc.data.encryption.key; + +public interface CryptoKey {} diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/CryptoKeyFactory.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/CryptoKeyFactory.java new file mode 100644 index 000000000..52fdb3c00 --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/CryptoKeyFactory.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.eclipse.tractusx.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/org/eclipse/tractusx/edc/data/encryption/key/CryptoKeyFactoryImpl.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/CryptoKeyFactoryImpl.java new file mode 100644 index 000000000..f3fa102a4 --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/CryptoKeyFactoryImpl.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.eclipse.tractusx.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/org/eclipse/tractusx/edc/data/encryption/provider/AesKeyProvider.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/provider/AesKeyProvider.java new file mode 100644 index 000000000..b009b51de --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/provider/AesKeyProvider.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.eclipse.tractusx.edc.data.encryption.provider; + +import java.util.Arrays; +import java.util.function.Predicate; +import java.util.stream.Stream; +import lombok.RequiredArgsConstructor; +import org.bouncycastle.util.encoders.Base64; +import org.eclipse.dataspaceconnector.spi.security.Vault; +import org.eclipse.tractusx.edc.data.encryption.DataEncryptionExtension; +import org.eclipse.tractusx.edc.data.encryption.key.AesKey; +import org.eclipse.tractusx.edc.data.encryption.key.CryptoKeyFactory; + +@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.EXTENSION_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/org/eclipse/tractusx/edc/data/encryption/provider/CachingKeyProvider.java similarity index 64% rename from edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/provider/CachingKeyProvider.java rename to edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/provider/CachingKeyProvider.java index e33a39551..b4b490918 100644 --- a/edc-extensions/data-encryption/src/main/java/net/catenax/edc/data/encryption/provider/CachingKeyProvider.java +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/provider/CachingKeyProvider.java @@ -1,18 +1,24 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.data.encryption.provider; +package org.eclipse.tractusx.edc.data.encryption.provider; import java.time.Clock; import java.time.Duration; @@ -22,7 +28,7 @@ import java.util.stream.Stream; import lombok.NonNull; import lombok.Value; -import net.catenax.edc.data.encryption.key.CryptoKey; +import org.eclipse.tractusx.edc.data.encryption.key.CryptoKey; public class CachingKeyProvider implements KeyProvider { diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/provider/KeyProvider.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/provider/KeyProvider.java new file mode 100644 index 000000000..86bd2d16d --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/provider/KeyProvider.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.eclipse.tractusx.edc.data.encryption.provider; + +import java.util.stream.Stream; +import org.eclipse.tractusx.edc.data.encryption.key.CryptoKey; + +public interface KeyProvider { + T getEncryptionKey(); + + Stream getDecryptionKeySet(); +} diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/util/ArrayUtil.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/util/ArrayUtil.java new file mode 100644 index 000000000..a600f9385 --- /dev/null +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/util/ArrayUtil.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.eclipse.tractusx.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 index 8a09148c9..bce166d66 100644 --- 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 @@ -1,14 +1,20 @@ # # Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2021,2022 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. # # This program and the accompanying materials are made available under the # terms of the Apache License, Version 2.0 which is available at # https://www.apache.org/licenses/LICENSE-2.0 # -# SPDX-License-Identifier: Apache-2.0 -# -# Contributors: -# Mercedes-Benz Tech Innovation GmbH - Initial ServiceExtension file +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. # +# SPDX-License-Identifier: Apache-2.0 # -net.catenax.edc.data.encryption.DataEncryptionExtension +org.eclipse.tractusx.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/org/eclipse/tractusx/edc/data/encryption/DataEncryptionExtensionTest.java similarity index 77% rename from edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/DataEncryptionExtensionTest.java rename to edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/DataEncryptionExtensionTest.java index 49fd8dd67..e91d69ee8 100644 --- a/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/DataEncryptionExtensionTest.java +++ b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/DataEncryptionExtensionTest.java @@ -1,24 +1,30 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.data.encryption; +package org.eclipse.tractusx.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.eclipse.tractusx.edc.data.encryption.encrypter.DataEncrypterFactory; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -60,7 +66,7 @@ void setup() { @Test void testName() { - Assertions.assertEquals(DataEncryptionExtension.NAME, extension.name()); + Assertions.assertEquals(DataEncryptionExtension.EXTENSION_NAME, extension.name()); } @Test 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/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesAlgorithmTest.java similarity index 61% rename from edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/algorithms/aes/AesAlgorithmTest.java rename to edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesAlgorithmTest.java index d66459fee..4d19927fb 100644 --- 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/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesAlgorithmTest.java @@ -1,25 +1,31 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.data.encryption.algorithms.aes; +package org.eclipse.tractusx.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.eclipse.tractusx.edc.data.encryption.data.CryptoDataFactory; +import org.eclipse.tractusx.edc.data.encryption.data.CryptoDataFactoryImpl; +import org.eclipse.tractusx.edc.data.encryption.data.DecryptedData; +import org.eclipse.tractusx.edc.data.encryption.data.EncryptedData; +import org.eclipse.tractusx.edc.data.encryption.key.AesKey; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; 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/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesInitializationVectorIteratorTest.java similarity index 65% rename from edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/algorithms/aes/AesInitializationVectorIteratorTest.java rename to edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesInitializationVectorIteratorTest.java index 5a79a42b5..ff466a2e2 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/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesInitializationVectorIteratorTest.java @@ -1,23 +1,29 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.data.encryption.algorithms.aes; +package org.eclipse.tractusx.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.eclipse.tractusx.edc.data.encryption.util.ArrayUtil; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; 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/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/ByteCounterTest.java similarity index 76% rename from edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/algorithms/aes/ByteCounterTest.java rename to edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/ByteCounterTest.java index 9beb2e190..5a16343a5 100644 --- 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/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/ByteCounterTest.java @@ -1,20 +1,26 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.data.encryption.algorithms.aes; +package org.eclipse.tractusx.edc.data.encryption.algorithms.aes; import java.util.stream.Stream; -import net.catenax.edc.data.encryption.util.ArrayUtil; +import org.eclipse.tractusx.edc.data.encryption.util.ArrayUtil; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtensionContext; 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/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterAesComponentTest.java similarity index 60% rename from edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/encrypter/DataEncrypterAesComponentTest.java rename to edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterAesComponentTest.java index 717459e4a..08d9245a9 100644 --- a/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/encrypter/DataEncrypterAesComponentTest.java +++ b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterAesComponentTest.java @@ -1,33 +1,39 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.data.encryption.encrypter; +package org.eclipse.tractusx.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.eclipse.tractusx.edc.data.encryption.algorithms.CryptoAlgorithm; +import org.eclipse.tractusx.edc.data.encryption.algorithms.aes.AesAlgorithm; +import org.eclipse.tractusx.edc.data.encryption.data.CryptoDataFactory; +import org.eclipse.tractusx.edc.data.encryption.data.CryptoDataFactoryImpl; +import org.eclipse.tractusx.edc.data.encryption.data.DecryptedData; +import org.eclipse.tractusx.edc.data.encryption.data.EncryptedData; +import org.eclipse.tractusx.edc.data.encryption.key.AesKey; +import org.eclipse.tractusx.edc.data.encryption.key.CryptoKeyFactory; +import org.eclipse.tractusx.edc.data.encryption.key.CryptoKeyFactoryImpl; +import org.eclipse.tractusx.edc.data.encryption.provider.AesKeyProvider; +import org.eclipse.tractusx.edc.data.encryption.provider.KeyProvider; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; 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/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterFactoryTest.java similarity index 70% rename from edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/encrypter/DataEncrypterFactoryTest.java rename to edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterFactoryTest.java index 957983503..e61ac96e9 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/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterFactoryTest.java @@ -1,23 +1,29 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.data.encryption.encrypter; +package org.eclipse.tractusx.edc.data.encryption.encrypter; import java.time.Duration; -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.eclipse.tractusx.edc.data.encryption.key.CryptoKeyFactoryImpl; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; 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/org/eclipse/tractusx/edc/data/encryption/key/CryptoKeyFactoryImplTest.java similarity index 50% rename from edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/key/CryptoKeyFactoryImplTest.java rename to edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/key/CryptoKeyFactoryImplTest.java index 00ec3e8e0..73354b76c 100644 --- a/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/key/CryptoKeyFactoryImplTest.java +++ b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/key/CryptoKeyFactoryImplTest.java @@ -1,17 +1,23 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.data.encryption.key; +package org.eclipse.tractusx.edc.data.encryption.key; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.params.ParameterizedTest; 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/org/eclipse/tractusx/edc/data/encryption/provider/AesKeyProviderTest.java similarity index 62% rename from edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/provider/AesKeyProviderTest.java rename to edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/provider/AesKeyProviderTest.java index f106ba9c4..25867acbc 100644 --- a/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/provider/AesKeyProviderTest.java +++ b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/provider/AesKeyProviderTest.java @@ -1,24 +1,30 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.data.encryption.provider; +package org.eclipse.tractusx.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.eclipse.tractusx.edc.data.encryption.key.AesKey; +import org.eclipse.tractusx.edc.data.encryption.key.CryptoKeyFactoryImpl; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; 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/org/eclipse/tractusx/edc/data/encryption/provider/CachingKeyProviderTest.java similarity index 72% rename from edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/provider/CachingKeyProviderTest.java rename to edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/provider/CachingKeyProviderTest.java index ea8fc4ffc..390b210f6 100644 --- a/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/provider/CachingKeyProviderTest.java +++ b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/provider/CachingKeyProviderTest.java @@ -1,23 +1,29 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.data.encryption.provider; +package org.eclipse.tractusx.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.eclipse.tractusx.edc.data.encryption.key.CryptoKey; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; 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/org/eclipse/tractusx/edc/data/encryption/util/ArrayUtilTest.java similarity index 71% rename from edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/util/ArrayUtilTest.java rename to edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/util/ArrayUtilTest.java index 611585c86..45b604b64 100644 --- a/edc-extensions/data-encryption/src/test/java/net/catenax/edc/data/encryption/util/ArrayUtilTest.java +++ b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/util/ArrayUtilTest.java @@ -1,17 +1,23 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.data.encryption.util; +package org.eclipse.tractusx.edc.data.encryption.util; import java.util.stream.Stream; import org.junit.jupiter.api.Assertions; diff --git a/edc-extensions/dataplane-selector-configuration/pom.xml b/edc-extensions/dataplane-selector-configuration/pom.xml index 5ce58e204..534da5448 100644 --- a/edc-extensions/dataplane-selector-configuration/pom.xml +++ b/edc-extensions/dataplane-selector-configuration/pom.xml @@ -1,24 +1,30 @@ edc-extensions - net.catenax.edc.extensions - 0.1.2 + org.eclipse.tractusx.edc.extensions + 0.1.3 4.0.0 @@ -58,6 +64,24 @@ data-plane-selector-spi + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + org.eclipse.dataspaceconnector 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/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtension.java similarity index 83% rename from edc-extensions/dataplane-selector-configuration/src/main/java/net/catenax/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtension.java rename to edc-extensions/dataplane-selector-configuration/src/main/java/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtension.java index 976d468d9..df0bc3843 100644 --- 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/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtension.java @@ -1,18 +1,24 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.dataplane.selector.configuration; +package org.eclipse.tractusx.edc.dataplane.selector.configuration; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; @@ -24,7 +30,6 @@ import java.util.stream.Collectors; import org.eclipse.dataspaceconnector.dataplane.selector.DataPlaneSelectorService; import org.eclipse.dataspaceconnector.dataplane.selector.instance.DataPlaneInstanceImpl; -import org.eclipse.dataspaceconnector.runtime.metamodel.annotation.EdcSetting; import org.eclipse.dataspaceconnector.runtime.metamodel.annotation.Requires; import org.eclipse.dataspaceconnector.spi.EdcException; import org.eclipse.dataspaceconnector.spi.monitor.Monitor; @@ -86,12 +91,12 @@ @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"; + public static final String CONFIG_PREFIX = "edc.dataplane.selector"; + public static final String URL_SUFFIX = "url"; + public static final String DESTINATION_TYPES_SUFFIX = "destinationtypes"; + public static final String SOURCE_TYPES_SUFFIX = "sourcetypes"; + public static final String PROPERTIES_SUFFIX = "properties"; + public static final String PUBLIC_API_URL_PROPERTY = "publicApiUrl"; private static final String NAME = "Data Plane Selector Configuration Extension"; private static final String COMMA = ","; diff --git a/edc-extensions/dataplane-selector-configuration/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension b/edc-extensions/dataplane-selector-configuration/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension index f23f403dd..7abfadd3c 100644 --- a/edc-extensions/dataplane-selector-configuration/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension +++ b/edc-extensions/dataplane-selector-configuration/src/main/resources/META-INF/services/org.eclipse.dataspaceconnector.spi.system.ServiceExtension @@ -1,15 +1,21 @@ # # Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2021,2022 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. # # This program and the accompanying materials are made available under the # terms of the Apache License, Version 2.0 which is available at # https://www.apache.org/licenses/LICENSE-2.0 # -# SPDX-License-Identifier: Apache-2.0 -# -# Contributors: -# Mercedes-Benz Tech Innovation GmbH - Initial ServiceExtension file +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. # +# SPDX-License-Identifier: Apache-2.0 # -net.catenax.edc.dataplane.selector.configuration.DataPlaneSelectorConfigurationServiceExtension +org.eclipse.tractusx.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/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtensionEdcExtensionTest.java similarity index 85% rename from edc-extensions/dataplane-selector-configuration/src/test/java/net/catenax/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtensionEdcExtensionTest.java rename to edc-extensions/dataplane-selector-configuration/src/test/java/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtensionEdcExtensionTest.java index 6ace3d84c..565da1c1e 100644 --- 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/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtensionEdcExtensionTest.java @@ -1,18 +1,24 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Added Test + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.dataplane.selector.configuration; +package org.eclipse.tractusx.edc.dataplane.selector.configuration; import java.util.HashMap; import java.util.Map; @@ -142,9 +148,7 @@ void registersDataPlaneInstance() { private class TestExtension implements ServiceExtension { public void initialize(ServiceExtensionContext context) { - context.registerService( - org.eclipse.dataspaceconnector.dataplane.selector.DataPlaneSelectorService.class, - dataPlaneSelectorService); + context.registerService(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/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtensionTest.java similarity index 90% rename from edc-extensions/dataplane-selector-configuration/src/test/java/net/catenax/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtensionTest.java rename to edc-extensions/dataplane-selector-configuration/src/test/java/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtensionTest.java index 58a744fc5..c8bd23144 100644 --- 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/org/eclipse/tractusx/edc/dataplane/selector/configuration/DataPlaneSelectorConfigurationServiceExtensionTest.java @@ -1,18 +1,24 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Added Test + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.dataplane.selector.configuration; +package org.eclipse.tractusx.edc.dataplane.selector.configuration; import java.util.HashMap; import java.util.Map; diff --git a/edc-extensions/hashicorp-vault/pom.xml b/edc-extensions/hashicorp-vault/pom.xml index b3806c341..6be7f43b1 100644 --- a/edc-extensions/hashicorp-vault/pom.xml +++ b/edc-extensions/hashicorp-vault/pom.xml @@ -1,23 +1,29 @@ - net.catenax.edc.extensions + org.eclipse.tractusx.edc.extensions edc-extensions - 0.1.2 + 0.1.3 4.0.0 @@ -138,6 +144,24 @@ okhttp + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + org.junit.jupiter @@ -185,7 +209,6 @@ hamcrest test - 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 deleted file mode 100644 index a9f0ee2a0..000000000 --- a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultClientConfig.java +++ /dev/null @@ -1,34 +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.hashicorpvault; - -import java.time.Duration; -import lombok.Builder; -import lombok.Getter; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; - -@Builder -@Getter -@RequiredArgsConstructor -class HashicorpVaultClientConfig { - @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/HashicorpVaultCreateEntryRequestPayload.java b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultCreateEntryRequestPayload.java deleted file mode 100644 index 4882c5477..000000000 --- a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultCreateEntryRequestPayload.java +++ /dev/null @@ -1,47 +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.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 deleted file mode 100644 index 0818c77a7..000000000 --- a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultCreateEntryResponsePayload.java +++ /dev/null @@ -1,33 +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.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 deleted file mode 100644 index ce9f16b7b..000000000 --- a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultEntryMetadata.java +++ /dev/null @@ -1,40 +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.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 deleted file mode 100644 index af106bb58..000000000 --- a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultException.java +++ /dev/null @@ -1,28 +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.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/HashicorpVaultGetEntryResponsePayload.java b/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultGetEntryResponsePayload.java deleted file mode 100644 index 712c92421..000000000 --- a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultGetEntryResponsePayload.java +++ /dev/null @@ -1,48 +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.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/AbstractHashicorpVaultExtension.java b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/AbstractHashicorpVaultExtension.java similarity index 77% rename from edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/AbstractHashicorpVaultExtension.java rename to edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/AbstractHashicorpVaultExtension.java index 23486385a..a30b593a0 100644 --- a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/AbstractHashicorpVaultExtension.java +++ b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/AbstractHashicorpVaultExtension.java @@ -1,22 +1,27 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Add vault health check + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.hashicorpvault; +package org.eclipse.tractusx.edc.hashicorpvault; import java.time.Duration; import okhttp3.OkHttpClient; -import org.eclipse.dataspaceconnector.runtime.metamodel.annotation.EdcSetting; import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; /** @@ -25,29 +30,23 @@ */ 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) { diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpCertificateResolver.java b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpCertificateResolver.java similarity index 63% rename from edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpCertificateResolver.java rename to edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpCertificateResolver.java index f067aec92..81375a538 100644 --- a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpCertificateResolver.java +++ b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpCertificateResolver.java @@ -1,18 +1,24 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.hashicorpvault; +package org.eclipse.tractusx.edc.hashicorpvault; import java.io.ByteArrayInputStream; import java.io.IOException; diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVault.java b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVault.java similarity index 60% rename from edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVault.java rename to edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVault.java index 6fe484081..e49563974 100644 --- a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVault.java +++ b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVault.java @@ -1,18 +1,24 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.hashicorpvault; +package org.eclipse.tractusx.edc.hashicorpvault; import lombok.NonNull; import lombok.RequiredArgsConstructor; diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultClient.java b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultClient.java similarity index 87% rename from edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultClient.java rename to edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultClient.java index 754ac5928..068a24387 100644 --- a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultClient.java +++ b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultClient.java @@ -1,19 +1,24 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * 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 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.hashicorpvault; +package org.eclipse.tractusx.edc.hashicorpvault; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonMappingException; @@ -95,7 +100,8 @@ public HashicorpVaultHealthResponse getHealth() throws IOException { 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 + // ignore. status code not checked, so it may be possible that no payload was + // provided } } @@ -156,13 +162,16 @@ private Headers getHeaders() { private HttpUrl getSecretUrl(String key, String entryType) { key = URLEncoder.encode(key, StandardCharsets.UTF_8); + // restore '/' characters to allow sub-directories + key = key.replace("%2F", "/"); + final String vaultApiPath = config.getVaultApiSecretPath(); return Objects.requireNonNull(HttpUrl.parse(config.getVaultUrl())) .newBuilder() .addPathSegments(PathUtil.trimLeadingOrEndingSlash(vaultApiPath)) .addPathSegment(entryType) - .addPathSegment(key) + .addPathSegments(key) .build(); } @@ -170,7 +179,8 @@ private HttpUrl getHealthUrl() { final String vaultHealthPath = config.getVaultApiHealthPath(); final boolean isVaultHealthStandbyOk = config.isVaultApiHealthStandbyOk(); - // by setting 'standbyok' and/or 'perfstandbyok' the vault will return an active status + // 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())) diff --git a/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultClientConfig.java b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultClientConfig.java new file mode 100644 index 000000000..495d6937c --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultClientConfig.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.edc.hashicorpvault; + +import java.time.Duration; +import lombok.Builder; +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; + +@Builder +@Getter +@RequiredArgsConstructor +class HashicorpVaultClientConfig { + @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/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultCreateEntryRequestPayload.java b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultCreateEntryRequestPayload.java new file mode 100644 index 000000000..fa31d7149 --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultCreateEntryRequestPayload.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.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/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultCreateEntryResponsePayload.java b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultCreateEntryResponsePayload.java new file mode 100644 index 000000000..77ab76772 --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultCreateEntryResponsePayload.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.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/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultEntryMetadata.java b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultEntryMetadata.java new file mode 100644 index 000000000..bd59309c9 --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultEntryMetadata.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.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/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultException.java b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultException.java new file mode 100644 index 000000000..57743d1f1 --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultException.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.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/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultGetEntryResponsePayload.java b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultGetEntryResponsePayload.java new file mode 100644 index 000000000..175c73d47 --- /dev/null +++ b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultGetEntryResponsePayload.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.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/HashicorpVaultHealthCheck.java b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthCheck.java similarity index 98% rename from edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthCheck.java rename to edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthCheck.java index e2177d157..464408d12 100644 --- a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthCheck.java +++ b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthCheck.java @@ -12,7 +12,7 @@ * */ -package net.catenax.edc.hashicorpvault; +package org.eclipse.tractusx.edc.hashicorpvault; import java.io.IOException; import lombok.RequiredArgsConstructor; diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthExtension.java b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthExtension.java similarity index 69% rename from edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthExtension.java rename to edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthExtension.java index 62bc5bc00..4c81cca20 100644 --- a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthExtension.java +++ b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthExtension.java @@ -1,21 +1,26 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Add vault health check + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.hashicorpvault; +package org.eclipse.tractusx.edc.hashicorpvault; import okhttp3.OkHttpClient; -import org.eclipse.dataspaceconnector.runtime.metamodel.annotation.EdcSetting; import org.eclipse.dataspaceconnector.runtime.metamodel.annotation.Requires; import org.eclipse.dataspaceconnector.spi.system.ServiceExtension; import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; @@ -25,7 +30,6 @@ 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; diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthResponse.java b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthResponse.java similarity index 97% rename from edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthResponse.java rename to edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthResponse.java index b987c7041..bbf991684 100644 --- a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthResponse.java +++ b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthResponse.java @@ -12,7 +12,7 @@ * */ -package net.catenax.edc.hashicorpvault; +package org.eclipse.tractusx.edc.hashicorpvault; import lombok.Builder; import lombok.Getter; diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthResponsePayload.java b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthResponsePayload.java similarity index 96% rename from edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthResponsePayload.java rename to edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthResponsePayload.java index d63b71408..67ef6533c 100644 --- a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthResponsePayload.java +++ b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthResponsePayload.java @@ -12,7 +12,7 @@ * */ -package net.catenax.edc.hashicorpvault; +package org.eclipse.tractusx.edc.hashicorpvault; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultVaultExtension.java b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultVaultExtension.java similarity index 68% rename from edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultVaultExtension.java rename to edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultVaultExtension.java index c7bfc6674..223c70406 100644 --- a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/HashicorpVaultVaultExtension.java +++ b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultVaultExtension.java @@ -1,20 +1,24 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * 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 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.hashicorpvault; +package org.eclipse.tractusx.edc.hashicorpvault; import okhttp3.OkHttpClient; import org.eclipse.dataspaceconnector.runtime.metamodel.annotation.Provides; diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/PathUtil.java b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/PathUtil.java similarity index 93% rename from edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/PathUtil.java rename to edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/PathUtil.java index fe5bb69fa..f22895355 100644 --- a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/PathUtil.java +++ b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/PathUtil.java @@ -12,7 +12,7 @@ * */ -package net.catenax.edc.hashicorpvault; +package org.eclipse.tractusx.edc.hashicorpvault; final class PathUtil { diff --git a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/PemUtil.java b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/PemUtil.java similarity index 63% rename from edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/PemUtil.java rename to edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/PemUtil.java index 8e72fdfb1..6596d781b 100644 --- a/edc-extensions/hashicorp-vault/src/main/java/net/catenax/edc/hashicorpvault/PemUtil.java +++ b/edc-extensions/hashicorp-vault/src/main/java/org/eclipse/tractusx/edc/hashicorpvault/PemUtil.java @@ -1,18 +1,24 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.hashicorpvault; +package org.eclipse.tractusx.edc.hashicorpvault; import java.io.IOException; import java.io.InputStream; 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 11f7b3617..b8d59a5b0 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 @@ -1,14 +1,24 @@ # # Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2021,2022 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. # # This program and the accompanying materials are made available under the # terms of the Apache License, Version 2.0 which is available at # https://www.apache.org/licenses/LICENSE-2.0 # +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# # SPDX-License-Identifier: Apache-2.0 # # Contributors: # Mercedes-Benz Tech Innovation GmbH - Initial ServiceExtension file # -net.catenax.edc.hashicorpvault.HashicorpVaultHealthExtension -net.catenax.edc.hashicorpvault.HashicorpVaultVaultExtension +org.eclipse.tractusx.edc.hashicorpvault.HashicorpVaultHealthExtension +org.eclipse.tractusx.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/org/eclipse/tractusx/edc/hashicorpvault/AbstractHashicorpIT.java similarity index 81% rename from edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/AbstractHashicorpIT.java rename to edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/AbstractHashicorpIT.java index 57b2b584e..925e05de5 100644 --- a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/AbstractHashicorpIT.java +++ b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/AbstractHashicorpIT.java @@ -1,22 +1,28 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial Test + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.hashicorpvault; +package org.eclipse.tractusx.edc.hashicorpvault; -import static net.catenax.edc.hashicorpvault.HashicorpVaultClient.VAULT_DATA_ENTRY_NAME; -import static net.catenax.edc.hashicorpvault.HashicorpVaultVaultExtension.VAULT_TOKEN; -import static net.catenax.edc.hashicorpvault.HashicorpVaultVaultExtension.VAULT_URL; +import static org.eclipse.tractusx.edc.hashicorpvault.HashicorpVaultClient.VAULT_DATA_ENTRY_NAME; +import static org.eclipse.tractusx.edc.hashicorpvault.HashicorpVaultVaultExtension.VAULT_TOKEN; +import static org.eclipse.tractusx.edc.hashicorpvault.HashicorpVaultVaultExtension.VAULT_URL; import java.util.ArrayList; import java.util.HashMap; diff --git a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpCertificateResolverIT.java b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpCertificateResolverIT.java similarity index 61% rename from edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpCertificateResolverIT.java rename to edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpCertificateResolverIT.java index 77d26e6df..ed52719e8 100644 --- a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpCertificateResolverIT.java +++ b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpCertificateResolverIT.java @@ -1,18 +1,24 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial Test + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.hashicorpvault; +package org.eclipse.tractusx.edc.hashicorpvault; import java.security.cert.X509Certificate; import java.util.UUID; diff --git a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpCertificateResolverTest.java b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpCertificateResolverTest.java similarity index 63% rename from edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpCertificateResolverTest.java rename to edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpCertificateResolverTest.java index 4a485cf4b..e5d87171e 100644 --- a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpCertificateResolverTest.java +++ b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpCertificateResolverTest.java @@ -1,18 +1,24 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial Test + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.hashicorpvault; +package org.eclipse.tractusx.edc.hashicorpvault; import java.security.cert.X509Certificate; import lombok.SneakyThrows; diff --git a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultClientTest.java b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultClientTest.java similarity index 91% rename from edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultClientTest.java rename to edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultClientTest.java index 525881619..d0dbabbf2 100644 --- a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultClientTest.java +++ b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultClientTest.java @@ -1,18 +1,24 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial Test + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.hashicorpvault; +package org.eclipse.tractusx.edc.hashicorpvault; import com.fasterxml.jackson.databind.ObjectMapper; import java.time.Duration; diff --git a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultExtensionTest.java b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultExtensionTest.java similarity index 77% rename from edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultExtensionTest.java rename to edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultExtensionTest.java index 212477ced..110020dd7 100644 --- a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultExtensionTest.java +++ b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultExtensionTest.java @@ -1,18 +1,24 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial Test + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.hashicorpvault; +package org.eclipse.tractusx.edc.hashicorpvault; import org.eclipse.dataspaceconnector.spi.monitor.Monitor; import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; diff --git a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthCheckExtensionTest.java b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthCheckExtensionTest.java similarity index 83% rename from edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthCheckExtensionTest.java rename to edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthCheckExtensionTest.java index 71605bd3e..28ee78640 100644 --- a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthCheckExtensionTest.java +++ b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthCheckExtensionTest.java @@ -1,18 +1,24 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial Test + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.hashicorpvault; +package org.eclipse.tractusx.edc.hashicorpvault; import org.eclipse.dataspaceconnector.spi.monitor.Monitor; import org.eclipse.dataspaceconnector.spi.system.ServiceExtensionContext; diff --git a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthCheckTest.java b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthCheckTest.java similarity index 66% rename from edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthCheckTest.java rename to edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthCheckTest.java index da198ba84..20b59c1db 100644 --- a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultHealthCheckTest.java +++ b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultHealthCheckTest.java @@ -1,18 +1,24 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial Test + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.hashicorpvault; +package org.eclipse.tractusx.edc.hashicorpvault; import java.io.IOException; import org.eclipse.dataspaceconnector.spi.monitor.Monitor; diff --git a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultIT.java b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultIT.java similarity index 55% rename from edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultIT.java rename to edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultIT.java index e23c5a9f0..1608a3666 100644 --- a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultIT.java +++ b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultIT.java @@ -1,24 +1,32 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial Test + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.hashicorpvault; +package org.eclipse.tractusx.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; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; class HashicorpVaultIT extends AbstractHashicorpIT { @@ -30,6 +38,30 @@ void testResolveSecret_exists() { Assertions.assertEquals(VAULT_ENTRY_VALUE, secretValue); } + @Test + @DisplayName("Resolve a secret from a sub directory") + void testResolveSecret_inASubDirectory() { + Vault vault = getVault(); + String key = "sub/" + VAULT_ENTRY_KEY; + String value = key + "value"; + + vault.storeSecret(key, value); + String secretValue = vault.resolveSecret(key); + Assertions.assertEquals(value, secretValue); + } + + @ParameterizedTest + @ValueSource(strings = {"foo!bar", "foo.bar", "foo[bar]", "sub/foo{bar}"}) + @DisplayName("Resolve a secret with url encoded characters") + void testResolveSecret_withUrlEncodedCharacters(String key) { + Vault vault = getVault(); + String value = key + "value"; + + vault.storeSecret(key, value); + String secretValue = vault.resolveSecret(key); + Assertions.assertEquals(value, secretValue); + } + @Test @DisplayName("Resolve a secret that does not exist") void testResolveSecret_doesNotExist() { diff --git a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultTest.java b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultTest.java similarity index 82% rename from edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultTest.java rename to edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultTest.java index 5b98c4f6e..e0809120d 100644 --- a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/HashicorpVaultTest.java +++ b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/HashicorpVaultTest.java @@ -1,18 +1,24 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial Test + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.hashicorpvault; +package org.eclipse.tractusx.edc.hashicorpvault; import java.util.UUID; import lombok.SneakyThrows; diff --git a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/PathUtilTest.java b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/PathUtilTest.java similarity index 96% rename from edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/PathUtilTest.java rename to edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/PathUtilTest.java index 01fb0f40a..a5d9821dd 100644 --- a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/PathUtilTest.java +++ b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/PathUtilTest.java @@ -12,7 +12,7 @@ * */ -package net.catenax.edc.hashicorpvault; +package org.eclipse.tractusx.edc.hashicorpvault; import java.util.stream.Stream; import org.junit.jupiter.api.Assertions; diff --git a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/X509CertificateTestUtil.java b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/X509CertificateTestUtil.java similarity index 86% rename from edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/X509CertificateTestUtil.java rename to edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/X509CertificateTestUtil.java index 210928a88..051fe00eb 100644 --- a/edc-extensions/hashicorp-vault/src/test/java/net/catenax/edc/hashicorpvault/X509CertificateTestUtil.java +++ b/edc-extensions/hashicorp-vault/src/test/java/org/eclipse/tractusx/edc/hashicorpvault/X509CertificateTestUtil.java @@ -1,18 +1,24 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.hashicorpvault; +package org.eclipse.tractusx.edc.hashicorpvault; import java.io.ByteArrayOutputStream; import java.io.IOException; diff --git a/edc-extensions/hashicorp-vault/src/test/resources/logback.xml b/edc-extensions/hashicorp-vault/src/test/resources/logback.xml index 3347fcbae..fcc51d48d 100644 --- a/edc-extensions/hashicorp-vault/src/test/resources/logback.xml +++ b/edc-extensions/hashicorp-vault/src/test/resources/logback.xml @@ -1,13 +1,22 @@ diff --git a/edc-extensions/pom.xml b/edc-extensions/pom.xml index e6e64c87a..6ee1bc444 100644 --- a/edc-extensions/pom.xml +++ b/edc-extensions/pom.xml @@ -1,27 +1,33 @@ - net.catenax.edc + org.eclipse.tractusx.edc product-edc-parent - 0.1.2 + 0.1.3 4.0.0 - net.catenax.edc.extensions + org.eclipse.tractusx.edc.extensions edc-extensions pom @@ -36,6 +42,7 @@ dataplane-selector-configuration data-encryption cx-oauth2 + control-plane-adapter diff --git a/edc-extensions/postgresql-migration/pom.xml b/edc-extensions/postgresql-migration/pom.xml index f31e80aad..8a3a03042 100644 --- a/edc-extensions/postgresql-migration/pom.xml +++ b/edc-extensions/postgresql-migration/pom.xml @@ -1,23 +1,29 @@ edc-extensions - net.catenax.edc.extensions - 0.1.2 + org.eclipse.tractusx.edc.extensions + 0.1.3 4.0.0 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 deleted file mode 100644 index 9876d17ea..000000000 --- a/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/AssetPostgresqlMigrationExtension.java +++ /dev/null @@ -1,29 +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 implementation - * - */ - -package net.catenax.edc.postgresql.migration; - -import org.eclipse.dataspaceconnector.sql.assetindex.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 deleted file mode 100644 index 31285de95..000000000 --- a/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/ContractDefinitionPostgresqlMigrationExtension.java +++ /dev/null @@ -1,33 +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 implementation - * - */ - -package net.catenax.edc.postgresql.migration; - -import org.eclipse.dataspaceconnector.runtime.metamodel.annotation.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 deleted file mode 100644 index b198b4834..000000000 --- a/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/ContractNegotiationPostgresqlMigrationExtension.java +++ /dev/null @@ -1,33 +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 implementation - * - */ - -package net.catenax.edc.postgresql.migration; - -import org.eclipse.dataspaceconnector.runtime.metamodel.annotation.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/PolicyPostgresqlMigrationExtension.java b/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/PolicyPostgresqlMigrationExtension.java deleted file mode 100644 index 5e6333ad1..000000000 --- a/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/PolicyPostgresqlMigrationExtension.java +++ /dev/null @@ -1,31 +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 implementation - * - */ - -package net.catenax.edc.postgresql.migration; - -import org.eclipse.dataspaceconnector.runtime.metamodel.annotation.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 deleted file mode 100644 index f17373cf5..000000000 --- a/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/TransferProcessPostgresqlMigrationExtension.java +++ /dev/null @@ -1,33 +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 implementation - * - */ - -package net.catenax.edc.postgresql.migration; - -import org.eclipse.dataspaceconnector.runtime.metamodel.annotation.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/java/net/catenax/edc/postgresql/migration/AbstractPostgresqlMigrationExtension.java b/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/AbstractPostgresqlMigrationExtension.java similarity index 76% rename from edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/AbstractPostgresqlMigrationExtension.java rename to edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/AbstractPostgresqlMigrationExtension.java index dbb17465a..73bc1abae 100644 --- a/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/AbstractPostgresqlMigrationExtension.java +++ b/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/AbstractPostgresqlMigrationExtension.java @@ -1,18 +1,24 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.postgresql.migration; +package org.eclipse.tractusx.edc.postgresql.migration; import java.util.Objects; import java.util.Properties; @@ -34,7 +40,7 @@ abstract class AbstractPostgresqlMigrationExtension implements ServiceExtension private static final String MIGRATION_LOCATION_BASE = String.format( "classpath:%s", - AbstractPostgresqlMigrationExtension.class.getPackageName().replaceAll("\\.", "/")); + AbstractPostgresqlMigrationExtension.class.getPackageName().replace(".", "/")); @Override public void initialize(final ServiceExtensionContext context) { @@ -50,7 +56,8 @@ public void initialize(final ServiceExtensionContext context) { context .getConfig() .getBoolean( - String.format("net.catenax.edc.postgresql.migration.%s.enabled", subSystemName), + String.format( + "org.eclipse.tractusx.edc.postgresql.migration.%s.enabled", subSystemName), true); if (!enabled) { diff --git a/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/AssetPostgresqlMigrationExtension.java b/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/AssetPostgresqlMigrationExtension.java new file mode 100644 index 000000000..d07d01841 --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/AssetPostgresqlMigrationExtension.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.edc.postgresql.migration; + +import org.eclipse.dataspaceconnector.sql.assetindex.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/org/eclipse/tractusx/edc/postgresql/migration/ContractDefinitionPostgresqlMigrationExtension.java b/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/ContractDefinitionPostgresqlMigrationExtension.java new file mode 100644 index 000000000..a5b65aafa --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/ContractDefinitionPostgresqlMigrationExtension.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.edc.postgresql.migration; + +public class ContractDefinitionPostgresqlMigrationExtension + extends AbstractPostgresqlMigrationExtension { + private static final String NAME_SUBSYSTEM = "contractdefinition"; + + 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/org/eclipse/tractusx/edc/postgresql/migration/ContractNegotiationPostgresqlMigrationExtension.java b/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/ContractNegotiationPostgresqlMigrationExtension.java new file mode 100644 index 000000000..c2da93732 --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/ContractNegotiationPostgresqlMigrationExtension.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.edc.postgresql.migration; + +public class ContractNegotiationPostgresqlMigrationExtension + extends AbstractPostgresqlMigrationExtension { + private static final String NAME_SUBSYSTEM = "contractnegotiation"; + + 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/org/eclipse/tractusx/edc/postgresql/migration/DriverManagerConnectionFactory.java similarity index 50% rename from edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/DriverManagerConnectionFactory.java rename to edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/DriverManagerConnectionFactory.java index 4e7c96220..594cab2b6 100644 --- a/edc-extensions/postgresql-migration/src/main/java/net/catenax/edc/postgresql/migration/DriverManagerConnectionFactory.java +++ b/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/DriverManagerConnectionFactory.java @@ -1,18 +1,24 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.postgresql.migration; +package org.eclipse.tractusx.edc.postgresql.migration; import java.sql.Connection; import java.sql.DriverManager; diff --git a/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/PolicyPostgresqlMigrationExtension.java b/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/PolicyPostgresqlMigrationExtension.java new file mode 100644 index 000000000..88f47d858 --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/PolicyPostgresqlMigrationExtension.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.edc.postgresql.migration; + +public class PolicyPostgresqlMigrationExtension extends AbstractPostgresqlMigrationExtension { + private static final String NAME_SUBSYSTEM = "policy"; + + 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/org/eclipse/tractusx/edc/postgresql/migration/TransferProcessPostgresqlMigrationExtension.java b/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/TransferProcessPostgresqlMigrationExtension.java new file mode 100644 index 000000000..1379391e7 --- /dev/null +++ b/edc-extensions/postgresql-migration/src/main/java/org/eclipse/tractusx/edc/postgresql/migration/TransferProcessPostgresqlMigrationExtension.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.edc.postgresql.migration; + +public class TransferProcessPostgresqlMigrationExtension + extends AbstractPostgresqlMigrationExtension { + private static final String NAME_SUBSYSTEM = "transferprocess"; + + 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 index fc52160ad..4c12f6272 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,18 +1,24 @@ # # Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2021,2022 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. # # This program and the accompanying materials are made available under the # terms of the Apache License, Version 2.0 which is available at # https://www.apache.org/licenses/LICENSE-2.0 # -# SPDX-License-Identifier: Apache-2.0 -# -# Contributors: -# Mercedes-Benz Tech Innovation GmbH - Initial ServiceExtension file +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. # +# SPDX-License-Identifier: Apache-2.0 # -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 +org.eclipse.tractusx.edc.postgresql.migration.AssetPostgresqlMigrationExtension +org.eclipse.tractusx.edc.postgresql.migration.ContractDefinitionPostgresqlMigrationExtension +org.eclipse.tractusx.edc.postgresql.migration.ContractNegotiationPostgresqlMigrationExtension +org.eclipse.tractusx.edc.postgresql.migration.PolicyPostgresqlMigrationExtension +org.eclipse.tractusx.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/org/eclipse/tractusx/edc/postgresql/migration/asset/V0_0_1__Init_Asset_Database_Schema.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/asset/V0_0_1__Init_Asset_Database_Schema.sql rename to edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/asset/V0_0_1__Init_Asset_Database_Schema.sql 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/org/eclipse/tractusx/edc/postgresql/migration/asset/V0_0_2__Milestone5_Update.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/asset/V0_0_2__Milestone5_Update.sql rename to edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/asset/V0_0_2__Milestone5_Update.sql 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/org/eclipse/tractusx/edc/postgresql/migration/asset/V0_0_3__Snapshot_20220815_Update.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/asset/V0_0_3__Snapshot_20220815_Update.sql rename to edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/asset/V0_0_3__Snapshot_20220815_Update.sql 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/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_1__Init_ContractDefinition_Database_Schema.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractdefinition/V0_0_1__Init_ContractDefinition_Database_Schema.sql rename to edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_1__Init_ContractDefinition_Database_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/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_2__Alter_ContractDefinition_Access_Contract_Policy_Id_Schema.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractdefinition/V0_0_2__Alter_ContractDefinition_Access_Contract_Policy_Id_Schema.sql rename to edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/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_3__Snapshot_20220815_Update.sql b/edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_3__Snapshot_20220815_Update.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractdefinition/V0_0_3__Snapshot_20220815_Update.sql rename to edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractdefinition/V0_0_3__Snapshot_20220815_Update.sql 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/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_1__Init_ContractNegotiation_Database_Schema.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractnegotiation/V0_0_1__Init_ContractNegotiation_Database_Schema.sql rename to edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_1__Init_ContractNegotiation_Database_Schema.sql 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/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_2__Alter_ContractNegotation_Contract_Agreement_Id_Schema.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractnegotiation/V0_0_2__Alter_ContractNegotation_Contract_Agreement_Id_Schema.sql rename to edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_2__Alter_ContractNegotation_Contract_Agreement_Id_Schema.sql 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/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_3__Milestone5_Update.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractnegotiation/V0_0_3__Milestone5_Update.sql rename to edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_3__Milestone5_Update.sql 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/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_4__Snapshot_20220815_Update.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/contractnegotiation/V0_0_4__Snapshot_20220815_Update.sql rename to edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/contractnegotiation/V0_0_4__Snapshot_20220815_Update.sql 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/org/eclipse/tractusx/edc/postgresql/migration/policy/V0_0_1__Init_Policy_Database_Schema.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/policy/V0_0_1__Init_Policy_Database_Schema.sql rename to edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/policy/V0_0_1__Init_Policy_Database_Schema.sql 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/org/eclipse/tractusx/edc/postgresql/migration/policy/V0_0_2__Milestone5_Update.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/policy/V0_0_2__Milestone5_Update.sql rename to edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/policy/V0_0_2__Milestone5_Update.sql 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/org/eclipse/tractusx/edc/postgresql/migration/policy/V0_0_3__Snapshot_20220815_Update.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/policy/V0_0_3__Snapshot_20220815_Update.sql rename to edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/policy/V0_0_3__Snapshot_20220815_Update.sql 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/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_1__Init_TransferProcess_Database_Schema.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/transferprocess/V0_0_1__Init_TransferProcess_Database_Schema.sql rename to edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_1__Init_TransferProcess_Database_Schema.sql 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/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_2__Alter_TransferProcess_Add_DataAddress.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/transferprocess/V0_0_2__Alter_TransferProcess_Add_DataAddress.sql rename to edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_2__Alter_TransferProcess_Add_DataAddress.sql 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/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_3__Alter_Rename_Id.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/transferprocess/V0_0_3__Alter_Rename_Id.sql rename to edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_3__Alter_Rename_Id.sql 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/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_4__Milestone5_Update.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/transferprocess/V0_0_4__Milestone5_Update.sql rename to edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_4__Milestone5_Update.sql 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/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_5__Snapshot_20220815_Update.sql similarity index 100% rename from edc-extensions/postgresql-migration/src/main/resources/net/catenax/edc/postgresql/migration/transferprocess/V0_0_5__Snapshot_20220815_Update.sql rename to edc-extensions/postgresql-migration/src/main/resources/org/eclipse/tractusx/edc/postgresql/migration/transferprocess/V0_0_5__Snapshot_20220815_Update.sql diff --git a/edc-tests/README.md b/edc-tests/README.md index 1169a1998..e7c100d04 100644 --- a/edc-tests/README.md +++ b/edc-tests/README.md @@ -1,7 +1,7 @@ # Invoke Business-Tests via Maven ```shell -./mvnw -pl edc-tests -Pbusiness-tests -pl edc-tests test -Dtest=net.catenax.edc.tests.features.RunCucumberTest +./mvnw -pl edc-tests -Pbusiness-tests -pl edc-tests test -Dtest=org.eclipse.tractusx.edc.tests.features.RunCucumberTest ``` # Test locally using Act Tool diff --git a/edc-tests/pom.xml b/edc-tests/pom.xml index 2c3e66e79..020f5288c 100644 --- a/edc-tests/pom.xml +++ b/edc-tests/pom.xml @@ -1,37 +1,44 @@ 4.0.0 - net.catenax.edc + org.eclipse.tractusx.edc product-edc-parent - 0.1.2 + 0.1.3 - net.catenax.edc.tests + org.eclipse.tractusx.edc.tests edc-tests jar ${project.groupId}_${project.artifactId} - 2.9.1 + 2.10 4.5.13 - 1.4.1 - 1.7.36 + 1.4.5 + 2.0.3 + 2.18.26 @@ -100,6 +107,12 @@ awaitility test + + software.amazon.awssdk + s3 + ${software.amazon.awssdk} + test + @@ -147,57 +160,65 @@ --> - net.catenax.edc.extensions + org.eclipse.tractusx.edc.extensions business-partner-validation - net.catenax.edc.extensions + org.eclipse.tractusx.edc.extensions dataplane-selector-configuration - net.catenax.edc.extensions + org.eclipse.tractusx.edc.extensions hashicorp-vault - net.catenax.edc.extensions + org.eclipse.tractusx.edc.extensions postgresql-migration - net.catenax.edc.extensions + org.eclipse.tractusx.edc.extensions data-encryption - net.catenax.edc.extensions + org.eclipse.tractusx.edc.extensions cx-oauth2 + + org.eclipse.tractusx.edc.extensions + control-plane-adapter + - net.catenax.edc + org.eclipse.tractusx.edc edc-controlplane-base - net.catenax.edc + org.eclipse.tractusx.edc edc-controlplane-memory - net.catenax.edc + org.eclipse.tractusx.edc + edc-controlplane-memory-hashicorp-vault + + + org.eclipse.tractusx.edc edc-controlplane-postgresql - net.catenax.edc + org.eclipse.tractusx.edc edc-controlplane-postgresql-hashicorp-vault - net.catenax.edc + org.eclipse.tractusx.edc edc-dataplane-base - net.catenax.edc + org.eclipse.tractusx.edc edc-dataplane-azure-vault - net.catenax.edc + org.eclipse.tractusx.edc edc-dataplane-hashicorp-vault 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 deleted file mode 100644 index 9880baf85..000000000 --- a/edc-tests/src/main/resources/deployment/helm/all-in-one/diagrams/deployed_components.puml +++ /dev/null @@ -1,47 +0,0 @@ -@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/templates/_helpers.tpl b/edc-tests/src/main/resources/deployment/helm/all-in-one/templates/_helpers.tpl deleted file mode 100644 index 782767de0..000000000 --- a/edc-tests/src/main/resources/deployment/helm/all-in-one/templates/_helpers.tpl +++ /dev/null @@ -1,62 +0,0 @@ -{{/* -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 deleted file mode 100644 index 5b5145d3a..000000000 --- a/edc-tests/src/main/resources/deployment/helm/all-in-one/templates/secret.yaml +++ /dev/null @@ -1,91 +0,0 @@ ---- - -# 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/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/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/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/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/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/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 }} - ---- - -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/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/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/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/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/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/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 }} - ---- - -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 deleted file mode 100644 index 4494b580b..000000000 --- a/edc-tests/src/main/resources/deployment/helm/all-in-one/values.yaml +++ /dev/null @@ -1,565 +0,0 @@ ---- - -# Default values for all-in-one deployment. -# This is a YAML-formatted file. -# 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 - name: sokrates - attributes: - referringConnector: http://sokrates-edc-controlplane/BPNSOKRATES - # 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 - attributes: - referringConnector: http://plato-edc-controlplane/BPNPLATO - # 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 # -######### -platobackendapplication: - enabled: true - fullnameOverride: "plato-backend-application" - service: - type: NodePort - frontend: - port: 80 -platopostgresql: - enabled: true - fullnameOverride: "plato-postgresql" - auth: - password: &psqlPassword "psql_password" - username: &psqlUsername "postgresql_sandbox_user" - database: &psqlDatabase "edc" - persistence: - enabled: false -platovault: - enabled: true - fullnameOverride: "plato-vault" - injector: - enabled: false - server: - dev: - enabled: true - devRootToken: "root" - - # Must be the same certificate that is configured in section 'ids-daps' - postStart: - - "sh" - - "-c" - - | - { - - 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 - 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 -platoedcdataplane: - enabled: true - fullnameOverride: "plato-edc-dataplane" - image: - 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: - 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_DATAPLANE_TOKEN_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:8200 -platoedccontrolplane: - enabled: true - 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 - pullPolicy: *pullPolicy - opentelemetry: - 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" - - ######################## - ## DAPS CONFIGURATION ## - ######################## - - # 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" - EDC_OAUTH_PRIVATE_KEY_ALIAS: my-plato-daps-key - EDC_OAUTH_PUBLIC_KEY_ALIAS: my-plato-daps-crt - - ############# - ## 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_ENDPOINT_AUDIENCE: http://plato-edc-controlplane:8282/api/v1/ids/data - EDC_IDS_DESCRIPTION: "Plato Control Plane" - - ################ - ## POSTGRESQL ## - ################ - - # 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/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/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/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/control-plane/store/sql/transfer-process-store-sql - EDC_DATASOURCE_TRANSFERPROCESS_NAME: transferprocess - EDC_DATASOURCE_TRANSFERPROCESS_USER: *psqlUsername - EDC_DATASOURCE_TRANSFERPROCESS_URL: *platoPsqlConStr - - ################ - ## 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/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/control-plane/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 - - ##################### - ## 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 # -############ -sokratesbackendapplication: - enabled: true - fullnameOverride: "sokrates-backend-application" - service: - type: NodePort - frontend: - port: 80 -sokratespostgresql: - enabled: true - fullnameOverride: "sokrates-postgresql" - auth: - password: *psqlPassword - username: *psqlUsername - database: *psqlDatabase -sokratesvault: - enabled: true - 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 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 - 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 -sokratesedcdataplane: - enabled: true - fullnameOverride: "sokrates-edc-dataplane" - image: - repository: *edcDataPlaneImage - tag: *edcDataPlaneImageTag - pullPolicy: *pullPolicy - envSecretName: "aio-plato-data-secret" - 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_DATAPLANE_TOKEN_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:8200 - EDC_VAULT_HASHICORP_TOKEN: *sokratesVaultToken -sokratesedccontrolplane: - enabled: true - 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 - tag: *edcControlPlaneImageTag - pullPolicy: *pullPolicy - 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 - - ############# - ## 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" - - ################ - ## POSTGRESQL ## - ################ - - # 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/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/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/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/control-plane/store/sql/transfer-process-store-sql - EDC_DATASOURCE_TRANSFERPROCESS_NAME: transferprocess - EDC_DATASOURCE_TRANSFERPROCESS_USER: *psqlUsername - EDC_DATASOURCE_TRANSFERPROCESS_URL: *SokratesPsqlConStr - - ################ - ## 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/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/control-plane/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 - - ##################### - ## 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/main/resources/deployment/helm/all-in-one/.gitignore b/edc-tests/src/main/resources/deployment/helm/supporting-infrastructure/.gitignore similarity index 100% rename from edc-tests/src/main/resources/deployment/helm/all-in-one/.gitignore rename to edc-tests/src/main/resources/deployment/helm/supporting-infrastructure/.gitignore diff --git a/edc-tests/src/main/resources/deployment/helm/all-in-one/.helmignore b/edc-tests/src/main/resources/deployment/helm/supporting-infrastructure/.helmignore similarity index 100% rename from edc-tests/src/main/resources/deployment/helm/all-in-one/.helmignore rename to edc-tests/src/main/resources/deployment/helm/supporting-infrastructure/.helmignore diff --git a/edc-tests/src/main/resources/deployment/helm/all-in-one/Chart.yaml b/edc-tests/src/main/resources/deployment/helm/supporting-infrastructure/Chart.yaml similarity index 51% rename from edc-tests/src/main/resources/deployment/helm/all-in-one/Chart.yaml rename to edc-tests/src/main/resources/deployment/helm/supporting-infrastructure/Chart.yaml index ab8eee0cd..c701cb199 100644 --- a/edc-tests/src/main/resources/deployment/helm/all-in-one/Chart.yaml +++ b/edc-tests/src/main/resources/deployment/helm/supporting-infrastructure/Chart.yaml @@ -25,63 +25,42 @@ version: 0.1.0 appVersion: "1.16.0" dependencies: - # IDS DAPS + # IDS Dynamic Attribute Provisioning Service (IAM) - name: ids-daps version: 0.0.1 repository: "file://../omejdn" alias: idsdaps - condition: idsdaps.enabled + condition: install.daps - # PLATO CONNECTOR - - name: edc-controlplane - version: ">=0.0.1" - repository: "file://../../../../../../../charts/edc-controlplane" - alias: platoedccontrolplane - condition: platoedccontrolplane.enabled - - name: edc-dataplane - version: ">=0.0.1" - repository: "file://../../../../../../../charts/edc-dataplane" - alias: platoedcdataplane - condition: platoedcdataplane.enabled - - name: backend-service - version: "0.0.6" - repository: https://denisneuling.github.io/cx-backend-service - alias: platobackendapplication - condition: platobackendapplication.enabled + # HashiCorp Vault - name: vault + alias: vault version: 0.20.0 repository: https://helm.releases.hashicorp.com - alias: platovault - condition: platovault.enabled + condition: install.vault + + # PostgreSQL - name: postgresql + alias: plato-postgresql version: 11.2.4 repository: https://charts.bitnami.com/bitnami - alias: platopostgresql - condition: platopostgresql.enabled + condition: install.postgresql - # SOKRATES CONNECTOR - - name: edc-controlplane - version: ">=0.0.1" - repository: "file://../../../../../../../charts/edc-controlplane" - alias: sokratesedccontrolplane - condition: sokratesedccontrolplane.enabled - - name: edc-dataplane - version: ">=0.0.1" - repository: "file://../../../../../../../charts/edc-dataplane" - alias: sokratesedcdataplane - condition: sokratesedcdataplane.enabled - - name: backend-service - version: "0.0.6" - repository: https://denisneuling.github.io/cx-backend-service - alias: sokratesbackendapplication - condition: sokratesbackendapplication.enabled - - name: vault - version: 0.20.0 - repository: https://helm.releases.hashicorp.com - alias: sokratesvault - condition: sokratesvault.enabled - name: postgresql + alias: sokrates-postgresql version: 11.2.4 repository: https://charts.bitnami.com/bitnami - alias: sokratespostgresql - condition: sokratespostgresql.enabled + condition: install.postgresql + + - name: backend-service + version: 0.0.6 + repository: https://denisneuling.github.io/cx-backend-service + alias: backend + condition: install.backendservice + + # MinIo + - name: minio + alias: minio + repository: https://charts.min.io + version: 4.1.0 + condition: install.minio diff --git a/edc-tests/src/main/resources/deployment/helm/all-in-one/README.md b/edc-tests/src/main/resources/deployment/helm/supporting-infrastructure/README.md similarity index 100% rename from edc-tests/src/main/resources/deployment/helm/all-in-one/README.md rename to edc-tests/src/main/resources/deployment/helm/supporting-infrastructure/README.md 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/supporting-infrastructure/diagrams/deployed_components.png similarity index 100% rename from edc-tests/src/main/resources/deployment/helm/all-in-one/diagrams/deployed_components.png rename to edc-tests/src/main/resources/deployment/helm/supporting-infrastructure/diagrams/deployed_components.png diff --git a/edc-tests/src/main/resources/deployment/helm/supporting-infrastructure/diagrams/deployed_components.puml b/edc-tests/src/main/resources/deployment/helm/supporting-infrastructure/diagrams/deployed_components.puml new file mode 100644 index 000000000..34c7da665 --- /dev/null +++ b/edc-tests/src/main/resources/deployment/helm/supporting-infrastructure/diagrams/deployed_components.puml @@ -0,0 +1,47 @@ +@startuml + +!define aliceColor 66CCFF +!define bobColor CCFF99 +!define dapsColor FFFF99 + +node PlatoSetup as "Plato Connector Setup" { + database PlatoPsql as "PostgreSQL" #bobColor + database PlatoKeyVault as "HashiCorp Vault" #bobColor + component PlatoConnector as "Eclipse Dataspace Connector" { + artifact BobControlPlane as "Control Plane" #bobColor + artifact BobDataPlane as "Data Plane" #bobColor + } + component PlatoBackendService as "Backend Application" #bobColor +} + +BobControlPlane -- PlatoPsql +BobControlPlane -- PlatoKeyVault +BobDataPlane -- PlatoKeyVault +BobDataPlane -left- BobControlPlane +BobControlPlane -left- PlatoBackendService + +node SokratesSetup as "Sokrates Connector Setup" { + database SokratesPsql as "PostgreSQL" #aliceColor + database SokratesKeyVault as "HashiCorp Vault" #aliceColor + component SokratesConnector as "Eclipse Dataspace Connector" { + artifact AliceControlPlane as "Control Plane" #aliceColor + artifact AliceDataPlane as "Data Plane" #aliceColor + } + component AliceBackendService as "Backend Application" #aliceColor +} + +AliceControlPlane -- SokratesPsql +AliceControlPlane -- SokratesKeyVault +AliceDataPlane -- SokratesKeyVault +AliceDataPlane -left- AliceControlPlane +AliceControlPlane -left- AliceBackendService + + +node IdentityProvider as "Identity Provider" { + component OmejdnDaps as "Omejdn DAPS" #dapsColor +} + +PlatoPsql -[hidden]down- OmejdnDaps +AliceControlPlane -[hidden]up- OmejdnDaps + +@enduml diff --git a/edc-tests/src/main/resources/deployment/helm/supporting-infrastructure/values.yaml b/edc-tests/src/main/resources/deployment/helm/supporting-infrastructure/values.yaml new file mode 100644 index 000000000..095c6b3d3 --- /dev/null +++ b/edc-tests/src/main/resources/deployment/helm/supporting-infrastructure/values.yaml @@ -0,0 +1,305 @@ +--- + +########### +# INSTALL # +########### +install: + daps: true + postgresql: true + vault: true + minio: true + backendservice: true + +################### +# Backend Service # +################### +backend: + fullnameOverride: "backend" + service: + frontend: + port: 80 + + +######## +# DAPS # +######## +idsdaps: + fullnameOverride: "ids-daps" + connectors: + - id: 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: + referringConnector: http://sokrates-edc-controlplane/BPNSOKRATES + # 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: 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: + referringConnector: http://plato-edc-controlplane/BPNPLATO + # 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----- + +############## +# PostgreSQL # +############## +plato-postgresql: + fullnameOverride: "plato-postgresql" + primary: + persistence: + enabled: false + readReplicas: + persistence: + enabled: false + auth: + database: "edc" + username: "user" + password: "password" +sokrates-postgresql: + fullnameOverride: "sokrates-postgresql" + primary: + persistence: + enabled: false + readReplicas: + persistence: + enabled: false + auth: + database: "edc" + username: "user" + password: "password" + +######### +# MINIO # +######### +minio: + fullnameOverride: minio + replicas: 2 + drivesPerNode: 0 + serviceAccount: + create: false + persistence: + size: 128Mi + resources: + requests: + memory: 128Mi + service: + type: NodePort + control: + port: 9000 + users: + - accessKey: platoqwerty123 + secretKey: platoqwerty123 + policy: customBucketPolicy + - accessKey: sokratesqwerty123 + secretKey: sokratesqwerty123 + policy: customBucketPolicy + buckets: + # in some cases the minio API acts strange if there exists no bucket at all + - name: dummybucket + policy: none + purge: true + policies: + - name: customBucketPolicy + statements: + - resources: + - 'arn:aws:s3:::*' + actions: + - "s3:PutObject" + - "s3:ListBucket" + - "s3:CreateBucket" + - "s3:GetObject" + - "s3:DeleteObject" + - "s3:DeleteBucket" + +######### +# VAULT # +######### +vault: + fullnameOverride: "vault" + injector: + enabled: false + server: + dev: + enabled: true + devRootToken: "root" + # Must be the same certificate that is configured in section 'ids-daps' + postStart: + - "sh" + - "-c" + - | + { + + sleep 5 + + /bin/vault kv put secret/plato/data-encryption-aes-keys content=H7j47H6vVQQOv/hbdAYz+w== + + cat << EOF | /bin/vault kv put secret/plato/daps/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/plato/daps/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 + + /bin/vault kv put secret/sokrates/data-encryption-aes-keys content=OcvxzWCK8ETSjt1jmZw3RA== + + cat << EOF | /bin/vault kv put secret/sokrates/daps/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/sokrates/daps/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 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 deleted file mode 100644 index 9a9d7e2ac..000000000 --- a/edc-tests/src/test/java/net/catenax/edc/tests/ConnectorFactory.java +++ /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 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/ConnectorSteps.java b/edc-tests/src/test/java/net/catenax/edc/tests/ConnectorSteps.java deleted file mode 100644 index 8af81d655..000000000 --- a/edc-tests/src/test/java/net/catenax/edc/tests/ConnectorSteps.java +++ /dev/null @@ -1,26 +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 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 deleted file mode 100644 index 6f16894ea..000000000 --- a/edc-tests/src/test/java/net/catenax/edc/tests/Constants.java +++ /dev/null @@ -1,29 +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.tests; - -import lombok.experimental.UtilityClass; - -@UtilityClass -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 deleted file mode 100644 index 699472a63..000000000 --- a/edc-tests/src/test/java/net/catenax/edc/tests/Environment.java +++ /dev/null @@ -1,59 +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.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; -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 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 dataManagementAuthKey; - @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; - - 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))) - .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))) - .build(); - } -} 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 deleted file mode 100644 index b402df750..000000000 --- a/edc-tests/src/test/java/net/catenax/edc/tests/data/Asset.java +++ /dev/null @@ -1,24 +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.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/Constraint.java b/edc-tests/src/test/java/net/catenax/edc/tests/data/Constraint.java deleted file mode 100644 index 1fbd2fa59..000000000 --- a/edc-tests/src/test/java/net/catenax/edc/tests/data/Constraint.java +++ /dev/null @@ -1,3 +0,0 @@ -package net.catenax.edc.tests.data; - -public interface Constraint {} 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 deleted file mode 100644 index dc2054a97..000000000 --- a/edc-tests/src/test/java/net/catenax/edc/tests/data/ContractDefinition.java +++ /dev/null @@ -1,29 +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.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/ContractNegotiation.java b/edc-tests/src/test/java/net/catenax/edc/tests/data/ContractNegotiation.java deleted file mode 100644 index aff80ff8b..000000000 --- a/edc-tests/src/test/java/net/catenax/edc/tests/data/ContractNegotiation.java +++ /dev/null @@ -1,25 +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 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 deleted file mode 100644 index 66b077601..000000000 --- a/edc-tests/src/test/java/net/catenax/edc/tests/data/ContractNegotiationState.java +++ /dev/null @@ -1,23 +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 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/ContractOffer.java b/edc-tests/src/test/java/net/catenax/edc/tests/data/ContractOffer.java deleted file mode 100644 index 329639fed..000000000 --- a/edc-tests/src/test/java/net/catenax/edc/tests/data/ContractOffer.java +++ /dev/null @@ -1,24 +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.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/PayMeConstraint.java b/edc-tests/src/test/java/net/catenax/edc/tests/data/PayMeConstraint.java deleted file mode 100644 index 3a12fe12f..000000000 --- a/edc-tests/src/test/java/net/catenax/edc/tests/data/PayMeConstraint.java +++ /dev/null @@ -1,26 +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 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 deleted file mode 100644 index d0f15b13f..000000000 --- a/edc-tests/src/test/java/net/catenax/edc/tests/data/Permission.java +++ /dev/null @@ -1,26 +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.tests.data; - -import java.util.List; -import lombok.NonNull; -import lombok.Value; - -@Value -public class Permission { - @NonNull String action; - String target; - - @NonNull List constraints; -} 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 deleted file mode 100644 index 2996fa032..000000000 --- a/edc-tests/src/test/java/net/catenax/edc/tests/data/Policy.java +++ /dev/null @@ -1,25 +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.tests.data; - -import java.util.List; -import lombok.NonNull; -import lombok.Value; - -@Value -public class Policy { - String id; - @NonNull List Permission; -} 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 deleted file mode 100644 index 0c4c9379c..000000000 --- a/edc-tests/src/test/java/net/catenax/edc/tests/features/RunCucumberTest.java +++ /dev/null @@ -1,22 +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.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/java/net/catenax/edc/tests/util/Timeouts.java b/edc-tests/src/test/java/net/catenax/edc/tests/util/Timeouts.java deleted file mode 100644 index 355de752d..000000000 --- a/edc-tests/src/test/java/net/catenax/edc/tests/util/Timeouts.java +++ /dev/null @@ -1,23 +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 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/java/net/catenax/edc/tests/AssetStepDefs.java b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/AssetStepDefs.java similarity index 57% rename from edc-tests/src/test/java/net/catenax/edc/tests/AssetStepDefs.java rename to edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/AssetStepDefs.java index 3c96d9de8..9bcb6d4eb 100644 --- a/edc-tests/src/test/java/net/catenax/edc/tests/AssetStepDefs.java +++ b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/AssetStepDefs.java @@ -1,18 +1,24 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.tests; +package org.eclipse.tractusx.edc.tests; import io.cucumber.datatable.DataTable; import io.cucumber.java.en.Given; @@ -20,7 +26,7 @@ import java.util.List; import java.util.Map; import java.util.UUID; -import net.catenax.edc.tests.data.Asset; +import org.eclipse.tractusx.edc.tests.data.Asset; public class AssetStepDefs { diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/BackendServiceBackendAPI.java b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/BackendServiceBackendAPI.java similarity index 90% rename from edc-tests/src/test/java/net/catenax/edc/tests/BackendServiceBackendAPI.java rename to edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/BackendServiceBackendAPI.java index f68d0da59..4295d9f10 100644 --- a/edc-tests/src/test/java/net/catenax/edc/tests/BackendServiceBackendAPI.java +++ b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/BackendServiceBackendAPI.java @@ -1,18 +1,24 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.tests; +package org.eclipse.tractusx.edc.tests; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; @@ -62,7 +68,7 @@ public BackendServiceBackendAPI(@NonNull final String backendServiceBackendApiUr /** Lists all files and directories associated by a backend-service path. */ @SneakyThrows - public List list(/*@Nullable*/ final String path) { + public List list(/* @Nullable */ final String path) { final URI uri = new URIBuilder(backendServiceBackendApiUrl) .setPath(Optional.ofNullable(path).orElse(PATH_ROOT)) diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/BackendServiceSteps.java b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/BackendServiceSteps.java similarity index 90% rename from edc-tests/src/test/java/net/catenax/edc/tests/BackendServiceSteps.java rename to edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/BackendServiceSteps.java index 1d5ec62ca..05960ddf7 100644 --- a/edc-tests/src/test/java/net/catenax/edc/tests/BackendServiceSteps.java +++ b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/BackendServiceSteps.java @@ -1,4 +1,4 @@ -package net.catenax.edc.tests; +package org.eclipse.tractusx.edc.tests; import io.cucumber.java.en.Given; diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/CatalogStepDefs.java b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/CatalogStepDefs.java similarity index 73% rename from edc-tests/src/test/java/net/catenax/edc/tests/CatalogStepDefs.java rename to edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/CatalogStepDefs.java index aa4468dbc..3bb8e9c5a 100644 --- a/edc-tests/src/test/java/net/catenax/edc/tests/CatalogStepDefs.java +++ b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/CatalogStepDefs.java @@ -1,18 +1,24 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.tests; +package org.eclipse.tractusx.edc.tests; import io.cucumber.datatable.DataTable; import io.cucumber.java.en.Then; @@ -20,7 +26,7 @@ import java.io.IOException; import java.util.List; import java.util.Map; -import net.catenax.edc.tests.data.ContractOffer; +import org.eclipse.tractusx.edc.tests.data.ContractOffer; import org.junit.jupiter.api.Assertions; public class CatalogStepDefs { diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/Connector.java b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/Connector.java similarity index 50% rename from edc-tests/src/test/java/net/catenax/edc/tests/Connector.java rename to edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/Connector.java index 137f007cf..9ca1bcb19 100644 --- a/edc-tests/src/test/java/net/catenax/edc/tests/Connector.java +++ b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/Connector.java @@ -1,23 +1,30 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.tests; +package org.eclipse.tractusx.edc.tests; import lombok.Getter; import lombok.NonNull; import lombok.RequiredArgsConstructor; -import net.catenax.edc.tests.util.DatabaseCleaner; +import org.eclipse.tractusx.edc.tests.util.DatabaseCleaner; +import org.eclipse.tractusx.edc.tests.util.S3Client; @RequiredArgsConstructor public class Connector { @@ -35,6 +42,9 @@ public class Connector { @Getter(lazy = true) private final DatabaseCleaner databaseCleaner = loadDatabaseCleaner(); + @Getter(lazy = true) + private final S3Client s3Client = createS3Client(); + private DataManagementAPI loadDataManagementAPI() { return new DataManagementAPI( environment.getDataManagementUrl(), environment.getDataManagementAuthKey()); @@ -50,4 +60,8 @@ private DatabaseCleaner loadDatabaseCleaner() { private BackendServiceBackendAPI loadBackendServiceBackendAPI() { return new BackendServiceBackendAPI(environment.getBackendServiceBackendApiUrl()); } + + private S3Client createS3Client() { + return new S3Client(environment); + } } diff --git a/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/ConnectorFactory.java b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/ConnectorFactory.java new file mode 100644 index 000000000..7a8ef81a1 --- /dev/null +++ b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/ConnectorFactory.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.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/org/eclipse/tractusx/edc/tests/ConnectorSteps.java b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/ConnectorSteps.java new file mode 100644 index 000000000..cde597521 --- /dev/null +++ b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/ConnectorSteps.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.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/org/eclipse/tractusx/edc/tests/Constants.java b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/Constants.java new file mode 100644 index 000000000..67b484e38 --- /dev/null +++ b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/Constants.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.edc.tests; + +import lombok.experimental.UtilityClass; + +@UtilityClass +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"; + public static final String EDC_AWS_ENDPOINT_OVERRIDE = "EDC_AWS_ENDPOINT_OVERRIDE"; + public static final String AWS_ACCESS_KEY_ID = "AWS_ACCESS_KEY_ID"; + public static final String AWS_SECRET_ACCESS_KEY = "AWS_SECRET_ACCESS_KEY"; +} diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/ContractDefinitionStepDefs.java b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/ContractDefinitionStepDefs.java similarity index 58% rename from edc-tests/src/test/java/net/catenax/edc/tests/ContractDefinitionStepDefs.java rename to edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/ContractDefinitionStepDefs.java index 11a097e68..d31653144 100644 --- a/edc-tests/src/test/java/net/catenax/edc/tests/ContractDefinitionStepDefs.java +++ b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/ContractDefinitionStepDefs.java @@ -1,25 +1,31 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.tests; +package org.eclipse.tractusx.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 net.catenax.edc.tests.data.ContractDefinition; +import org.eclipse.tractusx.edc.tests.data.ContractDefinition; public class ContractDefinitionStepDefs { diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/DataManagementAPI.java b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/DataManagementAPI.java similarity index 72% rename from edc-tests/src/test/java/net/catenax/edc/tests/DataManagementAPI.java rename to edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/DataManagementAPI.java index 5338a4f5e..92e84ef62 100644 --- a/edc-tests/src/test/java/net/catenax/edc/tests/DataManagementAPI.java +++ b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/DataManagementAPI.java @@ -1,18 +1,24 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.tests; +package org.eclipse.tractusx.edc.tests; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; @@ -24,24 +30,30 @@ import java.util.Map; import java.util.stream.Collectors; import lombok.Data; +import lombok.NonNull; 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.HttpClient; +import org.apache.http.client.methods.CloseableHttpResponse; 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.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; +import org.eclipse.tractusx.edc.tests.data.Asset; +import org.eclipse.tractusx.edc.tests.data.AssetWithDataAddress; +import org.eclipse.tractusx.edc.tests.data.BusinessPartnerNumberConstraint; +import org.eclipse.tractusx.edc.tests.data.Constraint; +import org.eclipse.tractusx.edc.tests.data.ContractDefinition; +import org.eclipse.tractusx.edc.tests.data.ContractNegotiation; +import org.eclipse.tractusx.edc.tests.data.ContractNegotiationState; +import org.eclipse.tractusx.edc.tests.data.ContractOffer; +import org.eclipse.tractusx.edc.tests.data.DataAddress; +import org.eclipse.tractusx.edc.tests.data.PayMeConstraint; +import org.eclipse.tractusx.edc.tests.data.Permission; +import org.eclipse.tractusx.edc.tests.data.Policy; +import org.eclipse.tractusx.edc.tests.data.TransferProcess; +import org.eclipse.tractusx.edc.tests.data.TransferProcessState; @Slf4j public class DataManagementAPI { @@ -51,10 +63,11 @@ public class DataManagementAPI { private static final String CONTRACT_DEFINITIONS_PATH = "/contractdefinitions"; private static final String CATALOG_PATH = "/catalog"; private static final String NEGOTIATIONS_PATH = "/contractnegotiations"; + private static final String TRANSFER_PATH = "/transferprocess"; private final String dataMgmtUrl; private final String dataMgmtAuthKey; - private final HttpClient httpClient; + private final CloseableHttpClient httpClient; public DataManagementAPI(String dataManagementUrl, String dataMgmtAuthKey) { this.httpClient = HttpClientBuilder.create().build(); @@ -104,6 +117,40 @@ public String initiateNegotiation( return response.getId(); } + public String initiateTransferProcess( + String receivingConnectorUrl, + String contractAgreementId, + String assetId, + DataAddress dataAddress) + throws IOException { + final DataManagementApiTransfer transfer = new DataManagementApiTransfer(); + + transfer.connectorAddress = receivingConnectorUrl; + transfer.contractId = contractAgreementId; + transfer.assetId = assetId; + transfer.transferType = new DataManagementApiTransferType(); + transfer.managedResources = false; + transfer.dataDestination = mapDataAddress(dataAddress); + transfer.protocol = "ids-multipart"; + + final DataManagementApiTransferResponse response = + post(TRANSFER_PATH, transfer, new TypeToken() {}); + + if (response == null) + throw new RuntimeException( + "Initiated transfer process. Connector did not answer with transfer process ID."); + + log.info("Initiated transfer process ( id= " + response.getId() + " )"); + + return response.getId(); + } + + public TransferProcess getTransferProcess(String id) throws IOException { + final DataManagementApiTransferProcess transferProcess = + get(TRANSFER_PATH + "/" + id, new TypeToken() {}); + return mapTransferProcess(transferProcess); + } + public ContractNegotiation getNegotiation(String id) throws IOException { final DataManagementApiNegotiation negotiation = get(NEGOTIATIONS_PATH + "/" + id, new TypeToken() {}); @@ -126,6 +173,14 @@ public void createAsset(Asset asset) throws IOException { post(ASSET_PATH, assetCreate); } + public void createAsset(AssetWithDataAddress assetWithDataAddress) throws IOException { + final DataManagementApiAssetCreate assetCreate = new DataManagementApiAssetCreate(); + assetCreate.asset = mapAsset(assetWithDataAddress.getAsset()); + assetCreate.dataAddress = mapDataAddress(assetWithDataAddress.getDataAddress()); + + post(ASSET_PATH, assetCreate); + } + public void createPolicy(Policy policy) throws IOException { post(POLICY_PATH, mapPolicyDefinition(policy)); } @@ -162,22 +217,27 @@ private T post(String path, Object object, TypeToken typeToken) throws IO log.debug("POST Payload: " + json); post.setEntity(new StringEntity(json)); + final CloseableHttpResponse response = sendRequest(post); + + T responseJson = null; + if (!typeToken.equals(new TypeToken() {})) { + final byte[] responseBytes = response.getEntity().getContent().readAllBytes(); + responseJson = + new Gson() + .fromJson(new String(responseBytes, StandardCharsets.UTF_8), typeToken.getType()); + } - final HttpResponse response = sendRequest(post); - - if (typeToken.equals(new TypeToken() {})) return null; + response.close(); - final byte[] responseJson = response.getEntity().getContent().readAllBytes(); - return new Gson() - .fromJson(new String(responseJson, StandardCharsets.UTF_8), typeToken.getType()); + return responseJson; } - private HttpResponse sendRequest(HttpRequestBase request) throws IOException { + private CloseableHttpResponse sendRequest(HttpRequestBase request) throws IOException { request.addHeader("X-Api-Key", dataMgmtAuthKey); log.debug(String.format("Send %-6s %s", request.getMethod(), request.getURI())); - final HttpResponse response = httpClient.execute(request); + final CloseableHttpResponse response = httpClient.execute(request); if (200 > response.getStatusLine().getStatusCode() || response.getStatusLine().getStatusCode() >= 300) { throw new RuntimeException( @@ -208,7 +268,28 @@ private ContractNegotiation mapNegotiation(DataManagementApiNegotiation negotiat state = ContractNegotiationState.UNKNOWN; } - return new ContractNegotiation(negotiation.id, negotiation.agreementId, state); + return new ContractNegotiation(negotiation.id, negotiation.contractAgreementId, state); + } + + private TransferProcess mapTransferProcess(DataManagementApiTransferProcess transferProcess) { + + TransferProcessState state; + + switch (transferProcess.state) { + case "COMPLETED": + state = TransferProcessState.COMPLETED; + break; + default: + state = TransferProcessState.UNKNOWN; + } + + return new TransferProcess(transferProcess.id, state); + } + + private DataManagementApiDataAddress mapDataAddress(@NonNull DataAddress dataAddress) { + final DataManagementApiDataAddress apiObject = new DataManagementApiDataAddress(); + apiObject.setProperties(dataAddress.getProperties()); + return apiObject; } private DataManagementApiAsset mapAsset(Asset asset) { @@ -369,7 +450,13 @@ private static class DataManagementApiNegotiationPayload { private static class DataManagementApiNegotiation { private String id; private String state; - private String agreementId; + private String contractAgreementId; + } + + @Data + private static class DataManagementApiTransferProcess { + private String id; + private String state; } @Data @@ -379,6 +466,29 @@ private static class DataManagementApiOffer { private DataManagementApiPolicy policy; } + @Data + private static class DataManagementApiTransfer { + private String connectorId = "foo"; + private String connectorAddress; + private String contractId; + private String assetId; + private String protocol; + private DataManagementApiDataAddress dataDestination; + private boolean managedResources; + private DataManagementApiTransferType transferType; + } + + @Data + private static class DataManagementApiTransferType { + private String contentType = "application/octet-stream"; + private boolean isFinite = true; + } + + @Data + private static class DataManagementApiTransferResponse { + private String id; + } + @Data private static class DataManagementApiAssetCreate { private DataManagementApiAsset asset; diff --git a/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/Environment.java b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/Environment.java new file mode 100644 index 000000000..d1a199fd1 --- /dev/null +++ b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/Environment.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.edc.tests; + +import static org.eclipse.tractusx.edc.tests.Constants.AWS_ACCESS_KEY_ID; +import static org.eclipse.tractusx.edc.tests.Constants.AWS_SECRET_ACCESS_KEY; +import static org.eclipse.tractusx.edc.tests.Constants.BACKEND_SERVICE_BACKEND_API_URL; +import static org.eclipse.tractusx.edc.tests.Constants.DATABASE_PASSWORD; +import static org.eclipse.tractusx.edc.tests.Constants.DATABASE_URL; +import static org.eclipse.tractusx.edc.tests.Constants.DATABASE_USER; +import static org.eclipse.tractusx.edc.tests.Constants.DATA_MANAGEMENT_API_AUTH_KEY; +import static org.eclipse.tractusx.edc.tests.Constants.DATA_MANAGEMENT_URL; +import static org.eclipse.tractusx.edc.tests.Constants.DATA_PLANE_URL; +import static org.eclipse.tractusx.edc.tests.Constants.EDC_AWS_ENDPOINT_OVERRIDE; +import static org.eclipse.tractusx.edc.tests.Constants.IDS_URL; + +import java.util.Locale; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NonNull; +import lombok.ToString; + +@Builder(access = AccessLevel.PRIVATE) +@Getter +@ToString +public class Environment { + @NonNull private final String dataManagementAuthKey; + @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; + @NonNull private final String awsEndpointOverride; + @NonNull private final String awsAccessKey; + @NonNull private final String awsSecretAccessKey; + + 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))) + .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))) + .awsEndpointOverride(System.getenv(EDC_AWS_ENDPOINT_OVERRIDE)) + .awsAccessKey(System.getenv(String.join("_", name, AWS_ACCESS_KEY_ID))) + .awsSecretAccessKey(System.getenv(String.join("_", name, AWS_SECRET_ACCESS_KEY))) + .build(); + } +} diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/NegotiationSteps.java b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/NegotiationSteps.java similarity index 64% rename from edc-tests/src/test/java/net/catenax/edc/tests/NegotiationSteps.java rename to edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/NegotiationSteps.java index bf4bcdd12..26e143347 100644 --- a/edc-tests/src/test/java/net/catenax/edc/tests/NegotiationSteps.java +++ b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/NegotiationSteps.java @@ -1,18 +1,24 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.tests; +package org.eclipse.tractusx.edc.tests; import static org.awaitility.Awaitility.await; @@ -26,11 +32,11 @@ 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.eclipse.tractusx.edc.tests.data.ContractNegotiation; +import org.eclipse.tractusx.edc.tests.data.ContractNegotiationState; +import org.eclipse.tractusx.edc.tests.data.Permission; +import org.eclipse.tractusx.edc.tests.data.Policy; +import org.eclipse.tractusx.edc.tests.util.Timeouts; import org.junit.jupiter.api.Assertions; @Slf4j @@ -70,7 +76,7 @@ public void assertLastNegotiationDeclined() { Assertions.assertEquals(ContractNegotiationState.DECLINED, lastInitiatedNegotiation.getState()); } - private boolean isNegotiationComplete(DataManagementAPI dataManagementAPI, String negotiationId) + static boolean isNegotiationComplete(DataManagementAPI dataManagementAPI, String negotiationId) throws IOException { var negotiation = dataManagementAPI.getNegotiation(negotiationId); return negotiation != null diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/PolicyStepDefs.java b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/PolicyStepDefs.java similarity index 55% rename from edc-tests/src/test/java/net/catenax/edc/tests/PolicyStepDefs.java rename to edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/PolicyStepDefs.java index 81f173c09..c9569e54f 100644 --- a/edc-tests/src/test/java/net/catenax/edc/tests/PolicyStepDefs.java +++ b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/PolicyStepDefs.java @@ -1,29 +1,35 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial API and Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.tests; +package org.eclipse.tractusx.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 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; +import org.eclipse.tractusx.edc.tests.data.BusinessPartnerNumberConstraint; +import org.eclipse.tractusx.edc.tests.data.Constraint; +import org.eclipse.tractusx.edc.tests.data.PayMeConstraint; +import org.eclipse.tractusx.edc.tests.data.Permission; +import org.eclipse.tractusx.edc.tests.data.Policy; public class PolicyStepDefs { diff --git a/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/S3FileTransferStepsDefs.java b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/S3FileTransferStepsDefs.java new file mode 100644 index 000000000..92e0c063b --- /dev/null +++ b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/S3FileTransferStepsDefs.java @@ -0,0 +1,198 @@ +/* Copyright (c) 2022 ZF Friedrichshafen AG + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.edc.tests; + +import static org.awaitility.Awaitility.await; +import static org.eclipse.tractusx.edc.tests.NegotiationSteps.isNegotiationComplete; +import static org.junit.jupiter.api.Assertions.fail; + +import io.cucumber.datatable.DataTable; +import io.cucumber.java.AfterAll; +import io.cucumber.java.en.Given; +import io.cucumber.java.en.Then; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.time.Duration; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.eclipse.tractusx.edc.tests.data.Asset; +import org.eclipse.tractusx.edc.tests.data.AssetWithDataAddress; +import org.eclipse.tractusx.edc.tests.data.DataAddress; +import org.eclipse.tractusx.edc.tests.data.Permission; +import org.eclipse.tractusx.edc.tests.data.Policy; +import org.eclipse.tractusx.edc.tests.data.TransferProcessState; +import org.eclipse.tractusx.edc.tests.util.S3Client; +import org.eclipse.tractusx.edc.tests.util.Timeouts; +import org.junit.jupiter.api.Assertions; + +public class S3FileTransferStepsDefs { + + @Given("'{connector}' has an empty storage bucket called {string}") + public void hasEmptyStorageBucket(Connector connector, String bucketName) { + S3Client s3 = connector.getS3Client(); + + s3.createBucket(bucketName); + + Assertions.assertTrue(s3.listBuckets().contains(bucketName)); + Assertions.assertEquals(0, s3.listBucketContent(bucketName).size()); + } + + private File fileToTransfer; + + @Given("'{connector}' has a storage bucket called {string} with the file called {string}") + public void hasAStorageBucketWithTheFile(Connector connector, String bucketName, String fileName) + throws IOException { + + S3Client s3 = connector.getS3Client(); + s3.createBucket(bucketName); + fileToTransfer = s3.uploadFile(bucketName, fileName); + + Set bucketContent = s3.listBucketContent(bucketName); + + Assertions.assertEquals(1, bucketContent.size()); + Assertions.assertTrue(bucketContent.contains(fileName)); + } + + @Given("'{connector}' has the following S3 assets") + public void hasAssets(Connector connector, DataTable table) { + final DataManagementAPI api = connector.getDataManagementAPI(); + + parseDataTable(table) + .forEach( + asset -> { + try { + api.createAsset(asset); + } catch (IOException e) { + fail(e.getMessage()); + } + }); + } + + private String assetId; + private String contractNegotiationId; + + @Then("'{connector}' negotiates the contract successfully with '{connector}'") + public void negotiateContract(Connector sender, Connector receiver, DataTable dataTable) + throws IOException { + + String definitionId = dataTable.asMaps().get(0).get("contract offer id"); + assetId = dataTable.asMaps().get(0).get("asset id"); + String policyId = dataTable.asMaps().get(0).get("policy id"); + + final Policy policy = + new Policy(policyId, List.of(new Permission("USE", null, new ArrayList<>()))); + + final DataManagementAPI dataManagementAPI = sender.getDataManagementAPI(); + final String receiverIdsUrl = receiver.getEnvironment().getIdsUrl() + "/data"; + + final String negotiationId = + dataManagementAPI.initiateNegotiation(receiverIdsUrl, definitionId, assetId, policy); + + await() + .pollDelay(Duration.ofMillis(500)) + .atMost(Timeouts.CONTRACT_NEGOTIATION) + .until(() -> isNegotiationComplete(dataManagementAPI, negotiationId)); + + contractNegotiationId = dataManagementAPI.getNegotiation(negotiationId).getAgreementId(); + } + + @Then("'{connector}' initiate transfer process from '{connector}'") + public void initiateTransferProcess(Connector sender, Connector receiver, DataTable dataTable) + throws IOException { + DataAddress dataAddress = createDataAddress(dataTable.asMaps().get(0)); + + final DataManagementAPI dataManagementAPI = sender.getDataManagementAPI(); + final String receiverIdsUrl = receiver.getEnvironment().getIdsUrl() + "/data"; + + final String transferProcessId = + dataManagementAPI.initiateTransferProcess( + receiverIdsUrl, contractNegotiationId, assetId, dataAddress); + + await() + .pollDelay(Duration.ofMillis(500)) + .atMost(Timeouts.FILE_TRANSFER) + .until(() -> isTransferComplete(dataManagementAPI, transferProcessId)); + + Assertions.assertNotNull(transferProcessId); + } + + private static final String COMPLETION_MARKER = ".complete"; + + @Then("'{connector}' has a storage bucket called {string} with transferred file called {string}") + public void consumerHasAStorageBucketWithFileTransferred( + Connector connector, String bucketName, String fileName) throws IOException { + S3Client s3 = connector.getS3Client(); + await() + .pollDelay(Duration.ofMillis(500)) + .atMost(Timeouts.FILE_TRANSFER) + .until(() -> isFilePresent(s3, bucketName, fileName + COMPLETION_MARKER)); + + Set bucketContent = s3.listBucketContent(bucketName); + + Assertions.assertEquals(2, bucketContent.size()); + Assertions.assertTrue(bucketContent.contains(fileName)); + Assertions.assertArrayEquals( + Files.readAllBytes(fileToTransfer.toPath()), + Files.readAllBytes(s3.downloadFile(bucketName, fileName).toPath())); + } + + private boolean isFilePresent(S3Client s3, String bucketName, String fileName) { + return s3.listBucketContent(bucketName).contains(fileName); + } + + private List parseDataTable(DataTable table) { + final List assetsWithDataAddresses = new ArrayList<>(); + + for (Map map : table.asMaps()) { + String id = map.get("id"); + String description = map.get("description"); + assetsWithDataAddresses.add( + new AssetWithDataAddress(new Asset(id, description), createDataAddress(map))); + } + + return assetsWithDataAddresses; + } + + private DataAddress createDataAddress(Map map) { + Map properties = new HashMap<>(); + properties.put("type", map.get("data_address_type")); + properties.put("bucketName", map.get("data_address_s3_bucket_name")); + properties.put("region", map.get("data_address_s3_region")); + properties.put("keyName", map.get("data_address_s3_key_name")); + return new DataAddress(properties); + } + + private static boolean isTransferComplete( + DataManagementAPI dataManagementAPI, String transferProcessId) throws IOException { + var transferProcess = dataManagementAPI.getTransferProcess(transferProcessId); + return transferProcess != null + && transferProcess.getState().equals(TransferProcessState.COMPLETED); + } + + @AfterAll + public static void bucketsCleanup() { + S3Client s3 = new S3Client(Environment.byName("Sokrates")); + s3.deleteAllBuckets(); + } +} diff --git a/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/Asset.java b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/Asset.java new file mode 100644 index 000000000..1c277ac0b --- /dev/null +++ b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/Asset.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.eclipse.tractusx.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/org/eclipse/tractusx/edc/tests/data/AssetWithDataAddress.java b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/AssetWithDataAddress.java new file mode 100644 index 000000000..f35557cf8 --- /dev/null +++ b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/AssetWithDataAddress.java @@ -0,0 +1,31 @@ +/* Copyright (c) 2022 ZF Friedrichshafen AG + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.edc.tests.data; + +import lombok.NonNull; +import lombok.Value; + +@Value +public class AssetWithDataAddress { + + @NonNull Asset asset; + + @NonNull DataAddress dataAddress; +} diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/data/BusinessPartnerNumberConstraint.java b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/BusinessPartnerNumberConstraint.java similarity index 78% rename from edc-tests/src/test/java/net/catenax/edc/tests/data/BusinessPartnerNumberConstraint.java rename to edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/BusinessPartnerNumberConstraint.java index d7cee2be9..b9c64d158 100644 --- a/edc-tests/src/test/java/net/catenax/edc/tests/data/BusinessPartnerNumberConstraint.java +++ b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/BusinessPartnerNumberConstraint.java @@ -1,4 +1,4 @@ -package net.catenax.edc.tests.data; +package org.eclipse.tractusx.edc.tests.data; import lombok.NonNull; import lombok.Value; diff --git a/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/Constraint.java b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/Constraint.java new file mode 100644 index 000000000..c63fbed8a --- /dev/null +++ b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/Constraint.java @@ -0,0 +1,3 @@ +package org.eclipse.tractusx.edc.tests.data; + +public interface Constraint {} diff --git a/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractDefinition.java b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractDefinition.java new file mode 100644 index 000000000..a4102af25 --- /dev/null +++ b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractDefinition.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.eclipse.tractusx.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/org/eclipse/tractusx/edc/tests/data/ContractNegotiation.java b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractNegotiation.java new file mode 100644 index 000000000..109249744 --- /dev/null +++ b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractNegotiation.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.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/org/eclipse/tractusx/edc/tests/data/ContractNegotiationState.java b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractNegotiationState.java new file mode 100644 index 000000000..9acd4368f --- /dev/null +++ b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractNegotiationState.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.edc.tests.data; + +public enum ContractNegotiationState { + UNKNOWN, + INITIAL, + DECLINED, + CONFIRMED, + ERROR +} diff --git a/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractOffer.java b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractOffer.java new file mode 100644 index 000000000..75dfd8d27 --- /dev/null +++ b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractOffer.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.eclipse.tractusx.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/org/eclipse/tractusx/edc/tests/data/DataAddress.java b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/DataAddress.java new file mode 100644 index 000000000..61222c954 --- /dev/null +++ b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/DataAddress.java @@ -0,0 +1,30 @@ +/* Copyright (c) 2022 ZF Friedrichshafen AG + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.edc.tests.data; + +import java.util.Map; +import lombok.NonNull; +import lombok.Value; + +@Value +public class DataAddress { + + @NonNull Map properties; +} diff --git a/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/PayMeConstraint.java b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/PayMeConstraint.java new file mode 100644 index 000000000..4376346b2 --- /dev/null +++ b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/PayMeConstraint.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.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/org/eclipse/tractusx/edc/tests/data/Permission.java b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/Permission.java new file mode 100644 index 000000000..128e6686f --- /dev/null +++ b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/Permission.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.eclipse.tractusx.edc.tests.data; + +import java.util.List; +import lombok.NonNull; +import lombok.Value; + +@Value +public class Permission { + @NonNull String action; + String target; + + @NonNull List constraints; +} diff --git a/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/Policy.java b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/Policy.java new file mode 100644 index 000000000..27ea65d7a --- /dev/null +++ b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/Policy.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.edc.tests.data; + +import java.util.List; +import lombok.NonNull; +import lombok.Value; + +@Value +public class Policy { + String id; + @NonNull List Permission; +} diff --git a/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/TransferProcess.java b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/TransferProcess.java new file mode 100644 index 000000000..28be5157a --- /dev/null +++ b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/TransferProcess.java @@ -0,0 +1,29 @@ +/* Copyright (c) 2022 ZF Friedrichshafen AG + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.edc.tests.data; + +import lombok.NonNull; +import lombok.Value; + +@Value +public class TransferProcess { + @NonNull String id; + @NonNull TransferProcessState state; +} diff --git a/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/TransferProcessState.java b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/TransferProcessState.java new file mode 100644 index 000000000..dfb567e73 --- /dev/null +++ b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/data/TransferProcessState.java @@ -0,0 +1,25 @@ +/* Copyright (c) 2022 ZF Friedrichshafen AG + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.edc.tests.data; + +public enum TransferProcessState { + COMPLETED, + UNKNOWN +} diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/features/ParameterTypes.java b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/features/ParameterTypes.java similarity index 56% rename from edc-tests/src/test/java/net/catenax/edc/tests/features/ParameterTypes.java rename to edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/features/ParameterTypes.java index 29a3b8072..7995e8d89 100644 --- a/edc-tests/src/test/java/net/catenax/edc/tests/features/ParameterTypes.java +++ b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/features/ParameterTypes.java @@ -1,8 +1,8 @@ -package net.catenax.edc.tests.features; +package org.eclipse.tractusx.edc.tests.features; import io.cucumber.java.ParameterType; -import net.catenax.edc.tests.Connector; -import net.catenax.edc.tests.ConnectorFactory; +import org.eclipse.tractusx.edc.tests.Connector; +import org.eclipse.tractusx.edc.tests.ConnectorFactory; public class ParameterTypes { diff --git a/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/features/RunCucumberTest.java b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/features/RunCucumberTest.java new file mode 100644 index 000000000..d9ab3254c --- /dev/null +++ b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/features/RunCucumberTest.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.edc.tests.features; + +import org.junit.platform.suite.api.SelectClasspathResource; +import org.junit.platform.suite.api.Suite; + +@Suite +@SelectClasspathResource("org/eclipse/tractusx/edc/tests/features") +public class RunCucumberTest {} diff --git a/edc-tests/src/test/java/net/catenax/edc/tests/util/DatabaseCleaner.java b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/util/DatabaseCleaner.java similarity index 51% rename from edc-tests/src/test/java/net/catenax/edc/tests/util/DatabaseCleaner.java rename to edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/util/DatabaseCleaner.java index 530278e75..53aececa9 100644 --- a/edc-tests/src/test/java/net/catenax/edc/tests/util/DatabaseCleaner.java +++ b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/util/DatabaseCleaner.java @@ -1,18 +1,24 @@ /* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * - * SPDX-License-Identifier: Apache-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 * - * Contributors: - * Mercedes-Benz Tech Innovation GmbH - Initial Implementation + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. * + * SPDX-License-Identifier: Apache-2.0 */ -package net.catenax.edc.tests.util; +package org.eclipse.tractusx.edc.tests.util; import java.sql.Connection; import java.sql.DriverManager; diff --git a/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/util/S3Client.java b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/util/S3Client.java new file mode 100644 index 000000000..c2779ce0d --- /dev/null +++ b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/util/S3Client.java @@ -0,0 +1,124 @@ +/* Copyright (c) 2022 ZF Friedrichshafen AG + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.edc.tests.util; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.tractusx.edc.tests.Environment; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.core.ResponseBytes; +import software.amazon.awssdk.core.sync.RequestBody; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.model.Bucket; +import software.amazon.awssdk.services.s3.model.BucketAlreadyOwnedByYouException; +import software.amazon.awssdk.services.s3.model.CreateBucketRequest; +import software.amazon.awssdk.services.s3.model.DeleteBucketRequest; +import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; +import software.amazon.awssdk.services.s3.model.GetObjectRequest; +import software.amazon.awssdk.services.s3.model.GetObjectResponse; +import software.amazon.awssdk.services.s3.model.ListObjectsRequest; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; +import software.amazon.awssdk.services.s3.model.S3Object; + +@Slf4j +public class S3Client { + + private final software.amazon.awssdk.services.s3.S3Client s3; + + public S3Client(Environment environment) { + + s3 = + software.amazon.awssdk.services.s3.S3Client.builder() + .region(Region.US_EAST_1) + .forcePathStyle(true) + .endpointOverride(URI.create(environment.getAwsEndpointOverride())) + .credentialsProvider( + StaticCredentialsProvider.create( + AwsBasicCredentials.create( + environment.getAwsAccessKey(), environment.getAwsSecretAccessKey()))) + .build(); + } + + public void createBucket(String bucketName) { + try { + s3.createBucket(CreateBucketRequest.builder().bucket(bucketName).build()); + } catch (BucketAlreadyOwnedByYouException e) { + log.info("'{}' bucket already owned - skipped bucket creation", bucketName); + } + } + + public File uploadFile(String bucketName, String fileName) throws IOException { + File tempFile = File.createTempFile(fileName, null); + Files.write( + tempFile.toPath(), "Will fail if the file has no content".getBytes(StandardCharsets.UTF_8)); + + s3.putObject( + PutObjectRequest.builder().bucket(bucketName).key(fileName).build(), + RequestBody.fromFile(tempFile)); + + return tempFile; + } + + public List listBuckets() { + return s3.listBuckets().buckets().stream().map(Bucket::name).collect(Collectors.toList()); + } + + public Set listBucketContent(String bucketName) { + return s3 + .listObjects(ListObjectsRequest.builder().bucket(bucketName).build()) + .contents() + .stream() + .map(S3Object::key) + .collect(Collectors.toSet()); + } + + public File downloadFile(String bucketName, String fileName) throws IOException { + ResponseBytes objectAsBytes = + s3.getObjectAsBytes(GetObjectRequest.builder().bucket(bucketName).key(fileName).build()); + + return Files.write(File.createTempFile(fileName, null).toPath(), objectAsBytes.asByteArray()) + .toFile(); + } + + public void deleteAllBuckets() { + List buckets = s3.listBuckets().buckets(); + buckets.forEach(this::clearBucket); + buckets.forEach( + bucket -> s3.deleteBucket(DeleteBucketRequest.builder().bucket(bucket.name()).build())); + } + + private void clearBucket(Bucket bucket) { + String bucketName = bucket.name(); + s3.listObjects(ListObjectsRequest.builder().bucket(bucketName).build()) + .contents() + .forEach( + s3Object -> + s3.deleteObject( + DeleteObjectRequest.builder().bucket(bucketName).key(s3Object.key()).build())); + } +} diff --git a/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/util/Timeouts.java b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/util/Timeouts.java new file mode 100644 index 000000000..0d67c0e5a --- /dev/null +++ b/edc-tests/src/test/java/org/eclipse/tractusx/edc/tests/util/Timeouts.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH + * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.eclipse.tractusx.edc.tests.util; + +import java.time.Duration; + +public class Timeouts { + private Timeouts() {} + + public static final Duration CONTRACT_NEGOTIATION = Duration.ofSeconds(90); + public static final Duration FILE_TRANSFER = Duration.ofSeconds(90); +} diff --git a/edc-tests/src/test/resources/logback-test.xml b/edc-tests/src/test/resources/logback-test.xml index b175f4c6c..d8c11cbef 100644 --- a/edc-tests/src/test/resources/logback-test.xml +++ b/edc-tests/src/test/resources/logback-test.xml @@ -1,13 +1,22 @@ 4.0.0 - net.catenax.edc + org.eclipse.tractusx.edc product-edc-parent - 0.1.2 + 0.1.3 pom product-edc @@ -34,7 +41,7 @@ Apache License 2.0 - https://www.apache.org/licenses/LICENSE-2.0.txt + https://www.apache.org/licenses/LICENSE-2.0txt repo @@ -48,7 +55,7 @@ 3.3.0 3.3.0 - 2.27.1 + 2.28.0 3.1.0 3.4.1 2.0.0 @@ -66,25 +73,26 @@ 0.0.1-SNAPSHOT - 0.0.1-20220922-SNAPSHOT - 1.2.2 - 42.5.0 - 9.3.1 + 0.0.1-20221006-SNAPSHOT + 1.2.8 + 42.5.1 + 9.8.3 8.23 + 2.14.0-rc2 5.9.1 1.8.2 - 7.8.0 - 5.2.0 + 7.9.0 + 5.3.0 1.1.0 - 4.8.0 + 4.9.0 1.18.24 1.70 4.9.3 - 1.17.3 - 2.0.0-beta1 - 1.4.1 + 1.17.6 + 2.0.3 + 1.4.5 2.2 @@ -321,73 +329,83 @@ - net.catenax.edc.extensions + org.eclipse.tractusx.edc.extensions business-partner-validation ${project.version} - net.catenax.edc.extensions + org.eclipse.tractusx.edc.extensions postgresql-migration ${project.version} - net.catenax.edc.extensions + org.eclipse.tractusx.edc.extensions hashicorp-vault ${project.version} - net.catenax.edc.extensions + org.eclipse.tractusx.edc.extensions dataplane-selector-configuration ${project.version} - net.catenax.edc.extensions + org.eclipse.tractusx.edc.extensions data-encryption ${project.version} - net.catenax.edc.extensions + org.eclipse.tractusx.edc.extensions cx-oauth2 ${project.version} + + org.eclipse.tractusx.edc.extensions + control-plane-adapter + ${project.version} + - net.catenax.edc + org.eclipse.tractusx.edc edc-controlplane-base ${project.version} - net.catenax.edc + org.eclipse.tractusx.edc edc-dataplane-base ${project.version} - net.catenax.edc + org.eclipse.tractusx.edc edc-controlplane-memory ${project.version} - net.catenax.edc + org.eclipse.tractusx.edc + edc-controlplane-memory-hashicorp-vault + ${project.version} + + + org.eclipse.tractusx.edc edc-controlplane-postgresql ${project.version} - net.catenax.edc + org.eclipse.tractusx.edc edc-controlplane-postgresql-hashicorp-vault ${project.version} - net.catenax.edc + org.eclipse.tractusx.edc edc-dataplane-azure-vault ${project.version} - net.catenax.edc + org.eclipse.tractusx.edc edc-dataplane-hashicorp-vault ${project.version} @@ -469,6 +487,24 @@ org.eclipse.dataspaceconnector asset-index-sql ${org.eclipse.dataspaceconnector.version} + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + org.eclipse.dataspaceconnector @@ -479,6 +515,24 @@ org.eclipse.dataspaceconnector auth-spi ${org.eclipse.dataspaceconnector.version} + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + org.eclipse.dataspaceconnector @@ -514,6 +568,24 @@ org.eclipse.dataspaceconnector azure-vault ${org.eclipse.dataspaceconnector.version} + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + org.eclipse.dataspaceconnector @@ -559,6 +631,24 @@ org.eclipse.dataspaceconnector common-sql ${org.eclipse.dataspaceconnector.version} + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + org.eclipse.dataspaceconnector @@ -619,6 +709,24 @@ org.eclipse.dataspaceconnector control-plane-core ${org.eclipse.dataspaceconnector.version} + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + org.eclipse.dataspaceconnector @@ -654,6 +762,24 @@ org.eclipse.dataspaceconnector core-spi ${org.eclipse.dataspaceconnector.version} + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + org.eclipse.dataspaceconnector @@ -719,6 +845,24 @@ org.eclipse.dataspaceconnector data-plane-selector-spi ${org.eclipse.dataspaceconnector.version} + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + org.eclipse.dataspaceconnector @@ -739,6 +883,24 @@ org.eclipse.dataspaceconnector data-plane-transfer-spi ${org.eclipse.dataspaceconnector.version} + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + org.eclipse.dataspaceconnector @@ -769,6 +931,24 @@ org.eclipse.dataspaceconnector filesystem-configuration ${org.eclipse.dataspaceconnector.version} + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + org.eclipse.dataspaceconnector @@ -914,11 +1094,47 @@ org.eclipse.dataspaceconnector oauth2-spi ${org.eclipse.dataspaceconnector.version} + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + org.eclipse.dataspaceconnector jwt-spi ${org.eclipse.dataspaceconnector.version} + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + org.eclipse.dataspaceconnector @@ -944,6 +1160,24 @@ org.eclipse.dataspaceconnector policy-engine-spi ${org.eclipse.dataspaceconnector.version} + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + org.eclipse.dataspaceconnector @@ -954,6 +1188,24 @@ org.eclipse.dataspaceconnector policy-spi ${org.eclipse.dataspaceconnector.version} + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + org.eclipse.dataspaceconnector @@ -1024,6 +1276,24 @@ org.eclipse.dataspaceconnector transaction-datasource-spi ${org.eclipse.dataspaceconnector.version} + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + org.eclipse.dataspaceconnector @@ -1034,6 +1304,24 @@ org.eclipse.dataspaceconnector transaction-spi ${org.eclipse.dataspaceconnector.version} + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + org.eclipse.dataspaceconnector @@ -1084,12 +1372,48 @@ org.eclipse.dataspaceconnector junit ${org.eclipse.dataspaceconnector.version} + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + org.eclipse.dataspaceconnector junit ${org.eclipse.dataspaceconnector.version} test-fixtures + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + @@ -1114,6 +1438,13 @@ pom import + + com.fasterxml.jackson + jackson-bom + ${com.fasterxml.jackson.version} + pom + import +