From c8f58286cdb89cdd4eb4690bc684cb5b7954fb80 Mon Sep 17 00:00:00 2001 From: Ernst-Christoph Schrewe Date: Thu, 23 Nov 2023 14:27:08 +0100 Subject: [PATCH 01/11] chore: squash commits --- NOTICE.md | 4 + backend/Dockerfile | 2 +- backend/INSTALL.md | 46 +- .../AssetCreatorCommandLineRunner.java | 52 +- .../DataInjectionCommandLineRunner.java | 30 +- .../api/logic/service/VariablesService.java | 109 +- .../common/edc/controller/EdcController.java | 153 +- .../EndpointDataReferenceReceiver.java | 58 +- .../edc/logic/service/EdcAdapterService.java | 621 ++-- .../service/EndpointDataReferenceService.java | 10 +- .../service/ExternalConnectorService.java | 2 +- .../edc/logic/util/EDCRequestBodyBuilder.java | 330 +- .../controller/DashboardController.java | 40 +- .../logic/service/PartnerServiceImpl.java | 6 +- .../ProductStockRequestApiController.java | 12 +- .../ProductStockResponseApiController.java | 17 +- .../stock/controller/StockViewController.java | 5 +- .../ProductStockRequestApiService.java | 4 +- .../ProductStockRequestApiServiceImpl.java | 60 +- .../resources/application-customer.properties | 63 - .../resources/application-supplier.properties | 64 - .../src/main/resources/application.properties | 48 +- local/.env | 5 - local/.gitignore | 1 + local/INSTALL.md | 60 +- local/cleanup.sh | 3 + local/daps/config/clients.yml | 41 - local/daps/config/omejdn.yml | 19 - local/daps/config/plugins.yml | 9 - local/daps/config/scope_description.yml | 9 - local/daps/config/scope_mapping.yml | 4 - local/daps/config/webfinger.yml | 1 - local/daps/docker-compose.yaml | 39 - local/docker-compose-infrastructure.yaml | 67 + local/docker-compose.yaml | 225 +- local/generate-keys.sh | 65 +- local/init-wallets.sh | 11 + local/miw/infrastructure.properties | 30 + local/miw/keycloak-setup.json | 2815 +++++++++++++++++ local/postgres/init-db.sql | 4 +- local/restart.sh | 8 - .../config/customer/control-plane.properties | 76 +- .../config/customer/data-plane.properties | 13 +- .../config/customer/puris-backend.properties | 24 + .../config/supplier/control-plane.properties | 78 +- .../config/supplier/data-plane.properties | 12 +- .../config/supplier/puris-backend.properties | 24 + local/tractus-x-edc/docker-compose.yaml | 13 +- local/vault/put-keys.sh | 2 + 49 files changed, 3905 insertions(+), 1489 deletions(-) delete mode 100755 backend/src/main/resources/application-customer.properties delete mode 100644 backend/src/main/resources/application-supplier.properties delete mode 100644 local/.env create mode 100644 local/cleanup.sh delete mode 100755 local/daps/config/clients.yml delete mode 100755 local/daps/config/omejdn.yml delete mode 100755 local/daps/config/plugins.yml delete mode 100755 local/daps/config/scope_description.yml delete mode 100755 local/daps/config/scope_mapping.yml delete mode 100755 local/daps/config/webfinger.yml delete mode 100644 local/daps/docker-compose.yaml create mode 100644 local/docker-compose-infrastructure.yaml create mode 100644 local/init-wallets.sh create mode 100644 local/miw/infrastructure.properties create mode 100644 local/miw/keycloak-setup.json delete mode 100644 local/restart.sh create mode 100644 local/tractus-x-edc/config/customer/puris-backend.properties create mode 100644 local/tractus-x-edc/config/supplier/puris-backend.properties diff --git a/NOTICE.md b/NOTICE.md index e1773779..fc26e5bb 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -37,6 +37,10 @@ This project leverages the following third party content. See `DEPENDENCIES_FRONTEND` and `DEPENDENCIES_BACKEND` file. Further, the following third-party content is used that isn't listed in any DEPENDENCIES file: +[Keycloak initial realm setup](./local/miw/keycloak-setup.json) derived from +https://github.com/eclipse-tractusx/managed-identity-wallet/blob/287b61d12cabf7e2a39f60e1f49d9baaa8401e8c/dev-assets/docker-environment/keycloak/miw_test_realm_docker.json#L809 +under SPDX-License-Identifier: Apache-2.0 + feather (4.29) * License: MIT License * Licence Path: https://github.com/feathericons/feather/blob/master/LICENSE diff --git a/backend/Dockerfile b/backend/Dockerfile index 82da5893..8be4edf7 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -22,7 +22,7 @@ FROM maven:3.8.7-eclipse-temurin-17 as build RUN mkdir /app WORKDIR /app COPY pom.xml . -RUN mvn initialize -f pom.xml +RUN mvn dependency:go-offline COPY src/ /app/src/ RUN mvn clean package -DskipTests diff --git a/backend/INSTALL.md b/backend/INSTALL.md index 1548300e..67d48592 100644 --- a/backend/INSTALL.md +++ b/backend/INSTALL.md @@ -6,30 +6,26 @@ The first steps are always the same: Depending on your needs of deployment, follow the following steps -### Running using mvn (local develpment) -3. Change the `src/main/resources/application.properties` or the respective environment +### Running using mvn (local development) and infrastructure services in kubernetes +1. Change the `src/main/resources/application.properties` or the respective environment variables to configure the port, the URL of the EDC control plane, backend application etc. -4. Run the application: -```shell -# build and run the generated .jar file -mvn install +2. Run the application: +mvn spring-boot:run -Dspring-boot.run.arguments=--spring.config.location="./src/main/resources/application.properties" + +3. It is highly suggested to install, configure and run the PURIS frontend afterward -# run for demo or development puroposes -# customer role -mvn spring-boot:run -Dspring-boot.run.arguments=--spring.config.location="./src/main/resources/application-customer.properties" +### Running using docker (deployment) -# supplier role -mvn spring-boot:run -Dspring-boot.run.arguments=--spring.config.location="./src/main/resources/application-supplier.properties" +1. First build a docker image: ``` -5. Done! The Swagger UI should be available at - - (Java & Docker) `http://YOURIP:8081/catena/swagger-ui/index.html` - - (Kubernetes) `http://CLUSTERIP:30001/catena/swagger-ui/index.html` -6. It is highly suggested to install and run the PURIS frontend afterward +cd backend -### Running using docker (deployment) -3. Optional (one can set properties via environment variables to docker): Change the `src/main/resources/application.properties` or the respective environment +docker build -t puris-backend:dev . +``` + +2. Optionally (one can set properties via environment variables to docker): Change the `src/main/resources/application.properties` or the respective environment variables to configure the port, the URL of the EDC control plane, backend application etc. -4. Run the application: +3. Run the application: ```shell cd backend @@ -39,21 +35,23 @@ docker build -t puris-backend:dev . docker run -d --rm -p 8081:8081 --name backend -e server.port=8082 puris-backend:dev CONTAINERID # B use docker-compose +cd .. +cd local docker-compose up ``` -5. Done! The Swagger UI should be available at +4. Done! The Swagger UI should be available at - (Java & Docker) `http://YOURIP:8081/catena/swagger-ui/index.html` - - (Kubernetes) `http://CLUSTERIP:30001/catena/swagger-ui/index.html` -6. It is highly suggested to install and run the PURIS frontend afterward +5. It is highly suggested to install and run the PURIS frontend afterward (unless you're using local/docker-compose.yaml) ### Running using helm (deployment) -3. Run the application: +1. Run the application: + ```shell cd charts/puris/charts/backend helm install backend --namespace puris --create-namespace . --set ingress.enabled=true ``` -4. Done! The Swagger UI should be available at +2. Done! The Swagger UI should be available at - (Java & Docker) `http://YOURIP:8081/catena/swagger-ui/index.html` - (Kubernetes) `http://CLUSTERIP:30001/catena/swagger-ui/index.html` -5. It is highly suggested to install and run the PURIS frontend afterward +3. It is highly suggested to install and run the PURIS frontend afterward diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/AssetCreatorCommandLineRunner.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/AssetCreatorCommandLineRunner.java index 899b1206..3a87b80b 100755 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/AssetCreatorCommandLineRunner.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/AssetCreatorCommandLineRunner.java @@ -21,14 +21,9 @@ */ package org.eclipse.tractusx.puris.backend; -import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; -import org.eclipse.tractusx.puris.backend.common.edc.logic.dto.CreateAssetDto; -import org.eclipse.tractusx.puris.backend.common.edc.logic.dto.datatype.DT_ApiMethodEnum; import org.eclipse.tractusx.puris.backend.common.edc.logic.service.EdcAdapterService; -import org.eclipse.tractusx.puris.backend.common.edc.logic.util.EDCRequestBodyBuilder; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; @@ -39,49 +34,14 @@ public class AssetCreatorCommandLineRunner implements CommandLineRunner { @Autowired private EdcAdapterService edcAdapterService; - @Value("${request.serverendpoint}") - private String requestApiBaseUrl; - - @Value("${response.serverendpoint}") - private String responseApiBaseUrl; - - private ObjectMapper objectMapper; - - @Autowired - private EDCRequestBodyBuilder edcRequestBodyBuilder; - - public AssetCreatorCommandLineRunner(ObjectMapper objectMapper) { - this.objectMapper = objectMapper; - } - @Override public void run(String... args) throws Exception { - - registerResponseAndRequestApiAsset(); - - } - - private void registerResponseAndRequestApiAsset() { - - // Create Request Api Asset - CreateAssetDto createRequestApiAssetDto = - edcRequestBodyBuilder.buildCreateAssetDtoForApi(DT_ApiMethodEnum.REQUEST, - requestApiBaseUrl); - - CreateAssetDto createResponseApiAssetDto = - edcRequestBodyBuilder.buildCreateAssetDtoForApi(DT_ApiMethodEnum.RESPONSE, - responseApiBaseUrl); - - try { - edcAdapterService.publishAssetAtEDC(createResponseApiAssetDto); - edcAdapterService.publishAssetAtEDC(createRequestApiAssetDto); - log.info("Published sample RequestAndResponseAssetData"); - } catch (Exception e) { - log.error("FAILED TO REGISTER REQUEST/RESPONSE ASSETS"); - log.error(e.getMessage()); + if (!edcAdapterService.doInitialAssetRegistration()) { + // retry + int retryDelaySeconds = 3; + log.warn("retrying initial asset registration in " + retryDelaySeconds + " seconds"); + Thread.sleep(retryDelaySeconds * 1000); + log.warn("retry successful: " + edcAdapterService.doInitialAssetRegistration()); } - } - - } diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/DataInjectionCommandLineRunner.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/DataInjectionCommandLineRunner.java index 2001a75d..b88edfd6 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/DataInjectionCommandLineRunner.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/DataInjectionCommandLineRunner.java @@ -44,7 +44,6 @@ import org.eclipse.tractusx.puris.backend.stock.logic.service.ProductStockRequestService; import org.eclipse.tractusx.puris.backend.stock.logic.service.ProductStockService; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; @@ -83,10 +82,6 @@ public class DataInjectionCommandLineRunner implements CommandLineRunner { @Autowired private VariablesService variablesService; - - @Value("${puris.demonstrator.role}") - private String demoRole; - private ObjectMapper objectMapper; private final String semiconductorMatNbrCustomer = "MNR-7307-AU340474.002"; @@ -99,11 +94,11 @@ public DataInjectionCommandLineRunner(ObjectMapper objectMapper) { @Override public void run(String... args) throws Exception { - //createOwnPartnerEntity(); - log.info("Creating setup for " + demoRole.toUpperCase()); - if (demoRole.equals("supplier")) { + createOwnPartnerEntity(); + log.info("Creating setup for " + variablesService.getDemoRole().toUpperCase()); + if (variablesService.getDemoRole().equals("supplier")) { setupSupplierRole(); - } else if (demoRole.equals(("customer"))) { + } else if (variablesService.getDemoRole().equals(("customer"))) { setupCustomerRole(); createRequest(); } else { @@ -120,7 +115,7 @@ private void createOwnPartnerEntity() { Partner mySelf; if(variablesService.getOwnDefaultBpns()!= null && variablesService.getOwnDefaultBpns().length()!=0) { mySelf = new Partner(variablesService.getOwnName(), - variablesService.getOwnEdcIdsUrl(), + variablesService.getEdcProtocolUrl(), variablesService.getOwnBpnl(), variablesService.getOwnDefaultBpns(), variablesService.getOwnDefaultSiteName(), @@ -130,7 +125,7 @@ private void createOwnPartnerEntity() { variablesService.getOwnDefaultCountry()); } else { mySelf = new Partner(variablesService.getOwnName(), - variablesService.getOwnEdcIdsUrl(), + variablesService.getEdcProtocolUrl(), variablesService.getOwnBpnl(), variablesService.getOwnDefaultBpna(), variablesService.getOwnDefaultStreetAndNumber(), @@ -281,9 +276,9 @@ private void setupSupplierRole() { private Partner createAndGetCustomerPartner() { Partner customerPartnerEntity = new Partner( "Scenario Customer", - "http://customer-control-plane:8184/api/v1/ids", + "http://customer-control-plane:8184/api/v1/dsp", "BPNL4444444444XX", - "BPNS4444444444XY", + "BPNS4444444444XX", "Hauptwerk Musterhausen", "BPNA4444444444ZZ", "Musterstraße 35b", @@ -306,9 +301,9 @@ private Partner createAndGetCustomerPartner() { private Partner createAndGetSupplierPartner() { Partner supplierPartnerEntity = new Partner( "Scenario Supplier", - "http://supplier-control-plane:9184/api/v1/ids", + "http://supplier-control-plane:9184/api/v1/dsp", "BPNL1234567890ZZ", - "BPNS1234567890XY", + "BPNS1234567890ZZ", "Konzernzentrale Dudelsdorf", "BPNA1234567890AA", "Heinrich-Supplier-Straße 1", @@ -331,7 +326,7 @@ private Partner createAndGetSupplierPartner() { private Partner createAndGetNonScenarioCustomer() { Partner nonScenarioCustomer = new Partner( "Non-Scenario Customer", - "http://nonscenario-customer.com/api/v1/ids", + "http://nonscenario-customer.com/api/v1/dsp", "BPNL2222222222RR", "BPNA2222222222XZ", "Fichtenweg 23", @@ -383,8 +378,7 @@ private void createRequest() throws JsonProcessingException { messageHeader.setRespondAssetId("product-stock-response-api"); messageHeader.setContractAgreementId("some cid"); messageHeader.setSender("BPNL1234567890ZZ"); - //messageHeader.setSenderEdc("http://plato-controlplane:8084/api/v1/ids"); - messageHeader.setSenderEdc("http://supplier-control-plane:9184/api/v1/ids"); + messageHeader.setSenderEdc("http://supplier-controlplane:8084/api/v1/dsp"); messageHeader.setReceiver("BPNL4444444444XX"); messageHeader.setUseCase(DT_UseCaseEnum.PURIS); messageHeader.setCreationDate(new Date()); diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/api/logic/service/VariablesService.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/api/logic/service/VariablesService.java index f393050d..3010c0a4 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/api/logic/service/VariablesService.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/api/logic/service/VariablesService.java @@ -29,41 +29,124 @@ @Getter @Service +/** + * This class contains the relevant + */ public class VariablesService { + @Value("${server.port}") + /** + * The port used by this apps server application. + */ + private String serverPort; + @Value("${puris.demonstrator.role}") + /** + * Must be set to "CUSTOMER" or "SUPPLIER" if + * you want to start with some initial settings + * defined in the DataInjectionCommandLineRunner + */ + private String demoRole; @Value("${puris.apiversion}") + /** + * The current version number + */ private String purisApiVersion; - - @Value("${puris.demonstrator.role}") - private String purisDemonstratorRole; - - @Value("${request.apiassetid}") + @Value("${puris.edr.endpoint}") + /** + * The edrEndpoint to be used during consumer pull asset transfers. + */ + private String edrEndpoint; + @Value("${puris.edr.deletiontimer}") + /** + * The number of minutes before received authentication data + * in the context of a consumer pull is removed from memory + */ + private long edrTokenDeletionTimer; + @Value("${puris.request.serverendpoint}") + /** + * The url under which this application's request endpoint can + * be reached by external machines. + */ + private String requestServerEndpoint; + @Value("${puris.request.apiassetid}") + /** + * The assetId that shall be assigned to the request API + * during asset creation. + */ private String requestApiAssetId; - - @Value("${response.apiassetid}") + @Value("${puris.response.serverendpoint}") + /** + * The url under which this application's response endpoint can + * be reached by external machines. + */ + private String responseServerEndpoint; + @Value("${puris.response.apiassetid}") + /** + * The assetId that shall be assigned to the request API + * during asset creation. + */ private String responseApiAssetId; + @Value("${edc.controlplane.key}") + /** + * The api key of your control plane + */ + private String edcApiKey; + @Value("${edc.controlplane.management.url}") + /** + * Your control plane's management url + */ + private String edcManagementUrl; + @Value("${edc.controlplane.protocol.url}") + /** + * Your control plane's protocol url + */ + private String edcProtocolUrl; + @Value("${own.bpnl}") + /** + * The BPNL that was assigned to you. + */ private String ownBpnl; - @Value("${own.name}") + /** + * A human-readable description of yourself, e.g. + * the name of your company. + */ private String ownName; - @Value("${edc.idsUrl}") - private String ownEdcIdsUrl; @Value("${own.default.bpns}") + /** + * A BPNS that was assigned to you. + */ private String ownDefaultBpns; - @Value("${own.default.streetandnumber}") - private String ownDefaultStreetAndNumber; @Value("${own.default.site.name}") + /** + * A human-readable description of the site that you referenced in + * the ownDefaultBpns. + */ private String ownDefaultSiteName; @Value("${own.default.bpna}") + /** A BPNA that was assigned to you. If you initialised the + * ownDefaultBpns variable, then it must be a BPNA that is associated + * to that BPNS. + */ private String ownDefaultBpna; + @Value("${own.default.streetandnumber}") + /** + * The street and number associated to the ownDefaultBpna + */ + private String ownDefaultStreetAndNumber; @Value("${own.default.zipcodeandcity}") + /** + * The zip code and name of the city associated to the ownDefaultBpna + */ private String ownDefaultZipCodeAndCity; @Value("${own.default.country}") + /** + * The country in which your ownDefaultBpna-address is located. + */ private String ownDefaultCountry; - /** * Returns the asset-id as defined in the properties file for the given api method * under request.apiassetid or response.apiassetid respectively. diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/controller/EdcController.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/controller/EdcController.java index 85fba1b1..a96c13d8 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/controller/EdcController.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/controller/EdcController.java @@ -20,6 +20,7 @@ */ package org.eclipse.tractusx.puris.backend.common.edc.controller; +import lombok.extern.slf4j.Slf4j; import org.eclipse.tractusx.puris.backend.common.edc.logic.service.EdcAdapterService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; @@ -27,110 +28,34 @@ import java.io.IOException; import java.net.URL; +import java.util.List; /** * Controller for forwarding/building requests to a productEDC. */ @RestController @RequestMapping("edc") +@Slf4j public class EdcController { @Autowired private EdcAdapterService edcAdapter; - /** - * Publish an order at the edc. - * - * @param orderId id of the order to be published at the edc. - * @return OK if order was published, else false. - */ - @GetMapping("/publish") - @CrossOrigin - public ResponseEntity publishAtEDC(@RequestParam String orderId) { - try { - var success = edcAdapter.publishOrderAtEDC(orderId); - return ResponseEntity.ok("Success: " + success); - } catch (IOException e) { - return ResponseEntity.internalServerError().body(e.getMessage()); - } - } /** * Get the catalog from an EDC. * - * @param idsUrl ids url of the edc to get catalog from. + * @param dspUrl url of the edc to get catalog from. * @return catalog of the requested edc. */ @GetMapping("/catalog") @CrossOrigin - public ResponseEntity getEDCCatalog(@RequestParam String idsUrl) { - if (!isValidURI(idsUrl)) { - return ResponseEntity.badRequest().body("Malformed URL!"); - } - try { - var catalog = edcAdapter.getCatalog(idsUrl); - return ResponseEntity.ok(catalog); - } catch (IOException e) { - return ResponseEntity.internalServerError().body(e.getMessage()); - } - } - - /** - * Start a negotiation with another EDC. - * - * @param orderId ID of the asset to request. - * @param connectorAddress address of the edc to start negotiating with. - * @return response of the own EDC. - */ - @GetMapping("/startNegotiation") - @CrossOrigin - public ResponseEntity startNegotiation(@RequestParam String orderId, @RequestParam String connectorAddress, - @RequestParam String contractDefinitionId) { - try { - var result = edcAdapter.startNegotiation(connectorAddress, contractDefinitionId, orderId); - return ResponseEntity.ok(result); - } catch (IOException e) { - return ResponseEntity.internalServerError().body(e.getMessage()); - } - } - - /** - * Start a data transfer with another EDC. - * - * @param orderId id of the asset to transfer. - * @param connectorAddress address of the requested EDC. - * @param transferId id used for the transfer process. - * @param contractId id of the negotiated contract. - * @return response of the own EDC. - */ - @GetMapping("/startTransfer") - @CrossOrigin - public ResponseEntity startTransfer( - @RequestParam String orderId, - @RequestParam String connectorAddress, - @RequestParam String transferId, - @RequestParam String contractId) { + public ResponseEntity getEDCCatalog(@RequestParam String dspUrl) { try { - var result = edcAdapter.startTransfer(transferId, connectorAddress, contractId, orderId); - return ResponseEntity.ok(result); + var catalog = edcAdapter.getDSPCatalog(dspUrl); + return ResponseEntity.ok(catalog.toPrettyString()); } catch (IOException e) { - return ResponseEntity.internalServerError().body(e.getMessage()); - } - } - - /** - * Get negotiations from own EDC. - * - * @param negotiationId optional parameter if only a specific negotiation should be retrieved. - * @return response from own EDC. - */ - @GetMapping("/negotiations") - @CrossOrigin - public ResponseEntity getNegotiations(@RequestParam(required = false) String negotiationId) { - try { - var result = edcAdapter.getFromEdc(negotiationId, "data", "contractnegotiations"); - return ResponseEntity.ok(result); - } catch (IOException e) { - return ResponseEntity.internalServerError().body(e.getMessage()); + log.warn(e.getMessage()); + return ResponseEntity.internalServerError().build(); } } @@ -142,62 +67,16 @@ public ResponseEntity getNegotiations(@RequestParam(required = false) St */ @GetMapping("/assets") @CrossOrigin - public ResponseEntity getAssets(@RequestParam(required = false) String assetId) { - try { - var result = edcAdapter.getFromEdc(assetId, "data", "assets"); - return ResponseEntity.ok(result); - } catch (IOException e) { - return ResponseEntity.internalServerError().body(e.getMessage()); - } - } - - /** - * Delete an asset from the EDC. - * - * @param assetId id of the asset to delete. - * @return response from own EDC. - */ - @DeleteMapping("/assets/{assetId}") - @CrossOrigin - public ResponseEntity deleteAsset(@PathVariable String assetId) { + public ResponseEntity getAssets(@RequestParam String assetId) { try { - var result = edcAdapter.deleteAsset(assetId); - return ResponseEntity.ok(result); + var result = edcAdapter.sendDspGetRequest(List.of("v3", "assets", assetId)); + var stringData = result.body().string(); + result.body().close(); + return ResponseEntity.ok(stringData); } catch (IOException e) { - return ResponseEntity.internalServerError().body(e.getMessage()); + log.warn(e.getMessage()); + return ResponseEntity.internalServerError().build(); } } - /** - * Get transfers from the EDC. - * - * @param transferId optional parameter if only a specific transfer should be retrieved. - * @return response from own EDC. - */ - @GetMapping("/transfers") - @CrossOrigin - public ResponseEntity getTransfers(@RequestParam(required = false) String transferId) { - try { - var result = edcAdapter.getFromEdc(transferId, "data", "transferprocess"); - return ResponseEntity.ok(result); - } catch (IOException e) { - return ResponseEntity.internalServerError().body(e.getMessage()); - } - } - - /** - * Utility method to check if a URI is valid. - * - * @param uriString input string to validate. - * @return true iff string is a valid URI. - */ - private static boolean isValidURI(String uriString) { - try { - URL url = new URL(uriString); - url.toURI(); - return true; - } catch (Exception e) { - return false; - } - } } diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/controller/EndpointDataReferenceReceiver.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/controller/EndpointDataReferenceReceiver.java index 9b254d91..3c1cd30b 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/controller/EndpointDataReferenceReceiver.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/controller/EndpointDataReferenceReceiver.java @@ -37,7 +37,7 @@ /** * This class contains the endpoint for receiving the authCodes from - * the counterparty's dataplane. + * the counterparty's dataplane. */ @RestController @Slf4j @@ -45,46 +45,46 @@ public class EndpointDataReferenceReceiver { @Autowired private EndpointDataReferenceService edcService; + private final static String prefix = "https://w3id.org/edc/v0.0.1/ns/"; /** - * This endpoint awaits incoming authCodes from external - * partners during a consumer pull transfer. + * This endpoint awaits incoming EDR Tokens from external + * partners during a consumer pull transfer. + * * @param body - * @return + * @return Status code 200 if request body was found, otherwise 400 */ @PostMapping("/edrendpoint") - @Operation(summary = "Endpoint for receiving the authCodes from the counterparty's dataplane", + @Operation(summary = "Endpoint for receiving the authCodes from the counterparty's connector", requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody( - content = @Content(examples = { - @ExampleObject(name = "EDR Token", - value = "{\n" + - " \"id\" : \"6c2e5600-294a-488e-8ce1-2073806c1927\",\n" + - " \"endpoint\" : \"http://sokrates-dataplane:8181/api/public\",\n" + - " \"authKey\" : \"Authorization\",\n" + - " \"authCode\" : \"eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE2OTMyMTgzNzIsImRhZCI6Ik5YY2J1VktDOVFCTWY5eG9BQUFBQWN3RmE2blhIS1JEcllHTnZCeUNlbTdCNTBOVkVWd05vczdsNU1YM0lKL1pVK0NUU3NvcE02ZUVFbzloYmx5ak1aTlJYYnRnOUZpZ04vampRVmVlaDFWQk01RHA1RXUyN2VIbk5TNCt0VE9uMEZFZTVGVGNITjh6dWwwZG5VbW1nbCtsVERxODZhdHUwbjYxZVV5dXNzQ25pbTZDaWhOai9lRXZ0cUZydVhCbXNIWVQvSTNYN3JrYk1FeFZWRFhBOHNPVGFGeFVpTnl3YTI0cURpWkFnc0Nka1FDenBaSFVIWk16ZDQrNURKTTVkVkNDWnEyUHBnRDhuR09wVzVVTTJUYUlmNHdMOTFQbnhEdEQ2a1dWTCtNQUNGSE41S2RyVUt2a3pOeXAyRzVYcWJ2V29waEhhY0VLTTR4UTZDc2dkUHFoMGN6elNnbFZGdy9IWVl3ZDBXQVpCcnVSSTlUekY4WTJkMXBscW5zRFFwcWh1bUUzUUtGTW5UbzUxWVFuVmdzcUZ5ZEpObkpMZjQzWnBwOXNPZ3U0V1Yxd3lxN096QzFXeFZjRk9xNVZQMkJRYk5pS29YeEZINXd3WmZJMzR6dFNCTFE0akUvY3BJTlk0Rks0Tk95YjNicENOYlpDamplbXRYaE1jTXlUQ0tyMU8zS2RvdkhEMnEzMjhVdDk0U3hzZW9jK0FCUXZaTk5EK1hGbm9Bcm01K01jbkVXdkdPVHJPT0NIaG83bWhnTGUxRzVEaHBqRjFaclBVTHFNNitzTmZzU0l4REhSOEtzMW1OMGhwajVwMUJ4Tm9rMDE2bGNJSURTbnVpclhyZWlzVzhEK0NHRDlEREdlUlVNQkk3cVBCSVc4eXY5RmU1eldrNHU0cERzbFAwR0dPYjBpMHVBMnFyS0dFS2JQUmd6ODRPeWZTNW84KzdiQ1dkMElKdmZERlRGK3UwVVgzVWordmFtYlZPREpQK1FmbXpOM0U2NFdaY1ozVDRMQURKZWhGcEZ5WGh6bUM5SnAyK2RYZ2syWE4rUnVzbGZFeGNMbXA0U05DenRxZDVQTXZqVjdOMXFYZnQ5a0hLLytwYWhoVUUxLzBENTEvS04razN4cmxoTDFPVnF3QzYvUjlScDA5WUk4dno3enNXS0V3aEpNOGk2OCtRdHlDMldNay9ucVpjTmtieVQ3T21WZ1R2bFhxYmV5WlpVOTlSdGxVNExJemZjM3hKRlIvUGpDa2xUK2dkZXpsaXFnbGFWMlFLL2EwVHpXbFlncXVDQ21kQzhieGJaR1JFWFdrbVFlNUwwZC9UWWZZYVRCNWdURkRDL1ZDRnYrRUFzNFhXQ2pBQ2NraW5rTDJDYWlZYm5WV0I5Zi9nRkJaWkY1cDhBNHFrMkIvdkNBS3ByckpQZkMwZTZIQXFxUlZxRFZ1VktZRHl1U3V5Umx6Q2hPbXZvNHRoRUhDcmtwVFlCNzZIVlNPeld2ZFJubmJRQmNyOW5YQkVCd2xvbGZkV3F4TGZUY3dmRnFJV2tla0YrT3NzTDE1TVV5Ym1vZXlxVnUxSkdpVGN1Sy90NGhINkxEQXNWeVhOT294NVF4eDE1b0dIN1hoWXVrWEpBb2J6SXduVWtrM2cxOE1GMko1LzNIbVpHVDM3bHU0SUNmR3g3d3JxY0xHSEJnb3l5MndsZDl3WVdGMHB4RXlwbmltZWZHZTQ4di9TbEIwYldRekY5S0UrVGwrcTA3d1c2aHVNbU51VDhUNWJLeXBIUVYzMnlpSkozbGx3WnpTVmc5NWtsWXo3VThxZ3FhNlJCa1plbXovNFhISG4vVXRGTUl5K1VnZkNEbVJ5L2dORmZOQkdmQ2RQSGNidTh3RXRvd2QrOTJ5R0JPLzZIbFgwdysxYlNzVGZYRDI1U21ZdWtRcDhTUlgyTEtTaUtzc3VKQ0QwOTdCbTAvRkorUkxJOVZHd2FJRXBLMTIzOEVyUTFublVIbGNnblByY2xKVE5jdWtXNFp5YkhESUYrT3YwNHFMNnpYa292NXBoYXB0b1dBaHNCaE4vbW5sUWlHYnpSOGN4WWJ1SEpRRDRxd3VuZzloYVR3b2RyeGhxTXh4RVM5SHhVN3UrQ3U2SkhaRkVoL0pEeWpzVnc5ZXg5Uy94aUxFNjNZR21iWnNBK3Q5QXRzdjJPVC9TbS9ZMkk9IiwiY2lkIjoicHJvZHVjdC1zdG9jay1yZXF1ZXN0LWFwaTphMDUwYmY1MS0xYWIxLTRkZTQtODM0Yy1mNWNlMGEzMDMwZWEifQ.oxERHPWzhunY18bJjgTGjlvZhHUGtDm2V_svDVkYz3VulluMjoFV5jm1EDuy46Z3vEgLQmKsFsG-VTsVwHaJKh5pnlx1QEj8SUFYu5JZraIL6vghI1X3cPb0qNfCBX191ztJCRgszyNMsxXGd4GQjkUdnP8J58UtBwaoNTQNWxMOIYgpBaNUuyPr6wSz1ek05B-TahoVjfjFmgAlDoKLjLQ-Ec-ejfM6FaITvifrVJyGUyHGiqzU7v4_Dd29rVHVHSE_F3rr6sLV56PeU30coBAn_q7hnTN6GWdulxg3vjc5uDcqSntxmxGE_STI-paBDHG5aToQzNNgARpv3SJDjg\",\n" + - " \"properties\" : {\n" + - " \"cid\" : \"product-stock-request-api:a050bf51-1ab1-4de4-834c-f5ce0a3030ea\"\n" + - " }\n" + - "}\n") - }) - )) + content = @Content(examples = {@ExampleObject(name = "EDR Token", value = sample)}))) @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Ok"), - @ApiResponse(responseCode = "400", description = "Received invalid message") + @ApiResponse(responseCode = "200", description = "Ok") }) private ResponseEntity authCodeReceivingEndpoint(@RequestBody JsonNode body) { - log.debug("Received edr data:\n" + body.toPrettyString()); - String transferId = body.get("id").asText(); - String authKey = body.get("authKey").asText(); - String authCode = body.get("authCode").asText(); - String endpoint = body.get("endpoint").asText(); + ResponseEntity response = ResponseEntity.status(200).build(); + var payload = body.get("payload"); + if (payload.get("dataAddress") == null) { + return response; + } + var dataAddress = payload.get("dataAddress"); + if (dataAddress.get("properties") == null) { + return response; + } + var properties = dataAddress.get("properties"); + String transferId = properties.get(prefix + "id").asText(); + String authKey = properties.get(prefix + "authKey").asText(); + String authCode = properties.get(prefix + "authCode").asText(); + String endpoint = properties.get(prefix + "endpoint").asText(); if (transferId == null || authCode == null) { - log.warn("authCodes endpoint received invalid message:\n" + body.toPrettyString()); - return ResponseEntity.status(400).build(); + log.warn("EDR endpoint received invalid message:\n" + body.toPrettyString()); + return response; } edcService.save(transferId, new EDR_Dto(authKey, authCode, endpoint)); - log.debug("authCodes endpoint stored authCode for " + transferId); - return ResponseEntity.status(200).build(); + log.info("EDR endpoint stored authCode for " + transferId); + return response; } + private final static String sample = "{\"id\":\"3b603c3e-0f1a-4989-90b7-a4b024496d04\",\"at\":1699446240954,\"payload\":{\"transferProcessId\":\"e5c59912-b88a-4c42-9766-9fa593b72603\",\"callbackAddresses\":[{\"uri\":\"http://host.docker.internal:4000\",\"events\":[\"contract.negotiation\",\"transfer.process\"],\"transactional\":false,\"authKey\":null,\"authCodeId\":null}],\"dataAddress\":{\"properties\":{\"https://w3id.org/edc/v0.0.1/ns/type\":\"EDR\",\"https://w3id.org/edc/v0.0.1/ns/endpoint\":\"http://supplier-data-plane:9285/api/public/\",\"https://w3id.org/edc/v0.0.1/ns/authCode\":\"eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE2OTk0NDY4NDAsImRhZCI6ImMvMnFYVThaemVRTnJ5WDloUjhFZytONXpaWjhaUnc5dHpDOWRPcDlQNnpQQUhxZE9XN0pyN3JIWk94R0k0dFBXMmUxZVZ6elRJNlA5dUZXREpVVWtvRjJpWm93aXp6UEhmbGF4YTdsY0JIcWNISThhZWlEcm5MYiswM1ZXemRtMEZqLzNYYVdUa3VGYzM4VGtWS1lMaEViOVRNRUV4UGhqbEM5WFplamc0ZWJHNUZ6QldDQXZ2bmlMaWpFMjA4ZDhOcE80M0w5cG4xWXBpUThkZ2IrYjhkcEsyMDZObDNzTFhyS1hsU09ZaERHR2tLM2dSYkxBRHpiRjl3RWlCd3Z6SjFvSzFXbllzMVJwTVNOY1ZPc1ZseDJ6YS9mT1J6M29NYnh4TGdsSzMxU1c3NjVNWlVyWWlLK3JDOFhFaHNNa2JMSlNIcXhKYlFWYnZtL3dic1FyQWoxbUVsajhjbk9FY1p2NUhJOHJoUElyaTQyeU1hbXpWSXhXWW9hWU5PV0x4WHk1SUhZc3ZKcUJMc1cwaWs4eDlOZDhTcDduUGhqempLYjlQeFVVbCthS3BZQWVJaE9XZnNGT2pSMFFHM3lYcmJDalM0S1ZlaHhZbW1MZ0ZIczhyeWNsd1h2VUJzRkk4bzVLZUVwSll2U2RPSjlTQ0dNODB5a1lNczFxK0dTRVZmN2VrTUZRU2pjeGRSMElncUtFRk92OE5Ra3ZXZHAxbVpXc25IbnZNY2E0MFYxTm5nQWFVRndtRDhEeGVyc3k4R0IiLCJjaWQiOiJNUT09OlptVjBZMmhCYzNObGRBPT06T0dWbE4yWmhZelV0WlRjek15MDBPR0prTFRneFl6a3RaV1kzTkRsbFlXSTVNMll6In0.z9Nm_csmyHGBPGdEGgiyUV7pLWes0KE2IK82BHtCOS8XBerJrGb_wqNCgcgph6Zx7j84FwaVSH190FQ98FhJORgVCQ8u187hz1iPjXne9GEclR5Xr9_fSb9ZNK8VNTJvCdevJO5uT7Jkkc_-2U8DKUDDOj_Wqby8uStoSSs0P0idQ4pAazFYTy_Dbl0ltJsz6xc3YxwXk3yk0P1Ys5zYN0ueBznUMEJ6-YXpafAS5kn_iN8zU3It3Q2AgS0ER_M9AzeBHXZmST2MkaXXo3s_kuVxCZEtGRWkv8gmI3XZ5dprJ6x6keQSZ2ApSrxtmhswq2hPcqSQXF1gIFTTSSzg8A\",\"https://w3id.org/edc/v0.0.1/ns/id\":\"e5c59912-b88a-4c42-9766-9fa593b72603\",\"https://w3id.org/edc/v0.0.1/ns/authKey\":\"Authorization\"}}},\"type\":\"TransferProcessStarted\"}"; + } diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/logic/service/EdcAdapterService.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/logic/service/EdcAdapterService.java index 1cdeba11..e9455d4c 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/logic/service/EdcAdapterService.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/logic/service/EdcAdapterService.java @@ -22,20 +22,20 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; import com.squareup.okhttp.*; import lombok.extern.slf4j.Slf4j; import org.eclipse.tractusx.puris.backend.common.api.logic.service.VariablesService; -import org.eclipse.tractusx.puris.backend.common.edc.logic.dto.CreateAssetDto; import org.eclipse.tractusx.puris.backend.common.edc.logic.dto.EDR_Dto; +import org.eclipse.tractusx.puris.backend.common.edc.logic.dto.datatype.DT_ApiMethodEnum; import org.eclipse.tractusx.puris.backend.common.edc.logic.util.EDCRequestBodyBuilder; -import org.eclipse.tractusx.puris.backend.model.repo.OrderRepository; +import org.eclipse.tractusx.puris.backend.masterdata.domain.model.Partner; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.io.IOException; -import java.util.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** * Service Layer of EDC Adapter. Builds and sends requests to a productEDC. @@ -45,39 +45,11 @@ @Slf4j public class EdcAdapterService { private static final OkHttpClient CLIENT = new OkHttpClient(); - @Autowired - private OrderRepository orderRepository; - - @Autowired - VariablesService variablesService; - - @Value("${edc.controlplane.host}") - private String edcHost; - - @Value("${edc.controlplane.data.port}") - private Integer dataPort; - - /** - * Path to data management api - */ - @Value("${edc.controlplane.data.path}") - private String dataPath; - - @Value("${edc.controlplane.key}") - private String edcApiKey; - - @Value("${server.port}") - private String serverPort; - - @Value("${minikube.ip}") - private String minikubeIp; - + private VariablesService variablesService; private ObjectMapper objectMapper; - @Autowired private EDCRequestBodyBuilder edcRequestBodyBuilder; - @Autowired private EndpointDataReferenceService edrService; @@ -85,449 +57,371 @@ public EdcAdapterService(ObjectMapper objectMapper) { this.objectMapper = objectMapper; } - /** - * Publish an order at own EDC. - * - * @param orderId id of the order to publish. - * @return true, if order was published. - * @throws IOException if the connection to the EDC failed. - */ - public boolean publishOrderAtEDC(String orderId) throws IOException { - var order = orderRepository.findByOrderId(orderId); - if (order.isPresent()) { - var orderUrl = "http://" + minikubeIp + ":" + serverPort + "/catena/orders/order/id/" + orderId; - var assetBody = edcRequestBodyBuilder.buildAssetRequestBody(orderUrl, orderId); - var policyBody = edcRequestBodyBuilder.buildPolicyRequestBody(orderId); - var contractBody = edcRequestBodyBuilder.buildContractRequestBody(orderId); - - var response = sendEdcRequest(assetBody, "/assets"); - var success = response.isSuccessful(); - response.body().close(); - response = sendEdcRequest(policyBody, "/policydefinitions"); - success &= response.isSuccessful(); - response.body().close(); - response = sendEdcRequest(contractBody, "/contractdefinitions"); - success &= response.isSuccessful(); - response.body().close(); - return success; - } - return false; - } /** - * Publish an Asset (ContractDefinition) using an {@link CreateAssetDto} with a public policy. + * Call this method at startup to register the necessary request and + * response apis, including unrestricted policy- and contract-definitions + * that allow any external Partner to contract them. * - * @param createAssetDto asset creation dto to use. - * @return true, if ContractDefinition has been created successfully - * @throws IOException if REST calls for creation could not be sent + * @return true if all registrations were successful, otherwise false */ - public boolean publishAssetAtEDC(CreateAssetDto createAssetDto) throws IOException { - - String assetId = createAssetDto.getAssetDto().getPropertiesDto().getId(); - - boolean success = true; - JsonNode assetBody = objectMapper.valueToTree(createAssetDto); - JsonNode policyBody = - edcRequestBodyBuilder.buildPolicyRequestBody(assetId); - log.info(String.format("Policy Body: \n%s", policyBody.toPrettyString())); - JsonNode contractBody = edcRequestBodyBuilder.buildContractRequestBody(assetId); - log.info(String.format("Contract Body: \n%s", contractBody.toPrettyString())); - log.info(String.format("Asset Body: \n%s", assetBody.toPrettyString())); - var response = sendEdcRequest(assetBody, "/assets"); - success &= response.isSuccessful(); - log.info(String.format("Creation of asset was successfull: %b", success)); - response.body().close(); - response = sendEdcRequest(policyBody, "/policydefinitions"); - log.info(String.format("Creation of policy was successfull: %b", response.isSuccessful())); - success &= response.isSuccessful(); - response.body().close(); - response = sendEdcRequest(contractBody, "/contractdefinitions"); - success &= response.isSuccessful(); - log.info(String.format("Created Contract Definition (%b) for Asset %s", response.isSuccessful(), - objectMapper.writeValueAsString(createAssetDto))); - response.body().close(); - return success; + public boolean doInitialAssetRegistration() { + boolean result; + log.info("Registration of product-stock request api successful " + (result = registerDSPApiAsset(DT_ApiMethodEnum.REQUEST))); + if (!result) return false; + log.info("Registration of product-stock response api successful " + (result = registerDSPApiAsset(DT_ApiMethodEnum.RESPONSE))); + if (!result) return false; + log.info("Registration of policy successful " + (result = registerDSPSimplePolicy())); + if (!result) return false; + log.info("Registration of contract definition successful " + (result = registerDSPSimpleContractDefinition())); + return result; } - /** - * Get catalog from an EDC. + * Util method to register a simple contract definition without restrictions + * regarding the asset selector. Will therefore be applicable to all assets + * that were registered previously. Must be called after registerDSPSimplePolicy() * - * @param idsUrl url of the EDC to get catalog from. - * @return catalog of the requested EDC. - * @throws IOException if the connection to the EDC failed. + * @return true if successful */ - public String getCatalog(String idsUrl) throws IOException { - return getCatalog(idsUrl, Optional.empty()); + private boolean registerDSPSimpleContractDefinition() { + var body = edcRequestBodyBuilder.buildDSPContractDefinitionWithPublicPolicy(); + try { + var response = sendDspPostRequest(body, List.of("v2", "contractdefinitions")); + boolean result = response.isSuccessful(); + if (!result) { + log.warn("Contract definition registration failed \n" + response.body().string()); + } + response.body().close(); + return result; + } catch (Exception e) { + log.error("Failed to register contract definition ", e); + return false; + } } /** - * Get catalog from an EDC. + * Util method to register a policy without any restrictions to + * your control plane. Should be called after asset creation. * - * @param idsUrl url of the EDC to get catalog from. - * @param filterProperties maps with key = asset property and value = filter value - * @return catalog of the requested EDC. - * @throws IOException if the connection to the EDC failed. + * @return true if successful */ - public String getCatalog(String idsUrl, Optional> filterProperties) throws IOException { - - HttpUrl.Builder urlBuilder = new HttpUrl.Builder(); - urlBuilder.scheme("http") - .host(edcHost) - .port(dataPort) - .addPathSegment("api") - .addPathSegment("v1") - .addPathSegment("data") - .addPathSegment("catalog") - .addEncodedQueryParameter("providerUrl", idsUrl + "/data"); - - HttpUrl httpUrl = urlBuilder.build(); - - // workaround EDC 0.3 takes filter=key=value, but HttpUrlBuilder encodes = to %3D - // which is not recognized - if (filterProperties.isPresent() && filterProperties.get().size() >= 1) { - String url = urlBuilder.build().toString(); - - for (Map.Entry entry : filterProperties.get().entrySet()) { - url = url + String.format("&filter=%s=%s", entry.getKey(), entry.getValue()); + private boolean registerDSPSimplePolicy() { + var body = edcRequestBodyBuilder.buildPublicDSPPolicy(); + try { + var response = sendDspPostRequest(body, List.of("v2", "policydefinitions")); + boolean result = response.isSuccessful(); + if (!result) { + log.warn("Policy registration failed \n" + response.body().string()); } - httpUrl = HttpUrl.parse(url); - } - log.debug(String.format("catalog request url: %s", httpUrl)); - - var request = new Request.Builder() - .get() - .url(httpUrl) - .header("X-Api-Key", edcApiKey) - .header("Content-Type", "application/json") - .build(); - var response = CLIENT.newCall(request).execute(); - String stringData = response.body().string(); - if (!response.isSuccessful()) { - throw new IOException(stringData); + response.body().close(); + return result; + } catch (Exception e) { + log.error("Failed to register policy definition ", e); + return false; } - response.body().close(); - return stringData; } /** - * Get a catalog from EDC. This method accepts a set of key/value - * pairs which are to be applied on the asset.properties level of - * the catalog. - * @param idsUrl url of the EDC to get catalog from. - * @param propertyObjectFilter the filter to be applied - * @return the catalog as JsonNode - * @throws IOException + * Util method to register an API asset to your control plane. + * + * @param apiMethod the api method to register. + * @return true if successful. */ - public JsonNode getCatalogFilteredByAssetPropertyObjectFilter(String idsUrl, Map propertyObjectFilter) throws IOException { - var catalogObject = objectMapper.readTree(getCatalog(idsUrl)); - var outputNode = objectMapper.createObjectNode(); - outputNode.put("id", catalogObject.get("id").asText()); - var contractOffersArray = objectMapper.createArrayNode(); - outputNode.set("contractOffers", contractOffersArray); - List catalogItems = objectMapper.readerForListOf(ObjectNode.class).readValue(catalogObject.get("contractOffers")); - for (var catalogItem : catalogItems) { - var properties = catalogItem.get("asset").get("properties"); - boolean testPassed = true; - for(var entry : propertyObjectFilter.entrySet()) { - String key = entry.getKey(); - String value = entry.getValue(); - testPassed = testPassed && properties.get(key) != null && value.equals(properties.get(key).asText()); - } - if (testPassed) { - contractOffersArray.add(catalogItem); + private boolean registerDSPApiAsset(DT_ApiMethodEnum apiMethod) { + var body = edcRequestBodyBuilder.buildDSPCreateAssetBody(apiMethod); + try { + var response = sendDspPostRequest(body, List.of("v3", "assets")); + boolean result = response.isSuccessful(); + if (!result) { + log.warn("Asset registration failed \n" + response.body().string()); } + response.body().close(); + return result; + } catch (Exception e) { + log.error("Failed to register api asset " + apiMethod.PURPOSE, e); + return false; } - return outputNode; } + /** - * Orders your own EDC Connector Controlplane to negotiate a contract with - * the owner of the given connector address for an asset (specified by the - * assetId) under conditions as stated in the contract defintion with the - * given contractDefinitionId - * @param connectorAddress - * @param contractDefinitionId - * @param assetId - * @return the response body as String - * @throws IOException + * Retrieve an (unfiltered) catalog from the partner with the + * given dspUrl + * + * @param dspUrl The dspUrl of your partner + * @return The full catalog + * @throws IOException If the connection to the partners control plane fails */ - public String startNegotiation(String connectorAddress, - String contractDefinitionId, String assetId) throws IOException { - var negotiationRequestBody = - edcRequestBodyBuilder.buildNegotiationRequestBody(connectorAddress, - contractDefinitionId, assetId); - var response = sendEdcRequest(negotiationRequestBody, "/contractnegotiations"); + public JsonNode getDSPCatalog(String dspUrl) throws IOException { + var response = sendDspPostRequest(edcRequestBodyBuilder.buildBasicDSPCatalogRequestBody(dspUrl, null), List.of("v2", "catalog", "request")); String stringData = response.body().string(); response.body().close(); - return stringData; + return objectMapper.readTree(stringData); } /** - * Sends a request to the own EDC Connector Controlplane in order to receive - * the current status of the previously initiated contractNegotiations as - * specified by the parameter. - * @param negotiationId - * @return the response body as String - * @throws IOException + * Retrieve the content of the catalog from a remote EDC Connector as an + * array of catalog items. + * You may specify filter criteria, consisting of a map of key-value-pairs. + * Catalog items that don't have these filter criteria will be removed from the output array. + * + * @param dspUrl The Protocol URL of the other EDC Connector + * @param filter A map of key-value-pairs. May be empty or null. + * @return An array of Catalog items. + * @throws IOException If the connection to the partners control plane fails */ - public String getNegotiationState(String negotiationId) throws IOException { - var response = sendEdcRequest("/contractnegotiations/" + negotiationId); + private JsonNode getDSPCatalogItems(String dspUrl, Map filter) throws IOException { + var response = sendDspPostRequest(edcRequestBodyBuilder. + buildBasicDSPCatalogRequestBody(dspUrl, filter), List.of("v2", "catalog", "request")); String stringData = response.body().string(); + if (!response.isSuccessful()) { + throw new IOException("Http Catalog Request unsuccessful"); + } response.body().close(); - return stringData; + JsonNode responseNode = objectMapper.readTree(stringData); + + var catalogArray = responseNode.get("dcat:dataset"); + // If there is exactly one asset, the catalogContent will be a JSON object. + // In all other cases catalogContent will be a JSON array. + // For the sake of uniformity we will embed a single object in an array. + if (catalogArray.isObject()) { + catalogArray = objectMapper.createArrayNode().add(catalogArray); + } + if (filter == null || filter.isEmpty()) { + return catalogArray; + } + var filteredNode = objectMapper.createArrayNode(); + for (var catalogEntry : catalogArray) { + boolean testPassed = true; + for (var filterEntry : filter.entrySet()) { + testPassed = testPassed && catalogEntry.has(filterEntry.getKey()) + && catalogEntry.get(filterEntry.getKey()).asText().equals(filterEntry.getValue()); + } + if (testPassed) { + filteredNode.add(catalogEntry); + } + } + return filteredNode; } /** - * Start a data transfer with another EDC. + * Helper method for contracting a certain asset as specified in the catalog item from + * a specific Partner. * - * @param transferId id created for the transferprocess. - * @param connectorAddress ids url of the transfer counterparty. - * @param contractId id of the negotiated contract. - * @param orderId id of the transfers target asset. - * @return response body received from the EDC. - * @throws IOException if the connection to the EDC failed. + * @param partner The Partner to negotiate with + * @param catalogItem An excerpt from a catalog. + * @return The JSON response to your contract offer. + * @throws IOException If the connection to the partners control plane fails */ - public String startTransfer(String transferId, - String connectorAddress, - String contractId, - String orderId) throws IOException { - var transferNode = edcRequestBodyBuilder.buildTransferRequestBody(transferId, connectorAddress, contractId, orderId); - log.debug("TransferRequestBody:\n" + transferNode.toPrettyString()); - var response = sendEdcRequest(transferNode, "/transferprocess"); - String stringData = response.body().string(); + private JsonNode startDspNegotiation(Partner partner, JsonNode catalogItem) throws IOException { + var requestBody = edcRequestBodyBuilder.buildDSPAssetNegotiation(partner, catalogItem); + var response = sendDspPostRequest(requestBody, List.of("v2", "contractnegotiations")); + String responseString = response.body().string(); response.body().close(); - return stringData; + return objectMapper.readTree(responseString); } + /** - * Sends a request to the own EDC Connector Controlplane in order to receive - * the current status of the previously initiated transfer as specified by - * the parameter. - * @param transferId - * @return - * @throws IOException + * Util method for issuing a GET request to the management api of your control plane. + * + * @param pathSegments The path segments + * @return The response + * @throws IOException If the connection to your control plane fails */ - public String getTransferState(String transferId) throws IOException { - var response = sendEdcRequest("/transferprocess/" + transferId); - String stringData = response.body().string(); - response.body().close(); - return stringData; + public Response sendDspGetRequest(List pathSegments) throws IOException { + HttpUrl.Builder urlBuilder = HttpUrl.parse(variablesService.getEdcManagementUrl()).newBuilder(); + for (var pathSegment : pathSegments) { + urlBuilder.addPathSegment(pathSegment); + } + var request = new Request.Builder() + .get() + .url(urlBuilder.build()) + .header("X-Api-Key", variablesService.getEdcApiKey()) + .build(); + return CLIENT.newCall(request).execute(); } /** - * Delete an asset from the own EDC. + * Util method for issuing a POST request to the management api of your control plane. * - * @param assetId id of the asset to delete. - * @return response body received from the EDC. - * @throws IOException if the connection to the EDC failed. + * @param requestBody The request body + * @param pathSegments The path segments + * @return The response from your control plane + * @throws IOException If the connection to your control plane fails */ - public String deleteAsset(String assetId) throws IOException { - var urlBuilder = new HttpUrl.Builder() - .scheme("http") - .host(edcHost) - .port(dataPort); - urlBuilder.addPathSegment("data"); - urlBuilder.addPathSegment("assets"); - urlBuilder.addPathSegment(assetId); - var url = urlBuilder.build(); + private Response sendDspPostRequest(JsonNode requestBody, List pathSegments) throws IOException { + HttpUrl.Builder urlBuilder = HttpUrl.parse(variablesService.getEdcManagementUrl()).newBuilder(); + for (var pathSegment : pathSegments) { + urlBuilder.addPathSegment(pathSegment); + } + RequestBody body = RequestBody.create(MediaType.parse("application/json"), requestBody.toString()); + var request = new Request.Builder() - .url(url) - .header("X-Api-Key", edcApiKey) - .header("Content-Type", "application/json") - .delete() - .build(); - var response = CLIENT.newCall(request).execute(); - String stringData = response.body().string(); - response.body().close(); - return stringData; + .post(body) + .url(urlBuilder.build()) + .header("X-Api-Key", variablesService.getEdcApiKey()) + .header("Content-Type", "application/json") + .build(); + return CLIENT.newCall(request).execute(); } /** - * Send a GET request to the own EDC. + * Sends a request to the own control plane in order to receive + * the current status of the previously initiated contractNegotiations as + * specified by the parameter. * - * @param resourceId (optional) id of the resource to request, will be left empty if null. - * @param pathSegments varargs for the path segments of the request - * (e.g "data", "assets" will be turned to /data/assets). - * @return response body received from the EDC. - * @throws IOException if the connection to the EDC failed. + * @param negotiationId The id of the ongoing negotiation + * @return The response body as String + * @throws IOException If the connection to your control plane fails */ - public String getFromEdc(String resourceId, String... pathSegments) throws IOException { - var urlBuilder = new HttpUrl.Builder() - .scheme("http") - .host(edcHost) - .port(dataPort); - for (var seg : pathSegments) { - urlBuilder.addPathSegment(seg); - } - if (resourceId != null) { - urlBuilder.addPathSegment(resourceId); - } - var url = urlBuilder.build(); - var request = new Request.Builder() - .get() - .url(url) - .header("X-Api-Key", edcApiKey) - .header("Content-Type", "application/json") - .build(); - var response = CLIENT.newCall(request).execute(); + public JsonNode getDspNegotiationState(String negotiationId) throws IOException { + var response = sendDspGetRequest(List.of("v2", "contractnegotiations", negotiationId)); String stringData = response.body().string(); response.body().close(); - return stringData; + return objectMapper.readTree(stringData); } /** - * Util method for building a http POST request to the own EDC. - * Any caller of this method has the responsibility to close - * the returned Response object after using it. + * Sends a request to the own control plane in order to initiate a transfer of + * a previously negotiated asset. * - * @param requestBody requestBody to be sent to the EDC. - * @param urlSuffix path to POST data to - * @return response received from the EDC. - * @throws IOException if the connection to the EDC failed. + * @param partner The partner + * @param contractId The contract id + * @param assetId The asset id + * @return The response object + * @throws IOException If the connection to your control plane fails */ - public Response sendEdcRequest(JsonNode requestBody, String urlSuffix) throws IOException { - Request request = new Request.Builder() - .header("X-Api-Key", edcApiKey) - .header("Content-Type", "application/json") - .post(RequestBody.create(MediaType.parse("application/json"), requestBody.toString())) - .url("http://" + edcHost + ":" + dataPort + dataPath + urlSuffix) - .build(); - log.debug(String.format("Request send to url: %s", request.urlString())); - log.debug(String.format("Request body of EDC Request: %s", requestBody)); - return CLIENT.newCall(request).execute(); + public JsonNode startDspPullTransfer(Partner partner, String contractId, String assetId) throws IOException { + var body = edcRequestBodyBuilder.buildDSPDataPullRequestBody(partner, contractId, assetId); + var response = sendDspPostRequest(body, List.of("v2", "transferprocesses")); + String data = response.body().string(); + response.body().close(); + return objectMapper.readTree(data); } /** - * Util method for building a http GET request to the own EDC. - * Any caller of this method has the responsibility to close - * the returned Response object after using it. - * - * @param urlSuffix path to send GET request to - * @return response received from the EDC. - * @throws IOException if the connection to the EDC failed. + * Sends a request to the own control plane in order to receive + * the current status of the previously initiated transfer as specified by + * the parameter. + * + * @param transferId The id of the transfer in question + * @return The response from your Controlplane + * @throws IOException If the connection to your control plane fails */ - public Response sendEdcRequest(String urlSuffix) throws IOException { - Request request = new Request.Builder() - .header("X-Api-Key", edcApiKey) - .header("Content-Type", "application/json") - .url("http://" + edcHost + ":" + dataPort + dataPath + urlSuffix) - .build(); - log.debug(String.format("Send Request to url: %s", request.urlString())); - - return CLIENT.newCall(request).execute(); + public JsonNode getDspTransferState(String transferId) throws IOException { + var response = sendDspGetRequest(List.of("v2", "transferprocesses", transferId)); + String data = response.body().string(); + response.body().close(); + return objectMapper.readTree(data); } + /** - * Util method for sending a post request to your own dataplane + * Util method for sending a post request the given endpoint * in order to initiate a consumer pull request. * Any caller of this method has the responsibility to close * the returned Response object after using it. - * - * @param url the URL of an endpoint you received to perform a pull request - * @param authKey authKey to be used in the HTTP request header - * @param authCode authCode to be used in the HTTP request header - * @param requestBodyString the request body in JSON format as String - * @return the response from your dataplane + * + * @param url The URL of an endpoint you received to perform a pull request + * @param authKey The authKey to be used in the HTTP request header + * @param authCode The authCode to be used in the HTTP request header + * @param requestBodyString The request body in JSON format as String + * @return The response from the endpoint defined in the url (which is usually the other party's data plane), carrying the asset payload */ - public Response sendDataPullRequest(String url, String authKey, String authCode, String requestBodyString){ - log.debug(String.format("Sending proxy call to endpoint '%s' with auth key '%s' and auth code '%s' with request body '%s'", url, authKey, authCode, requestBodyString)); + public Response sendDataPullRequest(String url, String authKey, String authCode, String requestBodyString) { try { RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), requestBodyString); Request request = new Request.Builder() - .url(url) - .header(authKey, authCode) - .post(requestBody) - .build(); + .url(url) + .header(authKey, authCode) + .post(requestBody) + .build(); return CLIENT.newCall(request).execute(); } catch (Exception e) { log.error("Failed to send Data Pull request to " + url, e); throw new RuntimeException(e); } - } /** * Tries to negotiate for the request api of the given partner, including the retrieval of the - * authCode for a request. - * It will return a String array of length 5. The authKey is stored under index 0, the - * authCode under index 1, the endpoint under index 2 and the contractId under index 3. - * - * @param partnerIdsUrl - * @return a String array or null, if negotiation or transfer have failed or the authCode did not arrive + * authCode for a request. + * It will return a String array of length 4. The authKey is stored under index 0, the + * authCode under index 1, the endpoint under index 2 and the contractId under index 3. + * + * @param partner The partner to negotiate with + * @return A String array or null, if negotiation or transfer have failed or the authCode did not arrive */ - public String[] getContractForRequestApi(String partnerIdsUrl) { + public String[] getContractForRequestApi(Partner partner) { HashMap filter = new HashMap<>(); filter.put("asset:prop:type", "api"); filter.put("asset:prop:apibusinessobject", "product-stock"); filter.put("asset:prop:apipurpose", "request"); filter.put("asset:prop:version", variablesService.getPurisApiVersion()); - return getContractForRequestOrResponseApiApi(partnerIdsUrl, filter); + return getContractForRequestOrResponseApiApi(partner, filter); } /** * Tries to negotiate for the response api of the given partner, including the retrieval of the - * authCode for a request. - * It will return a String array of length 5. The authKey is stored under index 0, the - * authCode under index 1, the endpoint under index 2 and the contractId under index 3. - * @param partnerIdsUrl - * @return a String array or null, if negotiation or transfer have failed or the authCode did not arrive + * authCode for a request. + * It will return a String array of length 4. The authKey is stored under index 0, the + * authCode under index 1, the endpoint under index 2 and the contractId under index 3. + * + * @param partner The partner to negotiate with + * @return A String array or null, if negotiation or transfer have failed or the authCode did not arrive */ - public String[] getContractForResponseApi(String partnerIdsUrl) { + public String[] getContractForResponseApi(Partner partner) { HashMap filter = new HashMap<>(); filter.put("asset:prop:type", "api"); filter.put("asset:prop:apibusinessobject", "product-stock"); filter.put("asset:prop:apipurpose", "response"); filter.put("asset:prop:version", variablesService.getPurisApiVersion()); - return getContractForRequestOrResponseApiApi(partnerIdsUrl, filter); + return getContractForRequestOrResponseApiApi(partner, filter); } /** * Tries to negotiate for the given api of the partner specified by the parameter - * and also tries to initiate the transfer of the edr token to the given endpoint. - * - * It will return a String array of length 5. The authKey is stored under index 0, the - * authCode under index 1, the endpoint under index 2 and the contractId under index 3. - * @param partnerIdsUrl counterparty's idsUrl - * @param filter the filter to be applied on the level of the asset's properties object. - * @return a String array or null, if negotiation or transfer have failed or the authCode did not arrive + * and also tries to initiate the transfer of the edr token to the given endpoint. + *

+ * It will return a String array of length 4. The authKey is stored under index 0, the + * authCode under index 1, the endpoint under index 2 and the contractId under index 3. + * + * @param partner The partner to negotiate with + * @param filter The filter to be applied on the level of the asset's properties object. + * @return A String array or null, if negotiation or transfer have failed or the authCode did not arrive */ - public String[] getContractForRequestOrResponseApiApi(String partnerIdsUrl, Map filter) { + public String[] getContractForRequestOrResponseApiApi(Partner partner, Map filter) { try { - JsonNode objectNode = getCatalogFilteredByAssetPropertyObjectFilter(partnerIdsUrl, filter); - JsonNode contractOffer = objectNode.get("contractOffers").get(0); - String assetApi = contractOffer.get("asset").get("id").asText(); - String contractDefinitionId = contractOffer.get("id").asText(); - String negotiationResponseString = startNegotiation(partnerIdsUrl + "/data", contractDefinitionId, assetApi); - String negotiationId = objectMapper.readTree(negotiationResponseString).get("id").asText(); - + JsonNode catalogItem = getDSPCatalogItems(partner.getEdcUrl(), filter).get(0); + JsonNode negotiationResponse = startDspNegotiation(partner, catalogItem); + String assetApi = catalogItem.get("@id").asText(); + String negotiationId = negotiationResponse.get("@id").asText(); // Await confirmation of contract and contractId String contractId = null; for (int i = 0; i < 100; i++) { Thread.sleep(100); - var negotiationState = getNegotiationState(negotiationId); - var responseObject = objectMapper.readTree(negotiationState); - if ("CONFIRMED".equals(responseObject.get("state").asText())) { - contractId = responseObject.get("contractAgreementId").asText(); + var responseObject = getDspNegotiationState(negotiationId); + if ("FINALIZED".equals(responseObject.get("edc:state").asText())) { + contractId = responseObject.get("edc:contractAgreementId").asText(); break; } } if (contractId == null) { - var negotiationState = getNegotiationState(negotiationId); - log.warn("no contract id, last negotiation state: " + negotiationState); - log.warn("Failed to obtain " + assetApi + " from " + partnerIdsUrl); + var negotiationState = getDspNegotiationState(negotiationId); + log.warn("no contract id, last negotiation state: \n" + negotiationState.toPrettyString()); + log.error("Failed to obtain " + assetApi + " from " + partner.getEdcUrl()); return null; } // Initiate transfer of edr - String randomTransferID = UUID.randomUUID().toString(); - String transferResponse = startTransfer(randomTransferID, partnerIdsUrl + "/data", contractId, assetApi); - String transferId = objectMapper.readTree(transferResponse).get("id").asText(); + var transferResp = startDspPullTransfer(partner, contractId, assetApi); + String transferId = transferResp.get("@id").asText(); for (int i = 0; i < 100; i++) { Thread.sleep(100); - transferResponse = getTransferState(transferId); - var transferResponseObject = objectMapper.readTree(transferResponse); - if ("COMPLETED".equals(transferResponseObject.get("state").asText())) { + transferResp = getDspTransferState(transferId); + if ("STARTED".equals(transferResp.get("edc:state").asText())) { break; } } @@ -535,21 +429,18 @@ public String[] getContractForRequestOrResponseApiApi(String partnerIdsUrl, Map< // Await arrival of edr for (int i = 0; i < 100; i++) { Thread.sleep(100); - EDR_Dto edr_Dto = edrService.findByTransferId(randomTransferID); + EDR_Dto edr_Dto = edrService.findByTransferId(transferId); if (edr_Dto != null) { - log.info("Successfully negotiated for " + assetApi + " with " + partnerIdsUrl); - return new String [] {edr_Dto.getAuthKey(),edr_Dto.getAuthCode(), edr_Dto.getEndpoint(), contractId}; + log.info("Successfully negotiated for " + assetApi + " with " + partner.getEdcUrl()); + return new String[]{edr_Dto.getAuthKey(), edr_Dto.getAuthCode(), edr_Dto.getEndpoint(), contractId}; } } log.warn("did not receive authCode"); - log.warn("Failed to obtain " + assetApi + " from " + partnerIdsUrl); + log.error("Failed to obtain " + assetApi + " from " + partner.getEdcUrl()); return null; - } catch (Exception e){ - log.warn("ERROR"); - log.error("Failed to obtain api from " + partnerIdsUrl, e); + } catch (Exception e) { + log.error("Failed to obtain api from " + partner.getEdcUrl(), e); return null; } - } - } diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/logic/service/EndpointDataReferenceService.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/logic/service/EndpointDataReferenceService.java index f374afbf..25fd4431 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/logic/service/EndpointDataReferenceService.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/logic/service/EndpointDataReferenceService.java @@ -21,7 +21,9 @@ package org.eclipse.tractusx.puris.backend.common.edc.logic.service; import lombok.extern.slf4j.Slf4j; +import org.eclipse.tractusx.puris.backend.common.api.logic.service.VariablesService; import org.eclipse.tractusx.puris.backend.common.edc.logic.dto.EDR_Dto; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @@ -41,10 +43,8 @@ public class EndpointDataReferenceService { * The key is the transferId, the value is the authCode */ final private HashMap nonpersistantRepository = new HashMap<>(); - - - @Value("${own.edr.deletiontimer}") - private long minutesUntilDeletion; + @Autowired + private VariablesService variablesService; /** * Stores transferId and authCode as a key/value-pair. @@ -55,7 +55,7 @@ public class EndpointDataReferenceService { */ public void save(String transferId, EDR_Dto edr_Dto) { nonpersistantRepository.put(transferId, edr_Dto); - final long timer = minutesUntilDeletion * 60 * 1000; + final long timer = variablesService.getEdrTokenDeletionTimer() * 60 * 1000; // Start timer for deletion new Thread(()-> { try { diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/logic/service/ExternalConnectorService.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/logic/service/ExternalConnectorService.java index b4e2d9e5..eaa217d8 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/logic/service/ExternalConnectorService.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/logic/service/ExternalConnectorService.java @@ -76,7 +76,7 @@ public List getAll() { */ private boolean checkUrl(String url) { try { - edcAdapter.getCatalog(url); + edcAdapter.getDSPCatalog(url); } catch (IOException e) { return false; } diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/logic/util/EDCRequestBodyBuilder.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/logic/util/EDCRequestBodyBuilder.java index f0244b92..9ad4d0a5 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/logic/util/EDCRequestBodyBuilder.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/logic/util/EDCRequestBodyBuilder.java @@ -2,25 +2,23 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import org.eclipse.tractusx.puris.backend.common.api.domain.model.datatype.DT_UseCaseEnum; +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.extern.slf4j.Slf4j; import org.eclipse.tractusx.puris.backend.common.api.logic.service.VariablesService; -import org.eclipse.tractusx.puris.backend.common.edc.logic.dto.*; -import org.eclipse.tractusx.puris.backend.common.edc.logic.dto.datatype.DT_ApiBusinessObjectEnum; import org.eclipse.tractusx.puris.backend.common.edc.logic.dto.datatype.DT_ApiMethodEnum; -import org.eclipse.tractusx.puris.backend.common.edc.logic.dto.datatype.DT_AssetTypeEnum; -import org.eclipse.tractusx.puris.backend.common.edc.logic.dto.datatype.DT_DataAddressTypeEnum; +import org.eclipse.tractusx.puris.backend.masterdata.domain.model.Partner; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; +import java.util.Map; + /** * Utility Component for building EDC request body json objects. */ @Component +@Slf4j public class EDCRequestBodyBuilder { - @Value("${edr.endpoint}") - private String endpointDataReferenceEndpoint; @Autowired private VariablesService variablesService; @@ -28,188 +26,200 @@ public class EDCRequestBodyBuilder { @Autowired private ObjectMapper MAPPER; + private final String publicPolicyId = "policy1"; + private final String publicContractDefinitionId = "contractdef1"; + private final String EDC_NAMESPACE = "https://w3id.org/edc/v0.0.1/ns/"; + private final String ODRL_NAMESPACE = "http://www.w3.org/ns/odrl/2/"; + private final String CX_TAXO_NAMESPACE = "https://w3id.org/catenax/taxonomy#"; + private final String CX_COMMON_NAMESPACE = "https://w3id.org/catenax/ontology/common#"; + private final String CX_VERSION_KEY = "https://w3id.org/catenax/ontology/common#version"; + private final String CX_VERSION_NUMBER = "2.0.0"; + private final String DCT_NAMESPACE = "https://purl.org/dc/terms/"; + private final String PURL_TYPE_KEY = "https://purl.org/dc/terms/type"; + + /** - * Build an EDC request body for the creation of an asset (using an order). + * Creates a request body for requesting a catalog in DSP protocol. + * You can add filter criteria. However, at the moment there are issues + * with nested catalog item properties, so it seems advisable to check + * for the filter criteria programmatically. * - * @param orderUrl url where the published order can be received by the controlplane. - * @param orderId id of the created asset (currently has to match policy and contract id). - * @return JsonNode used as requestBody for asset creation. + * @param counterPartyDspUrl The protocol url of the other party + * @param filter Key-value-pairs, may be empty or null + * @return The request body */ - public JsonNode buildAssetRequestBody(String orderUrl, String orderId) { - var assetRequest = MAPPER.createObjectNode(); - var assetNode = MAPPER.createObjectNode(); - var assetPropNode = MAPPER.createObjectNode(); - assetNode.set("properties", assetPropNode); - assetPropNode.put("asset:prop:id", orderId); - assetPropNode.put("asset:prop:description", "EDC Demo Asset"); - assetRequest.set("asset", assetNode); - var dataAddressNode = MAPPER.createObjectNode(); - var propertiesNode = MAPPER.createObjectNode(); - dataAddressNode.set("properties", propertiesNode); - propertiesNode.put("type", "HttpData"); - propertiesNode.put("baseUrl", orderUrl); - assetRequest.set("dataAddress", dataAddressNode); - return assetRequest; + public ObjectNode buildBasicDSPCatalogRequestBody(String counterPartyDspUrl, Map filter) { + var objectNode = getEDCContextObject(); + objectNode.put("protocol", "dataspace-protocol-http"); + objectNode.put("@type", "CatalogRequest"); + objectNode.put("counterPartyAddress", counterPartyDspUrl); + if (filter != null && !filter.isEmpty()) { + var querySpecNode = MAPPER.createObjectNode(); + objectNode.set("querySpec", querySpecNode); + for (var entry : filter.entrySet()) { + querySpecNode.put(entry.getKey(), entry.getValue()); + } + } + return objectNode; } /** - * Build an EDC request body for the creation of a simple USE policy. + * Build a request body for a request to register an api method as an asset in DSP protocol. * - * @param orderId id of the policy to create (currently has to match contract and asset id). - * @return JsonNode used as requestBody for policy creation. + * @param apiMethod The API method you want to register + * @return The request body */ - public JsonNode buildPolicyRequestBody(String orderId) { - var policyNode = MAPPER.createObjectNode(); - var policySubNode = MAPPER.createObjectNode(); - policySubNode.set("prohibitions", MAPPER.createArrayNode()); - policySubNode.set("obligations", MAPPER.createArrayNode()); - var permissionArray = MAPPER.createArrayNode(); - var permissionNode = MAPPER.createObjectNode(); - permissionNode.put("edctype", "dataspaceconnector:permission"); - permissionNode.set("constraints", MAPPER.createArrayNode()); - var actionNode = MAPPER.createObjectNode(); - actionNode.put("type", "USE"); - permissionNode.set("action", actionNode); - permissionArray.add(permissionNode); - policySubNode.set("permissions", permissionArray); - policyNode.put("id", orderId); - policyNode.set("policy", policySubNode); - return policyNode; + public JsonNode buildDSPCreateAssetBody(DT_ApiMethodEnum apiMethod) { + var body = MAPPER.createObjectNode(); + var context = MAPPER.createObjectNode(); + context.put("edc", EDC_NAMESPACE); + context.put("cx-taxo", CX_TAXO_NAMESPACE); + context.put("cx-common", CX_COMMON_NAMESPACE); + context.put("dct", DCT_NAMESPACE); + body.set("@context", context); + + String apiId = variablesService.getApiAssetId(apiMethod); + body.put("@id", apiId); + var properties = MAPPER.createObjectNode(); + properties.put("asset:prop:type", "api"); + properties.put("asset:prop:apibusinessobject", "product-stock"); + properties.put("asset:prop:version", variablesService.getPurisApiVersion()); + properties.put("asset:prop:apipurpose", apiMethod.PURPOSE); + body.set("properties", properties); + var edcProperties = MAPPER.createObjectNode(); + edcProperties.put(CX_VERSION_KEY, CX_VERSION_NUMBER); + var typeNode = MAPPER.createObjectNode(); + String taxonomy = DT_ApiMethodEnum.REQUEST == apiMethod ? "ProductStockRequestApi" : "ProductStockResponseApi"; + typeNode.put("@id", CX_TAXO_NAMESPACE + taxonomy); + edcProperties.set(PURL_TYPE_KEY, typeNode); + + var dataAddress = MAPPER.createObjectNode(); + String url = apiMethod == DT_ApiMethodEnum.REQUEST ? variablesService.getRequestServerEndpoint() : variablesService.getResponseServerEndpoint(); + dataAddress.put("baseUrl", url); + dataAddress.put("type", "HttpData"); + dataAddress.put("proxyPath", "true"); + dataAddress.put("proxyBody", "true"); + dataAddress.put("proxyMethod", "true"); + body.set("dataAddress", dataAddress); + return body; } + /** - * Build an EDC request body for the creation of a contract. + * Creates a request body for registering a public policy in DSP protocol that contains no + * restrictions whatsoever. * - * @param orderId id of the contract to create (currently has to match policy and asset id). - * @return JsonNode used as requestBody for contract creation. + * @return The request body */ - public JsonNode buildContractRequestBody(String orderId) { - var contractNode = MAPPER.createObjectNode(); - contractNode.put("id", orderId); - contractNode.put("accessPolicyId", orderId); - contractNode.put("contractPolicyId", orderId); - var criteriaArray = MAPPER.createArrayNode(); - var criteriaNode = MAPPER.createObjectNode(); - criteriaNode.put("operandLeft", "asset:prop:id"); - criteriaNode.put("operator", "="); - criteriaNode.put("operandRight", orderId); - criteriaArray.add(criteriaNode); - contractNode.set("criteria", criteriaArray); - return contractNode; + public JsonNode buildPublicDSPPolicy() { + var body = MAPPER.createObjectNode(); + var context = MAPPER.createObjectNode(); + context.put("odrl", ODRL_NAMESPACE); + body.set("@context", context); + body.put("@type", "PolicyDefinitionRequestDto"); + body.put("@id", publicPolicyId); + var policy = MAPPER.createObjectNode(); + policy.put("@type", "set"); + var emptyArray = MAPPER.createArrayNode(); + policy.set("odrl:permission", emptyArray); + policy.set("odrl:prohibition", emptyArray); + policy.set("odrl:obligation", emptyArray); + body.set("policy", policy); + return body; } /** - * Build an EDC request body used for starting a negotiation. - * @param connectorAddress ids url of the negotiation counterparty. - * @param contractDefinitionId id of a contract offer given by the counterparty. - * @param assetId id of the negotiations target asset. - * @return JsonNode used as requestBody for an EDC negotiation request. + * Creates the request body for registering a simple contract definition. + * Relies on the policy that is created via the buildPublicDSPPolicy() method. + * + * @return The request body */ - public JsonNode buildNegotiationRequestBody(String connectorAddress, - String contractDefinitionId, - String assetId) { - var negotiationNode = MAPPER.createObjectNode(); - negotiationNode.put("connectorId", "foo"); - negotiationNode.put("connectorAddress", connectorAddress); - negotiationNode.put("protocol", "ids-multipart"); + public JsonNode buildDSPContractDefinitionWithPublicPolicy() { + var body = getEDCContextObject(); + body.put("@id", publicContractDefinitionId); + body.put("accessPolicyId", publicPolicyId); + body.put("contractPolicyId", publicPolicyId); + body.set("assetsSelector", MAPPER.createArrayNode()); + return body; + } + + /** + * Creates the request body for initiating a negotiation in DSP protocol. + * Will use the policy terms as specified in the catalog item. + * + * @param partner The Partner to negotiate with + * @param dcatCatalogItem The catalog entry that describes the target asset. + * @return The request body + */ + public ObjectNode buildDSPAssetNegotiation(Partner partner, JsonNode dcatCatalogItem) { + var objectNode = MAPPER.createObjectNode(); + var contextNode = MAPPER.createObjectNode(); + contextNode.put("edc", EDC_NAMESPACE); + contextNode.put("odrl", ODRL_NAMESPACE); + objectNode.set("@context", contextNode); + objectNode.put("@type", "NegotiationInitiateRequestDto"); + objectNode.put("connectorId", partner.getBpnl()); + objectNode.put("connectorAddress", partner.getEdcUrl()); + objectNode.put("consumerId", variablesService.getOwnBpnl()); + objectNode.put("providerId", partner.getBpnl()); + objectNode.put("protocol", "dataspace-protocol-http"); + String assetId = dcatCatalogItem.get("@id").asText(); + var policyNode = dcatCatalogItem.get("odrl:hasPolicy"); var offerNode = MAPPER.createObjectNode(); - offerNode.put("offerId", contractDefinitionId); + String offerId = policyNode.get("@id").asText(); + offerNode.put("offerId", offerId); offerNode.put("assetId", assetId); - var policyNode = MAPPER.createObjectNode(); - policyNode.put("uid", assetId); - policyNode.set("prohibitions", MAPPER.createArrayNode()); - policyNode.set("obligations", MAPPER.createArrayNode()); - var permissionArray = MAPPER.createArrayNode(); - var permissionNode = MAPPER.createObjectNode(); - permissionNode.put("edctype", "dataspaceconnector:permission"); - permissionNode.put("target", assetId); - permissionNode.set("constraints", MAPPER.createArrayNode()); - var actionNode = MAPPER.createObjectNode(); - actionNode.put("type", "USE"); - permissionNode.set("action", actionNode); - permissionArray.add(permissionNode); - policyNode.set("permissions", permissionArray); offerNode.set("policy", policyNode); - negotiationNode.set("offer", offerNode); - return negotiationNode; + objectNode.set("offer", offerNode); + return objectNode; } /** - * Build an EDC request body used for starting a transfer. + * Creates the request body for requesting a data pull transfer using the + * DSP protocol and the Tractus-X-EDC. * - * @param transferId id created for the transferprocess. - * @param connectorAddress ids url of the negotiation counterparty. - * @param contractId id of the negotiated contract. - * @param orderId id of the transfers target asset. - * @return JsonNode used as requestBody for an EDC transfer request. + * @param partner The Partner who controls the target asset + * @param contractID The contractId + * @param assetId The assetId + * @return The request body */ - public JsonNode buildTransferRequestBody(String transferId, - String connectorAddress, - String contractId, - String orderId) { - var transferNode = MAPPER.createObjectNode(); - transferNode.put("edctype", "dataspaceconnector:datarequest"); - transferNode.put("protocol", "ids-multipart"); - transferNode.put("id", transferId); - transferNode.put("connectorId", "foo"); - transferNode.put("connectorAddress", connectorAddress); - transferNode.put("contractId", contractId); - transferNode.put("assetId", orderId); - transferNode.put("managedResources", "false"); - var destinationNode = MAPPER.createObjectNode(); - var propertiesNode = MAPPER.createObjectNode(); - propertiesNode.put("type", "HttpProxy"); - destinationNode.set("properties", propertiesNode); - transferNode.set("dataDestination", destinationNode); - var transferTypeNode = MAPPER.createObjectNode(); - transferTypeNode.put("contentType", "application/octet-stream"); - transferTypeNode.put("isFinite", true); - transferNode.set("transferType", transferTypeNode); - transferNode.put("managedResources", false); - propertiesNode = MAPPER.createObjectNode(); - propertiesNode.put("receiver.http.endpoint", endpointDataReferenceEndpoint); - transferNode.set("properties", propertiesNode); - - return transferNode; + public JsonNode buildDSPDataPullRequestBody(Partner partner, String contractID, String assetId) { + var body = getEDCContextObject(); + body.put("@type", "TransferRequestDto"); + body.put("connectorId", partner.getBpnl()); + body.put("connectorAddress", partner.getEdcUrl()); + body.put("contractId", contractID); + body.put("assetId", assetId); + body.put("protocol", "dataspace-protocol-http"); + var dataDestination = MAPPER.createObjectNode(); + dataDestination.put("type", "HttpProxy"); + body.set("dataDestination", dataDestination); + var callbackAddress = MAPPER.createObjectNode(); + callbackAddress.put("uri", variablesService.getEdrEndpoint()); + callbackAddress.put("transactional", false); + var events = MAPPER.createArrayNode(); + events.add("contract.negotiation"); + events.add("transfer.process"); + callbackAddress.set("events", events); + var callbackAddresses = MAPPER.createArrayNode(); + callbackAddresses.add(callbackAddress); + body.set("callbackAddresses", callbackAddresses); + return body; } /** - * Builds a CreateAssetDto for an API + * A helper method returning a basic request object that can be used to build other + * specific request bodies. * - * @param method api method to create - * @param apiBaseUrl api baseUrl to get the data at - * @return assetDto for creation. + * @return A request body stub */ - public CreateAssetDto buildCreateAssetDtoForApi(DT_ApiMethodEnum method, - String apiBaseUrl) { - AssetPropertiesDto apiAssetPropertiesDto = new AssetPropertiesDto(); - apiAssetPropertiesDto.setApiBusinessObject(DT_ApiBusinessObjectEnum.PRODUCT_STOCK.PROPERTIES_DESCRIPTION); - apiAssetPropertiesDto.setApiPurpose(method.PURPOSE); - apiAssetPropertiesDto.setContentType("application/json"); - apiAssetPropertiesDto.setId(variablesService.getApiAssetId(method)); - apiAssetPropertiesDto.setName(method.NAME); - apiAssetPropertiesDto.setType(DT_AssetTypeEnum.api); - apiAssetPropertiesDto.setUseCase(DT_UseCaseEnum.PURIS); - apiAssetPropertiesDto.setVersion(variablesService.getPurisApiVersion()); - - AssetDto apiAssetDto = new AssetDto(); - apiAssetDto.setPropertiesDto(apiAssetPropertiesDto); - - DataAddressPropertiesDto apiDataAddressPropertiesDto = - new DataAddressPropertiesDto(); - apiDataAddressPropertiesDto.setBaseUrl(apiBaseUrl); - apiDataAddressPropertiesDto.setType(DT_DataAddressTypeEnum.HttpData); - apiDataAddressPropertiesDto.setProxyBody(true); - apiDataAddressPropertiesDto.setProxyMethod(true); - - DataAddressDto apiDataAddressDto = new DataAddressDto(); - apiDataAddressDto.setDataAddressPropertiesDto(apiDataAddressPropertiesDto); - - CreateAssetDto createApiAssetDto = new CreateAssetDto(); - createApiAssetDto.setAssetDto(apiAssetDto); - createApiAssetDto.setDataAddressDto(apiDataAddressDto); - - return createApiAssetDto; + private ObjectNode getEDCContextObject() { + ObjectNode node = MAPPER.createObjectNode(); + var context = MAPPER.createObjectNode(); + context.put("edc", EDC_NAMESPACE); + node.set("@context", context); + return node; } + } diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/controller/DashboardController.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/controller/DashboardController.java index 525a1578..0724b31d 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/controller/DashboardController.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/controller/DashboardController.java @@ -39,27 +39,27 @@ @RequestMapping("dashboard") public class DashboardController { - private static final ObjectMapper MAPPER = new ObjectMapper(); + private static final ObjectMapper MAPPER = new ObjectMapper(); - @Autowired OrderRepository orderRepository; + @Autowired + OrderRepository orderRepository; - @Autowired - EdcAdapterService edcAdapter; + @Autowired + EdcAdapterService edcAdapter; - /** - * Collect information for frontends dashboard. - * - * @return information about created and published orders, used to show frontends dashboard. - * @throws IOException when connection to EDC fails. - */ - @GetMapping("data") - @CrossOrigin - public JsonNode getData() throws IOException { - var orders = orderRepository.findAll(); - var node = MAPPER.createObjectNode(); - node.put("orders", orders.size()); - node.put("ordersSent", edcAdapter.getFromEdc(null, "data", "assets")); - node.put("responses", edcAdapter.getFromEdc(null, "data", "transferprocess")); - return node; - } + /** + * Collect information for frontends dashboard. + * + * @return information about created and published orders, used to show frontends dashboard. + * @throws IOException when connection to EDC fails. + */ + @GetMapping("data") + @CrossOrigin + public JsonNode getData() throws IOException { + var orders = orderRepository.findAll(); + var node = MAPPER.createObjectNode(); + node.put("orders", orders.size()); + node.put("service", "currently not supported"); + return node; + } } diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/service/PartnerServiceImpl.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/service/PartnerServiceImpl.java index 3134bb98..76d03fdd 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/service/PartnerServiceImpl.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/logic/service/PartnerServiceImpl.java @@ -51,9 +51,9 @@ public class PartnerServiceImpl implements PartnerService { @Autowired private VariablesService variablesService; - private Pattern bpnlPattern = Pattern.compile("^BPNL[0-9]{10}[A-Z]{2}$"); - private Pattern bpnsPattern = Pattern.compile("^BPNS[0-9]{10}[A-Z]{2}$"); - private Pattern bpnaPattern = Pattern.compile("^BPNA[0-9]{10}[A-Z]{2}$"); + private final Pattern bpnlPattern = Pattern.compile("^BPNL[0-9a-zA-Z]{12}$"); + private final Pattern bpnsPattern = Pattern.compile("^BPNS[0-9a-zA-Z]{12}$"); + private final Pattern bpnaPattern = Pattern.compile("^BPNA[0-9a-zA-Z]{12}$"); @Override public Partner create(Partner partner) { diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/controller/ProductStockRequestApiController.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/controller/ProductStockRequestApiController.java index 759bb380..1a109922 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/controller/ProductStockRequestApiController.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/controller/ProductStockRequestApiController.java @@ -34,6 +34,7 @@ import org.eclipse.tractusx.puris.backend.common.api.domain.model.datatype.DT_RequestStateEnum; import org.eclipse.tractusx.puris.backend.common.api.logic.dto.MessageHeaderDto; import org.eclipse.tractusx.puris.backend.common.api.logic.dto.SuccessfulRequestDto; +import org.eclipse.tractusx.puris.backend.common.api.logic.service.VariablesService; import org.eclipse.tractusx.puris.backend.stock.domain.model.ProductStockRequest; import org.eclipse.tractusx.puris.backend.stock.logic.service.ProductStockRequestApiServiceImpl; import org.eclipse.tractusx.puris.backend.stock.logic.service.ProductStockRequestService; @@ -54,16 +55,13 @@ public class ProductStockRequestApiController { @Autowired - ModelMapper modelMapper; + private ObjectMapper objectMapper; @Autowired - ObjectMapper objectMapper; + private ProductStockRequestService productStockRequestService; @Autowired - ProductStockRequestService productStockRequestService; - - @Autowired - ProductStockRequestApiServiceImpl requestApiService; + private ProductStockRequestApiServiceImpl requestApiService; @PostMapping("request") @Operation(summary = "This endpoint receives the product stock requests from a consumer.", @@ -139,7 +137,7 @@ public ResponseEntity postRequest(@RequestBody String requestBody) { @ApiResponse(responseCode = "401", description = "Not authorized"), @ApiResponse(responseCode = "422", description = "The request ID is not known") }) - public ResponseEntity getRequest(@RequestBody JsonNode body) { + public ResponseEntity getRequest(@RequestBody(required = false) JsonNode body) { try { MessageHeaderDto header = objectMapper.convertValue(body.get("header"), MessageHeaderDto.class); var request = productStockRequestService.findRequestByHeaderUuid(header.getRequestId()); diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/controller/ProductStockResponseApiController.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/controller/ProductStockResponseApiController.java index 438c4f4e..55b19b83 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/controller/ProductStockResponseApiController.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/controller/ProductStockResponseApiController.java @@ -31,18 +31,15 @@ import lombok.extern.slf4j.Slf4j; import org.eclipse.tractusx.puris.backend.common.api.domain.model.datatype.DT_RequestStateEnum; import org.eclipse.tractusx.puris.backend.common.api.logic.dto.SuccessfulRequestDto; +import org.eclipse.tractusx.puris.backend.common.api.logic.service.VariablesService; import org.eclipse.tractusx.puris.backend.stock.domain.model.ProductStockRequest; import org.eclipse.tractusx.puris.backend.stock.domain.model.ProductStockResponse; import org.eclipse.tractusx.puris.backend.stock.logic.service.ProductStockRequestService; import org.eclipse.tractusx.puris.backend.stock.logic.service.ProductStockResponseApiServiceImpl; -import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatusCode; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import java.util.UUID; @@ -55,17 +52,13 @@ public class ProductStockResponseApiController { @Autowired - ProductStockRequestService productStockRequestService; + private ProductStockRequestService productStockRequestService; @Autowired - ModelMapper modelMapper; + private ObjectMapper objectMapper; @Autowired - ObjectMapper objectMapper; - - @Autowired - ProductStockResponseApiServiceImpl productStockResponseApiService; - + private ProductStockResponseApiServiceImpl productStockResponseApiService; @PostMapping("response") @Operation(summary = "This endpoint receives the responses to the consumer's requests.", diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/controller/StockViewController.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/controller/StockViewController.java index e1222085..69d2346c 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/controller/StockViewController.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/controller/StockViewController.java @@ -287,15 +287,12 @@ public List getCustomerPartnersOrderingMaterial(@RequestParam String "when the corresponding responses will be available. As soon as a response arrives, it will be available via a " + "call to the GET partner-product-stocks endpoint.") public List triggerPartnerProductStockUpdateForMaterial(@RequestParam String ownMaterialNumber) { - Material materialEntity = materialService.findByOwnMaterialNumber(ownMaterialNumber); log.info("Found material: " + (materialEntity != null) + " " + ownMaterialNumber); - List allSupplierPartnerEntities = mprService.findAllSuppliersForOwnMaterialNumber(ownMaterialNumber); for (Partner supplierPartner : allSupplierPartnerEntities) { - - productStockRequestApiService.request(materialEntity, supplierPartner); + productStockRequestApiService.doRequest(materialEntity, supplierPartner); } return allSupplierPartnerEntities.stream() diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/service/ProductStockRequestApiService.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/service/ProductStockRequestApiService.java index 8b342822..c7b0b597 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/service/ProductStockRequestApiService.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/service/ProductStockRequestApiService.java @@ -43,10 +43,10 @@ public interface ProductStockRequestApiService { void handleRequest(ProductStockRequest productStockRequest); /** - * requests an update for the stock of the material for the given supplierPartner + * This method requests an update for the stock of the material for the given supplierPartner. * * @param material material to get the latest stock quantity for * @param supplierPartner partner to get the update from */ - void request(Material material, Partner supplierPartner); + void doRequest(Material material, Partner supplierPartner); } diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/service/ProductStockRequestApiServiceImpl.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/service/ProductStockRequestApiServiceImpl.java index 82b3dc0d..fb503558 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/service/ProductStockRequestApiServiceImpl.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/logic/service/ProductStockRequestApiServiceImpl.java @@ -39,7 +39,6 @@ import org.eclipse.tractusx.puris.backend.stock.logic.adapter.ProductStockSammMapper; import org.eclipse.tractusx.puris.backend.stock.logic.dto.samm.ProductStockSammDto; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.util.*; @@ -83,21 +82,6 @@ public class ProductStockRequestApiServiceImpl implements ProductStockRequestApi @Autowired private ObjectMapper objectMapper; - @Value("${edc.dataplane.public.port}") - String dataPlanePort; - - @Value("${edc.controlplane.host}") - String dataPlaneHost; - - @Value("${edc.idsUrl}") - private String ownEdcIdsUrl; - - @Value("${own.bpnl}") - private String ownBPNL; - - @Value("${edc.applydataplaneworkaround}") - private boolean applyDataplaneWorkaround; - @Autowired private VariablesService variablesService; @@ -108,28 +92,14 @@ public static Predicate distinctByKey( return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null; } - - /** - * This method should be called in a separate Thread. - * - * It will evaluate the given ProductStockRequest and check, whether this Partner is - * currently known as a customer for the given products. Then this method will assemble - * all necessary information from database, generate ProductStockSammDto's and then send - * them to the Partner via his product-stock-response-api. - * - *

Please note that this method currently does not support multple BPNS's/BPNA's per Partner.

- * - * @param productStockRequest a ProductStockRequest you received from a Customer Partner - */ public void handleRequest(ProductStockRequest productStockRequest) { - productStockRequest = productStockRequestService.updateState(productStockRequest,DT_RequestStateEnum.Working); String requestingPartnerBpnl = productStockRequest.getHeader().getSender(); Partner requestingPartner = partnerService.findByBpnl(requestingPartnerBpnl); - String partnerIdsUrl = requestingPartner.getEdcUrl(); + String partnerEdcUrl = requestingPartner.getEdcUrl(); - if (productStockRequest.getHeader().getSenderEdc() != null && !partnerIdsUrl.equals(productStockRequest.getHeader().getSenderEdc())) { + if (productStockRequest.getHeader().getSenderEdc() != null && !partnerEdcUrl.equals(productStockRequest.getHeader().getSenderEdc())) { log.warn("Partner " + requestingPartner.getName() + " is using unknown idsUrl: " + productStockRequest.getHeader().getSenderEdc()); log.warn("Request will not be processed"); productStockRequestService.updateState(productStockRequest, DT_RequestStateEnum.Error); @@ -233,9 +203,9 @@ public void handleRequest(ProductStockRequest productStockRequest) { } - var data = edcAdapterService.getContractForResponseApi(partnerIdsUrl); + var data = edcAdapterService.getContractForResponseApi(requestingPartner); if(data == null) { - log.error("Failed to contract response api from " + partnerIdsUrl); + log.error("Failed to contract response api from " + partnerEdcUrl); productStockRequest = productStockRequestService.updateState(productStockRequest, DT_RequestStateEnum.Error); log.info("Request status: \n" + productStockRequest.toString()); return; @@ -248,25 +218,20 @@ public void handleRequest(ProductStockRequest productStockRequest) { MessageHeader messageHeader = new MessageHeader(); messageHeader.setRequestId(productStockRequest.getHeader().getRequestId()); messageHeader.setContractAgreementId(contractId); - messageHeader.setSender(ownBPNL); - messageHeader.setSenderEdc(ownEdcIdsUrl); + messageHeader.setSender(variablesService.getOwnBpnl()); + messageHeader.setSenderEdc(variablesService.getEdcProtocolUrl()); // set receiver per partner messageHeader.setReceiver(productStockRequest.getHeader().getSender()); messageHeader.setUseCase(DT_UseCaseEnum.PURIS); messageHeader.setCreationDate(new Date()); - ProductStockResponse response = new ProductStockResponse(); response.setHeader(messageHeader); response.getContent().setProductStocks(resultProductStocks); - if (applyDataplaneWorkaround) { - log.info("Applying Dataplane Address Workaround"); - endpoint = "http://" + dataPlaneHost + ":" + dataPlanePort + "/api/public"; - } try { String requestBody = objectMapper.writeValueAsString(response); var httpResponse = edcAdapterService.sendDataPullRequest( - endpoint, authKey, authCode, requestBody); + endpoint, authKey, authCode, requestBody); log.info(httpResponse.body().string()); httpResponse.body().close(); productStockRequest = productStockRequestService.updateState(productStockRequest, DT_RequestStateEnum.Completed); @@ -279,7 +244,8 @@ public void handleRequest(ProductStockRequest productStockRequest) { } - public void request(Material material, Partner supplierPartner){ + + public void doRequest(Material material, Partner supplierPartner){ ProductStockRequest productStockRequest = new ProductStockRequest(); MaterialPartnerRelation materialPartnerRelation = mprService.find(material, supplierPartner); @@ -297,7 +263,7 @@ public void request(Material material, Partner supplierPartner){ ); productStockRequest.getContent().getProductStock().add(materialToRequest); - String [] data = edcAdapterService.getContractForRequestApi(supplierPartner.getEdcUrl()); + String [] data = edcAdapterService.getContractForRequestApi(supplierPartner); if(data == null) { log.error("failed to obtain request api from " + supplierPartner.getEdcUrl()); return; @@ -305,10 +271,6 @@ public void request(Material material, Partner supplierPartner){ String authKey = data[0]; String authCode = data[1]; String endpoint = data[2]; - if (applyDataplaneWorkaround) { - log.info("Applying Dataplane Address Workaround"); - endpoint = "http://" + dataPlaneHost + ":" + dataPlanePort + "/api/public"; - } String cid = data[3]; MessageHeader messageHeader = new MessageHeader(); @@ -322,7 +284,7 @@ public void request(Material material, Partner supplierPartner){ messageHeader.setRespondAssetId(variablesService.getResponseApiAssetId()); messageHeader.setContractAgreementId(cid); messageHeader.setSender(variablesService.getOwnBpnl()); - messageHeader.setSenderEdc(variablesService.getOwnEdcIdsUrl()); + messageHeader.setSenderEdc(variablesService.getEdcProtocolUrl()); // set receiver per partner messageHeader.setReceiver(supplierPartner.getBpnl()); messageHeader.setUseCase(DT_UseCaseEnum.PURIS); diff --git a/backend/src/main/resources/application-customer.properties b/backend/src/main/resources/application-customer.properties deleted file mode 100755 index 6793d661..00000000 --- a/backend/src/main/resources/application-customer.properties +++ /dev/null @@ -1,63 +0,0 @@ -# Server Config -server.port=${SERVER_PORT:8081} -my.base.url=http://host.minikube.internal -request.serverendpoint=${my.base.url}:${server.port}/catena/product-stock/request -request.apiassetid=product-stock-request-api -response.serverendpoint=${my.base.url}:${server.port}/catena/product-stock/response -response.apiassetid=product-stock-response-api -edr.endpoint=${my.base.url}:${server.port}/catena/edrendpoint -# DB Configuration -spring.datasource.driver-class-name=${DATASOURCE_DRIVERCLASSNAME:org.hsqldb.jdbc.JDBCDriver} -spring.datasource.url=${DATASOURCE_URL:jdbc:hsqldb:mem:testdb;DB_CLOSE_DELAY=-1} -spring.datasource.username=${DATASOURCE_USERNAME:sa} -spring.datasource.password=${DATASOURCE_PASSWORD:} -spring.jpa.hibernate.ddl-auto=create -# API Root -server.servlet.context-path=${API_ROOTDIR:/catena} -# EDC Config -edc.controlplane.host=${EDC_CONTROLPLANE_HOST:192.168.49.2} -edc.controlplane.data.path=/api/v1/data -edc.controlplane.data.port=${EDC_CONTROLPLANE_DATA_PORT:31944} -edc.controlplane.key=${EDC_CONTROLPLANE_KEY:password} - -edc.applydataplaneworkaround=true -edc.dataplane.public.port=31944 -minikube.ip=${MINIKUBE_IP:host.minikube.internal} -# Jackson (JSON) -#spring.jackson.default-property-inclusion=non_empty -#logging.level.org.hibernate.SQL=DEBUG -#logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE -spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true - - -# Own EDC-IDS-URL -edc.idsUrl=${EDC_IDSURL:http://customer-control-plane:8184/api/v1/ids} - -# Own BPNL -own.bpnl=${OWN_BPNL:BPNL4444444444XX} - -# Own name (self-description) -own.name=${OWN_NAME:Scenario Customer} - -# Own BPNS (optional: if this is set, then set own.default.site.name as well) -own.default.bpns=${OWN_BPNS:BPNS4444444444XY} -# Name of Site (see above) -own.default.site.name=${OWN_SITE:Hauptwerk Musterhausen} - -# If a BPNS is set, then this BPNA will be attached to it. -# Otherwise, it will be attached immediately to the BPNL (see above) -own.default.bpna=${OWN_BPNS:BPNA4444444444ZZ} -own.default.streetandnumber=${OWN_STREETANDNUMBER:Musterstrasse 35b} -own.default.zipcodeandcity=${OWN_ZIPCODEANDCITY:77777 Musterhausen} -own.default.country=${OWN_COUNTRY:Germany} - -# The number of minutes before received authentication data -# in the context of a consumer pull is removed from memory -own.edr.deletiontimer=2 - -puris.apiversion=1.0.0 -puris.demonstrator.role=customer -# run with: -# ./mvnw spring-boot:run -Dspring-boot.run.arguments=--spring.profiles.active=customer -# alternatively: -# ./mvnw spring-boot:run -Dspring-boot.run.arguments=--spring.config.location="./src/main/resources/application-customer.properties" diff --git a/backend/src/main/resources/application-supplier.properties b/backend/src/main/resources/application-supplier.properties deleted file mode 100644 index 647eca99..00000000 --- a/backend/src/main/resources/application-supplier.properties +++ /dev/null @@ -1,64 +0,0 @@ -# Server Config -server.port=${SERVER_PORT:8082} -my.base.url=http://host.minikube.internal -request.serverendpoint=${my.base.url}:${server.port}/catena/product-stock/request -request.apiassetid=product-stock-request-api -response.serverendpoint=${my.base.url}:${server.port}/catena/product-stock/response -response.apiassetid=product-stock-response-api -edr.endpoint=${my.base.url}:${server.port}/catena/edrendpoint -# DB Configuration -spring.datasource.driver-class-name=${DATASOURCE_DRIVERCLASSNAME:org.hsqldb.jdbc.JDBCDriver} -spring.datasource.url=${DATASOURCE_URL:jdbc:hsqldb:mem:testdb;DB_CLOSE_DELAY=-1} -spring.datasource.username=${DATASOURCE_USERNAME:sa} -spring.datasource.password=${DATASOURCE_PASSWORD:} -spring.jpa.hibernate.ddl-auto=create -# API Root -server.servlet.context-path=${API_ROOTDIR:/catena} -# EDC Config -edc.controlplane.host=${EDC_CONTROLPLANE_HOST:192.168.49.2} -edc.controlplane.data.port=${EDC_CONTROLPLANE_DATA_PORT:32272} -edc.controlplane.data.path=/api/v1/data -edc.controlplane.key=${EDC_CONTROLPLANE_KEY:password} - -edc.applydataplaneworkaround=true -edc.dataplane.public.port=31102 -minikube.ip=${MINIKUBE_IP:host.minikube.internal} -# Jackson (JSON) -#spring.jackson.default-property-inclusion=non_empty -#logging.level.org.hibernate.SQL=DEBUG -#logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE -spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true - - -# Own EDC-IDS-URL -edc.idsUrl=${EDC_IDSURL:http://supplier-control-plane:9184/api/v1/ids} - -# Own BPNL -own.bpnl=${OWN_BPNL:BPNL1234567890ZZ} - -# Own name (self-description) -own.name=${OWN_NAME:Scenario Supplier} - -# Own BPNS (optional: if this is set, then set own.default.site.name as well) -own.default.bpns=${OWN_BPNS:BPNS1234567890ZZ} - -# Name of Site (see above) -own.default.site.name=${OWN_SITE:Konzernzentrale Dudelsdorf} - -# If a BPNS is set, then this BPNA will be attached to it. -# Otherwise, it will be attached immediately to the BPNL (see above) -own.default.bpna=${OWN_BPNS:BPNA1234567890AA} -own.default.streetandnumber=${OWN_STREETANDNUMBER:Heinrich-Supplier-Strasse 1} -own.default.zipcodeandcity=${OWN_ZIPCODEANDCITY:77785 Dudelsdorf} -own.default.country=${OWN_COUNTRY:Germany} - -# The number of minutes before received authentication data -# in the context of a consumer pull is removed from memory -own.edr.deletiontimer=2 - -puris.apiversion=1.0.0 -puris.demonstrator.role=supplier -# run with: -# ./mvnw spring-boot:run -Dspring-boot.run.arguments=--spring.profiles.active=supplier -# alternatively: -# ./mvnw spring-boot:run -Dspring-boot.run.arguments=--spring.config.location="./src/main/resources/application-supplier.properties" diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index 59df5890..07625fdf 100755 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -1,11 +1,15 @@ # Server Config server.port=${SERVER_PORT:8081} -my.base.url=http://localhost -request.serverendpoint=${my.base.url}:${server.port}/catena/product-stock/request -request.apiassetid=product-stock-request-api -response.serverendpoint=${my.base.url}:${server.port}/catena/product-stock/response -response.apiassetid=product-stock-response-api -edr.endpoint=${my.base.url}:${server.port}/catena/edrendpoint +puris.demonstrator.role=${PURIS_DEMONSTRATOR_ROLE:customer} +puris.apiversion=${PURIS_APIVERSION:1.0.0} +puris.edr.endpoint=${PURIS_EDR_ENDPOINT:http://customer-backend:8081/catena/edrendpoint} +puris.edr.deletiontimer=${PURIS_EDR_DELETIONTIMER:2} +puris.request.serverendpoint=${PURIS_REQUEST_SERVERENDPOINT:http://customer-backend:8081/catena/product-stock/request} +puris.request.apiassetid=${PURIS_REQUEST_APIASSETID:request-api-asset} +puris.response.serverendpoint=${PURIS_RESPONSE_SERVERENDPOINT:http://customer-backend:8081/catena/product-stock/response} +puris.response.apiassetid=${PURIS_RESPONSE_APIASSETID:response-api-asset} + + # DB Configuration spring.datasource.driver-class-name=${DATASOURCE_DRIVERCLASSNAME:org.hsqldb.jdbc.JDBCDriver} spring.datasource.url=${DATASOURCE_URL:jdbc:hsqldb:mem:testdb;DB_CLOSE_DELAY=-1} @@ -15,46 +19,30 @@ spring.jpa.hibernate.ddl-auto=create # API Root server.servlet.context-path=${API_ROOTDIR:/catena} # EDC Config -edc.controlplane.host=${EDC_CONTROLPLANE_HOST:172.17.0.2} -edc.controlplane.data.port=${EDC_CONTROLPLANE_DATA_PORT:31960} -edc.controlplane.data.path=/api/v1/data edc.controlplane.key=${EDC_CONTROLPLANE_KEY:password} +edc.controlplane.management.url=${EDC_CONTROLPLANE_MANAGEMENT_URL:http://customer-control-plane:8181/management} +edc.controlplane.protocol.url=${EDC_CONTROLPLANE_PROTOCOL_URL:http://customer-control-plane:8184/api/v1/dsp} -edc.applydataplaneworkaround=true -edc.dataplane.public.port=30703 -minikube.ip=${MINIKUBE_IP:host.minikube.internal} # Jackson (JSON) #spring.jackson.default-property-inclusion=non_empty #logging.level.org.hibernate.SQL=DEBUG #logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true -# Own EDC-IDS-URL -edc.idsUrl=${EDC_IDSURL:http://customer-control-plane:8184/api/v1/ids} - # Own BPNL own.bpnl=${OWN_BPNL:BPNL4444444444XX} - # Own name (self-description) own.name=${OWN_NAME:Scenario Customer} - # Own BPNS (optional: if this is set, then set own.default.site.name as well) -own.default.bpns=${OWN_BPNS:BPNS4444444444XY} +own.default.bpns=${OWN_DEFAULT_BPNS:BPNS4444444444XX} # Name of Site (see above) -own.default.site.name=${OWN_SITE:Hauptwerk Musterhausen} - +own.default.site.name=${OWN_DEFAULT_SITE_NAME:Hauptwerk Musterhausen} # If a BPNS is set, then this BPNA will be attached to it. # Otherwise, it will be attached immediately to the BPNL (see above) -own.default.bpna=${OWN_BPNS:BPNA4444444444ZZ} -own.default.streetandnumber=${OWN_STREETANDNUMBER:Musterstrasse 35b} -own.default.zipcodeandcity=${OWN_ZIPCODEANDCITY:77777 Musterhausen} -own.default.country=${OWN_COUNTRY:Germany} - -# The number of minutes before received authentication data -# in the context of a consumer pull is removed from memory -own.edr.deletiontimer=2 +own.default.bpna=${OWN_DEFAULT_BPNA:BPNA4444444444ZZ} +own.default.streetandnumber=${OWN_DEFAULT_STREETANDNUMBER:Musterstrasse 35b} +own.default.zipcodeandcity=${OWN_DEFAULT_ZIPCODEANDCITY:77777 Musterhausen} +own.default.country=${OWN_DEFAULT_COUNTRY:Germany} -puris.apiversion=1.0.0 -puris.demonstrator.role=customer # run with: # ./mvnw spring-boot:run -Dspring-boot.run.arguments=--spring.config.location="./src/main/resources/application.properties" diff --git a/local/.env b/local/.env deleted file mode 100644 index dabd766a..00000000 --- a/local/.env +++ /dev/null @@ -1,5 +0,0 @@ -VAULT_DEV_ROOT_TOKEN_ID= -EDC_API_PW= -PG_USER= -PG_PW= -VAULT_SECRETS_DIR= diff --git a/local/.gitignore b/local/.gitignore index d9f256bc..b7ed45e9 100644 --- a/local/.gitignore +++ b/local/.gitignore @@ -1,4 +1,5 @@ *.key *.cert *.keys +*.secret local/.env diff --git a/local/INSTALL.md b/local/INSTALL.md index 444e1561..66771be2 100644 --- a/local/INSTALL.md +++ b/local/INSTALL.md @@ -1,52 +1,46 @@ # Initial Setup -1. Generate keys +In case you had any previous installations of this project on your machine, it is advisable to remove them via the script +(see below in the Notes on debugging section). + +Run the following script to generate the necessary keys. It will also create an .env file in the ./local folder. +Make sure to have `openssl` and `jq` installed in your shell. ```shell cd local sh generate-keys.sh ``` -2. Define remaining secrets in `/local/.env` - - set root token for vault instance `VAULT_DEV_ROOT_TOKEN_ID` (e.g. "4Ko6r3UcHM4dXnOGmPKTHds3") - - set password for edc control plane `EDC_API_PW` (e.g. "password") - - set user `PG_USER` and password `PG_PW` for postgres (e.g. "edc-pg-user" and "edc-pg-passw0rd") - - set vault secrets dir as mapped via volume (e.g. `/vault/secrets/`) -# Start -```shell -docker-compose up -``` -or use -``` -sh restart.sh -``` -Wait for the startup and visit http://localhost:3000/ +# Build +If you are doing a fresh install and everytime you edited the code of the PURIS frontend or backend you have to create a +new build of docker images for the PURIS frontend/backend. -## Notes on debugging +Please see the INSTALL.md documents in the [frontend](../frontend/INSTALL.md) and [backend](../backend/INSTALL.md) -### DAPS -The omejdn-daps does not provide any further logging configuration. -It may make sense to log the whole tokens or responses to decode the JWT or similar. +The default image tag is 'dev'. Remember to also adjust the tag in the docker-compose.yaml if you want to use different +tags. + +# Start +First start the infrastructure: -Requires ruby, which can be installed on Ubuntu as follows: ```shell -sudo apt-get install ruby +docker compose -f docker-compose-infrastructure.yaml up ``` - -Then download the respective [omejdn release](https://github.com/Fraunhofer-AISEC/omejdn-server/releases/tag/v1.7.1) and unzip it. -In the `omejdn-server/omejdn.rb` -- search for token POST endpoint ("endpoint '/token', ['POST'],") -- go to end of endpoint definition (most left-hand end) -- add your echo / log upfront the status codes return (e.g. "puts.response.compact.to_json") -- build the omejdn server +After the MIW container has finished booting, use this script to initialise two wallets for customer and supplier: ```shell -docker build -t omejdn-server:local +sh ./init-wallets.sh ``` +Then start the PURIS demonstrator containers via: +```shell +docker compose up +``` +Wait for the startup and visit http://localhost:3000/ for the customer's frontend or http://localhost:3000/ for the supplier side. -Finally update the `./daps/docker-compose.yaml` to use this image instead. +## Notes on debugging ### Vault & Certs -When having problems with the certs or the vault, one need to delete the vault container. -Following script helps faster restarting +When having problems with the certs or the vault, one may need to delete the vault container. +The following script stops all infrastructure containers as well as the PURIS demonstrator containers: ```shell cd local -sh restart.sh +sh cleanup.sh ``` +Then start your containers again with the aforementioned commands. diff --git a/local/cleanup.sh b/local/cleanup.sh new file mode 100644 index 00000000..f5d62f7c --- /dev/null +++ b/local/cleanup.sh @@ -0,0 +1,3 @@ +docker compose down -v +docker compose -f docker-compose-infrastructure.yaml down -v +docker image rm local-vault diff --git a/local/daps/config/clients.yml b/local/daps/config/clients.yml deleted file mode 100755 index 4ff70080..00000000 --- a/local/daps/config/clients.yml +++ /dev/null @@ -1,41 +0,0 @@ ---- -# Customer -- client_id: customer - name: customer - import_certfile: keys/clients/customer.cert - token_endpoint_auth_method: private_key_jwt - grant_types: client_credentials - scope: idsc:IDS_CONNECTOR_ATTRIBUTES_ALL - attributes: - - key: idsc - value: IDS_CONNECTOR_ATTRIBUTES_ALL - - key: securityProfile - value: idsc:BASE_SECURITY_PROFILE - - key: referringConnector - value: http://customer-control-plane/ - - key: "@type" - value: ids:datPayload - - key: "@context" - value: https://w3id.org/idsa/contexts/context.jsonld - - key: transportCertsSha256 - value: ea3593699acad45973321dbe0011122fa965062ce68c0edcd7a8198d493be91d -# Supplier -- client_id: supplier - name: supplier - import_certfile: keys/clients/supplier.cert - token_endpoint_auth_method: private_key_jwt - grant_types: client_credentials - scope: idsc:IDS_CONNECTOR_ATTRIBUTES_ALL - attributes: - - key: idsc - value: IDS_CONNECTOR_ATTRIBUTES_ALL - - key: securityProfile - value: idsc:BASE_SECURITY_PROFILE - - key: referringConnector - value: http://supplier-control-plane/ #TODO - - key: "@type" - value: ids:datPayload - - key: "@context" - value: https://w3id.org/idsa/contexts/context.jsonld - - key: transportCertsSha256 - value: 89ab21422a70a198bd891d03e165297ce930a766b0c7eee0e24adb5e9bc92115 diff --git a/local/daps/config/omejdn.yml b/local/daps/config/omejdn.yml deleted file mode 100755 index 09b7775c..00000000 --- a/local/daps/config/omejdn.yml +++ /dev/null @@ -1,19 +0,0 @@ ---- -host: http://ids-daps:4567/ -path_prefix: '' -bind_to: 0.0.0.0 -allow_origin: "*" -issuer: http://ids-daps:4567/ -openid: false -accept_audience: idsc:IDS_CONNECTORS_ALL -default_audience: -- idsc:IDS_CONNECTORS_ALL -app_env: debug -environment: development -access_token: - expiration: 3600 - algorithm: RS256 -id_token: - expiration: 3600 - algorithm: RS256 -front_url: http://ids-daps:4567/ diff --git a/local/daps/config/plugins.yml b/local/daps/config/plugins.yml deleted file mode 100755 index 25672071..00000000 --- a/local/daps/config/plugins.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -plugins: - admin_api: - user_selfservice: - allow_deletion: false - allow_password_change: true - editable_attributes: [] - token_user_attributes: - skip_id_token: true diff --git a/local/daps/config/scope_description.yml b/local/daps/config/scope_description.yml deleted file mode 100755 index 012107ba..00000000 --- a/local/daps/config/scope_description.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -omejdn:read: Read access to the Omejdn server API -omejdn:write: Write access to the Omejdn server API -omejdn:admin: Access to the Omejdn server admin API -profile: 'Standard profile claims (e.g.: Name, picture, website, gender, birthdate, - location)' -email: Email-Address -address: Address -phone: Phone-number diff --git a/local/daps/config/scope_mapping.yml b/local/daps/config/scope_mapping.yml deleted file mode 100755 index 3a92285b..00000000 --- a/local/daps/config/scope_mapping.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -idsc:IDS_CONNECTOR_ATTRIBUTES_ALL: -- securityProfile -- referringConnector diff --git a/local/daps/config/webfinger.yml b/local/daps/config/webfinger.yml deleted file mode 100755 index 2fbf0ffd..00000000 --- a/local/daps/config/webfinger.yml +++ /dev/null @@ -1 +0,0 @@ ---- {} diff --git a/local/daps/docker-compose.yaml b/local/daps/docker-compose.yaml deleted file mode 100644 index ab43e3b2..00000000 --- a/local/daps/docker-compose.yaml +++ /dev/null @@ -1,39 +0,0 @@ -# -# Copyright (c) 2023 Volkswagen AG -# Copyright (c) 2023 Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. (represented by Fraunhofer ISST) -# Copyright (c) 2023 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 -# -version: "3" -services: - omejdn-daps: - image: ghcr.io/fraunhofer-aisec/omejdn-server:1.7.1 - container_name: omejdn-daps - ports: - - 4567:4567 -# networks: -# - ids-network - environment: - OMEJDN_JWT_AUD_OVERRIDE: idsc:IDS_CONNECTORS_ALL - OMEJDN_PLUGINS: config/plugins.yml - volumes: - - ./config:/opt/config - - ./keys:/opt/keys/omejdn - -#networks: -# ids-network: -# driver: bridge diff --git a/local/docker-compose-infrastructure.yaml b/local/docker-compose-infrastructure.yaml new file mode 100644 index 00000000..b61907de --- /dev/null +++ b/local/docker-compose-infrastructure.yaml @@ -0,0 +1,67 @@ +version: "3" + +services: + miw: + image: tractusx/managed-identity-wallet:0.2.0 + container_name: miw + env_file: + - ./miw/infrastructure.properties + ports: + - "127.0.0.1:8000:80" + - "127.0.0.1:8090:8090" + networks: + - miw-net + + postgres: + image: postgres:15.4-alpine + container_name: postgres + environment: + POSTGRES_DB: edc + POSTGRES_USER: ${PG_USER} + POSTGRES_PASSWORD: ${PG_PW} + volumes: + # use docker-compose down --volumes to kill db volume + # only then, changes to the script are executed! + - ./postgres/init-db.sql:/docker-entrypoint-initdb.d/init-db.sql + ports: + - "127.0.0.1:5432:5432" + networks: + - miw-net + + keycloak: + image: quay.io/keycloak/keycloak:21.1 + env_file: + - ./miw/infrastructure.properties + environment: + DB_SCHEMA: public + command: + - start-dev + - --import-realm + volumes: + - ./miw/keycloak-setup.json:/opt/keycloak/data/import/miw_test_realm.json + ports: + - "127.0.0.1:8080:8080" + depends_on: + - postgres + networks: + - miw-net + vault: + build: ./vault + container_name: vault + ports: + - "127.0.0.1:8200:8200" + environment: + # token id may not contain prefix + VAULT_DEV_ROOT_TOKEN_ID: ${VAULT_DEV_ROOT_TOKEN_ID} + VAULT_ADDR: http://vault:8200 + VAULT_PUT_SECRETS_DIR: ${VAULT_SECRETS_DIR} + # ATTENTION: Non productive environment + SKIP_SETCAP: true + SKIP_CHOWN: true + volumes: + - ./vault/secrets:${VAULT_SECRETS_DIR} + networks: + - miw-net +networks: + miw-net: + name: miw-net diff --git a/local/docker-compose.yaml b/local/docker-compose.yaml index 5e8801e8..1627d369 100644 --- a/local/docker-compose.yaml +++ b/local/docker-compose.yaml @@ -20,11 +20,12 @@ # version: "3" services: + puris-frontend-customer: image: puris-frontend:dev container_name: customer-frontend ports: - - 3000:8080 + - "127.0.0.1:3000:8080" environment: - APP_NAME=PURIS Customer - BACKEND_BASE_URL=http://localhost:8081/catena/ @@ -35,6 +36,10 @@ services: - ENDPOINT_CUSTOMER=stockView/customer?ownMaterialNumber= - ENDPOINT_PARTNER_PRODUCT_STOCKS=stockView/partner-product-stocks?ownMaterialNumber= - ENDPOINT_UPDATE_PARTNER_PRODUCT_STOCK=stockView/update-partner-product-stock?ownMaterialNumber= + networks: + - miw-net + extra_hosts: + - "host.docker.internal:host-gateway" # Adjusts container's host file to allow for communication with docker-host machine puris-backend-customer: image: puris-backend:dev @@ -42,120 +47,59 @@ services: depends_on: edc-customer-control-plane: condition: service_started #service_healthy + #restart: true ports: - - 8081:8081 # expose port of server.port - environment: - - server.port=8081 - - puris.demonstrator.role=customer - - edc.controlplane.host=customer-control-plane - - edc.controlplane.data.port=8181 - - edc.controlplane.data.path=/api/v1/data - - edc.controlplane.key=${EDC_API_PW} - - edc.dataplane.public.port=8285 # edc-customer-data-plane - - edc.applydataplaneworkaround=false - - edc.idsUrl=http://customer-control-plane:8184/api/v1/ids - - own.bpnl=BPNL4444444444XX - - own.bpns=BPNS4444444444XX - - edr.endpoint=http://customer-backend:8081/catena/edrendpoint - - request.serverendpoint=http://customer-backend:8081/catena/product-stock/request - - response.serverendpoint=http://customer-backend:8081/catena/product-stock/response + - "127.0.0.1:8081:8081" # expose port of server.port + env_file: + - ./tractus-x-edc/config/customer/puris-backend.properties + networks: + - miw-net + extra_hosts: + - "host.docker.internal:host-gateway" # Adjusts container's host file to allow for communication with docker-host machine edc-customer-control-plane: + restart: on-failure extends: file: ./tractus-x-edc/docker-compose.yaml service: control-plane container_name: customer-control-plane - depends_on: - omejdn-daps: - condition: service_started # has no health-check - vault: - condition: service_started # has no health-check, needs to run script to put secrets - postgres: - condition: service_started # has no health-check, needs to run db script to create edc tables - environment: - edc.vault.hashicorp.token: ${VAULT_DEV_ROOT_TOKEN_ID} - edc.api.auth.key: ${EDC_API_PW} - edc.datasource.asset.user: ${PG_USER} - edc.datasource.asset.password: ${PG_PW} - edc.datasource.contractdefinition.user: ${PG_USER} - edc.datasource.contractdefinition.password: ${PG_PW} - edc.datasource.contractnegotiation.user: ${PG_USER} - edc.datasource.contractnegotiation.password: ${PG_PW} - edc.datasource.policy.user: ${PG_USER} - edc.datasource.policy.password: ${PG_PW} - edc.datasource.transferprocess.user: ${PG_USER} - edc.datasource.transferprocess.password: ${PG_PW} + env_file: + - ./tractus-x-edc/config/customer/control-plane.properties ports: - - 8180:8180 - - 8181:8181 - - 8182:8182 - - 8183:8183 - - 8184:8184 - volumes: - - ./tractus-x-edc/config/customer/control-plane.properties:/app/configuration.properties + - "127.0.0.1:8180:8180" + - "127.0.0.1:8181:8181" + - "127.0.0.1:8182:8182" + - "127.0.0.1:8183:8183" + - "127.0.0.1:8184:8184" + networks: + - miw-net + extra_hosts: + - "host.docker.internal:host-gateway" # Adjusts container's host file to allow for communication with docker-host machine edc-customer-data-plane: + restart: on-failure extends: file: ./tractus-x-edc/docker-compose.yaml service: data-plane container_name: customer-data-plane - depends_on: - omejdn-daps: - condition: service_started # has no health-check - vault: - condition: service_started # has no health-check, needs to run script to put secrets - postgres: - condition: service_started # has no health-check, needs to run db script to create edc tables ports: - - 8280:8280 - - 8285:8285 + - "127.0.0.1:8280:8280" + - "127.0.0.1:8285:8285" + - "127.0.0.1:8299:8299" environment: edc.vault.hashicorp.token: ${VAULT_DEV_ROOT_TOKEN_ID} - volumes: - - ./tractus-x-edc/config/customer/data-plane.properties:/app/configuration.properties - - omejdn-daps: - extends: - file: ./daps/docker-compose.yaml - service: omejdn-daps - container_name: omejdn-daps - volumes: - - ./vault/secrets/customer.cert:/opt/keys/clients/customer.cert:ro - - ./vault/secrets/supplier.cert:/opt/keys/clients/supplier.cert:ro - - vault: - build: ./vault - container_name: vault - ports: - - 8200:8200 - environment: - # token id may not contain prefix - VAULT_DEV_ROOT_TOKEN_ID: ${VAULT_DEV_ROOT_TOKEN_ID} - VAULT_ADDR: http://vault:8200 - VAULT_PUT_SECRETS_DIR: ${VAULT_SECRETS_DIR} - # ATTENTION: Non productive environment - SKIP_SETCAP: "true" - SKIP_CHOWN: "true" - volumes: - - ./vault/secrets:${VAULT_SECRETS_DIR} - - postgres: - image: postgres:15.4-alpine - container_name: postgres - environment: - POSTGRES_DB: edc - POSTGRES_USER: ${PG_USER} - POSTGRES_PASSWORD: ${PG_PW} - volumes: - # use docker-compose down --volumes to kill db volume - # only then, changes to the script are executed! - - ./postgres/init-db.sql:/docker-entrypoint-initdb.d/init-db.sql + env_file: + - ./tractus-x-edc/config/customer/data-plane.properties + networks: + - miw-net + extra_hosts: + - "host.docker.internal:host-gateway" # Adjusts container's host file to allow for communication with docker-host machine puris-frontend-supplier: image: puris-frontend:dev container_name: supplier-frontend ports: - - 3001:8080 + - "127.0.0.1:3001:8080" environment: - APP_NAME=PURIS Customer - BACKEND_BASE_URL=http://localhost:8082/catena/ @@ -166,6 +110,10 @@ services: - ENDPOINT_CUSTOMER=stockView/customer?ownMaterialNumber= - ENDPOINT_PARTNER_PRODUCT_STOCKS=stockView/partner-product-stocks?ownMaterialNumber= - ENDPOINT_UPDATE_PARTNER_PRODUCT_STOCK=stockView/update-partner-product-stock?ownMaterialNumber= + networks: + - miw-net + extra_hosts: + - "host.docker.internal:host-gateway" # Adjusts container's host file to allow for communication with docker-host machine puris-backend-supplier: image: puris-backend:dev @@ -174,73 +122,54 @@ services: edc-supplier-control-plane: condition: service_started #service_healthy ports: - - 8082:8082 - environment: - - server.port=8082 - - puris.demonstrator.role=supplier - - edc.controlplane.host=supplier-control-plane - - edc.controlplane.data.port=9181 - - edc.controlplane.data.path=/api/v1/data - - edc.controlplane.key=${EDC_API_PW} - - edc.dataplane.public.port=9285 - - edc.applydataplaneworkaround=false - - edc.idsUrl=http://supplier-control-plane:9184/api/v1/ids - - own.bpnl=BPNL1234567890ZZ - - own.bpns=BPNS1234567890ZZ - - edr.endpoint=http://supplier-backend:8082/catena/edrendpoint - - request.serverendpoint=http://supplier-backend:8082/catena/product-stock/request - - response.serverendpoint=http://supplier-backend:8082/catena/product-stock/response - + - "127.0.0.1:8082:8082" + env_file: + - ./tractus-x-edc/config/supplier/puris-backend.properties + networks: + - miw-net + extra_hosts: + - "host.docker.internal:host-gateway" # Adjusts container's host file to allow for communication with docker-host machine + edc-supplier-control-plane: + restart: on-failure extends: file: ./tractus-x-edc/docker-compose.yaml service: control-plane container_name: supplier-control-plane - depends_on: - omejdn-daps: - condition: service_started # has no health-check - vault: - condition: service_started # has no health-check, needs to run script to put secrets - postgres: - condition: service_started # has no health-check, needs to run db script to create edc tables ports: - - 9180:9180 - - 9181:9181 - - 9182:9182 - - 9183:9183 - - 9184:9184 - environment: - edc.vault.hashicorp.token: ${VAULT_DEV_ROOT_TOKEN_ID} - edc.api.auth.key: ${EDC_API_PW} - edc.datasource.asset.user: ${PG_USER} - edc.datasource.asset.password: ${PG_PW} - edc.datasource.contractdefinition.user: ${PG_USER} - edc.datasource.contractdefinition.password: ${PG_PW} - edc.datasource.contractnegotiation.user: ${PG_USER} - edc.datasource.contractnegotiation.password: ${PG_PW} - edc.datasource.policy.user: ${PG_USER} - edc.datasource.policy.password: ${PG_PW} - edc.datasource.transferprocess.user: ${PG_USER} - edc.datasource.transferprocess.password: ${PG_PW} - volumes: - - ./tractus-x-edc/config/supplier/control-plane.properties:/app/configuration.properties + - "127.0.0.1:9180:9180" + - "127.0.0.1:9181:9181" + - "127.0.0.1:9182:9182" + - "127.0.0.1:9183:9183" + - "127.0.0.1:9184:9184" + - "127.0.0.1:1044:1044" + env_file: + - ./tractus-x-edc/config/supplier/control-plane.properties + networks: + - miw-net + extra_hosts: + - "host.docker.internal:host-gateway" # Adjusts container's host file to allow for communication with docker-host machine + edc-supplier-data-plane: + restart: on-failure extends: file: ./tractus-x-edc/docker-compose.yaml service: data-plane container_name: supplier-data-plane - depends_on: - omejdn-daps: - condition: service_started # has no health-check - vault: - condition: service_started # has no health-check, needs to run script to put secrets - postgres: - condition: service_started # has no health-check, needs to run db script to create edc tables ports: - - 9280:9280 - - 9285:9285 + - "127.0.0.1:9280:9280" + - "127.0.0.1:9285:9285" + - "127.0.0.1:9299:9299" environment: edc.vault.hashicorp.token: ${VAULT_DEV_ROOT_TOKEN_ID} - volumes: - - ./tractus-x-edc/config/supplier/data-plane.properties:/app/configuration.properties + env_file: + - ./tractus-x-edc/config/supplier/data-plane.properties + networks: + - miw-net + extra_hosts: + - "host.docker.internal:host-gateway" # Adjusts container's host file to allow for communication with docker-host machine + +networks: + miw-net: + external: true diff --git a/local/generate-keys.sh b/local/generate-keys.sh index a6178388..1d049378 100644 --- a/local/generate-keys.sh +++ b/local/generate-keys.sh @@ -1,21 +1,48 @@ #!/bin/bash # generate .key .cert (asymmetric encryption) and .keys (data encryption edc) for customer and supplier -# generate .key .cert (asymmetric encryption) and .keys (data encryption edc) for daps # create folders, if not existing mkdir -p ./vault/secrets -mkdir -p ./daps/keys -echo "Creating customer key, cert, keys and SHA..." +# generate .env +echo "Creating .env" +cat << EOF > .env +VAULT_DEV_ROOT_TOKEN_ID=`openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | head -c 32` +EDC_API_PW=`openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | head -c 32` +PG_USER=`openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | head -c 32` +PG_PW=`openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | head -c 32` +VAULT_SECRETS_DIR=/vault/secrets/ +KC_MIW_ENC=`openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | head -c 32` +CUSTOMER_OAUTH_SECRET_ALIAS=customer.miw.secret +CUSTOMER_OAUTH_CLIENT_ID=customer_private_client +CUSTOMER_PRIVATE_KEY_ALIAS=customer-key +CUSTOMER_PUBLIC_KEY_ALIAS=customer-cert +CUSTOMER_ENCRYPTION_KEYS_ALIAS=customer-encryption-keys +SUPPLIER_OAUTH_SECRET_ALIAS=supplier.miw.secret +SUPPLIER_OAUTH_CLIENT_ID=supplier_private_client +SUPPLIER_PRIVATE_KEY_ALIAS=supplier-key +SUPPLIER_PUBLIC_KEY_ALIAS=supplier-cert +SUPPLIER_ENCRYPTION_KEYS_ALIAS=supplier-encryption-keys +KEYCLOAK_MIW_PUBLIC_CLIENT=miw_public +KEYCLOAK_ADMIN=admin +KEYCLOAK_ADMIN_PASSWORD=`openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | head -c 32` +KEYCLOAK_CLIENT_ID=miw_private_client +EOF + +echo "Creating customer key, cert, keys and SHA... " CUSTOMER_CERT="./vault/secrets/customer.cert" CUSTOMER_KEY="./vault/secrets/customer.key" CUSTOMER_ENCRYPTION_KEYS="./vault/secrets/customer-encryption.keys" +CUSTOMER_MIW_CLIENT_SECRET="./vault/secrets/customer.miw.secret" openssl req -newkey rsa:2048 -new -batch -nodes -x509 -days 3650 -text -keyout $CUSTOMER_KEY -out $CUSTOMER_CERT # EDC token encryption keys for edc-extensions/data-encryption -key1=`openssl rand -base64 16` -key2=`openssl rand -base64 24` -key3=`openssl rand -base64 32` -echo "${key1},${key2},${key3}" > $CUSTOMER_ENCRYPTION_KEYS +key=`openssl rand -base64 32` +printf "${key}" > $CUSTOMER_ENCRYPTION_KEYS + +# Generate new random password for customer in miw +miw_secret=`openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | head -c 32` +printf "${miw_secret}" > $CUSTOMER_MIW_CLIENT_SECRET +jq ".clients[5].secret = \"$miw_secret\"" ./miw/keycloak-setup.json > ./miw/keycloak-setup-temp.json CUSTOMER_CERT_SHA="$(openssl x509 -in "$CUSTOMER_CERT" -noout -sha256 -fingerprint | tr '[:upper:]' '[:lower:]' | tr -d : | sed 's/.*=//')" @@ -23,25 +50,21 @@ echo "Creating supplier key, cert, keys and SHA..." SUPPLIER_CERT="./vault/secrets/supplier.cert" SUPPLIER_KEY="./vault/secrets/supplier.key" SUPPLIER_ENCRYPTION_KEYS="./vault/secrets/supplier-encryption.keys" +SUPPLIER_MIW_CLIENT_SECRET="./vault/secrets/supplier.miw.secret" openssl req -newkey rsa:2048 -new -batch -nodes -x509 -days 3650 -text -keyout $SUPPLIER_KEY -out $SUPPLIER_CERT # EDC token encryption keys for edc-extensions/data-encryption -key1=`openssl rand -base64 16` -key2=`openssl rand -base64 24` -key3=`openssl rand -base64 32` -echo "${key1},${key2},${key3}" > $SUPPLIER_ENCRYPTION_KEYS +key=`openssl rand -base64 32` +printf "${key}" > $SUPPLIER_ENCRYPTION_KEYS +# Generate new random password for supplier in miw +miw_secret=`openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | head -c 32` +printf "${miw_secret}" > $SUPPLIER_MIW_CLIENT_SECRET +jq ".clients[6].secret = \"$miw_secret\"" ./miw/keycloak-setup-temp.json > ./miw/keycloak-setup.json -SUPPLIER_CERT_SHA="$(openssl x509 -in "$SUPPLIER_CERT" -noout -sha256 -fingerprint | tr '[:upper:]' '[:lower:]' | tr -d : | sed 's/.*=//')" +# remove temp file +rm ./miw/keycloak-setup-temp.json -echo "Make sure to update the ./daps/config/clients.yml:" -echo "Customer.transportCertsSha256: $CUSTOMER_CERT_SHA" -echo "Supplier.transportCertsSha256: $SUPPLIER_CERT_SHA" - -# DAPS -echo "Creating daps key and cert..." -DAPS_CERT="./daps/keys/omejdn.cert" -DAPS_KEY="./daps/keys/omejdn.key" -openssl req -newkey rsa:2048 -new -batch -nodes -x509 -days 3650 -text -keyout $DAPS_KEY -out $DAPS_CERT +SUPPLIER_CERT_SHA="$(openssl x509 -in "$SUPPLIER_CERT" -noout -sha256 -fingerprint | tr '[:upper:]' '[:lower:]' | tr -d : | sed 's/.*=//')" # let everyone access the files so that the non-root user in vault container can put them chmod -R 755 ./vault/secrets diff --git a/local/init-wallets.sh b/local/init-wallets.sh new file mode 100644 index 00000000..73596b1f --- /dev/null +++ b/local/init-wallets.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +response=$(curl -X POST -d 'client_id=miw_private_client&grant_type=client_credentials&client_secret=miw_private_client&scope=openid' http://localhost:8080/realms/miw_test/protocol/openid-connect/token) + +token=$(echo "$response" | jq -r '.access_token') + +echo $result + +curl -X POST -H "Authorization: Bearer $token" -H "Content-Type: application/json" -d '{ "name": "customer wallet", "bpn": "BPNL4444444444XX" }' http://localhost:8000/api/wallets + +curl -X POST -H "Authorization: Bearer $token" -H "Content-Type: application/json" -d '{ "name": "supplier wallet", "bpn": "BPNL1234567890ZZ" }' http://localhost:8000/api/wallets diff --git a/local/miw/infrastructure.properties b/local/miw/infrastructure.properties new file mode 100644 index 00000000..cef54260 --- /dev/null +++ b/local/miw/infrastructure.properties @@ -0,0 +1,30 @@ +KEYCLOAK_MIW_PUBLIC_CLIENT=${KEYCLOAK_MIW_PUBLIC_CLIENT} +DB_DATABASE=keycloak +KEYCLOAK_ADMIN=${KEYCLOAK_ADMIN} +KEYCLOAK_ADMIN_PASSWORD=${KEYCLOAK_ADMIN_PASSWORD} +KC_HOSTNAME=keycloak + +ENFORCE_HTTPS_IN_DID_RESOLUTION=false + +KEYCLOAK_CLIENT_ID=${KEYCLOAK_CLIENT_ID} +ENCRYPTION_KEY=${KC_MIW_ENC} +AUTHORITY_WALLET_BPN=BPNL000000000000 +AUTHORITY_WALLET_DID=did:web:miw:BPNL000000000000 +AUTHORITY_WALLET_NAME=Catena-X +KEYCLOAK_REALM=miw_test +VC_SCHEMA_LINK="https://www.w3.org/2018/credentials/v1, https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json" +VC_EXPIRY_DATE=01-01-2025 +SUPPORTED_FRAMEWORK_VC_TYPES="PcfCredential, SustainabilityCredential, QualityCredential, TraceabilityCredential, BehaviorTwinCredential, ResiliencyCredential" +MIW_HOST_NAME=miw + +AUTH_SERVER_URL=http://keycloak:8080 +APPLICATION_PORT=80 +MANAGEMENT_PORT=8090 +APPLICATION_ENVIRONMENT=dev +DB_HOST=postgres +DB_PORT=5432 +USE_SSL=false +DB_USER=${PG_USER} +DB_NAME=miw +DB_USER_NAME=${PG_USER} +DB_PASSWORD=${PG_PW} diff --git a/local/miw/keycloak-setup.json b/local/miw/keycloak-setup.json new file mode 100644 index 00000000..5ab364f8 --- /dev/null +++ b/local/miw/keycloak-setup.json @@ -0,0 +1,2815 @@ +{ + "id": "e980fcc5-9e29-485c-bd56-440783e32014", + "realm": "miw_test", + "notBefore": 0, + "defaultSignatureAlgorithm": "RS256", + "revokeRefreshToken": false, + "refreshTokenMaxReuse": 0, + "accessTokenLifespan": 28800, + "accessTokenLifespanForImplicitFlow": 900, + "ssoSessionIdleTimeout": 1800, + "ssoSessionMaxLifespan": 36000, + "ssoSessionIdleTimeoutRememberMe": 0, + "ssoSessionMaxLifespanRememberMe": 0, + "offlineSessionIdleTimeout": 2592000, + "offlineSessionMaxLifespanEnabled": false, + "offlineSessionMaxLifespan": 5184000, + "clientSessionIdleTimeout": 0, + "clientSessionMaxLifespan": 0, + "clientOfflineSessionIdleTimeout": 0, + "clientOfflineSessionMaxLifespan": 0, + "accessCodeLifespan": 60, + "accessCodeLifespanUserAction": 300, + "accessCodeLifespanLogin": 1800, + "actionTokenGeneratedByAdminLifespan": 43200, + "actionTokenGeneratedByUserLifespan": 28800, + "oauth2DeviceCodeLifespan": 600, + "oauth2DevicePollingInterval": 5, + "enabled": true, + "sslRequired": "external", + "registrationAllowed": false, + "registrationEmailAsUsername": false, + "rememberMe": false, + "verifyEmail": false, + "loginWithEmailAllowed": true, + "duplicateEmailsAllowed": false, + "resetPasswordAllowed": false, + "editUsernameAllowed": false, + "bruteForceProtected": false, + "permanentLockout": false, + "maxFailureWaitSeconds": 900, + "minimumQuickLoginWaitSeconds": 60, + "waitIncrementSeconds": 60, + "quickLoginCheckMilliSeconds": 1000, + "maxDeltaTimeSeconds": 43200, + "failureFactor": 30, + "roles": { + "realm": [ + { + "id": "ad36b1ad-a3cb-4594-853b-b5744b86fcdb", + "name": "uma_authorization", + "description": "${role_uma_authorization}", + "composite": false, + "clientRole": false, + "containerId": "e980fcc5-9e29-485c-bd56-440783e32014", + "attributes": {} + }, + { + "id": "3247ecc3-6884-4548-bfaa-0f47cce0cda6", + "name": "default-roles-miw_test", + "description": "${role_default-roles}", + "composite": true, + "composites": { + "realm": [ + "offline_access", + "uma_authorization" + ], + "client": { + "realm-management": [ + "manage-users" + ], + "account": [ + "view-profile", + "manage-account" + ] + } + }, + "clientRole": false, + "containerId": "e980fcc5-9e29-485c-bd56-440783e32014", + "attributes": {} + }, + { + "id": "ce1ee2c7-517c-4cf0-a96f-3adac1d200a7", + "name": "offline_access", + "description": "${role_offline-access}", + "composite": false, + "clientRole": false, + "containerId": "e980fcc5-9e29-485c-bd56-440783e32014", + "attributes": {} + } + ], + "client": { + "realm-management": [ + { + "id": "e9eb031a-9dc3-413f-be30-8a396cf9a783", + "name": "manage-authorization", + "description": "${role_manage-authorization}", + "composite": false, + "clientRole": true, + "containerId": "f2604867-9227-4947-8d36-6abc754f9883", + "attributes": {} + }, + { + "id": "b33997ba-a7cb-4f47-8272-d04c18e51416", + "name": "manage-identity-providers", + "description": "${role_manage-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "f2604867-9227-4947-8d36-6abc754f9883", + "attributes": {} + }, + { + "id": "c66b4177-f470-4164-851c-018fa4445d78", + "name": "query-users", + "description": "${role_query-users}", + "composite": false, + "clientRole": true, + "containerId": "f2604867-9227-4947-8d36-6abc754f9883", + "attributes": {} + }, + { + "id": "ac2965ec-c2f2-4e30-b8fd-e3a34afc0070", + "name": "create-client", + "description": "${role_create-client}", + "composite": false, + "clientRole": true, + "containerId": "f2604867-9227-4947-8d36-6abc754f9883", + "attributes": {} + }, + { + "id": "fc813275-05d3-408f-a0d5-6943a66ada3f", + "name": "manage-events", + "description": "${role_manage-events}", + "composite": false, + "clientRole": true, + "containerId": "f2604867-9227-4947-8d36-6abc754f9883", + "attributes": {} + }, + { + "id": "73d25c6c-ca63-414e-a908-22d2f2cb18f6", + "name": "view-realm", + "description": "${role_view-realm}", + "composite": false, + "clientRole": true, + "containerId": "f2604867-9227-4947-8d36-6abc754f9883", + "attributes": {} + }, + { + "id": "2073b2f4-c5de-491f-a34d-ea0c687cae4e", + "name": "manage-users", + "description": "${role_manage-users}", + "composite": false, + "clientRole": true, + "containerId": "f2604867-9227-4947-8d36-6abc754f9883", + "attributes": {} + }, + { + "id": "3f5e2b33-5611-4289-a36d-236b81485938", + "name": "view-identity-providers", + "description": "${role_view-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "f2604867-9227-4947-8d36-6abc754f9883", + "attributes": {} + }, + { + "id": "9e9436f9-6f9a-4a86-adaa-da935522e551", + "name": "impersonation", + "description": "${role_impersonation}", + "composite": false, + "clientRole": true, + "containerId": "f2604867-9227-4947-8d36-6abc754f9883", + "attributes": {} + }, + { + "id": "272c47ae-68d9-459a-8d8c-39b95136681b", + "name": "query-realms", + "description": "${role_query-realms}", + "composite": false, + "clientRole": true, + "containerId": "f2604867-9227-4947-8d36-6abc754f9883", + "attributes": {} + }, + { + "id": "8d3984f8-408c-4c9f-8af5-dcdbbf76118c", + "name": "view-users", + "description": "${role_view-users}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "query-users", + "query-groups" + ] + } + }, + "clientRole": true, + "containerId": "f2604867-9227-4947-8d36-6abc754f9883", + "attributes": {} + }, + { + "id": "9beee882-a768-42ed-b142-74e238928634", + "name": "realm-admin", + "description": "${role_realm-admin}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "manage-identity-providers", + "manage-authorization", + "query-users", + "create-client", + "manage-events", + "view-realm", + "manage-users", + "view-identity-providers", + "impersonation", + "query-realms", + "view-users", + "view-clients", + "view-authorization", + "query-groups", + "query-clients", + "view-events", + "manage-clients", + "manage-realm" + ] + } + }, + "clientRole": true, + "containerId": "f2604867-9227-4947-8d36-6abc754f9883", + "attributes": {} + }, + { + "id": "df03dd95-6720-4ec8-a21e-25f124b9be51", + "name": "view-authorization", + "description": "${role_view-authorization}", + "composite": false, + "clientRole": true, + "containerId": "f2604867-9227-4947-8d36-6abc754f9883", + "attributes": {} + }, + { + "id": "9f0a02be-2609-496c-82cc-c07b82d2b4cc", + "name": "view-clients", + "description": "${role_view-clients}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "query-clients" + ] + } + }, + "clientRole": true, + "containerId": "f2604867-9227-4947-8d36-6abc754f9883", + "attributes": {} + }, + { + "id": "f2d938d7-835f-414b-af54-289c97fed144", + "name": "query-groups", + "description": "${role_query-groups}", + "composite": false, + "clientRole": true, + "containerId": "f2604867-9227-4947-8d36-6abc754f9883", + "attributes": {} + }, + { + "id": "6dea15cf-8398-442a-9df6-639c45cce53b", + "name": "query-clients", + "description": "${role_query-clients}", + "composite": false, + "clientRole": true, + "containerId": "f2604867-9227-4947-8d36-6abc754f9883", + "attributes": {} + }, + { + "id": "8f0da98f-988a-46cf-be03-44e12f1c3ad6", + "name": "view-events", + "description": "${role_view-events}", + "composite": false, + "clientRole": true, + "containerId": "f2604867-9227-4947-8d36-6abc754f9883", + "attributes": {} + }, + { + "id": "3f2173cd-352d-4928-9525-1fdbaf289309", + "name": "manage-clients", + "description": "${role_manage-clients}", + "composite": false, + "clientRole": true, + "containerId": "f2604867-9227-4947-8d36-6abc754f9883", + "attributes": {} + }, + { + "id": "d0c8168f-9ac4-4ac8-a908-715fda68959c", + "name": "manage-realm", + "description": "${role_manage-realm}", + "composite": false, + "clientRole": true, + "containerId": "f2604867-9227-4947-8d36-6abc754f9883", + "attributes": {} + } + ], + "security-admin-console": [], + "miw_private_client": [ + { + "id": "232e256b-81b3-4282-8198-2a4557a2687a", + "name": "view_wallets", + "description": "view_wallets", + "composite": false, + "clientRole": true, + "containerId": "774d507f-5aa3-4d16-be24-0e461f35d66a", + "attributes": {} + }, + { + "id": "2a1f1417-4eed-4ff9-b569-7461f7ae0ead", + "name": "add_wallets", + "description": "add_wallets", + "composite": false, + "clientRole": true, + "containerId": "774d507f-5aa3-4d16-be24-0e461f35d66a", + "attributes": {} + }, + { + "id": "737ec30a-c542-419a-8533-8caa7a267b68", + "name": "update_wallet", + "description": "update_wallet", + "composite": false, + "clientRole": true, + "containerId": "774d507f-5aa3-4d16-be24-0e461f35d66a", + "attributes": {} + }, + { + "id": "b32143a1-23cc-4ea5-96b0-aec079958ca0", + "name": "view_wallet", + "description": "view_wallet", + "composite": false, + "clientRole": true, + "containerId": "774d507f-5aa3-4d16-be24-0e461f35d66a", + "attributes": {} + }, + { + "id": "8ac5652e-103e-49a2-a7d0-4a9cdc958543", + "name": "update_wallets", + "description": "update_wallets", + "composite": false, + "clientRole": true, + "containerId": "774d507f-5aa3-4d16-be24-0e461f35d66a", + "attributes": {} + } + ], + "admin-cli": [], + "account-console": [], + "broker": [ + { + "id": "bd277caa-1e1f-474a-9fb9-a0f6ec21bfa5", + "name": "read-token", + "description": "${role_read-token}", + "composite": false, + "clientRole": true, + "containerId": "f6dd02a1-9c2b-4af9-81bf-200efc0fcf22", + "attributes": {} + } + ], + "miw_public": [], + "account": [ + { + "id": "cbe6b27b-83b2-4c40-ba6b-e776b32d919c", + "name": "manage-account-links", + "description": "${role_manage-account-links}", + "composite": false, + "clientRole": true, + "containerId": "356d12b7-0894-474f-8701-c51c78182351", + "attributes": {} + }, + { + "id": "2e9938b0-51ea-47f6-91d5-93020fbbe094", + "name": "view-profile", + "description": "${role_view-profile}", + "composite": false, + "clientRole": true, + "containerId": "356d12b7-0894-474f-8701-c51c78182351", + "attributes": {} + }, + { + "id": "000f2103-4f84-4ab2-b2e9-72e006a7aa7a", + "name": "delete-account", + "description": "${role_delete-account}", + "composite": false, + "clientRole": true, + "containerId": "356d12b7-0894-474f-8701-c51c78182351", + "attributes": {} + }, + { + "id": "d0d1ec92-4928-4446-ab70-af4a5ec941f0", + "name": "manage-consent", + "description": "${role_manage-consent}", + "composite": true, + "composites": { + "client": { + "account": [ + "view-consent" + ] + } + }, + "clientRole": true, + "containerId": "356d12b7-0894-474f-8701-c51c78182351", + "attributes": {} + }, + { + "id": "be516b3c-47c9-4da9-b65a-c0269c066cd2", + "name": "view-consent", + "description": "${role_view-consent}", + "composite": false, + "clientRole": true, + "containerId": "356d12b7-0894-474f-8701-c51c78182351", + "attributes": {} + }, + { + "id": "f628b4e8-783f-4b2b-ad20-9ce7191ef39b", + "name": "manage-account", + "description": "${role_manage-account}", + "composite": true, + "composites": { + "client": { + "account": [ + "manage-account-links" + ] + } + }, + "clientRole": true, + "containerId": "356d12b7-0894-474f-8701-c51c78182351", + "attributes": {} + }, + { + "id": "465eff9a-73da-4fd3-ac96-e84db10cc263", + "name": "view-applications", + "description": "${role_view-applications}", + "composite": false, + "clientRole": true, + "containerId": "356d12b7-0894-474f-8701-c51c78182351", + "attributes": {} + }, + { + "id": "631c870f-24e9-4058-b506-993520d68d24", + "name": "view-groups", + "description": "${role_view-groups}", + "composite": false, + "clientRole": true, + "containerId": "356d12b7-0894-474f-8701-c51c78182351", + "attributes": {} + } + ] + } + }, + "groups": [], + "defaultRole": { + "id": "3247ecc3-6884-4548-bfaa-0f47cce0cda6", + "name": "default-roles-miw_test", + "description": "${role_default-roles}", + "composite": true, + "clientRole": false, + "containerId": "e980fcc5-9e29-485c-bd56-440783e32014" + }, + "requiredCredentials": [ + "password" + ], + "otpPolicyType": "totp", + "otpPolicyAlgorithm": "HmacSHA1", + "otpPolicyInitialCounter": 0, + "otpPolicyDigits": 6, + "otpPolicyLookAheadWindow": 1, + "otpPolicyPeriod": 30, + "otpPolicyCodeReusable": false, + "otpSupportedApplications": [ + "totpAppMicrosoftAuthenticatorName", + "totpAppGoogleName", + "totpAppFreeOTPName" + ], + "webAuthnPolicyRpEntityName": "keycloak", + "webAuthnPolicySignatureAlgorithms": [ + "ES256" + ], + "webAuthnPolicyRpId": "", + "webAuthnPolicyAttestationConveyancePreference": "not specified", + "webAuthnPolicyAuthenticatorAttachment": "not specified", + "webAuthnPolicyRequireResidentKey": "not specified", + "webAuthnPolicyUserVerificationRequirement": "not specified", + "webAuthnPolicyCreateTimeout": 0, + "webAuthnPolicyAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyAcceptableAaguids": [], + "webAuthnPolicyPasswordlessRpEntityName": "keycloak", + "webAuthnPolicyPasswordlessSignatureAlgorithms": [ + "ES256" + ], + "webAuthnPolicyPasswordlessRpId": "", + "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified", + "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified", + "webAuthnPolicyPasswordlessRequireResidentKey": "not specified", + "webAuthnPolicyPasswordlessUserVerificationRequirement": "not specified", + "webAuthnPolicyPasswordlessCreateTimeout": 0, + "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyPasswordlessAcceptableAaguids": [], + "users": [ + { + "id": "7e5c957b-2f20-41e0-85fb-e84656caadfe", + "createdTimestamp": 1687957169104, + "username": "service-account-miw_private_client", + "enabled": true, + "totp": false, + "emailVerified": false, + "serviceAccountClientId": "miw_private_client", + "disableableCredentialTypes": [], + "requiredActions": [], + "realmRoles": [ + "default-roles-miw_test" + ], + "clientRoles": { + "miw_private_client": [ + "view_wallets", + "update_wallet", + "add_wallets", + "view_wallet", + "update_wallets" + ] + }, + "notBefore": 0, + "groups": [] + }, + { + "id": "44f821c3-823a-4271-9f7a-2fe026f9a41a", + "createdTimestamp": 1692873511927, + "username": "service-account-customer_private_client", + "enabled": true, + "totp": false, + "emailVerified": false, + "serviceAccountClientId": "customer_private_client", + "disableableCredentialTypes": [], + "requiredActions": [], + "realmRoles": [ + "default-roles-miw_test" + ], + "clientRoles": { + "miw_private_client": [ + "view_wallets", + "update_wallet", + "add_wallets", + "view_wallet", + "update_wallets" + ] + }, + "notBefore": 0, + "groups": [] + }, + { + "id": "c6d700e5-a61a-46a7-a097-ea130feb497c", + "createdTimestamp": 1687957169104, + "username": "service-account-supplier_private_client", + "enabled": true, + "totp": false, + "emailVerified": false, + "serviceAccountClientId": "supplier_private_client", + "disableableCredentialTypes": [], + "requiredActions": [], + "realmRoles": [ + "default-roles-miw_test" + ], + "clientRoles": { + "miw_private_client": [ + "view_wallets", + "update_wallet", + "add_wallets", + "view_wallet", + "update_wallets" + ] + }, + "notBefore": 0, + "groups": [] + } + ], + "scopeMappings": [ + { + "clientScope": "offline_access", + "roles": [ + "offline_access" + ] + } + ], + "clientScopeMappings": { + "account": [ + { + "client": "account-console", + "roles": [ + "manage-account", + "view-groups" + ] + } + ] + }, + "clients": [ + { + "id": "356d12b7-0894-474f-8701-c51c78182351", + "clientId": "account", + "name": "${client_account}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/miw_test/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "/realms/miw_test/account/*" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "e33fa081-88ee-4443-955a-22b57d96bd9a", + "clientId": "account-console", + "name": "${client_account-console}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/miw_test/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "/realms/miw_test/account/*" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+", + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "db8af579-9b62-4a5d-8f21-9113cacce594", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + } + ], + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "e6ecff04-23e9-4828-ae48-2eaf9cf21086", + "clientId": "admin-cli", + "name": "${client_admin-cli}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": false, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "f6dd02a1-9c2b-4af9-81bf-200efc0fcf22", + "clientId": "broker", + "name": "${client_broker}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "774d507f-5aa3-4d16-be24-0e461f35d66a", + "clientId": "miw_private_client", + "name": "miw_private_client", + "description": "miw_private_client", + "rootUrl": "", + "adminUrl": "", + "baseUrl": "", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "miw_private_client", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": true, + "publicClient": false, + "frontchannelLogout": true, + "protocol": "openid-connect", + "attributes": { + "oidc.ciba.grant.enabled": "false", + "client.secret.creation.time": "1684923648", + "backchannel.logout.session.required": "true", + "post.logout.redirect.uris": "+", + "display.on.consent.screen": "false", + "oauth2.device.authorization.grant.enabled": "false", + "backchannel.logout.revoke.offline.tokens": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "protocolMappers": [ + { + "id": "767fc59d-4812-4147-a4c0-c1d36854a111", + "name": "User Client Role", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-client-role-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "usermodel.clientRoleMapping.clientId": "miw_private_client", + "multivalued": "true", + "userinfo.token.claim": "true" + } + }, + { + "id": "c46e9cc6-3057-4640-a78b-e12fc3a714df", + "name": "BPN", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "aggregate.attrs": "false", + "userinfo.token.claim": "true", + "multivalued": "false", + "user.attribute": "BPN", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "BPN" + } + }, + { + "id": "f446598c-1637-4585-b2b6-0204d2e6e92e", + "name": "client_bpn_mapper", + "protocol": "openid-connect", + "protocolMapper": "oidc-hardcoded-claim-mapper", + "consentRequired": false, + "config": { + "claim.value": "BPNL000000000000", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "BPN", + "access.tokenResponse.claim": "false" + } + }, + { + "id": "1340463e-a737-4507-8ecb-b01715a9fde4", + "name": "Client IP Address", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientAddress", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientAddress", + "jsonType.label": "String" + } + }, + { + "id": "9096587b-3781-4104-b1ec-458c7ca95e8d", + "name": "Client ID", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientId", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientId", + "jsonType.label": "String" + } + }, + { + "id": "370515a5-370a-4b68-9704-9a67407c1390", + "name": "Client Host", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientHost", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientHost", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "0375eb3d-9526-4b9d-a651-7dddda3d1b41", + "clientId": "customer_private_client", + "name": "customer_private_client", + "description": "customer_private_client", + "rootUrl": "", + "adminUrl": "", + "baseUrl": "", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": true, + "publicClient": false, + "frontchannelLogout": true, + "protocol": "openid-connect", + "attributes": { + "oidc.ciba.grant.enabled": "false", + "client.secret.creation.time": "1692873511", + "backchannel.logout.session.required": "true", + "post.logout.redirect.uris": "+", + "display.on.consent.screen": "false", + "oauth2.device.authorization.grant.enabled": "false", + "backchannel.logout.revoke.offline.tokens": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "protocolMappers": [ + { + "id": "767fc59d-4812-4147-a4c0-c1d36854a222", + "name": "User Client Role", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-client-role-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "usermodel.clientRoleMapping.clientId": "customer_private_client", + "multivalued": "true", + "userinfo.token.claim": "true" + } + }, + { + "id": "c46e9cc6-3057-4640-a78b-e12fc3a71333", + "name": "BPN", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "aggregate.attrs": "false", + "userinfo.token.claim": "true", + "multivalued": "false", + "user.attribute": "BPN", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "BPN" + } + }, + { + "id": "f446598c-1637-4585-b2b6-0204d2e6e444", + "name": "client_bpn_mapper", + "protocol": "openid-connect", + "protocolMapper": "oidc-hardcoded-claim-mapper", + "consentRequired": false, + "config": { + "claim.value": "BPNL4444444444XX", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "BPN", + "access.tokenResponse.claim": "false" + } + }, + { + "id": "e807edbf-49c7-4104-bdb1-5369a4a88092", + "name": "Client IP Address", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientAddress", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientAddress", + "jsonType.label": "String" + } + }, + { + "id": "8f3c30da-f509-446a-a54d-b58c1eaa2cfa", + "name": "Client ID", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientId", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientId", + "jsonType.label": "String" + } + }, + { + "id": "f377ce45-0016-43f9-86f4-b81cb5bc7fd9", + "name": "Client Host", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientHost", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientHost", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "17d99f9a-22c7-4381-9a08-f843c36b64ac", + "clientId": "supplier_private_client", + "name": "supplier_private_client", + "description": "supplier_private_client", + "rootUrl": "", + "adminUrl": "", + "baseUrl": "", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": true, + "publicClient": false, + "frontchannelLogout": true, + "protocol": "openid-connect", + "attributes": { + "oidc.ciba.grant.enabled": "false", + "client.secret.creation.time": "1684923648", + "backchannel.logout.session.required": "true", + "post.logout.redirect.uris": "+", + "display.on.consent.screen": "false", + "oauth2.device.authorization.grant.enabled": "false", + "backchannel.logout.revoke.offline.tokens": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "protocolMappers": [ + { + "id": "97d1d2b2-f6cc-44b8-b21a-da97a85dc802", + "name": "User Client Role", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-client-role-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "usermodel.clientRoleMapping.clientId": "supplier_private_client", + "multivalued": "true", + "userinfo.token.claim": "true" + } + }, + { + "id": "88a5eb86-9660-4ae5-a333-31939392e74c", + "name": "BPN", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "aggregate.attrs": "false", + "userinfo.token.claim": "true", + "multivalued": "false", + "user.attribute": "BPN", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "BPN" + } + }, + { + "id": "fc48fb2e-e9a2-453a-b74a-c12dd1621a23", + "name": "client_bpn_mapper", + "protocol": "openid-connect", + "protocolMapper": "oidc-hardcoded-claim-mapper", + "consentRequired": false, + "config": { + "claim.value": "BPNL1234567890ZZ", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "BPN", + "access.tokenResponse.claim": "false" + } + }, + { + "id": "dc84e461-56b1-4bc1-9252-4a16f6cba69c", + "name": "Client IP Address", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientAddress", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientAddress", + "jsonType.label": "String" + } + }, + { + "id": "f184e1cf-f226-40c9-aee4-7699926fa41c", + "name": "Client ID", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientId", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientId", + "jsonType.label": "String" + } + }, + { + "id": "2c49f4ef-d427-49ac-8664-5c040271f381", + "name": "Client Host", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientHost", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientHost", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "7dbe3954-6da4-43f1-a1df-cf160fee58e2", + "clientId": "miw_public", + "name": "", + "description": "", + "rootUrl": "", + "adminUrl": "", + "baseUrl": "", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "http://localhost:8080/*", + "http://localhost/*", + "http://localhost:8087/*" + ], + "webOrigins": [ + "http://localhost:8080", + "http://localhost", + "http://localhost:8087" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": true, + "protocol": "openid-connect", + "attributes": { + "oidc.ciba.grant.enabled": "false", + "backchannel.logout.session.required": "true", + "post.logout.redirect.uris": "+", + "display.on.consent.screen": "false", + "oauth2.device.authorization.grant.enabled": "false", + "backchannel.logout.revoke.offline.tokens": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "protocolMappers": [ + { + "id": "1312c58c-7950-4e3f-b45d-a77b827a62d7", + "name": "BPN_user_attribute", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "aggregate.attrs": "false", + "userinfo.token.claim": "true", + "multivalued": "false", + "user.attribute": "BPN", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "BPN" + } + } + ], + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "f2604867-9227-4947-8d36-6abc754f9883", + "clientId": "realm-management", + "name": "${client_realm-management}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "d966ce87-fa07-4c99-9ed1-899961993d88", + "clientId": "security-admin-console", + "name": "${client_security-admin-console}", + "rootUrl": "${authAdminUrl}", + "baseUrl": "/admin/miw_test/console/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "/admin/miw_test/console/*" + ], + "webOrigins": [ + "+" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+", + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "088895dc-a6b7-4d7a-b8e8-70804dd7a4be", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + } + ], + "clientScopes": [ + { + "id": "e7addfcc-9187-43b2-9dd8-d883c3d7d4ce", + "name": "email", + "description": "OpenID Connect built-in scope: email", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${emailScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "7f56bfa8-3c9c-4ddb-ba03-bf3baee76b5e", + "name": "email verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "emailVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email_verified", + "jsonType.label": "boolean" + } + }, + { + "id": "7ae07240-7a54-4e77-a3ed-1cff45e70a6f", + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "6447876f-32c7-42b7-864c-61b8c12f651f", + "name": "offline_access", + "description": "OpenID Connect built-in scope: offline_access", + "protocol": "openid-connect", + "attributes": { + "consent.screen.text": "${offlineAccessScopeConsentText}", + "display.on.consent.screen": "true" + } + }, + { + "id": "7b162106-cbc9-4c05-9043-6fbece4d7600", + "name": "role_list", + "description": "SAML role list", + "protocol": "saml", + "attributes": { + "consent.screen.text": "${samlRoleListScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "445b2b60-0bf1-4eb8-ab60-99351b616da6", + "name": "role list", + "protocol": "saml", + "protocolMapper": "saml-role-list-mapper", + "consentRequired": false, + "config": { + "single": "false", + "attribute.nameformat": "Basic", + "attribute.name": "Role" + } + } + ] + }, + { + "id": "ad308290-1c37-4d33-99f3-8d23e2f74501", + "name": "microprofile-jwt", + "description": "Microprofile - JWT built-in scope", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "7fbc621e-a6ad-48d4-b981-55be57bae980", + "name": "groups", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "multivalued": "true", + "userinfo.token.claim": "true", + "user.attribute": "foo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "groups", + "jsonType.label": "String" + } + }, + { + "id": "3eacc647-eff9-48a4-a9ca-cdd8b1a02665", + "name": "upn", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "upn", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "f6d808aa-019d-4f3f-951e-dda5a77f841c", + "name": "acr", + "description": "OpenID Connect scope for add acr (authentication context class reference) to the token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "d3204d28-9023-4cf6-b996-fd845180c8dd", + "name": "acr loa level", + "protocol": "openid-connect", + "protocolMapper": "oidc-acr-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + } + ] + }, + { + "id": "fcfb1f12-dc72-4529-be32-51b16d4b7c58", + "name": "profile", + "description": "OpenID Connect built-in scope: profile", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${profileScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "7091a3bd-ffd1-40cf-82cf-636aa49728ce", + "name": "nickname", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "nickname", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "nickname", + "jsonType.label": "String" + } + }, + { + "id": "27f9ab53-8807-4ef1-b9a0-12a8a76ab5ec", + "name": "birthdate", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "birthdate", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "birthdate", + "jsonType.label": "String" + } + }, + { + "id": "29402017-bf33-48c2-8e7c-9eae2c44e929", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + }, + { + "id": "6e24f73b-8529-43ff-9815-2901cb1d5a91", + "name": "updated at", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "updatedAt", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "updated_at", + "jsonType.label": "long" + } + }, + { + "id": "a45c35be-f77d-4627-9bf9-a3414722e484", + "name": "picture", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "picture", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "picture", + "jsonType.label": "String" + } + }, + { + "id": "eba7c338-cce4-4cd6-8044-083273ddca3a", + "name": "full name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "id": "bfb08dad-0a9f-41fd-871b-1fbfb0d43594", + "name": "profile", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "profile", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "profile", + "jsonType.label": "String" + } + }, + { + "id": "b8f94365-aa92-44d7-9f96-84822aef4cad", + "name": "family name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "lastName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "family_name", + "jsonType.label": "String" + } + }, + { + "id": "b8849581-e158-4daf-98f0-b23f351b7362", + "name": "given name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "firstName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "given_name", + "jsonType.label": "String" + } + }, + { + "id": "7104be3f-1760-4fa7-9ad7-985959f852f2", + "name": "website", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "website", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "website", + "jsonType.label": "String" + } + }, + { + "id": "c7a9ba7a-62bf-4846-9b2f-56a8c6b31901", + "name": "gender", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "gender", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "gender", + "jsonType.label": "String" + } + }, + { + "id": "1e5a4e39-1fbc-4245-bced-f1271c01cf28", + "name": "middle name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "middleName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "middle_name", + "jsonType.label": "String" + } + }, + { + "id": "20bca7ef-8879-4b77-85fc-e38dd86518da", + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + }, + { + "id": "679465a3-8205-404b-ac12-f0ce50194f71", + "name": "zoneinfo", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "zoneinfo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "zoneinfo", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "fc9f5da4-557c-432f-87ec-128c07e09c79", + "name": "phone", + "description": "OpenID Connect built-in scope: phone", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${phoneScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "bbe96ba8-010c-4798-83e5-38fa3c7e7d66", + "name": "phone number", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "phoneNumber", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number", + "jsonType.label": "String" + } + }, + { + "id": "15f0c6ce-d7a5-4165-9ae2-978e3776d4a4", + "name": "phone number verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "phoneNumberVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number_verified", + "jsonType.label": "boolean" + } + } + ] + }, + { + "id": "96747a05-db5f-4289-bca2-8e3ebc0b244e", + "name": "roles", + "description": "OpenID Connect scope for add user roles to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "true", + "consent.screen.text": "${rolesScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "7db29b64-30f8-43df-99f7-73f16db774b4", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + }, + { + "id": "1fa84511-e274-4ffb-8cb7-a426dd5ebe4a", + "name": "realm roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "realm_access.roles", + "jsonType.label": "String", + "multivalued": "true" + } + }, + { + "id": "8594b20e-3ade-4661-bea2-bf0b5d47ff1e", + "name": "client roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-client-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "resource_access.${client_id}.roles", + "jsonType.label": "String", + "multivalued": "true" + } + } + ] + }, + { + "id": "801527ae-e765-4d90-8d87-5547fc96d2be", + "name": "address", + "description": "OpenID Connect built-in scope: address", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${addressScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "5519fbcf-8042-4b00-9c2a-a79bf16b9d59", + "name": "address", + "protocol": "openid-connect", + "protocolMapper": "oidc-address-mapper", + "consentRequired": false, + "config": { + "user.attribute.formatted": "formatted", + "user.attribute.country": "country", + "user.attribute.postal_code": "postal_code", + "userinfo.token.claim": "true", + "user.attribute.street": "street", + "id.token.claim": "true", + "user.attribute.region": "region", + "access.token.claim": "true", + "user.attribute.locality": "locality" + } + } + ] + }, + { + "id": "99a7cadd-76c0-406f-88bf-24947fec442e", + "name": "web-origins", + "description": "OpenID Connect scope for add allowed web origins to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false", + "consent.screen.text": "" + }, + "protocolMappers": [ + { + "id": "a57ca5de-7d7a-4695-b181-1099790ec07f", + "name": "allowed web origins", + "protocol": "openid-connect", + "protocolMapper": "oidc-allowed-origins-mapper", + "consentRequired": false, + "config": {} + } + ] + } + ], + "defaultDefaultClientScopes": [ + "role_list", + "profile", + "email", + "roles", + "web-origins", + "acr" + ], + "defaultOptionalClientScopes": [ + "offline_access", + "address", + "phone", + "microprofile-jwt" + ], + "browserSecurityHeaders": { + "contentSecurityPolicyReportOnly": "", + "xContentTypeOptions": "nosniff", + "xRobotsTag": "none", + "xFrameOptions": "SAMEORIGIN", + "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "xXSSProtection": "1; mode=block", + "strictTransportSecurity": "max-age=31536000; includeSubDomains" + }, + "smtpServer": {}, + "eventsEnabled": false, + "eventsListeners": [ + "jboss-logging" + ], + "enabledEventTypes": [], + "adminEventsEnabled": false, + "adminEventsDetailsEnabled": false, + "identityProviders": [], + "identityProviderMappers": [], + "components": { + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [ + { + "id": "bc6e125a-0c96-4a44-ac91-bf6ecc035cec", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allow-default-scopes": [ + "true" + ] + } + }, + { + "id": "a9aceec7-3d4d-4fc7-9ee7-b0862b3f212a", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allow-default-scopes": [ + "true" + ] + } + }, + { + "id": "476306a8-3346-430b-a6da-f3fc52910ce9", + "name": "Max Clients Limit", + "providerId": "max-clients", + "subType": "anonymous", + "subComponents": {}, + "config": { + "max-clients": [ + "200" + ] + } + }, + { + "id": "b3cc2af0-dc32-4a7d-9298-fdc664f3bb83", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "oidc-sha256-pairwise-sub-mapper", + "saml-user-attribute-mapper", + "saml-user-property-mapper", + "oidc-full-name-mapper", + "oidc-usermodel-attribute-mapper", + "saml-role-list-mapper", + "oidc-address-mapper", + "oidc-usermodel-property-mapper" + ] + } + }, + { + "id": "7da42bd3-7368-4be2-bc0c-82067fc48463", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "oidc-full-name-mapper", + "saml-user-attribute-mapper", + "oidc-address-mapper", + "oidc-sha256-pairwise-sub-mapper", + "oidc-usermodel-property-mapper", + "saml-user-property-mapper", + "oidc-usermodel-attribute-mapper", + "saml-role-list-mapper" + ] + } + }, + { + "id": "706c9166-d41a-4d1e-872c-45c587b0ac6b", + "name": "Full Scope Disabled", + "providerId": "scope", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "bc67afe8-8f95-49eb-915c-18d11f4bbc2b", + "name": "Consent Required", + "providerId": "consent-required", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "c8570184-4c4c-460f-9d78-95d36838e89a", + "name": "Trusted Hosts", + "providerId": "trusted-hosts", + "subType": "anonymous", + "subComponents": {}, + "config": { + "host-sending-registration-request-must-match": [ + "true" + ], + "client-uris-must-match": [ + "true" + ] + } + } + ], + "org.keycloak.userprofile.UserProfileProvider": [ + { + "id": "254a0e2b-b22b-4e1e-94ba-feb82f4e55f4", + "providerId": "declarative-user-profile", + "subComponents": {}, + "config": {} + } + ], + "org.keycloak.keys.KeyProvider": [ + { + "id": "f19c25ec-b555-4a60-8d98-fa41190c58d8", + "name": "hmac-generated", + "providerId": "hmac-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ], + "algorithm": [ + "HS256" + ] + } + }, + { + "id": "25496642-de17-48e8-8b48-49982e3e0bff", + "name": "aes-generated", + "providerId": "aes-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ] + } + }, + { + "id": "31fc839e-8c60-48b7-b9a2-66ecaa0902e5", + "name": "rsa-enc-generated", + "providerId": "rsa-enc-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ], + "algorithm": [ + "RSA-OAEP" + ] + } + }, + { + "id": "c09f0435-5a5c-4ee6-af62-6bc7db9fbc88", + "name": "rsa-generated", + "providerId": "rsa-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ] + } + } + ] + }, + "internationalizationEnabled": false, + "supportedLocales": [], + "authenticationFlows": [ + { + "id": "04cc2aa7-9e5b-4178-a1a2-dad58cf99367", + "alias": "Account verification options", + "description": "Method with which to verity the existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-email-verification", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Verify Existing Account by Re-authentication", + "userSetupAllowed": false + } + ] + }, + { + "id": "fa4d6b27-5fac-4b3b-9cbc-badb7cfe90ed", + "alias": "Authentication Options", + "description": "Authentication options.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "basic-auth", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "basic-auth-otp", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-spnego", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "266db702-5928-4149-b2bd-701d0722eb93", + "alias": "Browser - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-otp-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "dd326252-8827-445d-a098-9ec953932387", + "alias": "Direct Grant - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "direct-grant-validate-otp", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "b8f5c247-b9ba-40c7-a14e-05a235bed46f", + "alias": "First broker login - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-otp-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "f40cbe9a-ad2a-476c-b85d-ec426ce100b2", + "alias": "Handle Existing Account", + "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-confirm-link", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Account verification options", + "userSetupAllowed": false + } + ] + }, + { + "id": "60ba180d-92f3-4195-abd4-a925121994e7", + "alias": "Reset - Conditional OTP", + "description": "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-otp", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "0b5f7bb3-59e5-4d0e-9e8e-6d0e52984ad2", + "alias": "User creation or linking", + "description": "Flow for the existing/non-existing user alternatives", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "create unique user config", + "authenticator": "idp-create-user-if-unique", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Handle Existing Account", + "userSetupAllowed": false + } + ] + }, + { + "id": "37290b7b-23f8-4653-ad2c-2593db5760f3", + "alias": "Verify Existing Account by Re-authentication", + "description": "Reauthentication of existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-username-password-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "First broker login - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "2e5ceac1-9c0d-4109-b8f2-22c9efb00f0b", + "alias": "browser", + "description": "browser based authentication", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-cookie", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-spnego", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "identity-provider-redirector", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 25, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 30, + "autheticatorFlow": true, + "flowAlias": "forms", + "userSetupAllowed": false + } + ] + }, + { + "id": "c35579f7-cd70-4c66-9ee7-c21bf7ddd1e0", + "alias": "clients", + "description": "Base authentication for clients", + "providerId": "client-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "client-secret", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-jwt", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-secret-jwt", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-x509", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 40, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "c2487b50-dbf9-4536-be9d-940c8ac5eb21", + "alias": "direct grant", + "description": "OpenID Connect Resource Owner Grant", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "direct-grant-validate-username", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "direct-grant-validate-password", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 30, + "autheticatorFlow": true, + "flowAlias": "Direct Grant - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "e98419d1-4cb4-469d-a866-2adc9fdb4c6a", + "alias": "docker auth", + "description": "Used by Docker clients to authenticate against the IDP", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "docker-http-basic-authenticator", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "672acd89-be23-48ee-ac51-c5d846e77faf", + "alias": "first broker login", + "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "review profile config", + "authenticator": "idp-review-profile", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "User creation or linking", + "userSetupAllowed": false + } + ] + }, + { + "id": "1099c284-d2f6-44de-b1b3-87d5cb0990c1", + "alias": "forms", + "description": "Username, password, otp and other auth forms.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-username-password-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Browser - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "d02c9502-c51d-4968-ba5d-d3771054e85a", + "alias": "http challenge", + "description": "An authentication flow based on challenge-response HTTP Authentication Schemes", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "no-cookie-redirect", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Authentication Options", + "userSetupAllowed": false + } + ] + }, + { + "id": "18ee7c5d-3b4b-45c7-8d5a-761c2de30711", + "alias": "registration", + "description": "registration flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-page-form", + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": true, + "flowAlias": "registration form", + "userSetupAllowed": false + } + ] + }, + { + "id": "41c9dfb7-686d-4679-b471-abd04c08519d", + "alias": "registration form", + "description": "registration form", + "providerId": "form-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-user-creation", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-profile-action", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 40, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-password-action", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 50, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-recaptcha-action", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 60, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "2d4c9ede-ca14-4454-bf7b-60e9c23b1951", + "alias": "reset credentials", + "description": "Reset credentials for a user if they forgot their password or something", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "reset-credentials-choose-user", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-credential-email", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-password", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 40, + "autheticatorFlow": true, + "flowAlias": "Reset - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "d1fea7bd-8e31-4b67-9cb8-b720c2b5b49c", + "alias": "saml ecp", + "description": "SAML ECP Profile Authentication Flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "http-basic-authenticator", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + } + ], + "authenticatorConfig": [ + { + "id": "519345fd-5f36-411f-ac29-9a28fea6e1f1", + "alias": "create unique user config", + "config": { + "require.password.update.after.registration": "false" + } + }, + { + "id": "2ad5fe8b-f6aa-4608-bbc2-cbf2ff218b67", + "alias": "review profile config", + "config": { + "update.profile.on.first.login": "missing" + } + } + ], + "requiredActions": [ + { + "alias": "CONFIGURE_TOTP", + "name": "Configure OTP", + "providerId": "CONFIGURE_TOTP", + "enabled": true, + "defaultAction": false, + "priority": 10, + "config": {} + }, + { + "alias": "TERMS_AND_CONDITIONS", + "name": "Terms and Conditions", + "providerId": "TERMS_AND_CONDITIONS", + "enabled": false, + "defaultAction": false, + "priority": 20, + "config": {} + }, + { + "alias": "UPDATE_PASSWORD", + "name": "Update Password", + "providerId": "UPDATE_PASSWORD", + "enabled": true, + "defaultAction": false, + "priority": 30, + "config": {} + }, + { + "alias": "UPDATE_PROFILE", + "name": "Update Profile", + "providerId": "UPDATE_PROFILE", + "enabled": true, + "defaultAction": false, + "priority": 40, + "config": {} + }, + { + "alias": "VERIFY_EMAIL", + "name": "Verify Email", + "providerId": "VERIFY_EMAIL", + "enabled": true, + "defaultAction": false, + "priority": 50, + "config": {} + }, + { + "alias": "delete_account", + "name": "Delete Account", + "providerId": "delete_account", + "enabled": false, + "defaultAction": false, + "priority": 60, + "config": {} + }, + { + "alias": "webauthn-register", + "name": "Webauthn Register", + "providerId": "webauthn-register", + "enabled": true, + "defaultAction": false, + "priority": 70, + "config": {} + }, + { + "alias": "webauthn-register-passwordless", + "name": "Webauthn Register Passwordless", + "providerId": "webauthn-register-passwordless", + "enabled": true, + "defaultAction": false, + "priority": 80, + "config": {} + }, + { + "alias": "update_user_locale", + "name": "Update User Locale", + "providerId": "update_user_locale", + "enabled": true, + "defaultAction": false, + "priority": 1000, + "config": {} + } + ], + "browserFlow": "browser", + "registrationFlow": "registration", + "directGrantFlow": "direct grant", + "resetCredentialsFlow": "reset credentials", + "clientAuthenticationFlow": "clients", + "dockerAuthenticationFlow": "docker auth", + "attributes": { + "cibaBackchannelTokenDeliveryMode": "poll", + "cibaAuthRequestedUserHint": "login_hint", + "clientOfflineSessionMaxLifespan": "0", + "oauth2DevicePollingInterval": "5", + "clientSessionIdleTimeout": "0", + "actionTokenGeneratedByUserLifespan-execute-actions": "", + "actionTokenGeneratedByUserLifespan-verify-email": "", + "clientOfflineSessionIdleTimeout": "0", + "actionTokenGeneratedByUserLifespan-reset-credentials": "", + "cibaInterval": "5", + "realmReusableOtpCode": "false", + "cibaExpiresIn": "120", + "oauth2DeviceCodeLifespan": "600", + "actionTokenGeneratedByUserLifespan-idp-verify-account-via-email": "", + "parRequestUriLifespan": "60", + "clientSessionMaxLifespan": "0" + }, + "keycloakVersion": "21.1", + "userManagedAccessAllowed": false, + "clientProfiles": { + "profiles": [] + }, + "clientPolicies": { + "policies": [] + } +} diff --git a/local/postgres/init-db.sql b/local/postgres/init-db.sql index ebf9ce5a..41256c84 100644 --- a/local/postgres/init-db.sql +++ b/local/postgres/init-db.sql @@ -20,16 +20,16 @@ * SPDX-License-Identifier: Apache-2.0 */ +CREATE DATABASE miw; + CREATE DATABASE "edc_customer_asset"; CREATE DATABASE "edc_customer_contractdefinition"; CREATE DATABASE "edc_customer_contractnegotiation"; CREATE DATABASE "edc_customer_policy"; CREATE DATABASE "edc_customer_transferprocess"; ---CREATE DATABASE "edc_customer_edrs"; CREATE DATABASE "edc_supplier_asset"; CREATE DATABASE "edc_supplier_contractdefinition"; CREATE DATABASE "edc_supplier_contractnegotiation"; CREATE DATABASE "edc_supplier_policy"; CREATE DATABASE "edc_supplier_transferprocess"; ---CREATE DATABASE "edc_supplier_edrs"; diff --git a/local/restart.sh b/local/restart.sh deleted file mode 100644 index f8c5c3b3..00000000 --- a/local/restart.sh +++ /dev/null @@ -1,8 +0,0 @@ -# restarts the docker-compose and kill images, that may need a full refresh - -docker compose down - -# kill vault container that has been built with given secrets -docker image rm local-vault - -docker compose up diff --git a/local/tractus-x-edc/config/customer/control-plane.properties b/local/tractus-x-edc/config/customer/control-plane.properties index 37b05dad..15039642 100644 --- a/local/tractus-x-edc/config/customer/control-plane.properties +++ b/local/tractus-x-edc/config/customer/control-plane.properties @@ -4,7 +4,7 @@ web.http.default.path=/api # MANAGEMENT (replaced data in 0.3.0) web.http.management.port=8181 -web.http.management.path=/api/v1/data +web.http.management.path=/management # CONTROL (replaced validation in 0.3.0) web.http.control.port=8183 @@ -12,82 +12,78 @@ web.http.control.path=/api/controlplane/control # PROTOCOL (replaced IDS in 0.3.0) web.http.protocol.port=8184 -# note: EDC in this version appends a "data" per IDS protocol -# https://eclipse-edc.github.io/docs/#/submodule/Connector/docs/developer/decision-records/2022-11-09-api-refactoring/renaming -web.http.protocol.path=/api/v1/ids +web.http.protocol.path=/api/v1/dsp -edc.ids.title=Customer EDC -edc.ids.description=Customer EDC Control Plane -edc.ids.id=urn:connector:customer-control-plane -edc.ids.security.profile=base -edc.ids.maintainer=http://customer-control-plane -edc.ids.curator=http://customer-control-plane -edc.ids.catalog.id=urn:catalog:default - -# /api/v1/ids is the default HTTP IDS path -ids.webhook.address=http://customer-control-plane:8184 +edc.participant.id=BPNL4444444444XX +edc.api.auth.key= ${EDC_API_PW} +edc.dsp.callback.address=http://customer-control-plane:8184/api/v1/dsp edc.hostname=customer-control-plane -# set via .env -#edc.api.auth.key=password - -# OAuth / DAPS related configuration -edc.ids.endpoint=http://customer-control-plane:8184/api/v1/ids -## this may relate to version > 0.1.3 -edc.oauth.endpoint.audience=http://customer-control-plane:8184/api/v1/ids/data -## starting from 0.4.0 with new protocol -#edc.oauth.endpoint.audience=http://consumer-control-plane:8184/api/v1/dsp -edc.oauth.token.url=http://omejdn-daps:4567/token -edc.oauth.client.id=customer -edc.oauth.provider.jwks.url=http://omejdn-daps:4567/jwks.json -edc.oauth.provider.audience=idsc:IDS_CONNECTORS_ALL -edc.oauth.public.key.alias=customer-cert -edc.oauth.private.key.alias=customer-key -edc.ids.validation.referringconnector=false +tx.ssi.oauth.token.url=http://keycloak:8080/realms/miw_test/protocol/openid-connect/token +tx.ssi.oauth.client.id=${CUSTOMER_OAUTH_CLIENT_ID} +tx.ssi.oauth.client.secret.alias=${CUSTOMER_OAUTH_SECRET_ALIAS} +tx.ssi.miw.url=http://miw +tx.ssi.miw.authority.id=BPNL000000000000 +tx.ssi.endpoint.audience=http://customer-control-plane:8184/api/v1/dsp # HashiCorp vault related configuration edc.vault.hashicorp.url=http://vault:8200 -# set via docker-compose .env -#edc.vault.hashicorp.token= edc.vault.hashicorp.health.check.enabled=false +edc.vault.hashicorp.token= ${VAULT_DEV_ROOT_TOKEN_ID} # Data-Plane configuration edc.transfer.proxy.endpoint=http://customer-data-plane:8285/api/public/ -edc.transfer.proxy.token.signer.privatekey.alias=customer-key -edc.transfer.proxy.token.verifier.publickey.alias=customer-cert +edc.transfer.proxy.token.signer.privatekey.alias=${CUSTOMER_PRIVATE_KEY_ALIAS} +edc.transfer.proxy.token.verifier.publickey.alias=${CUSTOMER_PUBLIC_KEY_ALIAS} # Data-Plane selector configuration -edc.dataplane.selector.edchttp.url=http://customer-data-plane:8299/api/dataplane/control +edc.dataplane.selector.edchttp.url=http://customer-data-plane:8299/control/transfer edc.dataplane.selector.edchttp.sourcetypes=HttpData -edc.dataplane.selector.edchttp.destinationtypes=HttpProxy +edc.dataplane.selector.edchttp.destinationtypes=HttpData,HttpProxy edc.dataplane.selector.edchttp.properties={"publicApiUrl" : "http://customer-data-plane:8285/api/public/"} -# backend receiver for static Endpoint Data References -edc.receiver.http.endpoint=http://backend-app:8080 +#edc.receiver.http.endpoint=http://customer-backend:8081/catena/edrendpoint # Postgresql related configuration edc.datasource.asset.name=asset edc.datasource.asset.url=jdbc:postgresql://postgres:5432/edc_customer_asset # edc.datasource.asset.user and edc.datasource.asset.password are set via .env +edc.datasource.asset.user= ${PG_USER} +edc.datasource.asset.password= ${PG_PW} + edc.datasource.contractdefinition.name=contractdefinition edc.datasource.contractdefinition.url=jdbc:postgresql://postgres:5432/edc_customer_contractdefinition # edc.datasource.contractdefinition.user and edc.datasource.contractdefinition.password are set via .env +edc.datasource.contractdefinition.user= ${PG_USER} +edc.datasource.contractdefinition.password= ${PG_PW} + edc.datasource.contractnegotiation.name=contractnegotiation edc.datasource.contractnegotiation.url=jdbc:postgresql://postgres:5432/edc_customer_contractnegotiation # edc.datasource.contractnegotiation.user and edc.datasource.contractnegotiation.password are set via .env +edc.datasource.contractnegotiation.user= ${PG_USER} +edc.datasource.contractnegotiation.password= ${PG_PW} + edc.datasource.policy.name=policy edc.datasource.policy.url=jdbc:postgresql://postgres:5432/edc_customer_policy # edc.datasource.policy.user and edc.datasource.policy.password are set via .env +edc.datasource.policy.user= ${PG_USER} +edc.datasource.policy.password= ${PG_PW} + edc.datasource.transferprocess.name=transferprocess edc.datasource.transferprocess.url=jdbc:postgresql://postgres:5432/edc_customer_transferprocess # edc.datasource.transferprocess.user and edc.datasource.transferprocess.password are set via .env +edc.datasource.transferprocess.user= ${PG_USER} +edc.datasource.transferprocess.password= ${PG_PW} # new in 0.4.x #edc.datasource.edr.name=edr #edc.datasource.edr.url=jdbc:postgresql://postgres:5432/edc_customer_edrs # edc.datasource.edr.user and edc.datasource.edr.password are set via .env -#org.eclipse.tractusx.edc.postgresql.migration.edr.enabled=true +#org.eclipse.tractusx.edc.postgresql.migration.edr.enabled=false +#edc.datasource.edr.user= +#edc.datasource.edr.password= # Data Encryption edc-extensions/data-encryption -edc.data.encryption.keys.alias=customer-encryption-keys +edc.data.encryption.keys.alias=${CUSTOMER_ENCRYPTION_KEYS_ALIAS} + diff --git a/local/tractus-x-edc/config/customer/data-plane.properties b/local/tractus-x-edc/config/customer/data-plane.properties index a0aed114..aa008814 100644 --- a/local/tractus-x-edc/config/customer/data-plane.properties +++ b/local/tractus-x-edc/config/customer/data-plane.properties @@ -6,7 +6,7 @@ web.http.public.port=8285 web.http.public.path=/api/public # Control web.http.control.port=8299 -web.http.control.path=/api/dataplane/control +web.http.control.path=/control # new in 0.3.3 - why do we need the management in a data plane? web.http.management.port=8293 @@ -20,7 +20,12 @@ edc.hostname=customer-data-plane # HashiCorp vault related configuration edc.vault.hashicorp.url=http://vault:8200 -# set via docker-compose .env -#edc.vault.hashicorp.token= -#disable annoying logging - do we need this enabled? edc.vault.hashicorp.health.check.enabled=false +edc.vault.hashicorp.token= ${VAULT_DEV_ROOT_TOKEN_ID} + +#edc.datasource.edr.name=edr +#edc.datasource.edr.url=jdbc:postgresql://postgres:5432/edc_customer_edrs +# edc.datasource.edr.user and edc.datasource.edr.password are set via .env +#org.eclipse.tractusx.edc.postgresql.migration.edr.enabled=false +#edc.datasource.edr.user=${PG_USER} +#edc.datasource.edr.password=${PG_PW} diff --git a/local/tractus-x-edc/config/customer/puris-backend.properties b/local/tractus-x-edc/config/customer/puris-backend.properties new file mode 100644 index 00000000..0890fd60 --- /dev/null +++ b/local/tractus-x-edc/config/customer/puris-backend.properties @@ -0,0 +1,24 @@ +server.port=8081 +puris.demonstrator.role=customer +puris.apiversion=1.0.0 +puris.edr.endpoint=http://customer-backend:8081/catena/edrendpoint +puris.edr.deletiontimer=2 +puris.request.serverendpoint=http://customer-backend:8081/catena/product-stock/request +puris.request.apiassetid=request-api-asset +puris.response.serverendpoint=http://customer-backend:8081/catena/product-stock/response +puris.response.apiassetid=response-api-asset + +edc.controlplane.key=${EDC_API_PW} +edc.controlplane.management.url=http://customer-control-plane:8181/management +edc.controlplane.protocol.url=http://customer-control-plane:8184/api/v1/dsp + +own.bpnl=BPNL4444444444XX +own.name=Scenario Customer +own.default.bpns=BPNS4444444444XX +own.default.site.name=Hauptwerk Musterhausen +own.default.bpna=BPNA4444444444AA +own.default.streetandnumber=Musterstrasse 35b +own.default.zipcodeandcity=77777 Musterhausen +own.default.country=Germany + + diff --git a/local/tractus-x-edc/config/supplier/control-plane.properties b/local/tractus-x-edc/config/supplier/control-plane.properties index f0c60726..0661eb86 100644 --- a/local/tractus-x-edc/config/supplier/control-plane.properties +++ b/local/tractus-x-edc/config/supplier/control-plane.properties @@ -4,7 +4,7 @@ web.http.default.path=/api # MANAGEMENT (replaced data in 0.3.0) web.http.management.port=9181 -web.http.management.path=/api/v1/data +web.http.management.path=/management # CONTROL (replaced validation in 0.3.0) web.http.control.port=9183 @@ -12,81 +12,81 @@ web.http.control.path=/api/controlplane/control # PROTOCOL (replaced IDS in 0.3.0) web.http.protocol.port=9184 -# note: EDC in this version appends a "data" per IDS protocol -# https://eclipse-edc.github.io/docs/#/submodule/Connector/docs/developer/decision-records/2022-11-09-api-refactoring/renaming -web.http.protocol.path=/api/v1/ids +web.http.protocol.path=/api/v1/dsp -edc.ids.title=Supplier EDC -edc.ids.description=Supplier EDC Control Plane -edc.ids.id=urn:connector:supplier-control-plane -edc.ids.security.profile=base -edc.ids.maintainer=http://supplier-control-plane -edc.ids.curator=http://supplier-control-plane -edc.ids.catalog.id=urn:catalog:default +JAVA_TOOL_OPTIONS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:1044 -# /api/v1/ids is the default HTTP IDS path -ids.webhook.address=http://supplier-control-plane:9184 +edc.participant.id=BPNL1234567890ZZ +edc.api.auth.key= ${EDC_API_PW} +edc.dsp.callback.address=http://supplier-control-plane:9184/api/v1/dsp edc.hostname=supplier-control-plane -# set via .env -#edc.api.auth.key=password - -# OAuth / DAPS related configuration -edc.ids.endpoint=http://supplier-control-plane:9184/api/v1/ids -## this may relate to version > 0.1.3 -edc.oauth.endpoint.audience=http://supplier-control-plane:9184/api/v1/ids/data -## starting from 0.4.0 with new protocol -#edc.oauth.endpoint.audience=http://consumer-control-plane:9184/api/v1/dsp -edc.oauth.token.url=http://omejdn-daps:4567/token -edc.oauth.client.id=supplier -edc.oauth.provider.jwks.url=http://omejdn-daps:4567/jwks.json -edc.oauth.provider.audience=idsc:IDS_CONNECTORS_ALL -edc.oauth.public.key.alias=supplier-cert -edc.oauth.private.key.alias=supplier-key -edc.ids.validation.referringconnector=false +tx.ssi.oauth.token.url=http://keycloak:8080/realms/miw_test/protocol/openid-connect/token +tx.ssi.oauth.client.id=${SUPPLIER_OAUTH_CLIENT_ID} +tx.ssi.oauth.client.secret.alias=${SUPPLIER_OAUTH_SECRET_ALIAS} +tx.ssi.miw.url=http://miw +tx.ssi.miw.authority.id=BPNL000000000000 +tx.ssi.endpoint.audience=http://supplier-control-plane:9184/api/v1/dsp # HashiCorp vault related configuration edc.vault.hashicorp.url=http://vault:8200 -# set via docker-compose .env -#edc.vault.hashicorp.token= edc.vault.hashicorp.health.check.enabled=false +edc.vault.hashicorp.token= ${VAULT_DEV_ROOT_TOKEN_ID} # Data-Plane configuration edc.transfer.proxy.endpoint=http://supplier-data-plane:9285/api/public/ -edc.transfer.proxy.token.signer.privatekey.alias=supplier-key -edc.transfer.proxy.token.verifier.publickey.alias=supplier-cert +edc.transfer.proxy.token.signer.privatekey.alias=${SUPPLIER_PRIVATE_KEY_ALIAS} +edc.transfer.proxy.token.verifier.publickey.alias=${SUPPLIER_PUBLIC_KEY_ALIAS} # Data-Plane selector configuration -edc.dataplane.selector.edchttp.url=http://supplier-data-plane:9299/api/dataplane/control +edc.dataplane.selector.edchttp.url=http://supplier-data-plane:9299/control/transfer edc.dataplane.selector.edchttp.sourcetypes=HttpData -edc.dataplane.selector.edchttp.destinationtypes=HttpProxy +edc.dataplane.selector.edchttp.destinationtypes=HttpData,HttpProxy edc.dataplane.selector.edchttp.properties={"publicApiUrl" : "http://supplier-data-plane:9285/api/public/"} -# puris backend receiver -edc.receiver.http.endpoint=http://backend-app:8080 +#edc.receiver.http.endpoint=http://supplier-backend:8082/catena/edrendpoint # Postgresql related configuration edc.datasource.asset.name=asset edc.datasource.asset.url=jdbc:postgresql://postgres:5432/edc_supplier_asset # edc.datasource.asset.user and edc.datasource.asset.password are set via .env +edc.datasource.asset.user= ${PG_USER} +edc.datasource.asset.password= ${PG_PW} + edc.datasource.contractdefinition.name=contractdefinition edc.datasource.contractdefinition.url=jdbc:postgresql://postgres:5432/edc_supplier_contractdefinition + # edc.datasource.contractdefinition.user and edc.datasource.contractdefinition.password are set via .env +edc.datasource.contractdefinition.user= ${PG_USER} +edc.datasource.contractdefinition.password= ${PG_PW} + edc.datasource.contractnegotiation.name=contractnegotiation edc.datasource.contractnegotiation.url=jdbc:postgresql://postgres:5432/edc_supplier_contractnegotiation # edc.datasource.contractnegotiation.user and edc.datasource.contractnegotiation.password are set via .env +edc.datasource.contractnegotiation.user= ${PG_USER} +edc.datasource.contractnegotiation.password= ${PG_PW} + edc.datasource.policy.name=policy edc.datasource.policy.url=jdbc:postgresql://postgres:5432/edc_supplier_policy # edc.datasource.policy.user and edc.datasource.policy.password are set via .env +edc.datasource.policy.user= ${PG_USER} +edc.datasource.policy.password= ${PG_PW} + edc.datasource.transferprocess.name=transferprocess edc.datasource.transferprocess.url=jdbc:postgresql://postgres:5432/edc_supplier_transferprocess # edc.datasource.transferprocess.user and edc.datasource.transferprocess.password are set via .env +edc.datasource.transferprocess.user= ${PG_USER} +edc.datasource.transferprocess.password= ${PG_PW} + # new in 0.4.x #edc.datasource.edr.name=edr #edc.datasource.edr.url=jdbc:postgresql://postgres:5432/edc_supplier_edrs # edc.datasource.edr.user and edc.datasource.edr.password are set via .env -#org.eclipse.tractusx.edc.postgresql.migration.edr.enabled=true +#org.eclipse.tractusx.edc.postgresql.migration.edr.enabled=false +#edc.datasource.edr.user= +#edc.datasource.edr.password= # Data Encryption -edc.data.encryption.keys.alias=supplier-encryption-keys +edc.data.encryption.keys.alias=${SUPPLIER_ENCRYPTION_KEYS_ALIAS} + diff --git a/local/tractus-x-edc/config/supplier/data-plane.properties b/local/tractus-x-edc/config/supplier/data-plane.properties index 9b10f9cc..d2c4669b 100644 --- a/local/tractus-x-edc/config/supplier/data-plane.properties +++ b/local/tractus-x-edc/config/supplier/data-plane.properties @@ -6,7 +6,7 @@ web.http.public.port=9285 web.http.public.path=/api/public # Control web.http.control.port=9299 -web.http.control.path=/api/dataplane/control +web.http.control.path=/control # new in 0.3.3 - why do we need the management in a data plane? web.http.management.port=9293 @@ -21,6 +21,12 @@ edc.hostname=supplier-data-plane # HashiCorp vault related configuration edc.vault.hashicorp.url=http://vault:8200 # set via docker-compose .env -#edc.vault.hashicorp.token= -#disable annoying logging - do we need this enabled? edc.vault.hashicorp.health.check.enabled=false +edc.vault.hashicorp.token= ${VAULT_DEV_ROOT_TOKEN_ID} + +#edc.datasource.edr.name=edr +#edc.datasource.edr.url=jdbc:postgresql://postgres:5432/edc_customer_edrs +# edc.datasource.edr.user and edc.datasource.edr.password are set via .env +#org.eclipse.tractusx.edc.postgresql.migration.edr.enabled=false +#edc.datasource.edr.user=${PG_USER} +#edc.datasource.edr.password=${PG_PW} diff --git a/local/tractus-x-edc/config/supplier/puris-backend.properties b/local/tractus-x-edc/config/supplier/puris-backend.properties new file mode 100644 index 00000000..ca9220ef --- /dev/null +++ b/local/tractus-x-edc/config/supplier/puris-backend.properties @@ -0,0 +1,24 @@ +server.port=8082 +puris.demonstrator.role=supplier +puris.apiversion=1.0.0 +puris.edr.endpoint=http://supplier-backend:8082/catena/edrendpoint +puris.edr.deletiontimer=2 +puris.request.serverendpoint=http://supplier-backend:8082/catena/product-stock/request +puris.request.apiassetid=request-api-asset +puris.response.serverendpoint=http://supplier-backend:8082/catena/product-stock/response +puris.response.apiassetid=response-api-asset + +edc.controlplane.key=${EDC_API_PW} +edc.controlplane.management.url=http://supplier-control-plane:9181/management +edc.controlplane.protocol.url=http://supplier-control-plane:9184/api/v1/dsp + +own.bpnl=BPNL1234567890ZZ +own.name=Scenario Supplier +own.default.bpns=BPNS1234567890ZZ +own.default.site.name=Konzernzentrale Dudelsdorf +own.default.bpna=BPNA1234567890AA +own.default.streetandnumber=Heinrich-Supplier-Strasse 1 +own.default.zipcodeandcity=77785 Dudelsdorf +own.default.country=Germany + + diff --git a/local/tractus-x-edc/docker-compose.yaml b/local/tractus-x-edc/docker-compose.yaml index 88739817..05c99088 100644 --- a/local/tractus-x-edc/docker-compose.yaml +++ b/local/tractus-x-edc/docker-compose.yaml @@ -21,22 +21,13 @@ version: "3" services: control-plane: - image: ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql-hashicorp-vault:0.3.0 - #ports: - # - "8180:8080" # default - # - "8184:8084" # ids - # - "8181:8081" # data management api - should be private later on - # - "8182:8082" # validation - #entrypoint: find startup information in product-edc/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile + image : tractusx/edc-controlplane-memory-hashicorp-vault:0.5.3 volumes: - ./config/default/opentelemetry.properties:/app/opentelemetry.properties - ./config/default/logging.properties:/app/logging.properties data-plane: - image: ghcr.io/catenax-ng/product-edc/edc-dataplane-hashicorp-vault:0.3.0 - #ports: - # - "8080:8080" # default - # - "8185:8185" # public + image: tractusx/edc-dataplane-hashicorp-vault:0.5.3 volumes: - ./config/default/opentelemetry.properties:/app/opentelemetry.properties - ./config/default/logging.properties:/app/logging.properties diff --git a/local/vault/put-keys.sh b/local/vault/put-keys.sh index acd3416e..6bcf8cab 100644 --- a/local/vault/put-keys.sh +++ b/local/vault/put-keys.sh @@ -19,11 +19,13 @@ echo "Adding customer certificates" cat $VAULT_PUT_SECRETS_DIR/customer.key | vault kv put secret/customer-key content=- cat $VAULT_PUT_SECRETS_DIR/customer.cert | vault kv put secret/customer-cert content=- cat $VAULT_PUT_SECRETS_DIR/customer-encryption.keys | vault kv put secret/customer-encryption-keys content=- +cat $VAULT_PUT_SECRETS_DIR/customer.miw.secret | vault kv put secret/customer.miw.secret content=- echo "Adding supplier certificates" cat $VAULT_PUT_SECRETS_DIR/supplier.key | vault kv put secret/supplier-key content=- cat $VAULT_PUT_SECRETS_DIR/supplier.cert | vault kv put secret/supplier-cert content=- cat $VAULT_PUT_SECRETS_DIR/supplier-encryption.keys | vault kv put secret/supplier-encryption-keys content=- +cat $VAULT_PUT_SECRETS_DIR/supplier.miw.secret | vault kv put secret/supplier.miw.secret content=- # and get the actual server process back to the foreground fg %1 From 51e8a1ec43c26d42df2444d85e8ce3fb8f5f4b4e Mon Sep 17 00:00:00 2001 From: Ernst-Christoph Schrewe Date: Fri, 24 Nov 2023 09:00:37 +0100 Subject: [PATCH 02/11] fix: additional review fixes --- NOTICE.md | 4 +- .../edc/logic/util/EDCRequestBodyBuilder.java | 7 +- .../controller/DashboardController.java | 65 ------------------- .../ProductStockRequestApiController.java | 2 +- local/miw/keycloak-setup.json.license | 5 ++ 5 files changed, 11 insertions(+), 72 deletions(-) delete mode 100644 backend/src/main/java/org/eclipse/tractusx/puris/backend/controller/DashboardController.java create mode 100644 local/miw/keycloak-setup.json.license diff --git a/NOTICE.md b/NOTICE.md index fc26e5bb..6c25ef22 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -37,9 +37,7 @@ This project leverages the following third party content. See `DEPENDENCIES_FRONTEND` and `DEPENDENCIES_BACKEND` file. Further, the following third-party content is used that isn't listed in any DEPENDENCIES file: -[Keycloak initial realm setup](./local/miw/keycloak-setup.json) derived from -https://github.com/eclipse-tractusx/managed-identity-wallet/blob/287b61d12cabf7e2a39f60e1f49d9baaa8401e8c/dev-assets/docker-environment/keycloak/miw_test_realm_docker.json#L809 -under SPDX-License-Identifier: Apache-2.0 +Keycloak initial realm setup, see the details [here](./local/miw/keycloak-setup.json.license). feather (4.29) * License: MIT License diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/logic/util/EDCRequestBodyBuilder.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/logic/util/EDCRequestBodyBuilder.java index 9ad4d0a5..04eeb365 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/logic/util/EDCRequestBodyBuilder.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/logic/util/EDCRequestBodyBuilder.java @@ -29,6 +29,7 @@ public class EDCRequestBodyBuilder { private final String publicPolicyId = "policy1"; private final String publicContractDefinitionId = "contractdef1"; private final String EDC_NAMESPACE = "https://w3id.org/edc/v0.0.1/ns/"; + private final String VOCAB_KEY = "@vocab"; private final String ODRL_NAMESPACE = "http://www.w3.org/ns/odrl/2/"; private final String CX_TAXO_NAMESPACE = "https://w3id.org/catenax/taxonomy#"; private final String CX_COMMON_NAMESPACE = "https://w3id.org/catenax/ontology/common#"; @@ -72,7 +73,7 @@ public ObjectNode buildBasicDSPCatalogRequestBody(String counterPartyDspUrl, Map public JsonNode buildDSPCreateAssetBody(DT_ApiMethodEnum apiMethod) { var body = MAPPER.createObjectNode(); var context = MAPPER.createObjectNode(); - context.put("edc", EDC_NAMESPACE); + context.put(VOCAB_KEY, EDC_NAMESPACE); context.put("cx-taxo", CX_TAXO_NAMESPACE); context.put("cx-common", CX_COMMON_NAMESPACE); context.put("dct", DCT_NAMESPACE); @@ -154,7 +155,7 @@ public JsonNode buildDSPContractDefinitionWithPublicPolicy() { public ObjectNode buildDSPAssetNegotiation(Partner partner, JsonNode dcatCatalogItem) { var objectNode = MAPPER.createObjectNode(); var contextNode = MAPPER.createObjectNode(); - contextNode.put("edc", EDC_NAMESPACE); + contextNode.put(VOCAB_KEY, EDC_NAMESPACE); contextNode.put("odrl", ODRL_NAMESPACE); objectNode.set("@context", contextNode); objectNode.put("@type", "NegotiationInitiateRequestDto"); @@ -216,7 +217,7 @@ public JsonNode buildDSPDataPullRequestBody(Partner partner, String contractID, private ObjectNode getEDCContextObject() { ObjectNode node = MAPPER.createObjectNode(); var context = MAPPER.createObjectNode(); - context.put("edc", EDC_NAMESPACE); + context.put(VOCAB_KEY, EDC_NAMESPACE); node.set("@context", context); return node; } diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/controller/DashboardController.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/controller/DashboardController.java deleted file mode 100644 index 0724b31d..00000000 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/controller/DashboardController.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2022,2023 Volkswagen AG - * Copyright (c) 2022,2023 Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. (represented by Fraunhofer ISST) - * Copyright (c) 2022,2023 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.puris.backend.controller; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.eclipse.tractusx.puris.backend.common.edc.logic.service.EdcAdapterService; -import org.eclipse.tractusx.puris.backend.model.repo.OrderRepository; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.CrossOrigin; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import java.io.IOException; - -/** - * Controller returning stats for the frontends Dashboard. - */ -@RestController -@RequestMapping("dashboard") -public class DashboardController { - - private static final ObjectMapper MAPPER = new ObjectMapper(); - - @Autowired - OrderRepository orderRepository; - - @Autowired - EdcAdapterService edcAdapter; - - /** - * Collect information for frontends dashboard. - * - * @return information about created and published orders, used to show frontends dashboard. - * @throws IOException when connection to EDC fails. - */ - @GetMapping("data") - @CrossOrigin - public JsonNode getData() throws IOException { - var orders = orderRepository.findAll(); - var node = MAPPER.createObjectNode(); - node.put("orders", orders.size()); - node.put("service", "currently not supported"); - return node; - } -} diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/controller/ProductStockRequestApiController.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/controller/ProductStockRequestApiController.java index 1a109922..ad20fe30 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/controller/ProductStockRequestApiController.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/stock/controller/ProductStockRequestApiController.java @@ -137,7 +137,7 @@ public ResponseEntity postRequest(@RequestBody String requestBody) { @ApiResponse(responseCode = "401", description = "Not authorized"), @ApiResponse(responseCode = "422", description = "The request ID is not known") }) - public ResponseEntity getRequest(@RequestBody(required = false) JsonNode body) { + public ResponseEntity getRequest(@RequestBody JsonNode body) { try { MessageHeaderDto header = objectMapper.convertValue(body.get("header"), MessageHeaderDto.class); var request = productStockRequestService.findRequestByHeaderUuid(header.getRequestId()); diff --git a/local/miw/keycloak-setup.json.license b/local/miw/keycloak-setup.json.license new file mode 100644 index 00000000..3f6c51f5 --- /dev/null +++ b/local/miw/keycloak-setup.json.license @@ -0,0 +1,5 @@ +The keycloak-setup.json is licensed under the [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0). + +- SPDX-License-Identifier: Apache-2.0 +- SPDX-FileCopyrightText: https://github.com/eclipse-tractusx/managed-identity-wallet/blob/main/LICENSE +- Source URL: https://github.com/eclipse-tractusx/managed-identity-wallet From dfffb65c858e2a65d73dbfd97919819d94467da0 Mon Sep 17 00:00:00 2001 From: Ernst-Christoph Schrewe Date: Fri, 24 Nov 2023 11:17:33 +0100 Subject: [PATCH 03/11] chore: corrected copyright notice --- NOTICE.md | 6 +++++- local/miw/keycloak-setup.json.license | 5 ----- 2 files changed, 5 insertions(+), 6 deletions(-) delete mode 100644 local/miw/keycloak-setup.json.license diff --git a/NOTICE.md b/NOTICE.md index 6c25ef22..962d4dab 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -37,7 +37,11 @@ This project leverages the following third party content. See `DEPENDENCIES_FRONTEND` and `DEPENDENCIES_BACKEND` file. Further, the following third-party content is used that isn't listed in any DEPENDENCIES file: -Keycloak initial realm setup, see the details [here](./local/miw/keycloak-setup.json.license). +Keycloak initial realm setup +* SPDX-License-Identifier: Apache-2.0 +* SPDX-FileCopyrightText: https://github.com/eclipse-tractusx/managed-identity-wallet/blob/main/LICENSE +* Source URL: https://github.com/eclipse-tractusx/managed-identity-wallet + feather (4.29) * License: MIT License diff --git a/local/miw/keycloak-setup.json.license b/local/miw/keycloak-setup.json.license deleted file mode 100644 index 3f6c51f5..00000000 --- a/local/miw/keycloak-setup.json.license +++ /dev/null @@ -1,5 +0,0 @@ -The keycloak-setup.json is licensed under the [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0). - -- SPDX-License-Identifier: Apache-2.0 -- SPDX-FileCopyrightText: https://github.com/eclipse-tractusx/managed-identity-wallet/blob/main/LICENSE -- Source URL: https://github.com/eclipse-tractusx/managed-identity-wallet From b105371976384f4cc99fe7a5cbbf5f87180da978 Mon Sep 17 00:00:00 2001 From: Ernst-Christoph Schrewe Date: Mon, 27 Nov 2023 11:58:58 +0100 Subject: [PATCH 04/11] fix: corrected .env --- local/.gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/local/.gitignore b/local/.gitignore index b7ed45e9..e07617b5 100644 --- a/local/.gitignore +++ b/local/.gitignore @@ -2,4 +2,4 @@ *.cert *.keys *.secret -local/.env +.env From ddeff670ea7c50f7849b24b6f8ce9b22ad3c743e Mon Sep 17 00:00:00 2001 From: Ernst-Christoph Schrewe Date: Tue, 28 Nov 2023 14:31:46 +0100 Subject: [PATCH 05/11] fix: updated transfer request --- .../EndpointDataReferenceReceiver.java | 31 +++++++------------ .../edc/logic/util/EDCRequestBodyBuilder.java | 17 +++++----- .../config/customer/control-plane.properties | 3 ++ .../config/supplier/control-plane.properties | 3 ++ 4 files changed, 24 insertions(+), 30 deletions(-) diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/controller/EndpointDataReferenceReceiver.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/controller/EndpointDataReferenceReceiver.java index 3c1cd30b..495b3087 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/controller/EndpointDataReferenceReceiver.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/controller/EndpointDataReferenceReceiver.java @@ -44,7 +44,7 @@ public class EndpointDataReferenceReceiver { @Autowired - private EndpointDataReferenceService edcService; + private EndpointDataReferenceService edrService; private final static String prefix = "https://w3id.org/edc/v0.0.1/ns/"; /** @@ -62,27 +62,18 @@ public class EndpointDataReferenceReceiver { @ApiResponse(responseCode = "200", description = "Ok") }) private ResponseEntity authCodeReceivingEndpoint(@RequestBody JsonNode body) { - ResponseEntity response = ResponseEntity.status(200).build(); - var payload = body.get("payload"); - if (payload.get("dataAddress") == null) { - return response; - } - var dataAddress = payload.get("dataAddress"); - if (dataAddress.get("properties") == null) { - return response; - } - var properties = dataAddress.get("properties"); - String transferId = properties.get(prefix + "id").asText(); - String authKey = properties.get(prefix + "authKey").asText(); - String authCode = properties.get(prefix + "authCode").asText(); - String endpoint = properties.get(prefix + "endpoint").asText(); + log.debug("Received edr data:\n" + body.toPrettyString()); + String transferId = body.get("id").asText(); + String authKey = body.get("authKey").asText(); + String authCode = body.get("authCode").asText(); + String endpoint = body.get("endpoint").asText(); if (transferId == null || authCode == null) { - log.warn("EDR endpoint received invalid message:\n" + body.toPrettyString()); - return response; + log.warn("authCodes endpoint received invalid message:\n" + body.toPrettyString()); + return ResponseEntity.status(400).build(); } - edcService.save(transferId, new EDR_Dto(authKey, authCode, endpoint)); - log.info("EDR endpoint stored authCode for " + transferId); - return response; + edrService.save(transferId, new EDR_Dto(authKey, authCode, endpoint)); + log.debug("authCodes endpoint stored authCode for " + transferId); + return ResponseEntity.status(200).build(); } private final static String sample = "{\"id\":\"3b603c3e-0f1a-4989-90b7-a4b024496d04\",\"at\":1699446240954,\"payload\":{\"transferProcessId\":\"e5c59912-b88a-4c42-9766-9fa593b72603\",\"callbackAddresses\":[{\"uri\":\"http://host.docker.internal:4000\",\"events\":[\"contract.negotiation\",\"transfer.process\"],\"transactional\":false,\"authKey\":null,\"authCodeId\":null}],\"dataAddress\":{\"properties\":{\"https://w3id.org/edc/v0.0.1/ns/type\":\"EDR\",\"https://w3id.org/edc/v0.0.1/ns/endpoint\":\"http://supplier-data-plane:9285/api/public/\",\"https://w3id.org/edc/v0.0.1/ns/authCode\":\"eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE2OTk0NDY4NDAsImRhZCI6ImMvMnFYVThaemVRTnJ5WDloUjhFZytONXpaWjhaUnc5dHpDOWRPcDlQNnpQQUhxZE9XN0pyN3JIWk94R0k0dFBXMmUxZVZ6elRJNlA5dUZXREpVVWtvRjJpWm93aXp6UEhmbGF4YTdsY0JIcWNISThhZWlEcm5MYiswM1ZXemRtMEZqLzNYYVdUa3VGYzM4VGtWS1lMaEViOVRNRUV4UGhqbEM5WFplamc0ZWJHNUZ6QldDQXZ2bmlMaWpFMjA4ZDhOcE80M0w5cG4xWXBpUThkZ2IrYjhkcEsyMDZObDNzTFhyS1hsU09ZaERHR2tLM2dSYkxBRHpiRjl3RWlCd3Z6SjFvSzFXbllzMVJwTVNOY1ZPc1ZseDJ6YS9mT1J6M29NYnh4TGdsSzMxU1c3NjVNWlVyWWlLK3JDOFhFaHNNa2JMSlNIcXhKYlFWYnZtL3dic1FyQWoxbUVsajhjbk9FY1p2NUhJOHJoUElyaTQyeU1hbXpWSXhXWW9hWU5PV0x4WHk1SUhZc3ZKcUJMc1cwaWs4eDlOZDhTcDduUGhqempLYjlQeFVVbCthS3BZQWVJaE9XZnNGT2pSMFFHM3lYcmJDalM0S1ZlaHhZbW1MZ0ZIczhyeWNsd1h2VUJzRkk4bzVLZUVwSll2U2RPSjlTQ0dNODB5a1lNczFxK0dTRVZmN2VrTUZRU2pjeGRSMElncUtFRk92OE5Ra3ZXZHAxbVpXc25IbnZNY2E0MFYxTm5nQWFVRndtRDhEeGVyc3k4R0IiLCJjaWQiOiJNUT09OlptVjBZMmhCYzNObGRBPT06T0dWbE4yWmhZelV0WlRjek15MDBPR0prTFRneFl6a3RaV1kzTkRsbFlXSTVNMll6In0.z9Nm_csmyHGBPGdEGgiyUV7pLWes0KE2IK82BHtCOS8XBerJrGb_wqNCgcgph6Zx7j84FwaVSH190FQ98FhJORgVCQ8u187hz1iPjXne9GEclR5Xr9_fSb9ZNK8VNTJvCdevJO5uT7Jkkc_-2U8DKUDDOj_Wqby8uStoSSs0P0idQ4pAazFYTy_Dbl0ltJsz6xc3YxwXk3yk0P1Ys5zYN0ueBznUMEJ6-YXpafAS5kn_iN8zU3It3Q2AgS0ER_M9AzeBHXZmST2MkaXXo3s_kuVxCZEtGRWkv8gmI3XZ5dprJ6x6keQSZ2ApSrxtmhswq2hPcqSQXF1gIFTTSSzg8A\",\"https://w3id.org/edc/v0.0.1/ns/id\":\"e5c59912-b88a-4c42-9766-9fa593b72603\",\"https://w3id.org/edc/v0.0.1/ns/authKey\":\"Authorization\"}}},\"type\":\"TransferProcessStarted\"}"; diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/logic/util/EDCRequestBodyBuilder.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/logic/util/EDCRequestBodyBuilder.java index 04eeb365..f2445f26 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/logic/util/EDCRequestBodyBuilder.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/logic/util/EDCRequestBodyBuilder.java @@ -192,19 +192,16 @@ public JsonNode buildDSPDataPullRequestBody(Partner partner, String contractID, body.put("contractId", contractID); body.put("assetId", assetId); body.put("protocol", "dataspace-protocol-http"); + body.put("managedResources", false); + var dataDestination = MAPPER.createObjectNode(); dataDestination.put("type", "HttpProxy"); body.set("dataDestination", dataDestination); - var callbackAddress = MAPPER.createObjectNode(); - callbackAddress.put("uri", variablesService.getEdrEndpoint()); - callbackAddress.put("transactional", false); - var events = MAPPER.createArrayNode(); - events.add("contract.negotiation"); - events.add("transfer.process"); - callbackAddress.set("events", events); - var callbackAddresses = MAPPER.createArrayNode(); - callbackAddresses.add(callbackAddress); - body.set("callbackAddresses", callbackAddresses); + + var privateProperties = MAPPER.createObjectNode(); + privateProperties.put("receiverHttpEndpoint", variablesService.getEdrEndpoint()); + body.set("privateProperties", privateProperties); + log.info(body.toPrettyString()); return body; } diff --git a/local/tractus-x-edc/config/customer/control-plane.properties b/local/tractus-x-edc/config/customer/control-plane.properties index 15039642..c791fef2 100644 --- a/local/tractus-x-edc/config/customer/control-plane.properties +++ b/local/tractus-x-edc/config/customer/control-plane.properties @@ -18,6 +18,9 @@ edc.participant.id=BPNL4444444444XX edc.api.auth.key= ${EDC_API_PW} edc.dsp.callback.address=http://customer-control-plane:8184/api/v1/dsp +edc.receiver.http.dynamic.endpoint=http://customer-backend:8081/catena/edrendpoint +edc.receiver.http.dynamic.auth-key=x-api-key +edc.receiver.http.dynamic.auth-code=password edc.hostname=customer-control-plane tx.ssi.oauth.token.url=http://keycloak:8080/realms/miw_test/protocol/openid-connect/token diff --git a/local/tractus-x-edc/config/supplier/control-plane.properties b/local/tractus-x-edc/config/supplier/control-plane.properties index 0661eb86..67c4d503 100644 --- a/local/tractus-x-edc/config/supplier/control-plane.properties +++ b/local/tractus-x-edc/config/supplier/control-plane.properties @@ -20,6 +20,9 @@ edc.participant.id=BPNL1234567890ZZ edc.api.auth.key= ${EDC_API_PW} edc.dsp.callback.address=http://supplier-control-plane:9184/api/v1/dsp +edc.receiver.http.dynamic.endpoint=http://supplier-backend:8082/catena/edrendpoint +edc.receiver.http.dynamic.auth-key=x-api-key +edc.receiver.http.dynamic.auth-code=password edc.hostname=supplier-control-plane tx.ssi.oauth.token.url=http://keycloak:8080/realms/miw_test/protocol/openid-connect/token From 70121e3337e9abd7d95e4f360a92a3468dd6fa53 Mon Sep 17 00:00:00 2001 From: Ernst-Christoph Schrewe Date: Tue, 28 Nov 2023 15:48:55 +0100 Subject: [PATCH 06/11] feat: merge --- .github/workflows/chart-release.yaml | 52 ++ DEPENDENCIES_BACKEND | 14 +- backend/DEPENDENCIES | 14 +- backend/pom.xml | 2 +- .../puris/backend/PurisApplication.java | 3 +- .../api/logic/service/VariablesService.java | 5 + .../logic/dto/DataAddressPropertiesDto.java | 14 + .../edc/logic/util/EDCRequestBodyBuilder.java | 2 + .../common/security/SecurityConfig.java | 67 ++ .../security/domain/ApiKeyAuthentication.java | 74 +++ .../logic/ApiKeyAuthenticationFilter.java | 56 ++ .../logic/ApiKeyAuthenticationProvider.java | 62 ++ .../masterdata/domain/model/Material.java | 7 +- .../src/main/resources/application.properties | 2 +- .../backend/common/security/ApiKeyTest.java | 76 +++ .../security/annotation/WithMockApiKey.java | 32 + .../WithMockApiKeySecurityContextFactory.java | 47 ++ .../controller/StockViewControllerTest.java | 140 +++++ .../domain/MaterialRepositoryTest.java | 62 ++ .../masterdata/logic/MaterialServiceTest.java | 83 +++ frontend/.env | 19 +- frontend/.env.dockerbuild | 1 + frontend/src/config.json | 1 + frontend/src/views/StockView.vue | 571 ++++++++++-------- frontend/src/views/stock/PartnerStockSFC.vue | 51 +- local/docker-compose.yaml | 3 +- local/generate-keys.sh | 2 + .../config/customer/control-plane.properties | 9 +- .../config/customer/puris-backend.properties | 1 + .../config/default/logging.properties | 2 +- .../config/supplier/control-plane.properties | 9 +- .../config/supplier/puris-backend.properties | 1 + 32 files changed, 1179 insertions(+), 305 deletions(-) create mode 100644 .github/workflows/chart-release.yaml create mode 100644 backend/src/main/java/org/eclipse/tractusx/puris/backend/common/security/SecurityConfig.java create mode 100644 backend/src/main/java/org/eclipse/tractusx/puris/backend/common/security/domain/ApiKeyAuthentication.java create mode 100644 backend/src/main/java/org/eclipse/tractusx/puris/backend/common/security/logic/ApiKeyAuthenticationFilter.java create mode 100644 backend/src/main/java/org/eclipse/tractusx/puris/backend/common/security/logic/ApiKeyAuthenticationProvider.java create mode 100644 backend/src/test/java/org/eclipse/tractusx/puris/backend/common/security/ApiKeyTest.java create mode 100644 backend/src/test/java/org/eclipse/tractusx/puris/backend/common/security/annotation/WithMockApiKey.java create mode 100644 backend/src/test/java/org/eclipse/tractusx/puris/backend/common/security/annotation/WithMockApiKeySecurityContextFactory.java create mode 100644 backend/src/test/java/org/eclipse/tractusx/puris/backend/stock/controller/StockViewControllerTest.java create mode 100644 backend/src/test/java/org/eclipse/tractusx/puris/backend/stock/masterdata/domain/MaterialRepositoryTest.java create mode 100644 backend/src/test/java/org/eclipse/tractusx/puris/backend/stock/masterdata/logic/MaterialServiceTest.java diff --git a/.github/workflows/chart-release.yaml b/.github/workflows/chart-release.yaml new file mode 100644 index 00000000..a07b90d3 --- /dev/null +++ b/.github/workflows/chart-release.yaml @@ -0,0 +1,52 @@ +############################################################### +# Copyright (c) 2021, 2023 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 +############################################################### + +name: Release Chart + +on: + # only manually + workflow_dispatch: + +jobs: + release: + # depending on default permission settings for your org (contents being read-only or read-write for workloads), you will have to add permissions + # see: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#modifying-the-permissions-for-the-github_token + permissions: + contents: write + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + fetch-depth: 0 + + - name: Configure Git + run: | + git config user.name "$GITHUB_ACTOR" + git config user.email "$GITHUB_ACTOR@users.noreply.github.com" + + - name: Install Helm + uses: azure/setup-helm@5119fcb9089d432beecbf79bb2c7915207344b78 #v3.5 + with: + version: v3.8.1 + + - name: Run chart-releaser + uses: helm/chart-releaser-action@a917fd15b20e8b64b94d9158ad54cd6345335584 #v1.6.0 + env: + CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" diff --git a/DEPENDENCIES_BACKEND b/DEPENDENCIES_BACKEND index 9c8dc632..133ea933 100644 --- a/DEPENDENCIES_BACKEND +++ b/DEPENDENCIES_BACKEND @@ -17,9 +17,9 @@ maven/mavencentral/com.zaxxer/HikariCP/5.0.1, Apache-2.0, approved, clearlydefin maven/mavencentral/io.micrometer/micrometer-commons/1.11.4, Apache-2.0 AND (Apache-2.0 AND MIT), approved, #9243 maven/mavencentral/io.micrometer/micrometer-observation/1.11.4, Apache-2.0, approved, #9242 maven/mavencentral/io.smallrye/jandex/3.0.5, Apache-2.0, approved, clearlydefined -maven/mavencentral/io.swagger.core.v3/swagger-annotations-jakarta/2.2.9, Apache-2.0, approved, #5947 -maven/mavencentral/io.swagger.core.v3/swagger-core-jakarta/2.2.9, Apache-2.0, approved, #5929 -maven/mavencentral/io.swagger.core.v3/swagger-models-jakarta/2.2.9, Apache-2.0, approved, #5919 +maven/mavencentral/io.swagger.core.v3/swagger-annotations-jakarta/2.2.15, Apache-2.0, approved, #5947 +maven/mavencentral/io.swagger.core.v3/swagger-core-jakarta/2.2.15, Apache-2.0, approved, #5929 +maven/mavencentral/io.swagger.core.v3/swagger-models-jakarta/2.2.15, Apache-2.0, approved, #5919 maven/mavencentral/jakarta.activation/jakarta.activation-api/2.1.2, EPL-2.0 OR BSD-3-Clause OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jaf maven/mavencentral/jakarta.annotation/jakarta.annotation-api/2.1.1, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.ca maven/mavencentral/jakarta.inject/jakarta.inject-api/2.0.1, Apache-2.0, approved, clearlydefined @@ -67,9 +67,9 @@ maven/mavencentral/org.projectlombok/lombok/1.18.30, MIT AND LicenseRef-Public-D maven/mavencentral/org.skyscreamer/jsonassert/1.5.1, Apache-2.0, approved, clearlydefined maven/mavencentral/org.slf4j/jul-to-slf4j/2.0.9, MIT, approved, #7698 maven/mavencentral/org.slf4j/slf4j-api/2.0.9, MIT, approved, #5915 -maven/mavencentral/org.springdoc/springdoc-openapi-starter-common/2.1.0, Apache-2.0, approved, clearlydefined -maven/mavencentral/org.springdoc/springdoc-openapi-starter-webmvc-api/2.1.0, Apache-2.0, approved, clearlydefined -maven/mavencentral/org.springdoc/springdoc-openapi-starter-webmvc-ui/2.1.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springdoc/springdoc-openapi-starter-common/2.2.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springdoc/springdoc-openapi-starter-webmvc-api/2.2.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springdoc/springdoc-openapi-starter-webmvc-ui/2.2.0, Apache-2.0, approved, clearlydefined maven/mavencentral/org.springframework.boot/spring-boot-autoconfigure/3.1.4, Apache-2.0, approved, #9341 maven/mavencentral/org.springframework.boot/spring-boot-configuration-processor/3.1.4, Apache-2.0, approved, #11406 maven/mavencentral/org.springframework.boot/spring-boot-starter-aop/3.1.4, Apache-2.0, approved, #9338 @@ -106,6 +106,6 @@ maven/mavencentral/org.springframework/spring-test/6.0.12, Apache-2.0, approved, maven/mavencentral/org.springframework/spring-tx/6.0.12, Apache-2.0, approved, #5926 maven/mavencentral/org.springframework/spring-web/6.0.12, Apache-2.0, approved, #5942 maven/mavencentral/org.springframework/spring-webmvc/6.0.12, Apache-2.0, approved, #5944 -maven/mavencentral/org.webjars/swagger-ui/4.18.2, Apache-2.0, approved, #7850 +maven/mavencentral/org.webjars/swagger-ui/5.2.0, Apache-2.0, approved, #10221 maven/mavencentral/org.xmlunit/xmlunit-core/2.9.1, Apache-2.0, approved, #6272 maven/mavencentral/org.yaml/snakeyaml/2.2, Apache-2.0 AND (Apache-2.0 OR BSD-3-Clause OR EPL-1.0 OR GPL-2.0-or-later OR LGPL-2.1-or-later), approved, #10232 diff --git a/backend/DEPENDENCIES b/backend/DEPENDENCIES index 9c8dc632..133ea933 100644 --- a/backend/DEPENDENCIES +++ b/backend/DEPENDENCIES @@ -17,9 +17,9 @@ maven/mavencentral/com.zaxxer/HikariCP/5.0.1, Apache-2.0, approved, clearlydefin maven/mavencentral/io.micrometer/micrometer-commons/1.11.4, Apache-2.0 AND (Apache-2.0 AND MIT), approved, #9243 maven/mavencentral/io.micrometer/micrometer-observation/1.11.4, Apache-2.0, approved, #9242 maven/mavencentral/io.smallrye/jandex/3.0.5, Apache-2.0, approved, clearlydefined -maven/mavencentral/io.swagger.core.v3/swagger-annotations-jakarta/2.2.9, Apache-2.0, approved, #5947 -maven/mavencentral/io.swagger.core.v3/swagger-core-jakarta/2.2.9, Apache-2.0, approved, #5929 -maven/mavencentral/io.swagger.core.v3/swagger-models-jakarta/2.2.9, Apache-2.0, approved, #5919 +maven/mavencentral/io.swagger.core.v3/swagger-annotations-jakarta/2.2.15, Apache-2.0, approved, #5947 +maven/mavencentral/io.swagger.core.v3/swagger-core-jakarta/2.2.15, Apache-2.0, approved, #5929 +maven/mavencentral/io.swagger.core.v3/swagger-models-jakarta/2.2.15, Apache-2.0, approved, #5919 maven/mavencentral/jakarta.activation/jakarta.activation-api/2.1.2, EPL-2.0 OR BSD-3-Clause OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jaf maven/mavencentral/jakarta.annotation/jakarta.annotation-api/2.1.1, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.ca maven/mavencentral/jakarta.inject/jakarta.inject-api/2.0.1, Apache-2.0, approved, clearlydefined @@ -67,9 +67,9 @@ maven/mavencentral/org.projectlombok/lombok/1.18.30, MIT AND LicenseRef-Public-D maven/mavencentral/org.skyscreamer/jsonassert/1.5.1, Apache-2.0, approved, clearlydefined maven/mavencentral/org.slf4j/jul-to-slf4j/2.0.9, MIT, approved, #7698 maven/mavencentral/org.slf4j/slf4j-api/2.0.9, MIT, approved, #5915 -maven/mavencentral/org.springdoc/springdoc-openapi-starter-common/2.1.0, Apache-2.0, approved, clearlydefined -maven/mavencentral/org.springdoc/springdoc-openapi-starter-webmvc-api/2.1.0, Apache-2.0, approved, clearlydefined -maven/mavencentral/org.springdoc/springdoc-openapi-starter-webmvc-ui/2.1.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springdoc/springdoc-openapi-starter-common/2.2.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springdoc/springdoc-openapi-starter-webmvc-api/2.2.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springdoc/springdoc-openapi-starter-webmvc-ui/2.2.0, Apache-2.0, approved, clearlydefined maven/mavencentral/org.springframework.boot/spring-boot-autoconfigure/3.1.4, Apache-2.0, approved, #9341 maven/mavencentral/org.springframework.boot/spring-boot-configuration-processor/3.1.4, Apache-2.0, approved, #11406 maven/mavencentral/org.springframework.boot/spring-boot-starter-aop/3.1.4, Apache-2.0, approved, #9338 @@ -106,6 +106,6 @@ maven/mavencentral/org.springframework/spring-test/6.0.12, Apache-2.0, approved, maven/mavencentral/org.springframework/spring-tx/6.0.12, Apache-2.0, approved, #5926 maven/mavencentral/org.springframework/spring-web/6.0.12, Apache-2.0, approved, #5942 maven/mavencentral/org.springframework/spring-webmvc/6.0.12, Apache-2.0, approved, #5944 -maven/mavencentral/org.webjars/swagger-ui/4.18.2, Apache-2.0, approved, #7850 +maven/mavencentral/org.webjars/swagger-ui/5.2.0, Apache-2.0, approved, #10221 maven/mavencentral/org.xmlunit/xmlunit-core/2.9.1, Apache-2.0, approved, #6272 maven/mavencentral/org.yaml/snakeyaml/2.2, Apache-2.0 AND (Apache-2.0 OR BSD-3-Clause OR EPL-1.0 OR GPL-2.0-or-later OR LGPL-2.1-or-later), approved, #10232 diff --git a/backend/pom.xml b/backend/pom.xml index 097e8a9a..ad0667da 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -38,7 +38,7 @@ PURIS Backend 11 - 2.1.0 + 2.2.0 2.7.5 8.0.0.Final 2.2 diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/PurisApplication.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/PurisApplication.java index 7af0fee2..9625ce2b 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/PurisApplication.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/PurisApplication.java @@ -24,10 +24,9 @@ import org.modelmapper.config.Configuration; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; import org.springframework.context.annotation.Bean; -@SpringBootApplication(exclude = SecurityAutoConfiguration.class) +@SpringBootApplication public class PurisApplication { public static void main(String[] args) { diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/api/logic/service/VariablesService.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/api/logic/service/VariablesService.java index 3010c0a4..d1e1e280 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/api/logic/service/VariablesService.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/api/logic/service/VariablesService.java @@ -146,6 +146,11 @@ public class VariablesService { * The country in which your ownDefaultBpna-address is located. */ private String ownDefaultCountry; + /** + * The key for accessing the api. + */ + @Value("${puris.api.key}") + private String apiKey; /** * Returns the asset-id as defined in the properties file for the given api method diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/logic/dto/DataAddressPropertiesDto.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/logic/dto/DataAddressPropertiesDto.java index 662bd079..0b483c59 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/logic/dto/DataAddressPropertiesDto.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/logic/dto/DataAddressPropertiesDto.java @@ -62,4 +62,18 @@ public class DataAddressPropertiesDto { @JsonProperty("type") @NotNull private DT_DataAddressTypeEnum type; + + /** + * Defines the auth key used in httpProxy e.g. "X-API-KEY" + */ + @JsonProperty("authKey") + @NotNull + private String authKey; + + /** + * Defines the auth code used in httpProxy e.g. "some value" + */ + @JsonProperty("authCode") + @NotNull + private String authCode; } diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/logic/util/EDCRequestBodyBuilder.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/logic/util/EDCRequestBodyBuilder.java index f2445f26..53557a74 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/logic/util/EDCRequestBodyBuilder.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/edc/logic/util/EDCRequestBodyBuilder.java @@ -101,6 +101,8 @@ public JsonNode buildDSPCreateAssetBody(DT_ApiMethodEnum apiMethod) { dataAddress.put("proxyPath", "true"); dataAddress.put("proxyBody", "true"); dataAddress.put("proxyMethod", "true"); + dataAddress.put("authKey", "x-api-key"); + dataAddress.put("authCode", variablesService.getApiKey()); body.set("dataAddress", dataAddress); return body; } diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/security/SecurityConfig.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/security/SecurityConfig.java new file mode 100644 index 00000000..1e90210d --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/security/SecurityConfig.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2023 Volkswagen AG + * Copyright (c) 2023 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.puris.backend.common.security; + + +import lombok.AllArgsConstructor; +import org.eclipse.tractusx.puris.backend.common.security.logic.ApiKeyAuthenticationFilter; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.Customizer; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +@Configuration +@EnableWebSecurity +@AllArgsConstructor +public class SecurityConfig { + + private final ApiKeyAuthenticationFilter apiKeyAuthenticationFilter; + + /** + * Configuration of API Key Authentication for all routes except docker + */ + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http.csrf(AbstractHttpConfigurer::disable) + .authorizeHttpRequests( + // any request in spring context + (authorizeHttpRequests) -> authorizeHttpRequests + .requestMatchers("/stockView/**", "/partners/**", "/materials/**", "/materialpartnerrelations/**", "/product-stock/**", "/edrendpoint/**").authenticated() + .requestMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll() + ) + .httpBasic( + AbstractHttpConfigurer::disable + ) + .sessionManagement( + (sessionManagement) -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS) + ) + .cors(Customizer.withDefaults()); + + http.addFilterBefore(apiKeyAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); + + return http.build(); + } + +} diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/security/domain/ApiKeyAuthentication.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/security/domain/ApiKeyAuthentication.java new file mode 100644 index 00000000..00865a19 --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/security/domain/ApiKeyAuthentication.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2023 Volkswagen AG + * Copyright (c) 2023 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.puris.backend.common.security.domain; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; + +import java.util.Collection; + +/** + * Authentication holding apiKey as principal and authenticated flag. No authorities given as the key is set per config. + */ +@Getter +@Setter +@AllArgsConstructor +public class ApiKeyAuthentication implements Authentication { + + private final String apiKey; + private final boolean authenticatedFlag; + @Override + public Collection getAuthorities() { + return null; + } + + @Override + public Object getCredentials() { + return null; + } + + @Override + public Object getDetails() { + return null; + } + + @Override + public Object getPrincipal() { + return apiKey; + } + + @Override + public boolean isAuthenticated() { + return authenticatedFlag; + } + + @Override + public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException { + + } + + @Override + public String getName() { + return null; + } +} diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/security/logic/ApiKeyAuthenticationFilter.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/security/logic/ApiKeyAuthenticationFilter.java new file mode 100644 index 00000000..c53aa9e8 --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/security/logic/ApiKeyAuthenticationFilter.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2023 Volkswagen AG + * Copyright (c) 2023 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.puris.backend.common.security.logic; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.AllArgsConstructor; +import org.eclipse.tractusx.puris.backend.common.security.domain.ApiKeyAuthentication; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +/** + * Authentication filter that checks if X-API-KEY header is given and set to value from config + */ +@Component +@AllArgsConstructor +public class ApiKeyAuthenticationFilter extends OncePerRequestFilter { + + public final String API_KEY_HEADER = "X-API-KEY"; + private final ApiKeyAuthenticationProvider apiKeyAuthenticationProvider; + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + String headerKey = request.getHeader(API_KEY_HEADER); + + if (headerKey != null){ + ApiKeyAuthentication apiKeyAuthentication = new ApiKeyAuthentication(headerKey, false); + Authentication authenticatedObject = apiKeyAuthenticationProvider.authenticate(apiKeyAuthentication); + SecurityContextHolder.getContext().setAuthentication(authenticatedObject); + } + + filterChain.doFilter(request,response); + } +} diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/security/logic/ApiKeyAuthenticationProvider.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/security/logic/ApiKeyAuthenticationProvider.java new file mode 100644 index 00000000..d0bbf545 --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/security/logic/ApiKeyAuthenticationProvider.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2023 Volkswagen AG + * Copyright (c) 2023 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.puris.backend.common.security.logic; + +import lombok.extern.slf4j.Slf4j; +import org.eclipse.tractusx.puris.backend.common.security.domain.ApiKeyAuthentication; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.stereotype.Component; + +/** + * Authentication provider that sets authenticated if header is given and matches config + */ +@Component +@Slf4j +public class ApiKeyAuthenticationProvider implements org.springframework.security.authentication.AuthenticationProvider { + + @Value("${puris.api.key}") + private String apiKey; + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + ApiKeyAuthentication apiKeyAuthentication = (ApiKeyAuthentication) authentication; + + String headerKey = apiKeyAuthentication.getApiKey(); + + if (headerKey == null){ + throw new AuthenticationCredentialsNotFoundException("X-API-KEY has not been set"); + } + + if (apiKey.equals(headerKey)){ + log.info("Request has valid key."); + return new ApiKeyAuthentication(headerKey, true); + } + throw new BadCredentialsException("API key is wrong."); + } + + @Override + public boolean supports(Class authentication) { + return ApiKeyAuthentication.class.equals(authentication); + } +} diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/domain/model/Material.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/domain/model/Material.java index 614e2d47..cecf869f 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/domain/model/Material.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/masterdata/domain/model/Material.java @@ -25,10 +25,7 @@ import jakarta.persistence.Id; import jakarta.persistence.OneToMany; import jakarta.persistence.Table; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; +import lombok.*; import java.util.Objects; import java.util.Set; @@ -70,6 +67,8 @@ @Setter @ToString @NoArgsConstructor +@AllArgsConstructor +@Builder public class Material { /** diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index 07625fdf..1c6dc77a 100755 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -8,7 +8,7 @@ puris.request.serverendpoint=${PURIS_REQUEST_SERVERENDPOINT:http://customer-back puris.request.apiassetid=${PURIS_REQUEST_APIASSETID:request-api-asset} puris.response.serverendpoint=${PURIS_RESPONSE_SERVERENDPOINT:http://customer-backend:8081/catena/product-stock/response} puris.response.apiassetid=${PURIS_RESPONSE_APIASSETID:response-api-asset} - +puris.api.key=${PURIS_API_KEY:test} # DB Configuration spring.datasource.driver-class-name=${DATASOURCE_DRIVERCLASSNAME:org.hsqldb.jdbc.JDBCDriver} diff --git a/backend/src/test/java/org/eclipse/tractusx/puris/backend/common/security/ApiKeyTest.java b/backend/src/test/java/org/eclipse/tractusx/puris/backend/common/security/ApiKeyTest.java new file mode 100644 index 00000000..1cb4e878 --- /dev/null +++ b/backend/src/test/java/org/eclipse/tractusx/puris/backend/common/security/ApiKeyTest.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2023 Volkswagen AG + * Copyright (c) 2023 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.puris.backend.common.security; + +import org.eclipse.tractusx.puris.backend.common.security.annotation.WithMockApiKey; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.web.servlet.MockMvc; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + + +@SpringBootTest +@AutoConfigureMockMvc +public class ApiKeyTest { + + @Autowired + private MockMvc mockMvc; + + @Test + void StockViewController_MaterialsRequestWithoutAuthHeader_ShouldReturn403() throws Exception { + this.mockMvc.perform( + get("/stockView/materials")) + .andDo(print()) + .andExpect(status().is(403)); + } + + @Test + void StockViewController_MaterialsRequestWithAuthHeader_ShouldReturn200() throws Exception { + this.mockMvc.perform( + get("/stockView/materials") + .header("X-API-KEY", "test") + ) + .andExpect(status().is(200)); + } + + @Test + @WithMockApiKey(apiKey = "test2") + void StockViewController_MaterialsRequestWithWrongAnnotationAuth_ShouldReturn403() throws Exception { + this.mockMvc.perform( + get("/stockView/materials") + ) + .andExpect(status().is(403)); + } + + @Test + @WithMockApiKey + void StockViewController_MaterialsRequestWithCorrectAnnotationAuth_ShouldReturn200() throws Exception { + this.mockMvc.perform( + get("/stockView/materials") + ) + .andExpect(status().is(200)); + } + +} diff --git a/backend/src/test/java/org/eclipse/tractusx/puris/backend/common/security/annotation/WithMockApiKey.java b/backend/src/test/java/org/eclipse/tractusx/puris/backend/common/security/annotation/WithMockApiKey.java new file mode 100644 index 00000000..43d17f94 --- /dev/null +++ b/backend/src/test/java/org/eclipse/tractusx/puris/backend/common/security/annotation/WithMockApiKey.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023 Volkswagen AG + * Copyright (c) 2023 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.puris.backend.common.security.annotation; + +import org.springframework.security.test.context.support.WithSecurityContext; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +@WithSecurityContext(factory = WithMockApiKeySecurityContextFactory.class) +public @interface WithMockApiKey { + + String apiKey() default "test"; +} diff --git a/backend/src/test/java/org/eclipse/tractusx/puris/backend/common/security/annotation/WithMockApiKeySecurityContextFactory.java b/backend/src/test/java/org/eclipse/tractusx/puris/backend/common/security/annotation/WithMockApiKeySecurityContextFactory.java new file mode 100644 index 00000000..be141e56 --- /dev/null +++ b/backend/src/test/java/org/eclipse/tractusx/puris/backend/common/security/annotation/WithMockApiKeySecurityContextFactory.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2023 Volkswagen AG + * Copyright (c) 2023 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.puris.backend.common.security.annotation; + +import org.eclipse.tractusx.puris.backend.common.security.domain.ApiKeyAuthentication; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.test.context.support.WithSecurityContextFactory; + +public class WithMockApiKeySecurityContextFactory implements WithSecurityContextFactory { + + @Value("${puris.api.key}") + String apiKeyConfig; + + @Override + public SecurityContext createSecurityContext(WithMockApiKey annotation) { + SecurityContext context = SecurityContextHolder.createEmptyContext(); + + if (this.apiKeyConfig.equals((annotation.apiKey()))){ + ApiKeyAuthentication auth = new ApiKeyAuthentication(annotation.apiKey(), true); + context.setAuthentication(auth); + } else { + ApiKeyAuthentication auth = new ApiKeyAuthentication(annotation.apiKey(), false); + context.setAuthentication(auth); + } + + return context; + } +} diff --git a/backend/src/test/java/org/eclipse/tractusx/puris/backend/stock/controller/StockViewControllerTest.java b/backend/src/test/java/org/eclipse/tractusx/puris/backend/stock/controller/StockViewControllerTest.java new file mode 100644 index 00000000..5ff4fc9f --- /dev/null +++ b/backend/src/test/java/org/eclipse/tractusx/puris/backend/stock/controller/StockViewControllerTest.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2023 Volkswagen AG + * Copyright (c) 2023 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.puris.backend.stock.controller; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.eclipse.tractusx.puris.backend.common.security.SecurityConfig; +import org.eclipse.tractusx.puris.backend.common.security.annotation.WithMockApiKey; +import org.eclipse.tractusx.puris.backend.common.security.logic.ApiKeyAuthenticationProvider; +import org.eclipse.tractusx.puris.backend.masterdata.domain.model.Material; +import org.eclipse.tractusx.puris.backend.masterdata.logic.service.MaterialPartnerRelationService; +import org.eclipse.tractusx.puris.backend.masterdata.logic.service.MaterialService; +import org.eclipse.tractusx.puris.backend.masterdata.logic.service.PartnerService; +import org.eclipse.tractusx.puris.backend.stock.logic.dto.FrontendMaterialDto; +import org.eclipse.tractusx.puris.backend.stock.logic.service.MaterialStockService; +import org.eclipse.tractusx.puris.backend.stock.logic.service.PartnerProductStockService; +import org.eclipse.tractusx.puris.backend.stock.logic.service.ProductStockRequestApiService; +import org.eclipse.tractusx.puris.backend.stock.logic.service.ProductStockService; +import org.junit.jupiter.api.Test; +import org.modelmapper.ModelMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(StockViewController.class) +@Import({ SecurityConfig.class, ApiKeyAuthenticationProvider.class }) +class StockViewControllerTest { + + @Autowired + private MockMvc mockMvc; + + @MockBean + private MaterialService materialService; + + @MockBean + private ProductStockService productStockService; + + @MockBean + private MaterialStockService materialStockService; + + @MockBean + private PartnerProductStockService partnerProductStockService; + + @MockBean + private ProductStockRequestApiService productStockRequestApiService; + + @MockBean + private PartnerService partnerService; + + @MockBean + private MaterialPartnerRelationService mprService; + + @MockBean + private ModelMapper modelMapper; + + @Test + @WithMockApiKey + void getMaterials_GivenTwoMaterials_ReturnsListOfMaterials() throws Exception{ + + // given + Material material1 = Material.builder() + .ownMaterialNumber("MNR-4711") + .materialFlag(true) + .name("Test Material 1") + .materialNumberCx("urn:uuid:ccfffbba-cfa0-49c4-bc9c-4e13d7a4ac7a") + .build(); + Material material2 = Material.builder() + .ownMaterialNumber("MNR-4712") + .materialFlag(true) + .name("Test Material 2") + .build(); + List allMaterials = new ArrayList<>(); + allMaterials.add(material1); + allMaterials.add(material2); + when(materialService.findAllMaterials()).thenReturn(allMaterials); + + this.mockMvc.perform( + get("/stockView/materials") + ) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andDo(result -> { + String jsonResponse = result.getResponse().getContentAsString(); + ObjectMapper objectMapper = new ObjectMapper(); + + List returnedMaterials = objectMapper.readValue(jsonResponse, new TypeReference<>() { + }); + + assertAll( + () -> assertNotNull(returnedMaterials), + () -> assertEquals(2, returnedMaterials.size()) + ); + + assertAll( + () -> assertNotNull(returnedMaterials), + () -> assertEquals(2, returnedMaterials.size()) + ); + + FrontendMaterialDto returnedMaterial = returnedMaterials.stream().filter( + frontendMaterialDto -> frontendMaterialDto.getOwnMaterialNumber().equals("MNR-4711") + ).findFirst().get(); + assertAll( + () -> assertEquals("MNR-4711", returnedMaterial.getOwnMaterialNumber()), + () -> assertEquals("Test Material 1", returnedMaterial.getDescription()) + ); + }); + } + +} diff --git a/backend/src/test/java/org/eclipse/tractusx/puris/backend/stock/masterdata/domain/MaterialRepositoryTest.java b/backend/src/test/java/org/eclipse/tractusx/puris/backend/stock/masterdata/domain/MaterialRepositoryTest.java new file mode 100644 index 00000000..412c926a --- /dev/null +++ b/backend/src/test/java/org/eclipse/tractusx/puris/backend/stock/masterdata/domain/MaterialRepositoryTest.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2023 Volkswagen AG + * Copyright (c) 2023 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.puris.backend.stock.masterdata.domain; + +import org.eclipse.tractusx.puris.backend.masterdata.domain.model.Material; +import org.eclipse.tractusx.puris.backend.masterdata.domain.repository.MaterialRepository; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; + +import java.util.HashSet; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +@DataJpaTest +public class MaterialRepositoryTest { + + @Autowired + private MaterialRepository materialRepository; + + @Test + void findAllByMaterialFlagTrue_ReturnsListOfMaterials() { + // Given + Material material1 = new Material(true, false, "MNR-123", "uuid-value", "Test Material 1", new HashSet<>()); + + Material material2 = new Material(true, false, "MNR-234", "uuid-value-2", "Test Material 2", new HashSet<>()); + + // would be more realistic with relationship, but didn't add it here as we just want to test the MaterialRepo + Material product = new Material(false, true, "MNR-456", "uuid-value-2", "Test Product 1", new HashSet<>()); + + materialRepository.save(material1); + materialRepository.save(material2); + materialRepository.save(product); + + // When + List materials = materialRepository.findAllByMaterialFlagTrue(); + + // Then + assertNotNull(materials); + assertEquals(2, materials.size()); + assertTrue(materials.contains(material1)); + assertTrue(materials.contains(material2)); + } +} diff --git a/backend/src/test/java/org/eclipse/tractusx/puris/backend/stock/masterdata/logic/MaterialServiceTest.java b/backend/src/test/java/org/eclipse/tractusx/puris/backend/stock/masterdata/logic/MaterialServiceTest.java new file mode 100644 index 00000000..a8944774 --- /dev/null +++ b/backend/src/test/java/org/eclipse/tractusx/puris/backend/stock/masterdata/logic/MaterialServiceTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2023 Volkswagen AG + * Copyright (c) 2023 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.puris.backend.stock.masterdata.logic; + +import org.eclipse.tractusx.puris.backend.masterdata.domain.model.Material; +import org.eclipse.tractusx.puris.backend.masterdata.domain.repository.MaterialRepository; +import org.eclipse.tractusx.puris.backend.masterdata.logic.service.MaterialServiceImpl; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.HashSet; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +public class MaterialServiceTest { + + @Mock + private MaterialRepository materialRepository; + + @InjectMocks + private MaterialServiceImpl materialService; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + void create_WhenMaterialDoesNotExist_ReturnsCreatedMaterial() { + // Given + Material material = new Material(true, false, "MNR-123", "uuid-value", "Test Material", new HashSet<>()); + + // When + when(materialRepository.findById(material.getOwnMaterialNumber())).thenReturn(Optional.empty()); + when(materialRepository.save(material)).thenReturn(material); + + // Then + Material createdMaterial = materialService.create(material); + + assertNotNull(createdMaterial); + assertEquals(material, createdMaterial); + verify(materialRepository, times(1)).findById(material.getOwnMaterialNumber()); + verify(materialRepository, times(1)).save(material); + } + + @Test + void create_WhenMaterialExists_ReturnsNull() { + // Given + Material material = new Material(true, false, "MNR-123", "uuid-value", "Test Material", new HashSet<>()); + + // When + when(materialRepository.findById(material.getOwnMaterialNumber())).thenReturn(Optional.of(material)); + + // Then + Material createdMaterial = materialService.create(material); + + assertNull(createdMaterial); + verify(materialRepository, times(1)).findById(material.getOwnMaterialNumber()); + verify(materialRepository, never()).save(material); + } +} diff --git a/frontend/.env b/frontend/.env index 8afd5a5d..11b19bf6 100644 --- a/frontend/.env +++ b/frontend/.env @@ -1,10 +1,11 @@ # Local Dev Environment -APP_NAME=PURIS -BACKEND_BASE_URL=http://localhost:8081/catena/ -ENDPOINT_MATERIALS=stockView/materials -ENDPOINT_PRODUCTS=stockView/products -ENDPOINT_MATERIAL_STOCKS=stockView/material-stocks -ENDPOINT_PRODUCT_STOCKS=stockView/product-stocks -ENDPOINT_CUSTOMER=stockView/customer?ownMaterialNumber= -ENDPOINT_PARTNER_PRODUCT_STOCKS=stockView/partner-product-stocks?ownMaterialNumber= -ENDPOINT_UPDATE_PARTNER_PRODUCT_STOCK=stockView/update-partner-product-stock?ownMaterialNumber= +VITE_APP_NAME=PURIS +VITE_BACKEND_BASE_URL=http://localhost:8081/catena/ +VITE_BACKEND_API_KEY=test +VITE_ENDPOINT_MATERIALS=stockView/materials +VITE_ENDPOINT_PRODUCTS=stockView/products +VITE_ENDPOINT_MATERIAL_STOCKS=stockView/material-stocks +VITE_ENDPOINT_PRODUCT_STOCKS=stockView/product-stocks +VITE_ENDPOINT_CUSTOMER=stockView/customer?ownMaterialNumber= +VITE_ENDPOINT_PARTNER_PRODUCT_STOCKS=stockView/partner-product-stocks?ownMaterialNumber= +VITE_ENDPOINT_UPDATE_PARTNER_PRODUCT_STOCK=stockView/update-partner-product-stock?ownMaterialNumber= diff --git a/frontend/.env.dockerbuild b/frontend/.env.dockerbuild index 41222c60..5f2f36ec 100644 --- a/frontend/.env.dockerbuild +++ b/frontend/.env.dockerbuild @@ -1,5 +1,6 @@ VITE_APP_NAME=\$APP_NAME VITE_BACKEND_BASE_URL=\$BACKEND_BASE_URL +VITE_BACKEND_API_KEY=\$BACKEND_API_KEY VITE_ENDPOINT_MATERIALS=\$ENDPOINT_MATERIALS VITE_ENDPOINT_PRODUCTS=\$ENDPOINT_PRODUCTS VITE_ENDPOINT_MATERIAL_STOCKS=\$ENDPOINT_MATERIAL_STOCKS diff --git a/frontend/src/config.json b/frontend/src/config.json index 57ff88ae..26c7f3cf 100644 --- a/frontend/src/config.json +++ b/frontend/src/config.json @@ -1,6 +1,7 @@ { "APP_NAME": "$APP_NAME", "BACKEND_BASE_URL": "$BACKEND_BASE_URL", + "BACKEND_API_KEY": "$BACKEND_API_KEY", "ENDPOINT_MATERIALS": "$ENDPOINT_MATERIALS", "ENDPOINT_PRODUCTS": "$ENDPOINT_PRODUCTS", "ENDPOINT_MATERIAL_STOCKS": "$ENDPOINT_MATERIAL_STOCKS", diff --git a/frontend/src/views/StockView.vue b/frontend/src/views/StockView.vue index fe1ad2aa..307ae5f4 100644 --- a/frontend/src/views/StockView.vue +++ b/frontend/src/views/StockView.vue @@ -19,278 +19,357 @@ SPDX-License-Identifier: Apache-2.0 --> diff --git a/frontend/src/views/stock/PartnerStockSFC.vue b/frontend/src/views/stock/PartnerStockSFC.vue index 2c0ef2f2..ce4c411e 100644 --- a/frontend/src/views/stock/PartnerStockSFC.vue +++ b/frontend/src/views/stock/PartnerStockSFC.vue @@ -30,8 +30,8 @@ {{ this.selectedMaterialOrProductId }}. @@ -58,12 +58,13 @@ export default { name: "PartnerStockSFC", props: { - selectedMaterialOrProductId: {type: String, required: true}, - partnerRole: {type: String, required: true}, + selectedMaterialOrProductId: { type: String, required: true }, + partnerRole: { type: String, required: true }, }, data() { return { backendURL: import.meta.env.VITE_BACKEND_BASE_URL, + backendApiKey: import.meta.env.VITE_BACKEND_API_KEY, endpointPartnerProductStocks: import.meta.env.VITE_ENDPOINT_PARTNER_PRODUCT_STOCKS, endpointUpdatePartnerProductStock: import.meta.env.VITE_ENDPOINT_UPDATE_PARTNER_PRODUCT_STOCK, availableMaterialsOrProducts: [], @@ -74,17 +75,26 @@ export default { if (this.partnerRole === "supplier") { this.getAvailableMaterials(); } - // else if (this.partnerRole === "customer") { - // this.getAvailableProducts(); - // } + // else if (this.partnerRole === "customer") { + // this.getAvailableProducts(); + // } } }, methods: { getAvailableMaterials() { - fetch(this.backendURL + this.endpointPartnerProductStocks + this.selectedMaterialOrProductId) - .then(res => res.json()) - .then(data => this.availableMaterialsOrProducts = data) - .catch(err => console.log(err)); + fetch( + this.backendURL + + this.endpointPartnerProductStocks + + this.selectedMaterialOrProductId, + { + headers: { + "X-API-KEY": this.backendApiKey, + }, + } + ) + .then((res) => res.json()) + .then((data) => (this.availableMaterialsOrProducts = data)) + .catch((err) => console.log(err)); }, // getAvailableProducts() { // fetch(this.backendURL + this.endpointPartnerProductStocks) @@ -93,11 +103,20 @@ export default { // .catch(err => console.log(err)); // }, updateMaterialOrProduct() { - fetch(this.backendURL + this.endpointUpdatePartnerProductStock + this.selectedMaterialOrProductId) - .then(res => res.json()) - .then(data => console.log(data)) - .catch(err => console.log(err)); - } + fetch( + this.backendURL + + this.endpointUpdatePartnerProductStock + + this.selectedMaterialOrProductId, + { + headers: { + "X-API-KEY": this.backendApiKey, + }, + } + ) + .then((res) => res.json()) + .then((data) => console.log(data)) + .catch((err) => console.log(err)); + }, }, }; diff --git a/local/docker-compose.yaml b/local/docker-compose.yaml index 1627d369..5fe64a5f 100644 --- a/local/docker-compose.yaml +++ b/local/docker-compose.yaml @@ -29,6 +29,7 @@ services: environment: - APP_NAME=PURIS Customer - BACKEND_BASE_URL=http://localhost:8081/catena/ + - BACKEND_API_KEY=${CUSTOMER_BACKEND_API_KEY} - ENDPOINT_MATERIALS=stockView/materials - ENDPOINT_PRODUCTS=stockView/products - ENDPOINT_MATERIAL_STOCKS=stockView/material-stocks @@ -47,7 +48,6 @@ services: depends_on: edc-customer-control-plane: condition: service_started #service_healthy - #restart: true ports: - "127.0.0.1:8081:8081" # expose port of server.port env_file: @@ -103,6 +103,7 @@ services: environment: - APP_NAME=PURIS Customer - BACKEND_BASE_URL=http://localhost:8082/catena/ + - BACKEND_API_KEY=${SUPPLIER_BACKEND_API_KEY} - ENDPOINT_MATERIALS=stockView/materials - ENDPOINT_PRODUCTS=stockView/products - ENDPOINT_MATERIAL_STOCKS=stockView/material-stocks diff --git a/local/generate-keys.sh b/local/generate-keys.sh index 1d049378..093083b0 100644 --- a/local/generate-keys.sh +++ b/local/generate-keys.sh @@ -18,11 +18,13 @@ CUSTOMER_OAUTH_CLIENT_ID=customer_private_client CUSTOMER_PRIVATE_KEY_ALIAS=customer-key CUSTOMER_PUBLIC_KEY_ALIAS=customer-cert CUSTOMER_ENCRYPTION_KEYS_ALIAS=customer-encryption-keys +CUSTOMER_BACKEND_API_KEY=`openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | head -c 32` SUPPLIER_OAUTH_SECRET_ALIAS=supplier.miw.secret SUPPLIER_OAUTH_CLIENT_ID=supplier_private_client SUPPLIER_PRIVATE_KEY_ALIAS=supplier-key SUPPLIER_PUBLIC_KEY_ALIAS=supplier-cert SUPPLIER_ENCRYPTION_KEYS_ALIAS=supplier-encryption-keys +SUPPLIER_BACKEND_API_KEY=`openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | head -c 32` KEYCLOAK_MIW_PUBLIC_CLIENT=miw_public KEYCLOAK_ADMIN=admin KEYCLOAK_ADMIN_PASSWORD=`openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | head -c 32` diff --git a/local/tractus-x-edc/config/customer/control-plane.properties b/local/tractus-x-edc/config/customer/control-plane.properties index c791fef2..0933b47c 100644 --- a/local/tractus-x-edc/config/customer/control-plane.properties +++ b/local/tractus-x-edc/config/customer/control-plane.properties @@ -17,10 +17,6 @@ web.http.protocol.path=/api/v1/dsp edc.participant.id=BPNL4444444444XX edc.api.auth.key= ${EDC_API_PW} edc.dsp.callback.address=http://customer-control-plane:8184/api/v1/dsp - -edc.receiver.http.dynamic.endpoint=http://customer-backend:8081/catena/edrendpoint -edc.receiver.http.dynamic.auth-key=x-api-key -edc.receiver.http.dynamic.auth-code=password edc.hostname=customer-control-plane tx.ssi.oauth.token.url=http://keycloak:8080/realms/miw_test/protocol/openid-connect/token @@ -46,7 +42,10 @@ edc.dataplane.selector.edchttp.sourcetypes=HttpData edc.dataplane.selector.edchttp.destinationtypes=HttpData,HttpProxy edc.dataplane.selector.edchttp.properties={"publicApiUrl" : "http://customer-data-plane:8285/api/public/"} -#edc.receiver.http.endpoint=http://customer-backend:8081/catena/edrendpoint +# backend receiver for static Endpoint Data References +edc.receiver.http.dynamic.endpoint=http://customer-backend:8081/catena/edrendpoint +edc.receiver.http.dynamic.auth-key=X-API-KEY +edc.receiver.http.dynamic.auth-code=${CUSTOMER_BACKEND_API_KEY} # Postgresql related configuration edc.datasource.asset.name=asset diff --git a/local/tractus-x-edc/config/customer/puris-backend.properties b/local/tractus-x-edc/config/customer/puris-backend.properties index 0890fd60..7c87aa05 100644 --- a/local/tractus-x-edc/config/customer/puris-backend.properties +++ b/local/tractus-x-edc/config/customer/puris-backend.properties @@ -7,6 +7,7 @@ puris.request.serverendpoint=http://customer-backend:8081/catena/product-stock/r puris.request.apiassetid=request-api-asset puris.response.serverendpoint=http://customer-backend:8081/catena/product-stock/response puris.response.apiassetid=response-api-asset +puris.api.key=${CUSTOMER_BACKEND_API_KEY} edc.controlplane.key=${EDC_API_PW} edc.controlplane.management.url=http://customer-control-plane:8181/management diff --git a/local/tractus-x-edc/config/default/logging.properties b/local/tractus-x-edc/config/default/logging.properties index 213edc48..c5f70215 100644 --- a/local/tractus-x-edc/config/default/logging.properties +++ b/local/tractus-x-edc/config/default/logging.properties @@ -1,4 +1,4 @@ -level=INFO +level=DEBUG org.eclipse.edc.level=ALL handlers=java.util.logging.ConsoleHandler java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter diff --git a/local/tractus-x-edc/config/supplier/control-plane.properties b/local/tractus-x-edc/config/supplier/control-plane.properties index 67c4d503..7ce00630 100644 --- a/local/tractus-x-edc/config/supplier/control-plane.properties +++ b/local/tractus-x-edc/config/supplier/control-plane.properties @@ -19,10 +19,6 @@ JAVA_TOOL_OPTIONS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address= edc.participant.id=BPNL1234567890ZZ edc.api.auth.key= ${EDC_API_PW} edc.dsp.callback.address=http://supplier-control-plane:9184/api/v1/dsp - -edc.receiver.http.dynamic.endpoint=http://supplier-backend:8082/catena/edrendpoint -edc.receiver.http.dynamic.auth-key=x-api-key -edc.receiver.http.dynamic.auth-code=password edc.hostname=supplier-control-plane tx.ssi.oauth.token.url=http://keycloak:8080/realms/miw_test/protocol/openid-connect/token @@ -48,7 +44,10 @@ edc.dataplane.selector.edchttp.sourcetypes=HttpData edc.dataplane.selector.edchttp.destinationtypes=HttpData,HttpProxy edc.dataplane.selector.edchttp.properties={"publicApiUrl" : "http://supplier-data-plane:9285/api/public/"} -#edc.receiver.http.endpoint=http://supplier-backend:8082/catena/edrendpoint +# puris backend receiver for dynamic Endpoint Data References +edc.receiver.http.dynamic.endpoint=http://supplier-backend:8082/catena/edrendpoint +edc.receiver.http.dynamic.auth-key=X-API-KEY +edc.receiver.http.dynamic.auth-code=${SUPPLIER_BACKEND_API_KEY} # Postgresql related configuration edc.datasource.asset.name=asset diff --git a/local/tractus-x-edc/config/supplier/puris-backend.properties b/local/tractus-x-edc/config/supplier/puris-backend.properties index ca9220ef..4fed4d54 100644 --- a/local/tractus-x-edc/config/supplier/puris-backend.properties +++ b/local/tractus-x-edc/config/supplier/puris-backend.properties @@ -7,6 +7,7 @@ puris.request.serverendpoint=http://supplier-backend:8082/catena/product-stock/r puris.request.apiassetid=request-api-asset puris.response.serverendpoint=http://supplier-backend:8082/catena/product-stock/response puris.response.apiassetid=response-api-asset +puris.api.key=${SUPPLIER_BACKEND_API_KEY} edc.controlplane.key=${EDC_API_PW} edc.controlplane.management.url=http://supplier-control-plane:9181/management From d977d6713be8efb31602c877bcf759a538d87c74 Mon Sep 17 00:00:00 2001 From: Ernst-Christoph Schrewe Date: Wed, 29 Nov 2023 15:12:08 +0100 Subject: [PATCH 07/11] fix: added test properties --- .../resources/application-customer.properties | 64 ++++++++++++++++++ .../resources/application-supplier.properties | 65 +++++++++++++++++++ .../src/test/resources/application.properties | 61 +++++++++++++++++ 3 files changed, 190 insertions(+) create mode 100755 backend/src/test/resources/application-customer.properties create mode 100644 backend/src/test/resources/application-supplier.properties create mode 100755 backend/src/test/resources/application.properties diff --git a/backend/src/test/resources/application-customer.properties b/backend/src/test/resources/application-customer.properties new file mode 100755 index 00000000..631e7abf --- /dev/null +++ b/backend/src/test/resources/application-customer.properties @@ -0,0 +1,64 @@ +# Server Config +server.port=${SERVER_PORT:8081} +my.base.url=http://host.minikube.internal +request.serverendpoint=${my.base.url}:${server.port}/catena/product-stock/request +request.apiassetid=product-stock-request-api +response.serverendpoint=${my.base.url}:${server.port}/catena/product-stock/response +response.apiassetid=product-stock-response-api +edr.endpoint=${my.base.url}:${server.port}/catena/edrendpoint +# DB Configuration +spring.datasource.driver-class-name=${DATASOURCE_DRIVERCLASSNAME:org.hsqldb.jdbc.JDBCDriver} +spring.datasource.url=${DATASOURCE_URL:jdbc:hsqldb:mem:testdb;DB_CLOSE_DELAY=-1} +spring.datasource.username=${DATASOURCE_USERNAME:sa} +spring.datasource.password=${DATASOURCE_PASSWORD:} +spring.jpa.hibernate.ddl-auto=create +# API Root +server.servlet.context-path=${API_ROOTDIR:/catena} +# EDC Config +edc.controlplane.host=${EDC_CONTROLPLANE_HOST:192.168.49.2} +edc.controlplane.data.path=/api/v1/data +edc.controlplane.data.port=${EDC_CONTROLPLANE_DATA_PORT:31944} +edc.controlplane.key=${EDC_CONTROLPLANE_KEY:password} + +edc.applydataplaneworkaround=true +edc.dataplane.public.port=31944 +minikube.ip=${MINIKUBE_IP:host.minikube.internal} +# Jackson (JSON) +#spring.jackson.default-property-inclusion=non_empty +#logging.level.org.hibernate.SQL=DEBUG +#logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE +spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true + + +# Own EDC-IDS-URL +edc.idsUrl=${EDC_IDSURL:http://customer-control-plane:8184/api/v1/ids} + +# Own BPNL +own.bpnl=${OWN_BPNL:BPNL4444444444XX} + +# Own name (self-description) +own.name=${OWN_NAME:Scenario Customer} + +# Own BPNS (optional: if this is set, then set own.default.site.name as well) +own.default.bpns=${OWN_BPNS:BPNS4444444444XY} +# Name of Site (see above) +own.default.site.name=${OWN_SITE:Hauptwerk Musterhausen} + +# If a BPNS is set, then this BPNA will be attached to it. +# Otherwise, it will be attached immediately to the BPNL (see above) +own.default.bpna=${OWN_BPNS:BPNA4444444444ZZ} +own.default.streetandnumber=${OWN_STREETANDNUMBER:Musterstrasse 35b} +own.default.zipcodeandcity=${OWN_ZIPCODEANDCITY:77777 Musterhausen} +own.default.country=${OWN_COUNTRY:Germany} + +# The number of minutes before received authentication data +# in the context of a consumer pull is removed from memory +own.edr.deletiontimer=2 + +puris.apiversion=1.0.0 +puris.demonstrator.role=customer +puris.api.key=test +# run with: +# ./mvnw spring-boot:run -Dspring-boot.run.arguments=--spring.profiles.active=customer +# alternatively: +# ./mvnw spring-boot:run -Dspring-boot.run.arguments=--spring.config.location="./src/main/resources/application-customer.properties" diff --git a/backend/src/test/resources/application-supplier.properties b/backend/src/test/resources/application-supplier.properties new file mode 100644 index 00000000..8329b32f --- /dev/null +++ b/backend/src/test/resources/application-supplier.properties @@ -0,0 +1,65 @@ +# Server Config +server.port=${SERVER_PORT:8082} +my.base.url=http://host.minikube.internal +request.serverendpoint=${my.base.url}:${server.port}/catena/product-stock/request +request.apiassetid=product-stock-request-api +response.serverendpoint=${my.base.url}:${server.port}/catena/product-stock/response +response.apiassetid=product-stock-response-api +edr.endpoint=${my.base.url}:${server.port}/catena/edrendpoint +# DB Configuration +spring.datasource.driver-class-name=${DATASOURCE_DRIVERCLASSNAME:org.hsqldb.jdbc.JDBCDriver} +spring.datasource.url=${DATASOURCE_URL:jdbc:hsqldb:mem:testdb;DB_CLOSE_DELAY=-1} +spring.datasource.username=${DATASOURCE_USERNAME:sa} +spring.datasource.password=${DATASOURCE_PASSWORD:} +spring.jpa.hibernate.ddl-auto=create +# API Root +server.servlet.context-path=${API_ROOTDIR:/catena} +# EDC Config +edc.controlplane.host=${EDC_CONTROLPLANE_HOST:192.168.49.2} +edc.controlplane.data.port=${EDC_CONTROLPLANE_DATA_PORT:32272} +edc.controlplane.data.path=/api/v1/data +edc.controlplane.key=${EDC_CONTROLPLANE_KEY:password} + +edc.applydataplaneworkaround=true +edc.dataplane.public.port=31102 +minikube.ip=${MINIKUBE_IP:host.minikube.internal} +# Jackson (JSON) +#spring.jackson.default-property-inclusion=non_empty +#logging.level.org.hibernate.SQL=DEBUG +#logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE +spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true + + +# Own EDC-IDS-URL +edc.idsUrl=${EDC_IDSURL:http://supplier-control-plane:9184/api/v1/ids} + +# Own BPNL +own.bpnl=${OWN_BPNL:BPNL1234567890ZZ} + +# Own name (self-description) +own.name=${OWN_NAME:Scenario Supplier} + +# Own BPNS (optional: if this is set, then set own.default.site.name as well) +own.default.bpns=${OWN_BPNS:BPNS1234567890ZZ} + +# Name of Site (see above) +own.default.site.name=${OWN_SITE:Konzernzentrale Dudelsdorf} + +# If a BPNS is set, then this BPNA will be attached to it. +# Otherwise, it will be attached immediately to the BPNL (see above) +own.default.bpna=${OWN_BPNS:BPNA1234567890AA} +own.default.streetandnumber=${OWN_STREETANDNUMBER:Heinrich-Supplier-Strasse 1} +own.default.zipcodeandcity=${OWN_ZIPCODEANDCITY:77785 Dudelsdorf} +own.default.country=${OWN_COUNTRY:Germany} + +# The number of minutes before received authentication data +# in the context of a consumer pull is removed from memory +own.edr.deletiontimer=2 + +puris.apiversion=1.0.0 +puris.demonstrator.role=supplier +puris.api.key=test +# run with: +# ./mvnw spring-boot:run -Dspring-boot.run.arguments=--spring.profiles.active=supplier +# alternatively: +# ./mvnw spring-boot:run -Dspring-boot.run.arguments=--spring.config.location="./src/main/resources/application-supplier.properties" diff --git a/backend/src/test/resources/application.properties b/backend/src/test/resources/application.properties new file mode 100755 index 00000000..b99d11ce --- /dev/null +++ b/backend/src/test/resources/application.properties @@ -0,0 +1,61 @@ +# Server Config +server.port=${SERVER_PORT:8081} +my.base.url=http://localhost +request.serverendpoint=${my.base.url}:${server.port}/catena/product-stock/request +request.apiassetid=product-stock-request-api +response.serverendpoint=${my.base.url}:${server.port}/catena/product-stock/response +response.apiassetid=product-stock-response-api +edr.endpoint=${my.base.url}:${server.port}/catena/edrendpoint +# DB Configuration +spring.datasource.driver-class-name=${DATASOURCE_DRIVERCLASSNAME:org.hsqldb.jdbc.JDBCDriver} +spring.datasource.url=${DATASOURCE_URL:jdbc:hsqldb:mem:testdb;DB_CLOSE_DELAY=-1} +spring.datasource.username=${DATASOURCE_USERNAME:sa} +spring.datasource.password=${DATASOURCE_PASSWORD:} +spring.jpa.hibernate.ddl-auto=create +# API Root +server.servlet.context-path=${API_ROOTDIR:/catena} +# EDC Config +edc.controlplane.host=${EDC_CONTROLPLANE_HOST:172.17.0.2} +edc.controlplane.data.port=${EDC_CONTROLPLANE_DATA_PORT:31960} +edc.controlplane.data.path=/api/v1/data +edc.controlplane.key=${EDC_CONTROLPLANE_KEY:password} + +edc.applydataplaneworkaround=true +edc.dataplane.public.port=30703 +minikube.ip=${MINIKUBE_IP:host.minikube.internal} +# Jackson (JSON) +#spring.jackson.default-property-inclusion=non_empty +#logging.level.org.hibernate.SQL=DEBUG +#logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE +spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true + +# Own EDC-IDS-URL +edc.idsUrl=${EDC_IDSURL:http://customer-control-plane:8184/api/v1/ids} + +# Own BPNL +own.bpnl=${OWN_BPNL:BPNL4444444444XX} + +# Own name (self-description) +own.name=${OWN_NAME:Scenario Customer} + +# Own BPNS (optional: if this is set, then set own.default.site.name as well) +own.default.bpns=${OWN_BPNS:BPNS4444444444XY} +# Name of Site (see above) +own.default.site.name=${OWN_SITE:Hauptwerk Musterhausen} + +# If a BPNS is set, then this BPNA will be attached to it. +# Otherwise, it will be attached immediately to the BPNL (see above) +own.default.bpna=${OWN_BPNS:BPNA4444444444ZZ} +own.default.streetandnumber=${OWN_STREETANDNUMBER:Musterstrasse 35b} +own.default.zipcodeandcity=${OWN_ZIPCODEANDCITY:77777 Musterhausen} +own.default.country=${OWN_COUNTRY:Germany} + +# The number of minutes before received authentication data +# in the context of a consumer pull is removed from memory +own.edr.deletiontimer=2 + +puris.apiversion=1.0.0 +puris.demonstrator.role=customer +puris.api.key=test +# run with: +# ./mvnw spring-boot:run -Dspring-boot.run.arguments=--spring.config.location="./src/main/resources/application.properties" From 6bcfb5015901ef6c53f71226e3ec3ceff45eab5f Mon Sep 17 00:00:00 2001 From: Ernst-Christoph Schrewe Date: Wed, 29 Nov 2023 15:15:18 +0100 Subject: [PATCH 08/11] fix: added test properties --- .../src/test/resources/application.properties | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100755 backend/src/test/resources/application.properties diff --git a/backend/src/test/resources/application.properties b/backend/src/test/resources/application.properties new file mode 100755 index 00000000..1c6dc77a --- /dev/null +++ b/backend/src/test/resources/application.properties @@ -0,0 +1,48 @@ +# Server Config +server.port=${SERVER_PORT:8081} +puris.demonstrator.role=${PURIS_DEMONSTRATOR_ROLE:customer} +puris.apiversion=${PURIS_APIVERSION:1.0.0} +puris.edr.endpoint=${PURIS_EDR_ENDPOINT:http://customer-backend:8081/catena/edrendpoint} +puris.edr.deletiontimer=${PURIS_EDR_DELETIONTIMER:2} +puris.request.serverendpoint=${PURIS_REQUEST_SERVERENDPOINT:http://customer-backend:8081/catena/product-stock/request} +puris.request.apiassetid=${PURIS_REQUEST_APIASSETID:request-api-asset} +puris.response.serverendpoint=${PURIS_RESPONSE_SERVERENDPOINT:http://customer-backend:8081/catena/product-stock/response} +puris.response.apiassetid=${PURIS_RESPONSE_APIASSETID:response-api-asset} +puris.api.key=${PURIS_API_KEY:test} + +# DB Configuration +spring.datasource.driver-class-name=${DATASOURCE_DRIVERCLASSNAME:org.hsqldb.jdbc.JDBCDriver} +spring.datasource.url=${DATASOURCE_URL:jdbc:hsqldb:mem:testdb;DB_CLOSE_DELAY=-1} +spring.datasource.username=${DATASOURCE_USERNAME:sa} +spring.datasource.password=${DATASOURCE_PASSWORD:} +spring.jpa.hibernate.ddl-auto=create +# API Root +server.servlet.context-path=${API_ROOTDIR:/catena} +# EDC Config +edc.controlplane.key=${EDC_CONTROLPLANE_KEY:password} +edc.controlplane.management.url=${EDC_CONTROLPLANE_MANAGEMENT_URL:http://customer-control-plane:8181/management} +edc.controlplane.protocol.url=${EDC_CONTROLPLANE_PROTOCOL_URL:http://customer-control-plane:8184/api/v1/dsp} + +# Jackson (JSON) +#spring.jackson.default-property-inclusion=non_empty +#logging.level.org.hibernate.SQL=DEBUG +#logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE +spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true + +# Own BPNL +own.bpnl=${OWN_BPNL:BPNL4444444444XX} +# Own name (self-description) +own.name=${OWN_NAME:Scenario Customer} +# Own BPNS (optional: if this is set, then set own.default.site.name as well) +own.default.bpns=${OWN_DEFAULT_BPNS:BPNS4444444444XX} +# Name of Site (see above) +own.default.site.name=${OWN_DEFAULT_SITE_NAME:Hauptwerk Musterhausen} +# If a BPNS is set, then this BPNA will be attached to it. +# Otherwise, it will be attached immediately to the BPNL (see above) +own.default.bpna=${OWN_DEFAULT_BPNA:BPNA4444444444ZZ} +own.default.streetandnumber=${OWN_DEFAULT_STREETANDNUMBER:Musterstrasse 35b} +own.default.zipcodeandcity=${OWN_DEFAULT_ZIPCODEANDCITY:77777 Musterhausen} +own.default.country=${OWN_DEFAULT_COUNTRY:Germany} + +# run with: +# ./mvnw spring-boot:run -Dspring-boot.run.arguments=--spring.config.location="./src/main/resources/application.properties" From 0a38c90cf52aa159fbb6a3d8ddab151e83a21813 Mon Sep 17 00:00:00 2001 From: Ernst-Christoph Schrewe Date: Fri, 1 Dec 2023 08:21:52 +0100 Subject: [PATCH 09/11] chore: updated dependencies backend --- DEPENDENCIES_BACKEND | 2 +- backend/DEPENDENCIES | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPENDENCIES_BACKEND b/DEPENDENCIES_BACKEND index 133ea933..67c90818 100644 --- a/DEPENDENCIES_BACKEND +++ b/DEPENDENCIES_BACKEND @@ -39,7 +39,7 @@ maven/mavencentral/org.apache.tomcat.embed/tomcat-embed-core/10.1.13, Apache-2.0 maven/mavencentral/org.apache.tomcat.embed/tomcat-embed-el/10.1.13, Apache-2.0, approved, #6997 maven/mavencentral/org.apache.tomcat.embed/tomcat-embed-websocket/10.1.13, Apache-2.0, approved, #7920 maven/mavencentral/org.apiguardian/apiguardian-api/1.1.2, Apache-2.0, approved, clearlydefined -maven/mavencentral/org.aspectj/aspectjweaver/1.9.20, EPL-1.0, approved, tools.aspectj +maven/mavencentral/org.aspectj/aspectjweaver/1.9.20, Apache-2.0 AND BSD-3-Clause AND EPL-1.0 AND BSD-3-Clause AND Apache-1.1, approved, #7695 maven/mavencentral/org.assertj/assertj-core/3.24.2, Apache-2.0, approved, #6161 maven/mavencentral/org.eclipse.angus/angus-activation/2.0.1, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.angus maven/mavencentral/org.glassfish.jaxb/jaxb-core/4.0.3, BSD-3-Clause, approved, ee4j.jaxb diff --git a/backend/DEPENDENCIES b/backend/DEPENDENCIES index 133ea933..67c90818 100644 --- a/backend/DEPENDENCIES +++ b/backend/DEPENDENCIES @@ -39,7 +39,7 @@ maven/mavencentral/org.apache.tomcat.embed/tomcat-embed-core/10.1.13, Apache-2.0 maven/mavencentral/org.apache.tomcat.embed/tomcat-embed-el/10.1.13, Apache-2.0, approved, #6997 maven/mavencentral/org.apache.tomcat.embed/tomcat-embed-websocket/10.1.13, Apache-2.0, approved, #7920 maven/mavencentral/org.apiguardian/apiguardian-api/1.1.2, Apache-2.0, approved, clearlydefined -maven/mavencentral/org.aspectj/aspectjweaver/1.9.20, EPL-1.0, approved, tools.aspectj +maven/mavencentral/org.aspectj/aspectjweaver/1.9.20, Apache-2.0 AND BSD-3-Clause AND EPL-1.0 AND BSD-3-Clause AND Apache-1.1, approved, #7695 maven/mavencentral/org.assertj/assertj-core/3.24.2, Apache-2.0, approved, #6161 maven/mavencentral/org.eclipse.angus/angus-activation/2.0.1, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.angus maven/mavencentral/org.glassfish.jaxb/jaxb-core/4.0.3, BSD-3-Clause, approved, ee4j.jaxb From 678a4d8407e01c981cafb5010d70b74c5cc81480 Mon Sep 17 00:00:00 2001 From: Ernst-Christoph Schrewe Date: Fri, 1 Dec 2023 10:05:08 +0100 Subject: [PATCH 10/11] fix: cleanup deletes vaultsecrets and .env --- local/INSTALL.md | 2 +- local/cleanup.sh | 3 +++ local/init-wallets.sh | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/local/INSTALL.md b/local/INSTALL.md index 66771be2..2c265ebb 100644 --- a/local/INSTALL.md +++ b/local/INSTALL.md @@ -32,7 +32,7 @@ Then start the PURIS demonstrator containers via: ```shell docker compose up ``` -Wait for the startup and visit http://localhost:3000/ for the customer's frontend or http://localhost:3000/ for the supplier side. +Wait for the startup and visit http://localhost:3000/ for the customer's frontend or http://localhost:3001/ for the supplier side. ## Notes on debugging diff --git a/local/cleanup.sh b/local/cleanup.sh index f5d62f7c..05dc71ad 100644 --- a/local/cleanup.sh +++ b/local/cleanup.sh @@ -1,3 +1,6 @@ docker compose down -v docker compose -f docker-compose-infrastructure.yaml down -v docker image rm local-vault +rm .env +rm ./vault/secrets -r +echo "Deleted .env and vault/secrets" diff --git a/local/init-wallets.sh b/local/init-wallets.sh index 73596b1f..7fd80e85 100644 --- a/local/init-wallets.sh +++ b/local/init-wallets.sh @@ -9,3 +9,5 @@ echo $result curl -X POST -H "Authorization: Bearer $token" -H "Content-Type: application/json" -d '{ "name": "customer wallet", "bpn": "BPNL4444444444XX" }' http://localhost:8000/api/wallets curl -X POST -H "Authorization: Bearer $token" -H "Content-Type: application/json" -d '{ "name": "supplier wallet", "bpn": "BPNL1234567890ZZ" }' http://localhost:8000/api/wallets + +echo "" From 85ab47eb31061a421afa44673da75ae5f276a06f Mon Sep 17 00:00:00 2001 From: Ernst-Christoph Schrewe Date: Fri, 1 Dec 2023 10:22:57 +0100 Subject: [PATCH 11/11] chore: removed outdated test/properties files --- .../resources/application-customer.properties | 64 ------------------ .../resources/application-supplier.properties | 65 ------------------- 2 files changed, 129 deletions(-) delete mode 100755 backend/src/test/resources/application-customer.properties delete mode 100644 backend/src/test/resources/application-supplier.properties diff --git a/backend/src/test/resources/application-customer.properties b/backend/src/test/resources/application-customer.properties deleted file mode 100755 index 631e7abf..00000000 --- a/backend/src/test/resources/application-customer.properties +++ /dev/null @@ -1,64 +0,0 @@ -# Server Config -server.port=${SERVER_PORT:8081} -my.base.url=http://host.minikube.internal -request.serverendpoint=${my.base.url}:${server.port}/catena/product-stock/request -request.apiassetid=product-stock-request-api -response.serverendpoint=${my.base.url}:${server.port}/catena/product-stock/response -response.apiassetid=product-stock-response-api -edr.endpoint=${my.base.url}:${server.port}/catena/edrendpoint -# DB Configuration -spring.datasource.driver-class-name=${DATASOURCE_DRIVERCLASSNAME:org.hsqldb.jdbc.JDBCDriver} -spring.datasource.url=${DATASOURCE_URL:jdbc:hsqldb:mem:testdb;DB_CLOSE_DELAY=-1} -spring.datasource.username=${DATASOURCE_USERNAME:sa} -spring.datasource.password=${DATASOURCE_PASSWORD:} -spring.jpa.hibernate.ddl-auto=create -# API Root -server.servlet.context-path=${API_ROOTDIR:/catena} -# EDC Config -edc.controlplane.host=${EDC_CONTROLPLANE_HOST:192.168.49.2} -edc.controlplane.data.path=/api/v1/data -edc.controlplane.data.port=${EDC_CONTROLPLANE_DATA_PORT:31944} -edc.controlplane.key=${EDC_CONTROLPLANE_KEY:password} - -edc.applydataplaneworkaround=true -edc.dataplane.public.port=31944 -minikube.ip=${MINIKUBE_IP:host.minikube.internal} -# Jackson (JSON) -#spring.jackson.default-property-inclusion=non_empty -#logging.level.org.hibernate.SQL=DEBUG -#logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE -spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true - - -# Own EDC-IDS-URL -edc.idsUrl=${EDC_IDSURL:http://customer-control-plane:8184/api/v1/ids} - -# Own BPNL -own.bpnl=${OWN_BPNL:BPNL4444444444XX} - -# Own name (self-description) -own.name=${OWN_NAME:Scenario Customer} - -# Own BPNS (optional: if this is set, then set own.default.site.name as well) -own.default.bpns=${OWN_BPNS:BPNS4444444444XY} -# Name of Site (see above) -own.default.site.name=${OWN_SITE:Hauptwerk Musterhausen} - -# If a BPNS is set, then this BPNA will be attached to it. -# Otherwise, it will be attached immediately to the BPNL (see above) -own.default.bpna=${OWN_BPNS:BPNA4444444444ZZ} -own.default.streetandnumber=${OWN_STREETANDNUMBER:Musterstrasse 35b} -own.default.zipcodeandcity=${OWN_ZIPCODEANDCITY:77777 Musterhausen} -own.default.country=${OWN_COUNTRY:Germany} - -# The number of minutes before received authentication data -# in the context of a consumer pull is removed from memory -own.edr.deletiontimer=2 - -puris.apiversion=1.0.0 -puris.demonstrator.role=customer -puris.api.key=test -# run with: -# ./mvnw spring-boot:run -Dspring-boot.run.arguments=--spring.profiles.active=customer -# alternatively: -# ./mvnw spring-boot:run -Dspring-boot.run.arguments=--spring.config.location="./src/main/resources/application-customer.properties" diff --git a/backend/src/test/resources/application-supplier.properties b/backend/src/test/resources/application-supplier.properties deleted file mode 100644 index 8329b32f..00000000 --- a/backend/src/test/resources/application-supplier.properties +++ /dev/null @@ -1,65 +0,0 @@ -# Server Config -server.port=${SERVER_PORT:8082} -my.base.url=http://host.minikube.internal -request.serverendpoint=${my.base.url}:${server.port}/catena/product-stock/request -request.apiassetid=product-stock-request-api -response.serverendpoint=${my.base.url}:${server.port}/catena/product-stock/response -response.apiassetid=product-stock-response-api -edr.endpoint=${my.base.url}:${server.port}/catena/edrendpoint -# DB Configuration -spring.datasource.driver-class-name=${DATASOURCE_DRIVERCLASSNAME:org.hsqldb.jdbc.JDBCDriver} -spring.datasource.url=${DATASOURCE_URL:jdbc:hsqldb:mem:testdb;DB_CLOSE_DELAY=-1} -spring.datasource.username=${DATASOURCE_USERNAME:sa} -spring.datasource.password=${DATASOURCE_PASSWORD:} -spring.jpa.hibernate.ddl-auto=create -# API Root -server.servlet.context-path=${API_ROOTDIR:/catena} -# EDC Config -edc.controlplane.host=${EDC_CONTROLPLANE_HOST:192.168.49.2} -edc.controlplane.data.port=${EDC_CONTROLPLANE_DATA_PORT:32272} -edc.controlplane.data.path=/api/v1/data -edc.controlplane.key=${EDC_CONTROLPLANE_KEY:password} - -edc.applydataplaneworkaround=true -edc.dataplane.public.port=31102 -minikube.ip=${MINIKUBE_IP:host.minikube.internal} -# Jackson (JSON) -#spring.jackson.default-property-inclusion=non_empty -#logging.level.org.hibernate.SQL=DEBUG -#logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE -spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true - - -# Own EDC-IDS-URL -edc.idsUrl=${EDC_IDSURL:http://supplier-control-plane:9184/api/v1/ids} - -# Own BPNL -own.bpnl=${OWN_BPNL:BPNL1234567890ZZ} - -# Own name (self-description) -own.name=${OWN_NAME:Scenario Supplier} - -# Own BPNS (optional: if this is set, then set own.default.site.name as well) -own.default.bpns=${OWN_BPNS:BPNS1234567890ZZ} - -# Name of Site (see above) -own.default.site.name=${OWN_SITE:Konzernzentrale Dudelsdorf} - -# If a BPNS is set, then this BPNA will be attached to it. -# Otherwise, it will be attached immediately to the BPNL (see above) -own.default.bpna=${OWN_BPNS:BPNA1234567890AA} -own.default.streetandnumber=${OWN_STREETANDNUMBER:Heinrich-Supplier-Strasse 1} -own.default.zipcodeandcity=${OWN_ZIPCODEANDCITY:77785 Dudelsdorf} -own.default.country=${OWN_COUNTRY:Germany} - -# The number of minutes before received authentication data -# in the context of a consumer pull is removed from memory -own.edr.deletiontimer=2 - -puris.apiversion=1.0.0 -puris.demonstrator.role=supplier -puris.api.key=test -# run with: -# ./mvnw spring-boot:run -Dspring-boot.run.arguments=--spring.profiles.active=supplier -# alternatively: -# ./mvnw spring-boot:run -Dspring-boot.run.arguments=--spring.config.location="./src/main/resources/application-supplier.properties"