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 61b862f2..be5d0d15 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.SubmodelType; @@ -122,13 +123,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 = createContractPolicy())); boolean assetRegistration; log.info("Registration of DTR Asset successful {}", (assetRegistration = registerDtrAsset())); result &= assetRegistration; @@ -167,14 +163,13 @@ public boolean registerAssetsInitially() { * @return true, if all registrations ran successfully */ public boolean createPolicyAndContractDefForPartner(Partner partner) { - boolean result = createPolicyDefinitionForPartner(partner); + boolean result = createBpnlAndMembershipPolicyDefinitionForPartner(partner); result &= createSubmodelContractDefinitionForPartner(SubmodelType.ITEM_STOCK.URN_SEMANTIC_ID, variablesService.getItemStockSubmodelApiAssetId(), partner); result &= createSubmodelContractDefinitionForPartner(SubmodelType.PRODUCTION.URN_SEMANTIC_ID, variablesService.getProductionSubmodelApiAssetId(), partner); result &= createSubmodelContractDefinitionForPartner(SubmodelType.DEMAND.URN_SEMANTIC_ID, variablesService.getDemandSubmodelApiAssetId(), partner); result &= createSubmodelContractDefinitionForPartner(SubmodelType.DELIVERY.URN_SEMANTIC_ID, variablesService.getDeliverySubmodelApiAssetId(), partner); result &= createDtrContractDefinitionForPartner(partner); return createSubmodelContractDefinitionForPartner(SubmodelType.PART_TYPE_INFORMATION.URN_SEMANTIC_ID, variablesService.getPartTypeSubmodelApiAssetId(), partner) && result; - } private boolean createSubmodelContractDefinitionForPartner(String semanticId, String assetId, Partner partner) { @@ -209,14 +204,15 @@ private boolean createDtrContractDefinitionForPartner(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"); @@ -227,7 +223,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; } } @@ -237,8 +233,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.buildFrameworkPolicy(); try (var response = sendPostRequest(body, List.of("v2", "policydefinitions"))) { if (!response.isSuccessful()) { log.warn("Framework Policy Registration failed"); @@ -825,18 +821,14 @@ private boolean negotiateForSubmodel(MaterialPartnerRelation mpr, SubmodelType t } if (type.URN_SEMANTIC_ID.equals(idString) && submodelData.assetId.equals(entry.get("edc:id").asText())) { if (targetCatalogEntry == null) { - if (variablesService.isUseFrameworkPolicy()) { - if (testFrameworkAgreementConstraint(entry)) { - targetCatalogEntry = entry; - } else { - log.error("Contract Negotiation for " + type + " 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 (testContractPolicyConstraints(entry)) { targetCatalogEntry = entry; + } else { + log.error("Contract Negotiation for " + type + " 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()); @@ -898,36 +890,24 @@ public String getCxIdFromPartTypeInformation(MaterialPartnerRelation mpr) { * @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())) { - return false; - } - } catch (Exception e) { - log.error("Failed in Framework Agreement Test ", e); - return false; - } + private boolean testContractPolicyConstraints(JsonNode catalogEntry) { + var constraint = Optional.ofNullable(catalogEntry.get("odrl:hasPolicy")) + .map(policy -> policy.get("odrl:permission")) + .map(permission -> permission.get("odrl:constraint")); + if (constraint.isEmpty()) return false; + + var leftOperand = constraint.map(cons -> cons.get("odrl:leftOperand")) + .filter(operand -> variablesService.getPurisFrameworkAgreement().equals(operand.asText())); + + var operator = constraint.map(cons -> cons.get("odrl:operator")) + .map(op -> op.get("@id")) + .filter(operand -> "odrl:eq".equals(operand.asText())); + + var rightOperand = constraint.map(cons -> cons.get("odrl:rightOperand")) + .filter(operand -> "active".equals(operand.asText())); + + 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 962c15cb..b516d6b6 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 @@ -51,7 +51,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. @@ -79,35 +79,61 @@ public ObjectNode buildBasicCatalogRequestBody(String counterPartyDspUrl, Map { ? (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: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..9e2cff14 100644 --- a/frontend/src/models/types/edc/catalog.ts +++ b/frontend/src/models/types/edc/catalog.ts @@ -33,24 +33,36 @@ 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; + }[] }; }; 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; - '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..b7ae5ea9 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):

+
+ {'odrl:and' in item.permission['odrl:constraint'] ? + (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']} +
+ ) + } +
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 a1906d55..8ed2802c 100644 --- a/local/tractus-x-edc/config/customer/puris-backend.properties +++ b/local/tractus-x-edc/config/customer/puris-backend.properties @@ -7,7 +7,6 @@ puris.itemstocksubmodel.apiassetid=itemstocksubmodel-api-asset puris.productionsubmodel.apiassetid=productionsubmodel-api-asset puris.demandsubmodel.apiassetid=demandsubmodel-api-asset puris.deliverysubmodel.apiassetid=deliverysubmodel-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 32d6b697..4c704874 100644 --- a/local/tractus-x-edc/config/supplier/puris-backend.properties +++ b/local/tractus-x-edc/config/supplier/puris-backend.properties @@ -7,7 +7,6 @@ puris.itemstocksubmodel.apiassetid=itemstocksubmodel-api-asset puris.productionsubmodel.apiassetid=productionsubmodel-api-asset puris.demandsubmodel.apiassetid=demandsubmodel-api-asset puris.deliverysubmodel.apiassetid=deliverysubmodel-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