Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chore/542 default policy config improved #833

Closed
wants to merge 13 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "irs.fullname" . }}-configmap

Check warning on line 26 in charts/item-relationship-service/templates/configmap-spring-app-config.yaml

View workflow job for this annotation

GitHub Actions / Analyze

[MEDIUM] Using Unrecommended Namespace

Namespaces like 'default', 'kube-system' or 'kube-public' should not be used
namespace: {{ .Values.namespace }}
data:
application.yaml: |-
Expand Down Expand Up @@ -118,12 +118,7 @@
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:
Expand Down
43 changes: 34 additions & 9 deletions charts/item-relationship-service/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion docs/src/docs/administration/configuration.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 6 additions & 9 deletions irs-api/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
Expand All @@ -61,4 +72,84 @@ void shouldThrowParseExceptionWhenMappingFromString() {
assertThatThrownBy(() -> StringMapper.mapFromString("test", Description.class)).isInstanceOf(
JsonParseException.class);
}

@Test
void shouldMapFromBase64StringUsingTypeReference() {

// ARRANGE
final TypeReference<List<Policy>> 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<Policy> listOfPolicies = StringMapper.mapFromBase64String(originalJsonBase64, listOfPoliciesType);
final String backToString = StringMapper.mapToString(listOfPolicies);
final List<Policy> 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<Constraint> 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");
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 {
Expand All @@ -56,6 +65,13 @@ public static String mapToString(final Object value) {
}
}

public static <T> T mapFromBase64String(final String value, final TypeReference<T> typeReference) {
if (value == null) {
return null;
}
return mapFromString(fromBase64(value), typeReference);
}

public static <T> T mapFromString(final String value, final Class<T> clazz) {
try {
return MAPPER.readValue(value, clazz);
Expand All @@ -64,4 +80,22 @@ public static <T> T mapFromString(final String value, final Class<T> clazz) {
}
}

public static <T> T mapFromString(final String value, final TypeReference<T> 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);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -37,7 +35,11 @@
@ConfigurationProperties(prefix = "irs-edc-client.catalog")
@Data
public class DefaultAcceptedPoliciesConfig {
private List<AcceptedPolicy> acceptedPolicies;

/**
* Accepted policies as a Base64 encoded string.
*/
private String acceptedPolicies;

/**
* Accepted Policy for
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -71,6 +66,9 @@
})
public class PolicyStoreService implements AcceptedPoliciesProvider {

private static final TypeReference<List<Policy>> LIST_OF_POLICIES_TYPE = new TypeReference<>() {
};

private final List<Policy> allowedPoliciesFromConfig;

private final PolicyPersistence persistence;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -297,26 +296,8 @@ private AcceptedPolicy toAcceptedPolicy(final Policy policy) {
return new AcceptedPolicy(policy, policy.getValidUntil());
}

private List<Policy> createDefaultPolicyFromConfig(
final DefaultAcceptedPoliciesConfig defaultAcceptedPoliciesConfig) {

final List<Constraint> 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<Policy> createDefaultPolicyFromConfig(final DefaultAcceptedPoliciesConfig defaultPoliciesConfig) {
return StringMapper.mapFromBase64String(defaultPoliciesConfig.getAcceptedPolicies(), LIST_OF_POLICIES_TYPE);
}

}
Loading
Loading