diff --git a/CHANGELOG.md b/CHANGELOG.md index 58c6ec2c0a..1c768dcaae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ _**For better traceability add the corresponding GitHub issue number in each cha ## [Unreleased] +### Changed +- Default policies are now configured using JSON in accordance with the ODRL schema. #542 + ## [5.3.0] - 2024-07-15 ### Added diff --git a/charts/item-relationship-service/CHANGELOG.md b/charts/item-relationship-service/CHANGELOG.md index 860501bd74..479d90ae7a 100644 --- a/charts/item-relationship-service/CHANGELOG.md +++ b/charts/item-relationship-service/CHANGELOG.md @@ -6,6 +6,55 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Changed configuration for default policies (#542) from: + ``` + edc: + catalog: + acceptedPolicies: + - leftOperand: "https://w3id.org/catenax/policy/FrameworkAgreement" + operator: "eq" + rightOperand: "traceability:1.0" + - leftOperand: "https://w3id.org/catenax/policy/UsagePurpose" + operator: "eq" + rightOperand: "cx.core.industrycore:1" + ``` + to: + ``` + edc: + catalog: + acceptedPolicies: > + [{ + "policyId": "default-policy", + "createdOn": "2024-07-17T16:15:14.12345678Z", + "validUntil": "9999-01-01T00:00:00.00000000Z", + "permissions": [ + { + "action": "use", + "constraint": { + "and": [ + { + "leftOperand": "https://w3id.org/catenax/policy/FrameworkAgreement", + "operator": { + "@id": "eq" + }, + "rightOperand": "traceability:1.0" + }, + { + "leftOperand": "https://w3id.org/catenax/policy/UsagePurpose", + "operator": { + "@id": "eq" + }, + "rightOperand": "cx.core.industrycore:1" + } + ] + } + } + ] + }] + ``` + ## [7.3.1] - 2024-07-16 ### Changed diff --git a/charts/item-relationship-service/templates/configmap-spring-app-config.yaml b/charts/item-relationship-service/templates/configmap-spring-app-config.yaml index 92b09f6f6b..aefab9cf42 100644 --- a/charts/item-relationship-service/templates/configmap-spring-app-config.yaml +++ b/charts/item-relationship-service/templates/configmap-spring-app-config.yaml @@ -118,12 +118,7 @@ data: submodel-suffix: {{ tpl (.Values.edc.submodel.suffix | default "/$value") . | quote }} catalog: - acceptedPolicies: - {{- range .Values.edc.catalog.acceptedPolicies}} - - leftOperand: {{ .leftOperand | quote }} - operator: {{ .operator | quote }} - rightOperand: {{ .rightOperand | quote }} - {{- end }} + acceptedPolicies: {{ .Values.edc.catalog.acceptedPolicies | trim | b64enc | quote }} discoveryFinderClient: cacheTTL: {{ .Values.edc.discoveryFinderClient.cacheTTL | quote }} connectorEndpointService: diff --git a/charts/item-relationship-service/values.yaml b/charts/item-relationship-service/values.yaml index 3fd4b42f15..fa818ec8f6 100644 --- a/charts/item-relationship-service/values.yaml +++ b/charts/item-relationship-service/values.yaml @@ -187,15 +187,40 @@ edc: urnprefix: /urn suffix: /$value catalog: - # IRS will only negotiate contracts for offers with a policy as defined in the allowedNames list. - # If a requested asset does not provide one of these policies, a tombstone will be created and this node will not be processed. - acceptedPolicies: - - leftOperand: "https://w3id.org/catenax/policy/FrameworkAgreement" - operator: "eq" - rightOperand: "traceability:1.0" - - leftOperand: "https://w3id.org/catenax/policy/UsagePurpose" - operator: "eq" - rightOperand: "cx.core.industrycore:1" + # IRS will only negotiate contracts for offers with a policy as defined in the Policy Store. + # The following configuration value allows the definition of default policies to be used + # if no policy has been defined via the Policy Store API. + # If the policy check fails, a tombstone will be created and this node will not be processed. + # Configure the default policies as JSON array using multiline string here. + acceptedPolicies: > + [{ + "policyId": "default-policy", + "createdOn": "2024-07-17T16:15:14.12345678Z", + "validUntil": "9999-01-01T00:00:00.00000000Z", + "permissions": [ + { + "action": "use", + "constraint": { + "and": [ + { + "leftOperand": "https://w3id.org/catenax/policy/FrameworkAgreement", + "operator": { + "@id": "eq" + }, + "rightOperand": "traceability:1.0" + }, + { + "leftOperand": "https://w3id.org/catenax/policy/UsagePurpose", + "operator": { + "@id": "eq" + }, + "rightOperand": "cx.core.industrycore:1" + } + ] + } + } + ] + }] discoveryFinderClient: cacheTTL: PT24H # Time to live for DiscoveryFinderClient for findDiscoveryEndpoints method cache connectorEndpointService: diff --git a/docs/src/docs/administration/configuration.adoc b/docs/src/docs/administration/configuration.adoc index 5921895f1f..6f9dbb509c 100644 --- a/docs/src/docs/administration/configuration.adoc +++ b/docs/src/docs/administration/configuration.adoc @@ -14,7 +14,7 @@ include::irs-spring-config.adoc[leveloffset=+1] [source,yaml] ---- -include::../../../../charts/item-relationship-service/values.yaml[lines=104..302] +include::../../../../charts/item-relationship-service/values.yaml[lines=104..338] ---- <1> Use this to enable or disable the monitoring components diff --git a/irs-api/src/main/resources/application.yml b/irs-api/src/main/resources/application.yml index 93b9a76f7a..c4a22509a5 100644 --- a/irs-api/src/main/resources/application.yml +++ b/irs-api/src/main/resources/application.yml @@ -154,15 +154,12 @@ irs-edc-client: connect: PT90S # HTTP connect timeout for the submodel client catalog: - # IRS will only negotiate contracts for offers with a policy as defined in the acceptedPolicies list. - # If a requested asset does not provide one of these policies, a tombstone will be created and this node will not be processed. - acceptedPolicies: - - leftOperand: "cx-policy:FrameworkAgreement" - operator: "eq" - rightOperand: "traceability:1.0" - - leftOperand: "cx-policy:UsagePurpose" - operator: "eq" - rightOperand: "cx.core.industrycore:1" + # IRS will only negotiate contracts for offers with a policy as defined in the Policy Store. + # The following configuration value allows the definition of default policies to be used + # if no policy has been defined via the Policy Store API. + # If the policy check fails, a tombstone will be created and this node will not be processed. + # The value must be Base64 encoded here. See decoded value in charts/item-relationship-service/values.yaml. + acceptedPolicies: "W3sKICAgICJwb2xpY3lJZCI6ICJkZWZhdWx0LXBvbGljeSIsCiAgICAiY3JlYXRlZE9uIjogIjIwMjQtMDctMTdUMTY6MTU6MTQuMTIzNDU2NzhaIiwKICAgICJ2YWxpZFVudGlsIjogIjk5OTktMDEtMDFUMDA6MDA6MDAuMDAwMDAwMDBaIiwKICAgICJwZXJtaXNzaW9ucyI6IFsKICAgICAgICB7CiAgICAgICAgICAgICJhY3Rpb24iOiAidXNlIiwKICAgICAgICAgICAgImNvbnN0cmFpbnQiOiB7CiAgICAgICAgICAgICAgICAiYW5kIjogWwogICAgICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxlZnRPcGVyYW5kIjogImh0dHBzOi8vdzNpZC5vcmcvY2F0ZW5heC9wb2xpY3kvRnJhbWV3b3JrQWdyZWVtZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhdG9yIjogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgIkBpZCI6ICJlcSIKICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgInJpZ2h0T3BlcmFuZCI6ICJ0cmFjZWFiaWxpdHk6MS4wIgogICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgICAgICAibGVmdE9wZXJhbmQiOiAiaHR0cHM6Ly93M2lkLm9yZy9jYXRlbmF4L3BvbGljeS9Vc2FnZVB1cnBvc2UiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmF0b3IiOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQGlkIjogImVxIgogICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAicmlnaHRPcGVyYW5kIjogImN4LmNvcmUuaW5kdXN0cnljb3JlOjEiCiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgXQogICAgICAgICAgICB9CiAgICAgICAgfQogICAgXQp9XQ==" discoveryFinderClient: cacheTTL: PT24H # Time to live for DiscoveryFinderClient for findDiscoveryEndpoints method cache connectorEndpointService: diff --git a/irs-api/src/test/java/org/eclipse/tractusx/irs/IrsWireMockIntegrationTest.java b/irs-api/src/test/java/org/eclipse/tractusx/irs/IrsWireMockIntegrationTest.java index 67c1efa978..4f70fb6c7b 100644 --- a/irs-api/src/test/java/org/eclipse/tractusx/irs/IrsWireMockIntegrationTest.java +++ b/irs-api/src/test/java/org/eclipse/tractusx/irs/IrsWireMockIntegrationTest.java @@ -337,8 +337,7 @@ void shouldCreateDetailedTombstoneForEdcErrors() { assertThat(jobForJobId.getTombstones()).hasSize(1); final Tombstone actualTombstone = jobForJobId.getTombstones().get(0); assertThat(actualTombstone.getProcessingError().getRootCauses()).hasSize(1); - assertThat(actualTombstone.getProcessingError().getRootCauses().get(0)).contains( - "502 Bad Gateway"); + assertThat(actualTombstone.getProcessingError().getRootCauses().get(0)).contains("502 Bad Gateway"); } @Test diff --git a/irs-api/src/test/java/org/eclipse/tractusx/irs/util/StringMapperTest.java b/irs-api/src/test/java/org/eclipse/tractusx/irs/util/StringMapperTest.java index 4120d778cb..7ee83c63a0 100644 --- a/irs-api/src/test/java/org/eclipse/tractusx/irs/util/StringMapperTest.java +++ b/irs-api/src/test/java/org/eclipse/tractusx/irs/util/StringMapperTest.java @@ -26,10 +26,22 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import java.nio.charset.StandardCharsets; +import java.time.OffsetDateTime; +import java.util.Base64; +import java.util.List; + +import com.fasterxml.jackson.core.type.TypeReference; import org.eclipse.tractusx.irs.component.Bpn; import org.eclipse.tractusx.irs.component.Description; import org.eclipse.tractusx.irs.data.JsonParseException; import org.eclipse.tractusx.irs.data.StringMapper; +import org.eclipse.tractusx.irs.edc.client.policy.Constraint; +import org.eclipse.tractusx.irs.edc.client.policy.Constraints; +import org.eclipse.tractusx.irs.edc.client.policy.OperatorType; +import org.eclipse.tractusx.irs.edc.client.policy.Permission; +import org.eclipse.tractusx.irs.edc.client.policy.Policy; +import org.eclipse.tractusx.irs.edc.client.policy.PolicyType; import org.junit.jupiter.api.Test; class StringMapperTest { @@ -44,8 +56,7 @@ void mapToString() { @Test void shouldThrowParseExceptionWhenMappingToString() { final var object = new Object(); - assertThatThrownBy(() -> StringMapper.mapToString(object)).isInstanceOf( - JsonParseException.class); + assertThatThrownBy(() -> StringMapper.mapToString(object)).isInstanceOf(JsonParseException.class); } @Test @@ -61,4 +72,84 @@ void shouldThrowParseExceptionWhenMappingFromString() { assertThatThrownBy(() -> StringMapper.mapFromString("test", Description.class)).isInstanceOf( JsonParseException.class); } + + @Test + void shouldMapFromBase64StringUsingTypeReference() { + + // ARRANGE + final TypeReference> listOfPoliciesType = new TypeReference<>() { + }; + + final String originalJsonStr = """ + [{ + "policyId": "default-trace-policy", + "createdOn": "2024-07-17T16:15:14.12345678Z", + "validUntil": "9999-01-01T00:00:00.00000000Z", + "permissions": [ + { + "action": "use", + "constraint": { + "and": [ + { + "leftOperand": "https://w3id.org/catenax/policy/FrameworkAgreement", + "operator": { + "@id": "eq" + }, + "rightOperand": "traceability:1.0" + }, + { + "leftOperand": "https://w3id.org/catenax/policy/UsagePurpose", + "operator": { + "@id": "eq" + }, + "rightOperand": "cx.core.industrycore:1" + } + ] + } + } + ] + }] + """; + final String originalJsonBase64 = new String( + Base64.getEncoder().encode(originalJsonStr.getBytes(StandardCharsets.UTF_8))); + + // ACT + // convert back andConstraints forth to facilitate comparison + final List listOfPolicies = StringMapper.mapFromBase64String(originalJsonBase64, listOfPoliciesType); + final String backToString = StringMapper.mapToString(listOfPolicies); + final List backToObj = StringMapper.mapFromString(backToString, listOfPoliciesType); + + // ASSERT + { + assertThat(listOfPolicies).hasSize(1); + assertThat(backToObj).hasSize(1); + assertThat(backToObj).usingRecursiveComparison().isEqualTo(listOfPolicies); + + final Policy policy = listOfPolicies.get(0); + assertThat(policy.getPolicyId()).isEqualTo("default-trace-policy"); + assertThat(policy.getValidUntil()).isEqualTo(OffsetDateTime.parse("9999-01-01T00:00:00.00000000Z")); + assertThat(policy.getCreatedOn()).isEqualTo(OffsetDateTime.parse("2024-07-17T16:15:14.12345678Z")); + assertThat(policy.getPermissions()).hasSize(1); + + final Permission permission = policy.getPermissions().get(0); + assertThat(permission.getAction()).isEqualTo(PolicyType.USE); + + final Constraints constraints = permission.getConstraint(); + final List andConstraints = constraints.getAnd(); + assertThat(andConstraints).hasSize(2); + { + final Constraint constraint = andConstraints.get(0); + assertThat(constraint.getLeftOperand()).isEqualTo("https://w3id.org/catenax/policy/FrameworkAgreement"); + assertThat(constraint.getOperator().getOperatorType()).isEqualTo(OperatorType.EQ); + assertThat(constraint.getRightOperand()).isEqualTo("traceability:1.0"); + } + { + final Constraint constraint = andConstraints.get(1); + assertThat(constraint.getLeftOperand()).isEqualTo("https://w3id.org/catenax/policy/UsagePurpose"); + assertThat(constraint.getOperator().getOperatorType()).isEqualTo(OperatorType.EQ); + assertThat(constraint.getRightOperand()).isEqualTo("cx.core.industrycore:1"); + } + } + } + } \ No newline at end of file diff --git a/irs-models/src/main/java/org/eclipse/tractusx/irs/data/StringMapper.java b/irs-models/src/main/java/org/eclipse/tractusx/irs/data/StringMapper.java index 9f60be9b4c..7ca34c08c0 100644 --- a/irs-models/src/main/java/org/eclipse/tractusx/irs/data/StringMapper.java +++ b/irs-models/src/main/java/org/eclipse/tractusx/irs/data/StringMapper.java @@ -23,7 +23,13 @@ ********************************************************************************/ package org.eclipse.tractusx.irs.data; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Objects; + import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; @@ -39,6 +45,9 @@ @Slf4j @NoArgsConstructor(access = AccessLevel.PRIVATE) public class StringMapper { + + private static final Charset CHARSET = StandardCharsets.UTF_8; + private static final ObjectMapper MAPPER = new ObjectMapper(); static { @@ -56,6 +65,13 @@ public static String mapToString(final Object value) { } } + public static T mapFromBase64String(final String value, final TypeReference typeReference) { + if (value == null) { + return null; + } + return mapFromString(fromBase64(value), typeReference); + } + public static T mapFromString(final String value, final Class clazz) { try { return MAPPER.readValue(value, clazz); @@ -64,4 +80,22 @@ public static T mapFromString(final String value, final Class clazz) { } } + public static T mapFromString(final String value, final TypeReference typeReference) { + try { + return MAPPER.readValue(value, typeReference); + } catch (final JsonProcessingException e) { + throw new JsonParseException(e); + } + } + + public static String toBase64(final String str) { + Objects.requireNonNull(str); + return new String(Base64.getEncoder().encode(str.trim().getBytes(CHARSET))); + } + + public static String fromBase64(final String value) { + Objects.requireNonNull(value); + return new String(Base64.getDecoder().decode(value.trim()), CHARSET); + } + } diff --git a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/config/DefaultAcceptedPoliciesConfig.java b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/config/DefaultAcceptedPoliciesConfig.java index 68b69ee6c8..8b511d7956 100644 --- a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/config/DefaultAcceptedPoliciesConfig.java +++ b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/config/DefaultAcceptedPoliciesConfig.java @@ -23,8 +23,6 @@ ********************************************************************************/ package org.eclipse.tractusx.irs.policystore.config; -import java.util.List; - import lombok.AllArgsConstructor; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -37,7 +35,11 @@ @ConfigurationProperties(prefix = "irs-edc-client.catalog") @Data public class DefaultAcceptedPoliciesConfig { - private List acceptedPolicies; + + /** + * Accepted policies as a Base64 encoded string. + */ + private String acceptedPolicies; /** * Accepted Policy for diff --git a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/services/PolicyStoreService.java b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/services/PolicyStoreService.java index 776623dd57..0f50e5d1ae 100644 --- a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/services/PolicyStoreService.java +++ b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/services/PolicyStoreService.java @@ -28,7 +28,6 @@ import java.time.Clock; import java.time.OffsetDateTime; import java.util.AbstractMap; -import java.util.ArrayList; import java.util.Comparator; import java.util.LinkedList; import java.util.List; @@ -38,18 +37,14 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import com.fasterxml.jackson.core.type.TypeReference; import jakarta.json.JsonObject; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.eclipse.tractusx.irs.data.StringMapper; import org.eclipse.tractusx.irs.edc.client.policy.AcceptedPoliciesProvider; import org.eclipse.tractusx.irs.edc.client.policy.AcceptedPolicy; -import org.eclipse.tractusx.irs.edc.client.policy.Constraint; -import org.eclipse.tractusx.irs.edc.client.policy.Constraints; -import org.eclipse.tractusx.irs.edc.client.policy.Operator; -import org.eclipse.tractusx.irs.edc.client.policy.OperatorType; -import org.eclipse.tractusx.irs.edc.client.policy.Permission; import org.eclipse.tractusx.irs.edc.client.policy.Policy; -import org.eclipse.tractusx.irs.edc.client.policy.PolicyType; import org.eclipse.tractusx.irs.edc.client.transformer.EdcTransformer; import org.eclipse.tractusx.irs.policystore.config.DefaultAcceptedPoliciesConfig; import org.eclipse.tractusx.irs.policystore.exceptions.PolicyStoreException; @@ -71,6 +66,9 @@ }) public class PolicyStoreService implements AcceptedPoliciesProvider { + private static final TypeReference> LIST_OF_POLICIES_TYPE = new TypeReference<>() { + }; + private final List allowedPoliciesFromConfig; private final PolicyPersistence persistence; @@ -200,7 +198,8 @@ public void deletePolicy(final String policyId) { throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Policy with id '%s' not found".formatted(policyId)); } else if (bpnsContainingPolicyId.stream().noneMatch(StringUtils::isNotEmpty)) { - throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "A configured default policy cannot be deleted. " + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, // + "A configured default policy cannot be deleted. " + "It can be overridden by defining a default policy via the API instead."); } else { try { @@ -297,26 +296,8 @@ private AcceptedPolicy toAcceptedPolicy(final Policy policy) { return new AcceptedPolicy(policy, policy.getValidUntil()); } - private List createDefaultPolicyFromConfig( - final DefaultAcceptedPoliciesConfig defaultAcceptedPoliciesConfig) { - - final List constraints = new ArrayList<>(); - defaultAcceptedPoliciesConfig.getAcceptedPolicies() - .forEach(acceptedPolicy -> constraints.add( - new Constraint(acceptedPolicy.getLeftOperand(), - new Operator(OperatorType.fromValue(acceptedPolicy.getOperator())), - acceptedPolicy.getRightOperand()))); - - final OffsetDateTime now = OffsetDateTime.now(clock); - return List.of(Policy.builder() - .policyId(ConfiguredDefaultPolicy.DEFAULT_POLICY_ID) - .createdOn(now) - .validUntil(now.plusYears(ConfiguredDefaultPolicy.DEFAULT_POLICY_LIFETIME_YEARS)) - .permissions(List.of(Permission.builder() - .action(PolicyType.USE) - .constraint(new Constraints(constraints, constraints)) - .build())) - .build()); + private List createDefaultPolicyFromConfig(final DefaultAcceptedPoliciesConfig defaultPoliciesConfig) { + return StringMapper.mapFromBase64String(defaultPoliciesConfig.getAcceptedPolicies(), LIST_OF_POLICIES_TYPE); } } diff --git a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/services/PolicyStoreServiceTest.java b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/services/PolicyStoreServiceTest.java index 27857d9483..a5a95bb4a2 100644 --- a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/services/PolicyStoreServiceTest.java +++ b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/services/PolicyStoreServiceTest.java @@ -47,7 +47,9 @@ import org.eclipse.edc.core.transform.TypeTransformerRegistryImpl; import org.eclipse.edc.jsonld.TitaniumJsonLd; import org.eclipse.edc.spi.monitor.ConsoleMonitor; +import org.eclipse.tractusx.irs.data.StringMapper; import org.eclipse.tractusx.irs.edc.client.policy.AcceptedPolicy; +import org.eclipse.tractusx.irs.edc.client.policy.Constraint; import org.eclipse.tractusx.irs.edc.client.policy.ConstraintConstants; import org.eclipse.tractusx.irs.edc.client.policy.Constraints; import org.eclipse.tractusx.irs.edc.client.policy.Permission; @@ -76,8 +78,6 @@ class PolicyStoreServiceTest { private static final String BPN = "testBpn"; - private static final String EXAMPLE_ALLOWED_NAME = "ID 3.1 Trace"; - private static final String EXAMPLE_ACCEPTED_LEFT_OPERAND = "PURPOSE"; private static final String REGISTER_POLICY_EXAMPLE_PAYLOAD = PolicyStoreControllerTest.REGISTER_POLICY_EXAMPLE_PAYLOAD; private final Clock clock = Clock.systemUTC(); @@ -100,7 +100,46 @@ class PolicyStoreServiceTest { @BeforeEach void setUp() { final DefaultAcceptedPoliciesConfig defaultAcceptedPoliciesConfig = new DefaultAcceptedPoliciesConfig(); - defaultAcceptedPoliciesConfig.setAcceptedPolicies(List.of()); + final String defaultPoliciesStr = """ + [{ + "policyId": "default-policy", + "createdOn": "2024-07-17T16:15:14.12345678Z", + "validUntil": "9999-01-01T00:00:00.00000000Z", + "permissions": [ + { + "action": "use", + "constraint": { + "and": [ + { + "leftOperand": "https://w3id.org/catenax/policy/FrameworkAgreement", + "operator": { + "@id": "eq" + }, + "rightOperand": "traceability:1.0" + }, + { + "leftOperand": "https://w3id.org/catenax/policy/UsagePurpose", + "operator": { + "@id": "eq" + }, + "rightOperand": "cx.core.industrycore:1" + } + ], + "or": [ + { + "leftOperand": "https://w3id.org/catenax/policy/SomethingElse", + "operator": { + "@id": "eq" + }, + "rightOperand": "somethingElse:1.0" + } + ] + } + } + ] + }] + """; + defaultAcceptedPoliciesConfig.setAcceptedPolicies(StringMapper.toBase64(defaultPoliciesStr)); testee = new PolicyStoreService(defaultAcceptedPoliciesConfig, persistenceMock, edcTransformer, clock); } @@ -279,18 +318,6 @@ void getStoredPolicies_shouldReturnAllPoliciesStoredForTheBpn() { @Test void getStoredPolicies_whenNoPoliciesForBpn_shouldReturnTheConfiguredDefaultPolicies() { - - // ARRANGE - - // default policy configuration - final DefaultAcceptedPoliciesConfig.AcceptedPolicy acceptedPolicy1 = new DefaultAcceptedPoliciesConfig.AcceptedPolicy( - EXAMPLE_ACCEPTED_LEFT_OPERAND, "eq", EXAMPLE_ALLOWED_NAME); - final DefaultAcceptedPoliciesConfig.AcceptedPolicy acceptedPolicy2 = new DefaultAcceptedPoliciesConfig.AcceptedPolicy( - EXAMPLE_ACCEPTED_LEFT_OPERAND, "eq", EXAMPLE_ALLOWED_NAME); - final DefaultAcceptedPoliciesConfig defaultAcceptedPoliciesConfig = new DefaultAcceptedPoliciesConfig(); - defaultAcceptedPoliciesConfig.setAcceptedPolicies(List.of(acceptedPolicy1, acceptedPolicy2)); - testee = new PolicyStoreService(defaultAcceptedPoliciesConfig, persistenceMock, edcTransformer, clock); - // ACT final var defaultPolicies = testee.getStoredPolicies(List.of(BPN)); @@ -301,8 +328,14 @@ void getStoredPolicies_whenNoPoliciesForBpn_shouldReturnTheConfiguredDefaultPoli assertThat(permissionList).hasSize(1); final Constraints constraints = permissionList.get(0).getConstraint(); - assertThat(constraints.getOr()).hasSize(2); + assertThat(constraints.getOr()).hasSize(1); + assertThat(constraints.getOr().get(0).getLeftOperand()).isEqualTo( + "https://w3id.org/catenax/policy/SomethingElse"); + assertThat(constraints.getAnd()).hasSize(2); + assertThat(constraints.getAnd().stream().map(Constraint::getLeftOperand)).containsExactlyInAnyOrder( + "https://w3id.org/catenax/policy/FrameworkAgreement", + "https://w3id.org/catenax/policy/UsagePurpose"); } } diff --git a/irs-registry-client/README.md b/irs-registry-client/README.md index 15778aba25..af30297c27 100644 --- a/irs-registry-client/README.md +++ b/irs-registry-client/README.md @@ -64,18 +64,13 @@ irs-edc-client: catalog: - # IRS will only negotiate contracts for offers with a policy as defined in the acceptedPolicies list. - # If a requested asset does not provide one of these policies, a tombstone will be created and this node will not be processed. - acceptedPolicies: - - leftOperand: "PURPOSE" - operator: "eq" - rightOperand: "ID 3.1 Trace" - - leftOperand: "FrameworkAgreement.traceability" - operator: "eq" - rightOperand: "active" - - leftOperand: "Membership" - operator: "eq" - rightOperand: "active" + # IRS will only negotiate contracts for offers with a policy as defined in the Policy Store. + # The following configuration value allows the definition of default policies to be used + # if no policy has been defined via the Policy Store API. + # If the policy check fails, a tombstone will be created and this node will not be processed. + # The value must be Base64 encoded here. + acceptedPolicies: "W3sKICAgICJwb2xpY3lJZCI6ICJkZWZhdWx0LXRyYWNlLXBvbGljeSIsCiAgICAiY3JlYXRlZE9uIjogIjIwMjQtMDctMTdUMTY6MTU6MTQuMTIzNDU2NzhaIiwKICAgICJ2YWxpZFVudGlsIjogIjk5OTktMDEtMDFUMDA6MDA6MDAuMDAwMDAwMDBaIiwKICAgICJwZXJtaXNzaW9ucyI6IFsKICAgICAgICB7CiAgICAgICAgICAgICJhY3Rpb24iOiAidXNlIiwKICAgICAgICAgICAgImNvbnN0cmFpbnQiOiB7CiAgICAgICAgICAgICAgICAiYW5kIjogWwogICAgICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxlZnRPcGVyYW5kIjogImh0dHBzOi8vdzNpZC5vcmcvY2F0ZW5heC9wb2xpY3kvRnJhbWV3b3JrQWdyZWVtZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhdG9yIjogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgIkBpZCI6ICJlcSIKICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgInJpZ2h0T3BlcmFuZCI6ICJ0cmFjZWFiaWxpdHk6MS4wIgogICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgICAgICAibGVmdE9wZXJhbmQiOiAiaHR0cHM6Ly93M2lkLm9yZy9jYXRlbmF4L3BvbGljeS9Vc2FnZVB1cnBvc2UiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmF0b3IiOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQGlkIjogImVxIgogICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAicmlnaHRPcGVyYW5kIjogImN4LmNvcmUuaW5kdXN0cnljb3JlOjEiCiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgXQogICAgICAgICAgICB9CiAgICAgICAgfQogICAgXQp9XQ==" + ``` diff --git a/irs-testing/src/main/java/org/eclipse/tractusx/irs/testing/wiremock/SubmodelFacadeWiremockSupport.java b/irs-testing/src/main/java/org/eclipse/tractusx/irs/testing/wiremock/SubmodelFacadeWiremockSupport.java index a245cb29af..ffbd23bd3b 100644 --- a/irs-testing/src/main/java/org/eclipse/tractusx/irs/testing/wiremock/SubmodelFacadeWiremockSupport.java +++ b/irs-testing/src/main/java/org/eclipse/tractusx/irs/testing/wiremock/SubmodelFacadeWiremockSupport.java @@ -52,9 +52,9 @@ public final class SubmodelFacadeWiremockSupport { public static final String IRS_INTERNAL_CALLBACK_URL = "https://irs.test/internal/endpoint-data-reference"; public static final String EDC_PROVIDER_BPN = "BPNL00000000TEST"; public static final int STATUS_CODE_OK = 200; - public static final String CX_POLICY_FRAMEWORK_AGREEMENT = "cx-policy:FrameworkAgreement"; + public static final String CX_POLICY_FRAMEWORK_AGREEMENT = "https://w3id.org/catenax/policy/FrameworkAgreement"; public static final String TRACEABILITY_1_0 = "traceability:1.0"; - public static final String CX_POLICY_USAGE_PURPOSE = "cx-policy:UsagePurpose"; + public static final String CX_POLICY_USAGE_PURPOSE = "https://w3id.org/catenax/policy/UsagePurpose"; public static final String CX_CORE_INDUSTRYCORE_1 = "cx.core.industrycore:1"; public static final String PERMISSION_TYPE = "use"; public static final int STATUS_CODE_BAD_GATEWAY = 502; @@ -76,8 +76,7 @@ public static void prepareNegotiation(final String negotiationId, final String t stubFor(post(urlPathEqualTo(PATH_CATALOG)).willReturn(WireMockConfig.responseWithStatus(STATUS_CODE_OK) .withBody(getCatalogResponse(edcAssetId, contractAgreementId, - PERMISSION_TYPE, - EDC_PROVIDER_BPN, + PERMISSION_TYPE, EDC_PROVIDER_BPN, createConstraints())))); stubFor(post(urlPathEqualTo(PATH_NEGOTIATE)).willReturn( @@ -113,14 +112,13 @@ public static void prepareMissmatchPolicyCatalog(final String edcAssetId, final stubFor(post(urlPathEqualTo(PATH_CATALOG)).willReturn(WireMockConfig.responseWithStatus(STATUS_CODE_OK) .withBody(getCatalogResponse(edcAssetId, contractAgreementId, - PERMISSION_TYPE, - EDC_PROVIDER_BPN, + PERMISSION_TYPE, EDC_PROVIDER_BPN, createNotAcceptedConstraints())))); } public static void prepareFailingCatalog() { - stubFor(post(urlPathEqualTo(PATH_CATALOG)).willReturn(WireMockConfig.responseWithStatus(STATUS_CODE_BAD_GATEWAY) - .withBody(""))); + stubFor(post(urlPathEqualTo(PATH_CATALOG)).willReturn( + WireMockConfig.responseWithStatus(STATUS_CODE_BAD_GATEWAY).withBody(""))); } private static String startTransferProcessResponse(final String transferProcessId) { @@ -253,8 +251,8 @@ public static String getCatalogResponse(final String edcAssetId, final String of "dspace:participantId": "%s", "@context": %s } - """.formatted(edcAssetId, offerId, permissionType, constraints, EDC_PROVIDER_DUMMY_URL, - edcAssetId, EDC_PROVIDER_DUMMY_URL, edcProviderBpn, edcProviderBpn, CONTEXT); + """.formatted(edcAssetId, offerId, permissionType, constraints, EDC_PROVIDER_DUMMY_URL, edcAssetId, + EDC_PROVIDER_DUMMY_URL, edcProviderBpn, edcProviderBpn, CONTEXT); } private static String createConstraints() { @@ -270,8 +268,7 @@ private static String createConstraints() { } private static String createNotAcceptedConstraints() { - final List atomitConstraints = List.of( - createAtomicConstraint("test", "test")); + final List atomitConstraints = List.of(createAtomicConstraint("test", "test")); return """ { "odrl:and": [