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 635ca97d..75d48c4a 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 @@ -34,8 +34,6 @@ import org.eclipse.tractusx.puris.backend.stock.logic.dto.itemstocksamm.DirectionCharacteristic; import org.jetbrains.annotations.Nullable; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.RequestEntity; -import org.springframework.http.RequestEntity.BodyBuilder; import org.springframework.stereotype.Service; import java.io.IOException; @@ -1153,6 +1151,17 @@ public boolean testContractPolicyConstraints(JsonNode catalogEntry) { log.debug("Constraint mismatch: we expect to have a constraint in permission node."); return false; } + + for (String rule : new String[] {"obligation", "prohibition"}) { + var policy = catalogEntry.get(EdcRequestBodyBuilder.ODRL_NAMESPACE + "hasPolicy").get(0); + var ruleNode = policy.get(EdcRequestBodyBuilder.ODRL_NAMESPACE + rule); + boolean test = ruleNode == null || (ruleNode.isArray() && ruleNode.isEmpty()); + if (!test) { + log.warn("Unexpected {} found, rejecting: {}", rule, catalogEntry.toPrettyString()); + return false; + } + } + boolean result = true; if (constraint.get().isArray() && constraint.get().size() == 2) { diff --git a/backend/src/test/java/org/eclipse/tractusx/puris/backend/common/edc/logic/service/EdcAdapterServiceTest.java b/backend/src/test/java/org/eclipse/tractusx/puris/backend/common/edc/logic/service/EdcAdapterServiceTest.java index 9de6f374..498f9b6f 100644 --- a/backend/src/test/java/org/eclipse/tractusx/puris/backend/common/edc/logic/service/EdcAdapterServiceTest.java +++ b/backend/src/test/java/org/eclipse/tractusx/puris/backend/common/edc/logic/service/EdcAdapterServiceTest.java @@ -262,4 +262,136 @@ public void oneConstraint_testContractPolicyConstraints_fails() throws JsonProce assertFalse(result); } + /** + * Tests with unexpected (non-empty) prohibition, which must be rejected + * + * @throws JsonProcessingException if json is invalid + */ + @Test + public void wrongProhibition_testContractPolicyConstraints_fails() throws JsonProcessingException { + // given + String validJson = "{\n" + + " \"@id\" : \"PartTypeInformationSubmodelApi@BPNL00000007RXRX\",\n" + + " \"@type\" : \"dcat:Dataset\",\n" + + " \"odrl:hasPolicy\" : {\n" + + " \"@id\" : \"QlBOTDAwMDAwMDA3UlRVUF9jb250cmFjdGRlZmluaXRpb25fZm9yX1BhcnRUeXBlSW5mb3JtYXRpb25TdWJtb2RlbEFwaUBCUE5MMDAwMDAwMDdSWFJY:UGFydFR5cGVJbmZvcm1hdGlvblN1Ym1vZGVsQXBpQEJQTkwwMDAwMDAwN1JYUlg=:NzE3MGJmZDMtYTg5NS00YmU2LWI5Y2EtMDVhYTUwY2VjMDk2\",\n" + + " \"@type\" : \"odrl:Offer\",\n" + + " \"odrl:permission\" : {\n" + + " \"odrl:action\" : {\n" + + " \"@id\" : \"odrl:use\"\n" + + " },\n" + + " \"odrl:constraint\" : {\n" + + " \"odrl:and\" : [ {\n" + + " \"odrl:leftOperand\" : { \"@id\": \"cx-policy:FrameworkAgreement\"},\n" + + " \"odrl:operator\" : {\n" + + " \"@id\" : \"odrl:eq\"\n" + + " },\n" + + " \"odrl:rightOperand\" : \"Puris:1.0\"\n" + + " }, {\n" + + " \"odrl:leftOperand\" : { \"@id\": \"cx-policy:UsagePurpose\"},\n" + + " \"odrl:operator\" : {\n" + + " \"@id\" : \"odrl:eq\"\n" + + " },\n" + + " \"odrl:rightOperand\" : \"cx.puris.base:1\"\n" + + " } ]\n" + + " }\n" + + " },\n" + + " \"odrl:prohibition\" : [ {\"foo\": \"bar\"} ],\n" + + " \"odrl:obligation\" : [ ]\n" + + " }," + + " \"@context\": {\n" + + " \"@vocab\": \"https://w3id.org/edc/v0.0.1/ns/\",\n" + + " \"edc\": \"https://w3id.org/edc/v0.0.1/ns/\",\n" + + " \"tx\": \"https://w3id.org/tractusx/v0.0.1/ns/\",\n" + + " \"tx-auth\": \"https://w3id.org/tractusx/auth/\",\n" + + " \"cx-policy\": \"https://w3id.org/catenax/policy/\",\n" + + " \"dcat\": \"http://www.w3.org/ns/dcat#\",\n" + + " \"dct\": \"http://purl.org/dc/terms/\",\n" + + " \"odrl\": \"http://www.w3.org/ns/odrl/2/\",\n" + + " \"dspace\": \"https://w3id.org/dspace/v0.8/\"\n" + + " }" + + "}"; + + JsonNode validJsonNode = objectMapper.readTree(validJson); + JsonLdUtils jsonLdUtils = new JsonLdUtils(); + validJsonNode = jsonLdUtils.expand(validJsonNode); + System.out.println(validJsonNode.toPrettyString()); + + + // when + when(variablesService.getPurisFrameworkAgreementWithVersion()).thenReturn("Puris:1.0"); + when(variablesService.getPurisPurposeWithVersion()).thenReturn("cx.puris.base:1"); + + // then + boolean result = edcAdapterService.testContractPolicyConstraints(validJsonNode); + + assertFalse(result); + } + + /** + * Tests policy with unexpected (non-empty) obligation, which must be rejected + * + * @throws JsonProcessingException if json is invalid + */ + @Test + public void wrongObligation_testContractPolicyConstraints_fails() throws JsonProcessingException { + // given + String validJson = "{\n" + + " \"@id\" : \"PartTypeInformationSubmodelApi@BPNL00000007RXRX\",\n" + + " \"@type\" : \"dcat:Dataset\",\n" + + " \"odrl:hasPolicy\" : {\n" + + " \"@id\" : \"QlBOTDAwMDAwMDA3UlRVUF9jb250cmFjdGRlZmluaXRpb25fZm9yX1BhcnRUeXBlSW5mb3JtYXRpb25TdWJtb2RlbEFwaUBCUE5MMDAwMDAwMDdSWFJY:UGFydFR5cGVJbmZvcm1hdGlvblN1Ym1vZGVsQXBpQEJQTkwwMDAwMDAwN1JYUlg=:NzE3MGJmZDMtYTg5NS00YmU2LWI5Y2EtMDVhYTUwY2VjMDk2\",\n" + + " \"@type\" : \"odrl:Offer\",\n" + + " \"odrl:permission\" : {\n" + + " \"odrl:action\" : {\n" + + " \"@id\" : \"odrl:use\"\n" + + " },\n" + + " \"odrl:constraint\" : {\n" + + " \"odrl:and\" : [ {\n" + + " \"odrl:leftOperand\" : { \"@id\": \"cx-policy:FrameworkAgreement\"},\n" + + " \"odrl:operator\" : {\n" + + " \"@id\" : \"odrl:eq\"\n" + + " },\n" + + " \"odrl:rightOperand\" : \"Puris:1.0\"\n" + + " }, {\n" + + " \"odrl:leftOperand\" : { \"@id\": \"cx-policy:UsagePurpose\"},\n" + + " \"odrl:operator\" : {\n" + + " \"@id\" : \"odrl:eq\"\n" + + " },\n" + + " \"odrl:rightOperand\" : \"cx.puris.base:1\"\n" + + " } ]\n" + + " }\n" + + " },\n" + + " \"odrl:prohibition\" : [ ],\n" + + " \"odrl:obligation\" : [ {\"foo\": \"bar\"} ]\n" + + " }," + + " \"@context\": {\n" + + " \"@vocab\": \"https://w3id.org/edc/v0.0.1/ns/\",\n" + + " \"edc\": \"https://w3id.org/edc/v0.0.1/ns/\",\n" + + " \"tx\": \"https://w3id.org/tractusx/v0.0.1/ns/\",\n" + + " \"tx-auth\": \"https://w3id.org/tractusx/auth/\",\n" + + " \"cx-policy\": \"https://w3id.org/catenax/policy/\",\n" + + " \"dcat\": \"http://www.w3.org/ns/dcat#\",\n" + + " \"dct\": \"http://purl.org/dc/terms/\",\n" + + " \"odrl\": \"http://www.w3.org/ns/odrl/2/\",\n" + + " \"dspace\": \"https://w3id.org/dspace/v0.8/\"\n" + + " }" + + "}"; + + JsonNode validJsonNode = objectMapper.readTree(validJson); + JsonLdUtils jsonLdUtils = new JsonLdUtils(); + validJsonNode = jsonLdUtils.expand(validJsonNode); + System.out.println(validJsonNode.toPrettyString()); + + + // when + when(variablesService.getPurisFrameworkAgreementWithVersion()).thenReturn("Puris:1.0"); + when(variablesService.getPurisPurposeWithVersion()).thenReturn("cx.puris.base:1"); + + // then + boolean result = edcAdapterService.testContractPolicyConstraints(validJsonNode); + + assertFalse(result); + } + }