From 14eb2ab092e26ccadd3ba472e0e19a4c60880639 Mon Sep 17 00:00:00 2001 From: Florian Zimmer Date: Tue, 9 Apr 2024 11:32:09 +0200 Subject: [PATCH 1/7] feat: Added membership credential as contract policy. Made framework credential mandatory. Adjusted config and deployment files and docs accordingly. Enhanced frontend to display multiple permission conditions. --- .../edc/logic/service/EdcAdapterService.java | 121 +++++++++--------- .../edc/logic/util/EdcRequestBodyBuilder.java | 63 +++++---- .../backend/common/util/VariablesService.java | 7 - charts/puris/README.md | 1 - charts/puris/values.yaml | 2 - docs/adminGuide/Admin_Guide.md | 3 +- docs/arc42/08_concepts.md | 6 +- local/init-wallets.sh | 6 + .../config/customer/puris-backend.properties | 1 - .../config/supplier/puris-backend.properties | 1 - 10 files changed, 110 insertions(+), 101 deletions(-) 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 1520b8e4..40461c3c 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,6 +22,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.Optional; import lombok.extern.slf4j.Slf4j; import okhttp3.*; import org.eclipse.tractusx.puris.backend.common.edc.domain.model.EdcContractMapping; @@ -123,13 +124,8 @@ private Response sendPostRequest(JsonNode requestBody, List pathSegments * @return true if all registrations were successful, otherwise false */ public boolean registerAssetsInitially() { - boolean result = true; - if (variablesService.isUseFrameworkPolicy()) { - log.info("Registration of framework agreement policy successful " + (result = createFrameWorkPolicy())); - if (!result) return false; - } else { - log.info("Skipping registration of framework agreement policy"); - } + boolean result; + log.info("Registration of framework agreement policy successful " + (result = createFrameWorkPolicy())); log.info("Registration of DTR Asset successful " + (result &= registerDtrAsset())); log.info("Registration of ItemStock 2.0.0 submodel successful " + (result &= registerItemStockSubmodel())); return result; @@ -169,7 +165,6 @@ private boolean createItemStockSubmodelContractDefinitionForPartner(Partner part } } - private boolean createDtrContractDefinitionForPartner(Partner partner) { var body = edcRequestBodyBuilder.buildDtrContractDefinitionForPartner(partner); try (var response = sendPostRequest(body, List.of("v2", "contractdefinitions"))) { @@ -229,8 +224,8 @@ private boolean createPolicyDefinitionForPartner(Partner partner) { * * @return true, if registration ran successfully */ - private boolean createFrameWorkPolicy() { - var body = edcRequestBodyBuilder.buildFrameworkAgreementPolicy(); + private boolean createContractPolicy() { + var body = edcRequestBodyBuilder.buildFrameworkAndMembershipPolicy(); try (var response = sendPostRequest(body, List.of("v2", "policydefinitions"))) { if (!response.isSuccessful()) { log.warn("Framework Policy Registration failed"); @@ -642,18 +637,14 @@ private boolean negotiateForPartnerItemStockSubmodel(MaterialPartnerRelation mpr if ("urn:samm:io.catenax.item_stock:2.0.0#ItemStock".equals(aasSemantics.get("@id").asText())) { if (itemStockAssetId.equals(entry.get("edc:id").asText())) { if (targetCatalogEntry == null) { - if (variablesService.isUseFrameworkPolicy()) { - if (testFrameworkAgreementConstraint(entry)) { - targetCatalogEntry = entry; - } else { - log.error("Contract Negotiation with partner " + partner.getBpnl() + " has " + - "been aborted. This partner's contract policy does not match the policy " + - "supported by this application. \n Supported Policy: " + variablesService.getPurisFrameworkAgreement() + - "\n Received offer from Partner: \n" + entry.toPrettyString()); - break; - } - } else { + if (testFrameworkAgreementConstraint(entry)) { targetCatalogEntry = entry; + } else { + log.error("Contract Negotiation with partner " + partner.getBpnl() + " has " + + "been aborted. This partner's contract policy does not match the policy " + + "supported by this application. \n Supported Policy: " + variablesService.getPurisFrameworkAgreement() + + "\n Received offer from Partner: \n" + entry.toPrettyString()); + break; } } else { log.warn("Ambiguous catalog entries found! \n" + catalogArray.toPrettyString()); @@ -896,8 +887,9 @@ private boolean initiateDtrTransferForLookupApi(Partner partner, String specific * 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 - * @return A String array or null, if negotiation failed or the authCode did not arrive + * @param partner the partner + * @param apiMethod the api method + * @return A String array or null, if negotiation or transfer have failed or the authCode did not arrive */ public String[] getContractForPartTypeInfoSubmodel(Partner partner) { try { @@ -919,18 +911,14 @@ public String[] getContractForPartTypeInfoSubmodel(Partner partner) { String idString = semanticId.get("@id").asText(); if ("urn:samm:io.catenax.part_type_information:1.0.0#PartTypeInformation".equals(idString)) { if (targetCatalogEntry == null) { - if (variablesService.isUseFrameworkPolicy()) { - if (testFrameworkAgreementConstraint(entry)) { - targetCatalogEntry = entry; - } else { - log.error("Contract Negotiation for PartTypeInformation Submodel asset with partner " + partner.getBpnl() + " has " + - "been aborted. This partner's contract policy does not match the policy " + - "supported by this application. \n Supported Policy: " + variablesService.getPurisFrameworkAgreement() + - "\n Received offer from Partner: \n" + entry.toPrettyString()); - break; - } - } else { + if (testFrameworkAgreementConstraint(entry)) { targetCatalogEntry = entry; + } else { + log.error("Contract Negotiation for PartTypeInformation Submodel asset with partner " + partner.getBpnl() + " has " + + "been aborted. This partner's contract policy does not match the policy " + + "supported by this application. \n Supported Policy: " + variablesService.getPurisFrameworkAgreement() + + "\n Received offer from Partner: \n" + entry.toPrettyString()); + break; } } else { log.warn("Ambiguous catalog entries found! \n" + catalogArray.toPrettyString()); @@ -1001,36 +989,47 @@ public String[] getContractForPartTypeInfoSubmodel(Partner partner) { * @param catalogEntry the catalog item containing the desired api asset * @return true, if the policy matches yours, otherwise false */ - private boolean testFrameworkAgreementConstraint(JsonNode catalogEntry) { - try { - var policyObject = catalogEntry.get("odrl:hasPolicy"); - if (policyObject == null) { - return false; - } - var permissionObject = policyObject.get("odrl:permission"); - if (permissionObject == null) { - return false; - } - var constraintObject = permissionObject.get("odrl:constraint"); - if (constraintObject == null) { - return false; - } - var leftOperandObject = constraintObject.get("odrl:leftOperand"); - if (!variablesService.getPurisFrameworkAgreement().equals(leftOperandObject.asText())) { - return false; - } - var operatorObject = constraintObject.get("odrl:operator"); - if (!"odrl:eq".equals(operatorObject.get("@id").asText())) { - return false; - } - var rightOperandObject = constraintObject.get("odrl:rightOperand"); - if (!"active".equals(rightOperandObject.asText())) { + private boolean testContractPolicyConstraints(JsonNode catalogEntry) { + var constraints = Optional.ofNullable(catalogEntry.get("odrl:hasPolicy")) + .map(policy -> policy.get("odrl:permission")) + .map(permission -> permission.get("odrl:constraint")) + .map(and -> and.get("odrl:and")); + if (constraints.isEmpty()) return false; + + var firstConstraint = constraints.map(membership -> membership.get(0)); + var secondConstraint = constraints.map(membership -> membership.get(1)); + + var firstLeftOperandOptional = firstConstraint.map(constraint -> constraint.get("odrl:leftOperand")); + var secondLeftOperandOptional = secondConstraint.map(constraint -> constraint.get("odrl:leftOperand")); + if (firstLeftOperandOptional.isEmpty() || secondLeftOperandOptional.isEmpty()) { + return false; + } else { + var firstLeftOperand = firstLeftOperandOptional.get(); + var secondLeftOperand = secondLeftOperandOptional.get(); + var frameworkCredential = variablesService.getPurisFrameworkAgreement(); + + if (!((frameworkCredential.equals(firstLeftOperand.asText()) && "Membership".equals(secondLeftOperand.asText())) + || ("Membership".equals(firstLeftOperand.asText()) && frameworkCredential.equals(secondLeftOperand.asText())))) { return false; } - } catch (Exception e) { - log.error("Failed in Framework Agreement Test ", e); - return false; } + + var firstOperator = firstConstraint.map(constraint -> constraint.get("odrl:operator")) + .map(operator -> operator.get("@id")) + .filter(operand -> "odrl:eq".equals(operand.asText())); + + var secondOperator = secondConstraint.map(constraint -> constraint.get("odrl:operator")) + .map(operator -> operator.get("@id")) + .filter(operand -> "odrl:eq".equals(operand.asText())); + + var firstRightOperand = firstConstraint.map(constraint -> constraint.get("odrl:rightOperand")) + .filter(operand -> "active".equals(operand.asText())); + + var secondRightOperand = secondConstraint.map(constraint -> constraint.get("odrl:rightOperand")) + .filter(operand -> "active".equals(operand.asText())); + + if (firstOperator.isEmpty() || secondOperator.isEmpty() || firstRightOperand.isEmpty() || secondRightOperand.isEmpty()) return false; + return true; } } 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 14c97830..a6c6a0ca 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 @@ -49,7 +49,7 @@ public class EdcRequestBodyBuilder { 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 DCT_NAMESPACE = "https://purl.org/dc/terms/"; - private final String FRAMEWORK_POLICY_ID = "Framework_Agreement_Policy"; + private final String CONTRACT_POLICY_ID = "Contract_Policy"; /** * Creates a request body for requesting a catalog in DSP protocol. @@ -111,33 +111,58 @@ public JsonNode buildBpnRestrictedPolicy(Partner partner) { /** * Creates a request body in order to register a policy that - * allows only participants of the framework agreement. + * allows only participants of the framework agreement with valid membership credentials. * * @return the request body */ - public JsonNode buildFrameworkAgreementPolicy() { + public JsonNode buildFrameworkAndMembershipPolicy() { var body = MAPPER.createObjectNode(); var context = MAPPER.createObjectNode(); context.put("odrl", ODRL_NAMESPACE); body.set("@context", context); body.put("@type", "PolicyDefinitionRequestDto"); - body.put("@id", FRAMEWORK_POLICY_ID); + body.put("@id", CONTRACT_POLICY_ID); + var policy = MAPPER.createObjectNode(); body.set("policy", policy); policy.put("@type", "Policy"); + var permissionsArray = MAPPER.createArrayNode(); policy.set("odrl:permission", permissionsArray); - var permissionsObject = MAPPER.createObjectNode(); - permissionsArray.add(permissionsObject); - permissionsObject.put("odrl:action", "USE"); + + var permissionObject = MAPPER.createObjectNode(); + permissionsArray.add(permissionObject); + permissionObject.put("odrl:action", "USE"); + var constraintObject = MAPPER.createObjectNode(); - permissionsObject.set("odrl:constraint", constraintObject); + permissionObject.set("odrl:constraint", constraintObject); constraintObject.put("@type", "LogicalConstraint"); - constraintObject.put("odrl:leftOperand", variablesService.getPurisFrameworkAgreement()); - var operatorObject = MAPPER.createObjectNode(); - constraintObject.set("odrl:operator", operatorObject); - operatorObject.put("@id", "odrl:eq"); - constraintObject.put("odrl:rightOperand", "active"); + + var andArray = MAPPER.createArrayNode(); + constraintObject.set("odrl:and", andArray); + + var membershipConstraint = MAPPER.createObjectNode(); + andArray.add(membershipConstraint); + membershipConstraint.put("@type", "Constraint"); + membershipConstraint.put("odrl:leftOperand", "Membership"); + + var membershipOperator = MAPPER.createObjectNode(); + membershipConstraint.set("odrl:operator", membershipOperator); + membershipOperator.put("@id", "odrl:eq"); + + membershipConstraint.put("odrl:rightOperand", "active"); + + var frameworkAgreementConstraint = MAPPER.createObjectNode(); + andArray.add(frameworkAgreementConstraint); + frameworkAgreementConstraint.put("@type", "Constraint"); + frameworkAgreementConstraint.put("odrl:leftOperand", variablesService.getPurisFrameworkAgreement()); + + var frameworkAgreementOperator = MAPPER.createObjectNode(); + frameworkAgreementConstraint.set("odrl:operator", frameworkAgreementOperator); + frameworkAgreementOperator.put("@id", "odrl:eq"); + + frameworkAgreementConstraint.put("odrl:rightOperand", "active"); + return body; } @@ -145,11 +170,7 @@ public JsonNode buildItemStockSubmodelContractDefinitionWithBpnRestrictedPolicy( var body = getEdcContextObject(); body.put("@id", partner.getBpnl() + "_contractdefinition_for_" + getItemStockSubmodelAssetId()); body.put("accessPolicyId", getBpnPolicyId(partner)); - if(variablesService.isUseFrameworkPolicy()) { - body.put("contractPolicyId", FRAMEWORK_POLICY_ID); - } else { - body.put("contractPolicyId", getBpnPolicyId(partner)); - } + body.put("contractPolicyId", CONTRACT_POLICY_ID); var assetsSelector = MAPPER.createObjectNode(); body.set("assetsSelector", assetsSelector); assetsSelector.put("@type", "CriterionDto"); @@ -177,11 +198,7 @@ public JsonNode buildPartTypeInfoContractDefinitionForPartner(Partner partner) { var body = getEdcContextObject(); body.put("@id", partner.getBpnl() +"_contractdefinition_for_PartTypeInfoAsset"); body.put("accessPolicyId", getBpnPolicyId(partner)); - if(variablesService.isUseFrameworkPolicy()) { - body.put("contractPolicyId", FRAMEWORK_POLICY_ID); - } else { - body.put("contractPolicyId", getBpnPolicyId(partner)); - } + body.put("contractPolicyId", CONTRACT_POLICY_ID); var assetsSelector = MAPPER.createObjectNode(); body.set("assetsSelector", assetsSelector); assetsSelector.put("@type", "CriterionDto"); diff --git a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/util/VariablesService.java b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/util/VariablesService.java index a346cf80..fdced8a3 100644 --- a/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/util/VariablesService.java +++ b/backend/src/main/java/org/eclipse/tractusx/puris/backend/common/util/VariablesService.java @@ -77,13 +77,6 @@ public class VariablesService { */ private String itemStockSubmodelAssetId; - @Value("${puris.frameworkagreement.use}") - /** - * A flag that signals whether a framework policy - * shall be used as contract policy for your api assets. - */ - private boolean useFrameworkPolicy; - @Value("${puris.frameworkagreement.credential}") /** * The name of the framework agreement to be used. diff --git a/charts/puris/README.md b/charts/puris/README.md index bd26756d..1f6bb1d9 100644 --- a/charts/puris/README.md +++ b/charts/puris/README.md @@ -74,7 +74,6 @@ $ helm install puris --namespace puris --create-namespace . | backend.puris.edr.deletiontimer | int | `2` | Number of minutes before received authentication data of a consumer pull is removed from memory | | backend.puris.existingSecret | string | `"secret-backend-puris"` | Secret for backend passwords. For more information look into 'backend-secrets.yaml' file. | | backend.puris.frameworkagreement.credential | string | `"FrameworkAgreement.traceability"` | The name of the framework agreement | -| backend.puris.frameworkagreement.use | bool | `false` | Flag to determine whether to use a framework agreement in puris | | backend.puris.generatematerialcatenaxid | bool | `true` | Flag that decides whether the auto-generation feature of the puris backend is enabled. Since all Material entities are required to have a CatenaX-Id, you must enter any pre-existing CatenaX-Id via the materials-API of the backend, when you are inserting a new Material entity to the backend's database. If a CatenaX-Id was not assigned to your Material so far, then this feature can auto-generate one randomly. In a real-world-scenario, you must then use this randomly generated CatenaX-Id for the lifetime of that Material entity. | | backend.puris.itemstocksubmodel.apiassetid | string | `"itemstocksubmodel-api-asset"` | Asset ID for ItemStockSubmodel API | | backend.puris.jpa.hibernate.ddl-auto | string | `"create"` | Initialises SQL database with Hibernate property "create" to allow Hibernate to first drop all tables and then create new ones | diff --git a/charts/puris/values.yaml b/charts/puris/values.yaml index 80a844fd..d69ffc0b 100644 --- a/charts/puris/values.yaml +++ b/charts/puris/values.yaml @@ -415,8 +415,6 @@ backend: # -- Asset ID for ItemStockSubmodel API apiassetid: itemstocksubmodel-api-asset frameworkagreement: - # -- Flag to determine whether to use a framework agreement in puris - use: false # -- The name of the framework agreement credential: FrameworkAgreement.traceability edr: diff --git a/docs/adminGuide/Admin_Guide.md b/docs/adminGuide/Admin_Guide.md index 6d6c68cd..7fe60383 100644 --- a/docs/adminGuide/Admin_Guide.md +++ b/docs/adminGuide/Admin_Guide.md @@ -81,9 +81,8 @@ _Note: The application does NOT make use of the `Client Authentication` (private ## Configure Framework Agreement Credential Usage To configure the usage of a framework agreement credential, that is automatically enforced by the EDC during contracting -(see further details in [ARC42 - Chapter 8](../arc42/08_concepts.md)), the following properties need to be configures: +(see further details in [ARC42 - Chapter 8](../arc42/08_concepts.md)), the following property needs to be configured: -- `backend.frameworkagreement.use` (docker `PURIS_FRAMEWORKAGREEMENT_USE`) = true - `backend.frameworkagreement.credential` (docker `PURIS_FRAMEWORKAGREEMENT_CREDENTIAL`) = 'puris' (NOTE: not available for R24.03) diff --git a/docs/arc42/08_concepts.md b/docs/arc42/08_concepts.md index 78892737..d2559345 100644 --- a/docs/arc42/08_concepts.md +++ b/docs/arc42/08_concepts.md @@ -42,10 +42,10 @@ information is routed through the EDC data plane using the proxy pull mechanism. Whenever a partner is created, all exchanged information APIs that comply to a Catena-X standard are registered as contract offer for the partner's BPNL. -Additionally, the administrator may configure the application to use a framework agreement policy for data offers (see -[Administration Guide](../adminGuide/Admin_Guide.md)). When activated, +Additionally, the application is configured to demand several policies to be fulfilled when accessing or exchanging data, that is: -- the application requires the respective credential to be active for the partner. +- the application requires the BPNL of the partner as part of the access policy when accessing any asset. +- the application requires the membership credential as part of the access policy when accessing any asset. - the application requires the framework agreement credential as contract policy when contracting a partner's offer as a consumer. diff --git a/local/init-wallets.sh b/local/init-wallets.sh index 6c474d4c..4d0fef30 100644 --- a/local/init-wallets.sh +++ b/local/init-wallets.sh @@ -39,3 +39,9 @@ echo "" # register supplier for framework agreement at miw curl -X POST -H "Authorization: Bearer $token" -H "Content-Type: application/json" -d '{ "holderIdentifier": "BPNL1234567890ZZ", "type": "TraceabilityCredential", "contract-template": "https://public.catena-x.org/contracts/traceabilty.v1.pdf", "contract-version": "1.0.0" }' http://localhost:8000/api/credentials/issuer/framework | jq echo "" +# register customer for membership credential at miw +curl -X POST -H "Authorization: Bearer $token" -H "Content-Type: application/json" -d '{ "bpn": "BPNL4444444444XX" }' http://localhost:8000/api/credentials/issuer/membership | jq +echo "" +# register supplier for membership credential at miw +curl -X POST -H "Authorization: Bearer $token" -H "Content-Type: application/json" -d '{ "bpn": "BPNL1234567890ZZ" }' http://localhost:8000/api/credentials/issuer/membership | jq +echo "" diff --git a/local/tractus-x-edc/config/customer/puris-backend.properties b/local/tractus-x-edc/config/customer/puris-backend.properties index d8128a3b..2f02a1f6 100644 --- a/local/tractus-x-edc/config/customer/puris-backend.properties +++ b/local/tractus-x-edc/config/customer/puris-backend.properties @@ -3,7 +3,6 @@ puris.demonstrator.role=customer puris.baseurl=http://customer-backend:8081/ puris.edr.deletiontimer=2 puris.itemstocksubmodel.apiassetid=itemstocksubmodel-api-asset -puris.frameworkagreement.use=true puris.frameworkagreement.credential=FrameworkAgreement.traceability puris.api.key=${CUSTOMER_BACKEND_API_KEY} puris.dtr.url=http://dtr-customer:4243 diff --git a/local/tractus-x-edc/config/supplier/puris-backend.properties b/local/tractus-x-edc/config/supplier/puris-backend.properties index cdabbb39..ce930e5b 100644 --- a/local/tractus-x-edc/config/supplier/puris-backend.properties +++ b/local/tractus-x-edc/config/supplier/puris-backend.properties @@ -3,7 +3,6 @@ puris.demonstrator.role=supplier puris.baseurl=http://supplier-backend:8082/ puris.edr.deletiontimer=2 puris.itemstocksubmodel.apiassetid=itemstocksubmodel-api-asset -puris.frameworkagreement.use=true puris.frameworkagreement.credential=FrameworkAgreement.traceability puris.api.key=${SUPPLIER_BACKEND_API_KEY} puris.dtr.url=http://dtr-supplier:4243 From c6f3e18f343aeef1b98e6f0410d23194589a1ca9 Mon Sep 17 00:00:00 2001 From: Florian Zimmer Date: Tue, 9 Apr 2024 14:58:15 +0200 Subject: [PATCH 2/7] feat: Refactored membership credential to be used as access policy rather than contract policy. Incremented chart.yml semver. --- .../edc/logic/service/EdcAdapterService.java | 58 +++++--------- .../edc/logic/util/EdcRequestBodyBuilder.java | 79 ++++++++++--------- charts/puris/Chart.yaml | 2 +- 3 files changed, 62 insertions(+), 77 deletions(-) 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 40461c3c..a264d73f 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 @@ -140,7 +140,7 @@ public boolean registerAssetsInitially() { * @return true, if all registrations ran successfully */ public boolean createPolicyAndContractDefForPartner(Partner partner) { - boolean result = createPolicyDefinitionForPartner(partner); + boolean result = createBpnlAndMembershipPolicyDefinitionForPartner(partner); result &= createItemStockSubmodelContractDefinitionForPartner(partner); result &= createDtrContractDefinitionForPartner(partner); result &= registerPartTypeAssetForPartner(partner); @@ -196,14 +196,15 @@ private boolean createPartTypeInfoContractDefForPartner(Partner partner) { /** - * Registers a policy definition that allows only the given partner's - * BPNL. + * Registers a policy definition that evaluates to true in case all the following conditions apply: + * 1. The BPNL of the requesting connector is equal to the BPNL of the partner + * 2. There's a CX membership credential present * - * @param partner the partner + * @param partner the partner to create the policy for * @return true, if registration ran successfully */ - private boolean createPolicyDefinitionForPartner(Partner partner) { - var body = edcRequestBodyBuilder.buildBpnRestrictedPolicy(partner); + private boolean createBpnlAndMembershipPolicyDefinitionForPartner(Partner partner) { + var body = edcRequestBodyBuilder.buildBpnAndMembershipRestrictedPolicy(partner); try (var response = sendPostRequest(body, List.of("v2", "policydefinitions"))) { if (!response.isSuccessful()) { log.warn("Policy Registration failed"); @@ -214,7 +215,7 @@ private boolean createPolicyDefinitionForPartner(Partner partner) { } return true; } catch (Exception e) { - log.error("Failed to register policy definition for partner " + partner.getBpnl(), e); + log.error("Failed to register bpnl and membership policy definition for partner " + partner.getBpnl(), e); return false; } } @@ -225,7 +226,7 @@ private boolean createPolicyDefinitionForPartner(Partner partner) { * @return true, if registration ran successfully */ private boolean createContractPolicy() { - var body = edcRequestBodyBuilder.buildFrameworkAndMembershipPolicy(); + var body = edcRequestBodyBuilder.buildFrameworkPolicy(); try (var response = sendPostRequest(body, List.of("v2", "policydefinitions"))) { if (!response.isSuccessful()) { log.warn("Framework Policy Registration failed"); @@ -990,45 +991,22 @@ public String[] getContractForPartTypeInfoSubmodel(Partner partner) { * @return true, if the policy matches yours, otherwise false */ private boolean testContractPolicyConstraints(JsonNode catalogEntry) { - var constraints = Optional.ofNullable(catalogEntry.get("odrl:hasPolicy")) + var constraint = Optional.ofNullable(catalogEntry.get("odrl:hasPolicy")) .map(policy -> policy.get("odrl:permission")) - .map(permission -> permission.get("odrl:constraint")) - .map(and -> and.get("odrl:and")); - if (constraints.isEmpty()) return false; - - var firstConstraint = constraints.map(membership -> membership.get(0)); - var secondConstraint = constraints.map(membership -> membership.get(1)); + .map(permission -> permission.get("odrl:constraint")); + if (constraint.isEmpty()) return false; - var firstLeftOperandOptional = firstConstraint.map(constraint -> constraint.get("odrl:leftOperand")); - var secondLeftOperandOptional = secondConstraint.map(constraint -> constraint.get("odrl:leftOperand")); - if (firstLeftOperandOptional.isEmpty() || secondLeftOperandOptional.isEmpty()) { - return false; - } else { - var firstLeftOperand = firstLeftOperandOptional.get(); - var secondLeftOperand = secondLeftOperandOptional.get(); - var frameworkCredential = variablesService.getPurisFrameworkAgreement(); + var leftOperand = constraint.map(cons -> cons.get("odrl:leftOperand")) + .filter(operand -> variablesService.getPurisFrameworkAgreement().equals(operand.asText())); - if (!((frameworkCredential.equals(firstLeftOperand.asText()) && "Membership".equals(secondLeftOperand.asText())) - || ("Membership".equals(firstLeftOperand.asText()) && frameworkCredential.equals(secondLeftOperand.asText())))) { - return false; - } - } - - var firstOperator = firstConstraint.map(constraint -> constraint.get("odrl:operator")) - .map(operator -> operator.get("@id")) - .filter(operand -> "odrl:eq".equals(operand.asText())); - - var secondOperator = secondConstraint.map(constraint -> constraint.get("odrl:operator")) - .map(operator -> operator.get("@id")) + var operator = constraint.map(cons -> cons.get("odrl:operator")) + .map(op -> op.get("@id")) .filter(operand -> "odrl:eq".equals(operand.asText())); - var firstRightOperand = firstConstraint.map(constraint -> constraint.get("odrl:rightOperand")) - .filter(operand -> "active".equals(operand.asText())); - - var secondRightOperand = secondConstraint.map(constraint -> constraint.get("odrl:rightOperand")) + var rightOperand = constraint.map(cons -> cons.get("odrl:rightOperand")) .filter(operand -> "active".equals(operand.asText())); - if (firstOperator.isEmpty() || secondOperator.isEmpty() || firstRightOperand.isEmpty() || secondRightOperand.isEmpty()) return false; + if (leftOperand.isEmpty() || operator.isEmpty() || rightOperand.isEmpty()) return false; return true; } 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 a6c6a0ca..fd9e1ddb 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 @@ -77,45 +77,71 @@ public ObjectNode buildBasicCatalogRequestBody(String counterPartyDspUrl, Map Date: Tue, 16 Apr 2024 17:27:25 +0200 Subject: [PATCH 3/7] feat: Enhanced frontend to display AND permission constraints. Fixed small label typo of prohibition and obligation. --- frontend/src/hooks/edc/useCatalog.ts | 4 ++-- frontend/src/models/types/edc/catalog.ts | 4 ++-- frontend/src/views/CatalogView.tsx | 23 +++++++++++++++++++---- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/frontend/src/hooks/edc/useCatalog.ts b/frontend/src/hooks/edc/useCatalog.ts index 53399202..0f97e9d3 100644 --- a/frontend/src/hooks/edc/useCatalog.ts +++ b/frontend/src/hooks/edc/useCatalog.ts @@ -36,8 +36,8 @@ export const useCatalog = (edcUrl: string | null) => { assetType: item['asset:prop:type'], assetVersion: item['https://w3id.org/catenax/ontology/common#version'], permission: item['odrl:hasPolicy'] && item['odrl:hasPolicy']['odrl:permission'], - prohibitions: item['odrl:hasPolicy'] && item['odrl:hasPolicy']['odrl:prohibitions'], - obligations: item['odrl:hasPolicy'] && item['odrl:hasPolicy']['odrl:obligations'], + prohibitions: item['odrl:hasPolicy'] && item['odrl:hasPolicy']['odrl:prohibition'], + obligations: item['odrl:hasPolicy'] && item['odrl:hasPolicy']['odrl:obligation'], }; }) ?? null) : null; diff --git a/frontend/src/models/types/edc/catalog.ts b/frontend/src/models/types/edc/catalog.ts index 6ee3cdee..816b7109 100644 --- a/frontend/src/models/types/edc/catalog.ts +++ b/frontend/src/models/types/edc/catalog.ts @@ -49,8 +49,8 @@ export type RawCatalogData = { 'https://w3id.org/catenax/ontology/common#version': string; 'odrl:hasPolicy': { 'odrl:permission': CatalogPermission; - 'odrl:prohibitions': CatalogOperation[]; - 'odrl:obligations': CatalogOperation[]; + 'odrl:prohibition': CatalogOperation[]; + 'odrl:obligation': CatalogOperation[]; }; }[]; }; diff --git a/frontend/src/views/CatalogView.tsx b/frontend/src/views/CatalogView.tsx index 4208b128..14838caf 100644 --- a/frontend/src/views/CatalogView.tsx +++ b/frontend/src/views/CatalogView.tsx @@ -96,10 +96,25 @@ export const CatalogView = () => { {item.permission['odrl:action']['odrl:type']} {item.permission['odrl:target']}
-

Asset condition:

- {item.permission['odrl:constraint']['odrl:leftOperand'] + ' '} - {getCatalogOperator(item.permission['odrl:constraint']['odrl:operator']['@id']) + ' '} - {item.permission['odrl:constraint']['odrl:rightOperand']} +

Asset condition(s):

+
+ {item.permission['odrl:constraint']['odrl:and'] ? + (item.permission['odrl:constraint']['odrl:and'].map(constraint => ( +
+ {constraint['odrl:leftOperand'] + ' '} + {getCatalogOperator(constraint['odrl:operator']['@id']) + ' '} + {constraint['odrl:rightOperand']} +
+ ))) : + ( +
+ {item.permission['odrl:constraint']['odrl:leftOperand'] + ' '} + {getCatalogOperator(item.permission['odrl:constraint']['odrl:operator']['@id']) + ' '} + {item.permission['odrl:constraint']['odrl:rightOperand']} +
+ ) + } +
From 6ed34b241981c2b9351a8c7de55d819b9180e37b Mon Sep 17 00:00:00 2001 From: Florian Zimmer Date: Wed, 17 Apr 2024 09:45:52 +0200 Subject: [PATCH 4/7] chore: Removed asset:prop:type. Fixed typescript error. --- frontend/src/models/types/edc/catalog.ts | 12 +++++++++++- frontend/src/views/CatalogView.tsx | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/frontend/src/models/types/edc/catalog.ts b/frontend/src/models/types/edc/catalog.ts index 816b7109..82300a34 100644 --- a/frontend/src/models/types/edc/catalog.ts +++ b/frontend/src/models/types/edc/catalog.ts @@ -33,12 +33,22 @@ export type CatalogPermission = { 'odrl:action': { 'odrl:type': string; } - 'odrl:constraint': { + 'odrl:constraint': { 'odrl:leftOperand': string; 'odrl:operator': { '@id': string; }; 'odrl:rightOperand': string; + } | { + '@type': string, + 'odrl:and': { + '@type': string, + 'odrl:leftOperand': string; + 'odrl:operator': { + '@id': string; + }; + 'odrl:rightOperand': string; + }[] }; }; diff --git a/frontend/src/views/CatalogView.tsx b/frontend/src/views/CatalogView.tsx index 14838caf..b7ae5ea9 100644 --- a/frontend/src/views/CatalogView.tsx +++ b/frontend/src/views/CatalogView.tsx @@ -98,7 +98,7 @@ export const CatalogView = () => {

Asset condition(s):

- {item.permission['odrl:constraint']['odrl:and'] ? + {'odrl:and' in item.permission['odrl:constraint'] ? (item.permission['odrl:constraint']['odrl:and'].map(constraint => (
{constraint['odrl:leftOperand'] + ' '} From 1ad8c046815257b87378c3c832fa836a2b9796b3 Mon Sep 17 00:00:00 2001 From: Florian Zimmer Date: Wed, 17 Apr 2024 11:43:49 +0200 Subject: [PATCH 5/7] chore: Replaced asset:prop:type with dct type in frontend. Fixed small code mispellings in EdcAdapterService. --- .../common/edc/logic/service/EdcAdapterService.java | 7 +++---- frontend/src/hooks/edc/useCatalog.ts | 2 +- frontend/src/models/types/edc/catalog.ts | 4 +++- 3 files changed, 7 insertions(+), 6 deletions(-) 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 a264d73f..42c583f5 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 @@ -125,7 +125,7 @@ private Response sendPostRequest(JsonNode requestBody, List pathSegments */ public boolean registerAssetsInitially() { boolean result; - log.info("Registration of framework agreement policy successful " + (result = createFrameWorkPolicy())); + log.info("Registration of framework agreement policy successful " + (result = createContractPolicy())); log.info("Registration of DTR Asset successful " + (result &= registerDtrAsset())); log.info("Registration of ItemStock 2.0.0 submodel successful " + (result &= registerItemStockSubmodel())); return result; @@ -638,7 +638,7 @@ private boolean negotiateForPartnerItemStockSubmodel(MaterialPartnerRelation mpr if ("urn:samm:io.catenax.item_stock:2.0.0#ItemStock".equals(aasSemantics.get("@id").asText())) { if (itemStockAssetId.equals(entry.get("edc:id").asText())) { if (targetCatalogEntry == null) { - if (testFrameworkAgreementConstraint(entry)) { + if (testContractPolicyConstraints(entry)) { targetCatalogEntry = entry; } else { log.error("Contract Negotiation with partner " + partner.getBpnl() + " has " + @@ -889,7 +889,6 @@ private boolean initiateDtrTransferForLookupApi(Partner partner, String specific * authCode under index 1, the endpoint under index 2 and the contractId under index 3. * * @param partner the partner - * @param apiMethod the api method * @return A String array or null, if negotiation or transfer have failed or the authCode did not arrive */ public String[] getContractForPartTypeInfoSubmodel(Partner partner) { @@ -912,7 +911,7 @@ public String[] getContractForPartTypeInfoSubmodel(Partner partner) { String idString = semanticId.get("@id").asText(); if ("urn:samm:io.catenax.part_type_information:1.0.0#PartTypeInformation".equals(idString)) { if (targetCatalogEntry == null) { - if (testFrameworkAgreementConstraint(entry)) { + if (testContractPolicyConstraints(entry)) { targetCatalogEntry = entry; } else { log.error("Contract Negotiation for PartTypeInformation Submodel asset with partner " + partner.getBpnl() + " has " + diff --git a/frontend/src/hooks/edc/useCatalog.ts b/frontend/src/hooks/edc/useCatalog.ts index 0f97e9d3..59092671 100644 --- a/frontend/src/hooks/edc/useCatalog.ts +++ b/frontend/src/hooks/edc/useCatalog.ts @@ -33,7 +33,7 @@ export const useCatalog = (edcUrl: string | null) => { ? (data['dcat:dataset']?.map((item) => { return { assetId: item['@id'], - assetType: item['asset:prop:type'], + assetType: item['dct:type']['@id'], assetVersion: item['https://w3id.org/catenax/ontology/common#version'], permission: item['odrl:hasPolicy'] && item['odrl:hasPolicy']['odrl:permission'], prohibitions: item['odrl:hasPolicy'] && item['odrl:hasPolicy']['odrl:prohibition'], diff --git a/frontend/src/models/types/edc/catalog.ts b/frontend/src/models/types/edc/catalog.ts index 82300a34..9e2cff14 100644 --- a/frontend/src/models/types/edc/catalog.ts +++ b/frontend/src/models/types/edc/catalog.ts @@ -55,7 +55,9 @@ export type CatalogPermission = { export type RawCatalogData = { 'dcat:dataset': { '@id': string; - 'asset:prop:type': string; + 'dct:type': { + '@id': string; + } 'https://w3id.org/catenax/ontology/common#version': string; 'odrl:hasPolicy': { 'odrl:permission': CatalogPermission; From 7d0e7eae4f8d65be1329fc66a8c3a34a41503be4 Mon Sep 17 00:00:00 2001 From: Florian Zimmer Date: Fri, 19 Apr 2024 13:05:38 +0200 Subject: [PATCH 6/7] chore: Updated backend dependency files. --- DEPENDENCIES_BACKEND | 6 +++--- backend/DEPENDENCIES | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/DEPENDENCIES_BACKEND b/DEPENDENCIES_BACKEND index b8eb1e37..d5be54a6 100644 --- a/DEPENDENCIES_BACKEND +++ b/DEPENDENCIES_BACKEND @@ -45,9 +45,9 @@ maven/mavencentral/org.assertj/assertj-core/3.24.2, Apache-2.0, approved, #6161 maven/mavencentral/org.awaitility/awaitility/4.2.0, Apache-2.0, approved, #14178 maven/mavencentral/org.checkerframework/checker-qual/3.42.0, MIT, approved, clearlydefined 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.4, BSD-3-Clause, approved, ee4j.jaxb -maven/mavencentral/org.glassfish.jaxb/jaxb-runtime/4.0.4, BSD-3-Clause, approved, ee4j.jaxb -maven/mavencentral/org.glassfish.jaxb/txw2/4.0.4, BSD-3-Clause, approved, ee4j.jaxb +maven/mavencentral/org.glassfish.jaxb/jaxb-core/4.0.4, BSD-3-Clause, approved, ee4j.jaxb-impl +maven/mavencentral/org.glassfish.jaxb/jaxb-runtime/4.0.4, BSD-3-Clause, approved, ee4j.jaxb-impl +maven/mavencentral/org.glassfish.jaxb/txw2/4.0.4, BSD-3-Clause, approved, ee4j.jaxb-impl maven/mavencentral/org.hamcrest/hamcrest/2.2, BSD-3-Clause, approved, clearlydefined maven/mavencentral/org.hibernate.common/hibernate-commons-annotations/6.0.6.Final, LGPL-2.1-only, approved, #6962 maven/mavencentral/org.hibernate.orm/hibernate-core/6.4.1.Final, LGPL-2.1-or-later AND (EPL-2.0 OR BSD-3-Clause) AND MIT, approved, #12490 diff --git a/backend/DEPENDENCIES b/backend/DEPENDENCIES index b8eb1e37..d5be54a6 100644 --- a/backend/DEPENDENCIES +++ b/backend/DEPENDENCIES @@ -45,9 +45,9 @@ maven/mavencentral/org.assertj/assertj-core/3.24.2, Apache-2.0, approved, #6161 maven/mavencentral/org.awaitility/awaitility/4.2.0, Apache-2.0, approved, #14178 maven/mavencentral/org.checkerframework/checker-qual/3.42.0, MIT, approved, clearlydefined 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.4, BSD-3-Clause, approved, ee4j.jaxb -maven/mavencentral/org.glassfish.jaxb/jaxb-runtime/4.0.4, BSD-3-Clause, approved, ee4j.jaxb -maven/mavencentral/org.glassfish.jaxb/txw2/4.0.4, BSD-3-Clause, approved, ee4j.jaxb +maven/mavencentral/org.glassfish.jaxb/jaxb-core/4.0.4, BSD-3-Clause, approved, ee4j.jaxb-impl +maven/mavencentral/org.glassfish.jaxb/jaxb-runtime/4.0.4, BSD-3-Clause, approved, ee4j.jaxb-impl +maven/mavencentral/org.glassfish.jaxb/txw2/4.0.4, BSD-3-Clause, approved, ee4j.jaxb-impl maven/mavencentral/org.hamcrest/hamcrest/2.2, BSD-3-Clause, approved, clearlydefined maven/mavencentral/org.hibernate.common/hibernate-commons-annotations/6.0.6.Final, LGPL-2.1-only, approved, #6962 maven/mavencentral/org.hibernate.orm/hibernate-core/6.4.1.Final, LGPL-2.1-or-later AND (EPL-2.0 OR BSD-3-Clause) AND MIT, approved, #12490 From 4021d76867d06d87c1a8156d93c00a58a766985c Mon Sep 17 00:00:00 2001 From: Florian Zimmer Date: Wed, 24 Apr 2024 09:44:13 +0200 Subject: [PATCH 7/7] chore: Merged EdcAdapterService. --- .../backend/common/edc/logic/service/EdcAdapterService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 486aef79..d398e10c 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 @@ -126,7 +126,7 @@ private Response sendPostRequest(JsonNode requestBody, List pathSegments */ public boolean registerAssetsInitially() { boolean result; - log.info("Registration of framework agreement policy successful {}", (result = createFrameWorkPolicy())); + log.info("Registration of framework agreement policy successful {}", (result = createContractPolicy())); boolean assetRegistration; log.info("Registration of DTR Asset successful {}", (assetRegistration = registerDtrAsset())); result &= assetRegistration;