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 17e95222..139a43d1 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 @@ -1151,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..0f786251 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 @@ -30,6 +30,8 @@ import org.eclipse.tractusx.puris.backend.common.util.VariablesService; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -59,6 +61,8 @@ public class EdcAdapterServiceTest { private final Pattern urlPattern = PatternStore.URL_PATTERN; + private final JsonLdUtils jsonLdUtils = new JsonLdUtils(); + @BeforeEach void setUp() throws Exception { MockitoAnnotations.openMocks(this); @@ -120,7 +124,6 @@ public void correctConstraints_testContractPolicyConstraints_succeed() throws Js "}"; JsonNode validJsonNode = objectMapper.readTree(validJson); - JsonLdUtils jsonLdUtils = new JsonLdUtils(); validJsonNode = jsonLdUtils.expand(validJsonNode); System.out.println(validJsonNode.toPrettyString()); @@ -190,7 +193,6 @@ public void wrongConstraints_testContractPolicyConstraints_fails() throws JsonPr "}"; JsonNode invalidJsonNode = objectMapper.readTree(invalidJson); - JsonLdUtils jsonLdUtils = new JsonLdUtils(); invalidJsonNode = jsonLdUtils.expand(invalidJsonNode); // when @@ -249,7 +251,6 @@ public void oneConstraint_testContractPolicyConstraints_fails() throws JsonProce "}"; JsonNode invalidJsonNode = objectMapper.readTree(invalidJson); - JsonLdUtils jsonLdUtils = new JsonLdUtils(); invalidJsonNode = jsonLdUtils.expand(invalidJsonNode); // when @@ -262,4 +263,111 @@ public void oneConstraint_testContractPolicyConstraints_fails() throws JsonProce assertFalse(result); } + /** + * Tests policy with unexpected (non-empty) obligation or prohibition, which must be rejected + * + * @throws JsonProcessingException if json is invalid + */ + @ParameterizedTest + @ValueSource(strings = {unexpectedObligation, unexpectedProhibition}) + public void unexpectedRule_testContractPolicyConstraints_fails(String input) throws JsonProcessingException { + // given + JsonNode invalidJsonNode = objectMapper.readTree(input); + invalidJsonNode = jsonLdUtils.expand(invalidJsonNode); + System.out.println(invalidJsonNode.toPrettyString()); + + // when + when(variablesService.getPurisFrameworkAgreementWithVersion()).thenReturn("Puris:1.0"); + when(variablesService.getPurisPurposeWithVersion()).thenReturn("cx.puris.base:1"); + + // then + boolean result = edcAdapterService.testContractPolicyConstraints(invalidJsonNode); + assertFalse(result); + } + + + private final static String unexpectedProhibition = "{\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" + + " }" + + "}"; + + private final static String unexpectedObligation = "{\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" + + " }" + + "}"; + }