From 11ba40f57453e6fad4b10e2da4c6f83916f79de8 Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Tue, 7 May 2024 13:48:50 +0200 Subject: [PATCH 01/39] feat(impl): [#528] allow only BPNL --- .../BusinessPartnerNumberListValidator.java | 2 +- .../BusinessPartnerNumberListValidatorTest.java | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/BusinessPartnerNumberListValidator.java b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/BusinessPartnerNumberListValidator.java index e143126d07..a487906cb5 100644 --- a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/BusinessPartnerNumberListValidator.java +++ b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/BusinessPartnerNumberListValidator.java @@ -34,7 +34,7 @@ public class BusinessPartnerNumberListValidator /** * Regex for BPN. */ - public static final String BPN_REGEX = "(BPN)[LSA][\\w\\d]{10}[\\w\\d]{2}"; + public static final String BPN_REGEX = "(BPNL)[\\w\\d]{10}[\\w\\d]{2}"; private static final Pattern PATTERN = Pattern.compile(BPN_REGEX); diff --git a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/BusinessPartnerNumberListValidatorTest.java b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/BusinessPartnerNumberListValidatorTest.java index 178f25b76e..16b1d0b10c 100644 --- a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/BusinessPartnerNumberListValidatorTest.java +++ b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/BusinessPartnerNumberListValidatorTest.java @@ -28,6 +28,8 @@ import jakarta.validation.ConstraintValidatorContext; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.mockito.Answers; import org.mockito.ArgumentCaptor; import org.mockito.Captor; @@ -73,4 +75,19 @@ void withListContainingInvalidBPN() { verify(contextMock).buildConstraintViolationWithTemplate(messageCaptor.capture()); assertThat(messageCaptor.getValue()).contains("BPN").contains(" index 1 ").contains("invalid"); } + + @ParameterizedTest + @ValueSource(strings = { "BPN", + "BPNL", + "BPNACB", + "BPNA1234567890AB", + "BPNS1234567890AB", + "DELETE * FROM Table", + "ERRRES" + }) + void withInvalidBPN(final String invalidBPN) { + assertThat(validator.isValid(Collections.singletonList(invalidBPN), contextMock)).isFalse(); + verify(contextMock).buildConstraintViolationWithTemplate(messageCaptor.capture()); + assertThat(messageCaptor.getValue()).contains("BPN").contains(" index 0 ").contains("invalid"); + } } From 55b09f8db06a7c9748f694f635bc00453550620b Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Tue, 7 May 2024 17:47:58 +0200 Subject: [PATCH 02/39] feat(impl): [#528] harden tests --- .../persistence/PolicyPersistenceTest.java | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/persistence/PolicyPersistenceTest.java b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/persistence/PolicyPersistenceTest.java index ee193a9ea2..9ac9f96d4d 100644 --- a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/persistence/PolicyPersistenceTest.java +++ b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/persistence/PolicyPersistenceTest.java @@ -43,6 +43,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import org.assertj.core.api.ThrowableAssert; import org.eclipse.tractusx.irs.common.persistence.BlobPersistence; import org.eclipse.tractusx.irs.common.persistence.BlobPersistenceException; import org.eclipse.tractusx.irs.edc.client.policy.Policy; @@ -104,18 +105,22 @@ void saveDuplicate() throws BlobPersistenceException, JsonProcessingException { when(mockPersistence.getBlob(anyString())).thenReturn(Optional.of(mapper.writeValueAsBytes(policies))); // ACT & ASSERT - assertThatThrownBy(() -> testee.save("testBpn", policy)).isInstanceOf(PolicyStoreException.class); + assertThatThrownBy(() -> testee.save("testBpn", policy)).isInstanceOf(PolicyStoreException.class) + .hasMessageContaining("'test'") + .hasMessageContaining("already exists"); } @Test - void saveWithError() throws BlobPersistenceException { + void saveWithReadError() throws BlobPersistenceException { // ARRANGE final var policy = new Policy("test", OffsetDateTime.now(), OffsetDateTime.now(), emptyList()); when(mockPersistence.getBlob(any())).thenThrow( new BlobPersistenceException("test", new IllegalStateException())); // ACT & ASSERT - assertThatThrownBy(() -> testee.save("testBpn", policy)).isInstanceOf(PolicyStoreException.class); + final ThrowableAssert.ThrowingCallable call = () -> testee.save("testBpn", policy); + assertThatThrownBy(call).isInstanceOf(PolicyStoreException.class) + .hasMessageContaining("Unable to read policy data"); } @Test @@ -126,7 +131,9 @@ void saveWithWriteError() throws BlobPersistenceException { .putBlob(any(), any()); // ACT & ASSERT - assertThatThrownBy(() -> testee.save("testBpn", policy)).isInstanceOf(PolicyStoreException.class); + final ThrowableAssert.ThrowingCallable call = () -> testee.save("testBpn", policy); + assertThatThrownBy(call).isInstanceOf(PolicyStoreException.class) + .hasMessageContaining("Unable to store policy data"); } } @@ -213,7 +220,9 @@ void readAll_withBpn_withError() throws BlobPersistenceException, JsonProcessing final var localTestee = new PolicyPersistence(mockPersistence, mapperMock); // ACT & ASSERT - assertThatThrownBy(() -> localTestee.readAll("testBpn")).isInstanceOf(PolicyStoreException.class); + final ThrowableAssert.ThrowingCallable call = () -> localTestee.readAll("testBpn"); + assertThatThrownBy(call).isInstanceOf(PolicyStoreException.class) + .hasMessageContaining("Could not read the policies from the store"); } } } \ No newline at end of file From 573767c1fd5da91d65c315796db9a1f83dcf4654 Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Tue, 7 May 2024 20:07:01 +0200 Subject: [PATCH 03/39] feat(impl): [#528] code formatting --- .../irs/policystore/persistence/PolicyPersistenceTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/persistence/PolicyPersistenceTest.java b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/persistence/PolicyPersistenceTest.java index 9ac9f96d4d..9cbbeac40a 100644 --- a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/persistence/PolicyPersistenceTest.java +++ b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/persistence/PolicyPersistenceTest.java @@ -105,9 +105,10 @@ void saveDuplicate() throws BlobPersistenceException, JsonProcessingException { when(mockPersistence.getBlob(anyString())).thenReturn(Optional.of(mapper.writeValueAsBytes(policies))); // ACT & ASSERT - assertThatThrownBy(() -> testee.save("testBpn", policy)).isInstanceOf(PolicyStoreException.class) - .hasMessageContaining("'test'") - .hasMessageContaining("already exists"); + final ThrowableAssert.ThrowingCallable call = () -> testee.save("testBpn", policy); + assertThatThrownBy(call).isInstanceOf(PolicyStoreException.class) + .hasMessageContaining("'test'") + .hasMessageContaining("already exists"); } @Test From c7efaa6f3be16ee3bc78b393f1da5c3a943fdee0 Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Tue, 7 May 2024 21:40:31 +0200 Subject: [PATCH 04/39] feat(impl): [#528] add @Future to validUntil attribute --- .../tractusx/irs/policystore/models/CreatePolicyRequest.java | 2 ++ .../tractusx/irs/policystore/models/UpdatePolicyRequest.java | 2 ++ 2 files changed, 4 insertions(+) diff --git a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/models/CreatePolicyRequest.java b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/models/CreatePolicyRequest.java index 59624d8d47..3942416ac5 100644 --- a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/models/CreatePolicyRequest.java +++ b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/models/CreatePolicyRequest.java @@ -29,6 +29,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.json.JsonObject; +import jakarta.validation.constraints.Future; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; @@ -42,6 +43,7 @@ public record CreatePolicyRequest( @Schema(description = "Timestamp after which the policy will no longer be accepted in negotiations.", example = "2025-12-12T23:59:59.999Z") // @NotNull // + @Future(message = "must be in future") // OffsetDateTime validUntil, // @Schema(description = """ diff --git a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/models/UpdatePolicyRequest.java b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/models/UpdatePolicyRequest.java index cf10874e5d..1f20802afc 100644 --- a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/models/UpdatePolicyRequest.java +++ b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/models/UpdatePolicyRequest.java @@ -27,6 +27,7 @@ import java.util.List; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Future; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import org.eclipse.tractusx.irs.policystore.validators.ValidListOfBusinessPartnerNumbers; @@ -39,6 +40,7 @@ public record UpdatePolicyRequest( @Schema(description = "Timestamp after which the policy will no longer be accepted in negotiations.") // @NotNull // + @Future(message = "must be in future") // OffsetDateTime validUntil, // @Schema(description = "Business Partner Number (BPN).") // From e7f0ccd5c4d42c26de33ff7cb253344dd67cc2fc Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Tue, 7 May 2024 21:41:01 +0200 Subject: [PATCH 05/39] feat(impl): [#528] add @Builder to request entities --- .../tractusx/irs/policystore/models/CreatePolicyRequest.java | 2 ++ .../tractusx/irs/policystore/models/UpdatePolicyRequest.java | 2 ++ 2 files changed, 4 insertions(+) diff --git a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/models/CreatePolicyRequest.java b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/models/CreatePolicyRequest.java index 3942416ac5..8ad85f37ed 100644 --- a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/models/CreatePolicyRequest.java +++ b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/models/CreatePolicyRequest.java @@ -32,12 +32,14 @@ import jakarta.validation.constraints.Future; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; +import lombok.Builder; /** * Object for API to create policy */ @SuppressWarnings("FileTabCharacter") @Schema(description = "Request to add a policy") +@Builder public record CreatePolicyRequest( @Schema(description = "Timestamp after which the policy will no longer be accepted in negotiations.", diff --git a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/models/UpdatePolicyRequest.java b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/models/UpdatePolicyRequest.java index 1f20802afc..6731811d33 100644 --- a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/models/UpdatePolicyRequest.java +++ b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/models/UpdatePolicyRequest.java @@ -30,12 +30,14 @@ import jakarta.validation.constraints.Future; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; +import lombok.Builder; import org.eclipse.tractusx.irs.policystore.validators.ValidListOfBusinessPartnerNumbers; /** * Request object for policy update */ @Schema(description = "Request to update a policy") +@Builder public record UpdatePolicyRequest( @Schema(description = "Timestamp after which the policy will no longer be accepted in negotiations.") // From 1240f77ef3c66029b888b494ae2ab423b2b1184b Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Tue, 7 May 2024 21:41:59 +0200 Subject: [PATCH 06/39] feat(impl): [#528] add validUntil validation tests --- .../models/ValidUntilValidationTest.java | 137 ++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/models/ValidUntilValidationTest.java diff --git a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/models/ValidUntilValidationTest.java b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/models/ValidUntilValidationTest.java new file mode 100644 index 0000000000..e35809b4f3 --- /dev/null +++ b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/models/ValidUntilValidationTest.java @@ -0,0 +1,137 @@ +/******************************************************************************** + * Copyright (c) 2022,2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +package org.eclipse.tractusx.irs.policystore.models; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import java.time.Clock; +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.util.Set; +import java.util.function.Predicate; + +import jakarta.validation.ClockProvider; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import jakarta.validation.ValidatorFactory; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class ValidUntilValidationTest { + + public static final String PROPERTY_PATH_VALID_UNTIL = "validUntil"; + public static final String MSG_MUST_BE_IN_FUTURE = "must be in future"; + + private Clock fixedClock; + private Validator validator; + + @BeforeEach + public void setUp() { + this.fixedClock = Clock.fixed(Instant.now(), ZoneId.systemDefault()); + final ValidatorFactory validatorFactory = getValidatorFactory(() -> this.fixedClock); + this.validator = validatorFactory.getValidator(); + } + + private ValidatorFactory getValidatorFactory(final ClockProvider clockProvider) { + return Validation.byDefaultProvider().configure().clockProvider(clockProvider).buildValidatorFactory(); + } + + @Test + void CreatePolicyRequest_validUntil_null_shouldHaveValidationMessage() { + + final CreatePolicyRequest requestEntity = CreatePolicyRequest.builder().validUntil(null).build(); + final Set> violations = this.validator.validate(requestEntity); + + assertThat(violations).isNotEmpty(); + assertThat(violations.stream()).describedAs( + requestEntity.getClass().getSimpleName() + " should contain validation message that " + + PROPERTY_PATH_VALID_UNTIL + " is mandatory") + .anyMatch(hasValidUntilMessageContaining("NotNull")); + } + + @Test + void UpdatePolicyRequest_validUntil_null_shouldHaveValidationMessage() { + + final UpdatePolicyRequest requestEntity = UpdatePolicyRequest.builder().validUntil(null).build(); + final Set> violations = this.validator.validate(requestEntity); + + assertThat(violations).isNotEmpty(); + assertThat(violations.stream()).describedAs( + requestEntity.getClass().getSimpleName() + " should contain validation message that " + + PROPERTY_PATH_VALID_UNTIL + " is mandatory") + .anyMatch(hasValidUntilMessageContaining("NotNull")); + } + + @Test + void validUntil_inFuture_shouldNotHaveValidationMessage() { + + final OffsetDateTime now = OffsetDateTime.now(this.fixedClock); + final OffsetDateTime oneSecondInFuture = now.plusSeconds(1); + + assertAll(() -> assertThatNoValidUntilValidationMessage( + CreatePolicyRequest.builder().validUntil(oneSecondInFuture).build()), + () -> assertThatNoValidUntilValidationMessage( + UpdatePolicyRequest.builder().validUntil(oneSecondInFuture).build())); + + } + + @Test + void validUntil_notInFuture_shouldHaveValidationMessage() { + + final OffsetDateTime now = OffsetDateTime.now(this.fixedClock); + + assertAll(() -> assertThatValidUntilMustBeInTheFutureMessage( + CreatePolicyRequest.builder().validUntil(now).build()), + () -> assertThatValidUntilMustBeInTheFutureMessage( + UpdatePolicyRequest.builder().validUntil(now).build())); + + } + + private void assertThatValidUntilMustBeInTheFutureMessage(final Object requestEntity) { + + final Set> violations = this.validator.validate(requestEntity); + + assertThat(violations).isNotEmpty(); + final String msg = MSG_MUST_BE_IN_FUTURE; + assertThat(violations.stream()).describedAs( + requestEntity.getClass().getSimpleName() + " should contain validation message that " + + PROPERTY_PATH_VALID_UNTIL + " has to be in the future") + .anyMatch(hasValidUntilMessageContaining(msg)); + } + + private void assertThatNoValidUntilValidationMessage(final Object requestEntity) { + + final Set> violations = this.validator.validate(requestEntity); + + assertThat(violations.stream()).describedAs( + requestEntity.getClass().getSimpleName() + " should not contain validation message that " + + PROPERTY_PATH_VALID_UNTIL + " has to be in the future") + .noneMatch(hasValidUntilMessageContaining(MSG_MUST_BE_IN_FUTURE)); + } + + private Predicate> hasValidUntilMessageContaining(final String msg) { + return violation -> violation.getPropertyPath().toString().equals(PROPERTY_PATH_VALID_UNTIL) + && violation.getMessageTemplate().contains(msg); + } + +} \ No newline at end of file From d3e1dcf14ba5cbb8c4b7fde003914d06cefae528 Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Tue, 7 May 2024 22:41:49 +0200 Subject: [PATCH 07/39] feat(impl): [#528] add and use PolicyStoreTestUtil in order to deduplicate common test code --- .../PolicyStoreControllerTest.java | 9 +---- .../services/PolicyStoreServiceTest.java | 14 ++----- .../testutil/PolicyStoreTestUtil.java | 37 +++++++++++++++++++ 3 files changed, 42 insertions(+), 18 deletions(-) create mode 100644 irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/testutil/PolicyStoreTestUtil.java diff --git a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/controllers/PolicyStoreControllerTest.java b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/controllers/PolicyStoreControllerTest.java index efb004e775..7c805f2504 100644 --- a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/controllers/PolicyStoreControllerTest.java +++ b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/controllers/PolicyStoreControllerTest.java @@ -28,16 +28,13 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import java.io.StringReader; import java.time.OffsetDateTime; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import jakarta.json.Json; import jakarta.json.JsonObject; -import jakarta.json.JsonReader; import jakarta.servlet.http.HttpServletRequest; import org.eclipse.tractusx.irs.edc.client.policy.Constraint; import org.eclipse.tractusx.irs.edc.client.policy.Constraints; @@ -51,6 +48,7 @@ import org.eclipse.tractusx.irs.policystore.models.PolicyResponse; import org.eclipse.tractusx.irs.policystore.models.UpdatePolicyRequest; import org.eclipse.tractusx.irs.policystore.services.PolicyStoreService; +import org.eclipse.tractusx.irs.policystore.testutil.PolicyStoreTestUtil; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -112,10 +110,7 @@ class RegisterPolicyTests { void registerAllowedPolicy() { // arrange final OffsetDateTime now = OffsetDateTime.now(); - final JsonObject jsonObject; - try (JsonReader jsonReader = Json.createReader(new StringReader(REGISTER_POLICY_EXAMPLE_PAYLOAD))) { - jsonObject = jsonReader.readObject(); - } + final JsonObject jsonObject = PolicyStoreTestUtil.toJsonObject(REGISTER_POLICY_EXAMPLE_PAYLOAD); // act final CreatePolicyRequest request = new CreatePolicyRequest(now.plusMinutes(1), null, 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 802a60114c..f2b75bbd35 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 @@ -34,16 +34,13 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import java.io.StringReader; import java.time.Clock; import java.time.OffsetDateTime; import java.util.Collections; import java.util.List; import java.util.Map; -import jakarta.json.Json; import jakarta.json.JsonObject; -import jakarta.json.JsonReader; import org.assertj.core.api.AbstractThrowableAssert; import org.assertj.core.api.ThrowableAssert; import org.eclipse.edc.core.transform.TypeTransformerRegistryImpl; @@ -64,6 +61,7 @@ import org.eclipse.tractusx.irs.policystore.models.CreatePolicyRequest; import org.eclipse.tractusx.irs.policystore.models.UpdatePolicyRequest; import org.eclipse.tractusx.irs.policystore.persistence.PolicyPersistence; +import org.eclipse.tractusx.irs.policystore.testutil.PolicyStoreTestUtil; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -115,10 +113,7 @@ void registerPolicy_withBpnNull_shouldStoreAsDefault() { // ARRANGE final OffsetDateTime now = OffsetDateTime.now(); - final JsonObject jsonObject; - try (JsonReader jsonReader = Json.createReader(new StringReader(REGISTER_POLICY_EXAMPLE_PAYLOAD))) { - jsonObject = jsonReader.readObject(); - } + final JsonObject jsonObject = PolicyStoreTestUtil.toJsonObject(REGISTER_POLICY_EXAMPLE_PAYLOAD); // ACT testee.registerPolicy(new CreatePolicyRequest(now, null, jsonObject)); @@ -135,10 +130,7 @@ void registerPolicy_success() { // ARRANGE final OffsetDateTime now = OffsetDateTime.now(); - final JsonObject jsonObject; - try (JsonReader jsonReader = Json.createReader(new StringReader(REGISTER_POLICY_EXAMPLE_PAYLOAD))) { - jsonObject = jsonReader.readObject(); - } + final JsonObject jsonObject = PolicyStoreTestUtil.toJsonObject(REGISTER_POLICY_EXAMPLE_PAYLOAD); // ACT final OffsetDateTime validUntil = now.plusMonths(1); diff --git a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/testutil/PolicyStoreTestUtil.java b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/testutil/PolicyStoreTestUtil.java new file mode 100644 index 0000000000..24076b2bb2 --- /dev/null +++ b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/testutil/PolicyStoreTestUtil.java @@ -0,0 +1,37 @@ +/******************************************************************************** + * Copyright (c) 2022,2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +package org.eclipse.tractusx.irs.policystore.testutil; + +import java.io.StringReader; + +import jakarta.json.Json; +import jakarta.json.JsonObject; +import jakarta.json.JsonReader; + +public class PolicyStoreTestUtil { + + public static JsonObject toJsonObject(final String jsonString) { + final JsonObject jsonObject; + try (JsonReader jsonReader = Json.createReader(new StringReader(jsonString))) { + jsonObject = jsonReader.readObject(); + } + return jsonObject; + } +} From 49361390fe2dab118b9050cacb9c685f8c230065 Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Tue, 7 May 2024 22:42:35 +0200 Subject: [PATCH 08/39] feat(impl): [#528] add more validation tests and improve test structure --- .../models/RequestEntityValidationTest.java | 172 ++++++++++++++++++ .../models/ValidUntilValidationTest.java | 137 -------------- 2 files changed, 172 insertions(+), 137 deletions(-) create mode 100644 irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/models/RequestEntityValidationTest.java delete mode 100644 irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/models/ValidUntilValidationTest.java diff --git a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/models/RequestEntityValidationTest.java b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/models/RequestEntityValidationTest.java new file mode 100644 index 0000000000..6c74068875 --- /dev/null +++ b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/models/RequestEntityValidationTest.java @@ -0,0 +1,172 @@ +/******************************************************************************** + * Copyright (c) 2022,2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +package org.eclipse.tractusx.irs.policystore.models; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.Clock; +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.util.Set; +import java.util.function.Predicate; + +import jakarta.validation.ClockProvider; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import jakarta.validation.ValidatorFactory; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +class RequestEntityValidationTest { + + public static final String PROPERTY_PATH_VALID_UNTIL = "validUntil"; + public static final String PROPERTY_PATH_PAYLOAD = "payload"; + public static final String MSG_MUST_BE_IN_FUTURE = "must be in future"; + public static final String MSG_TEMPLATE_MANDATORY = "%s should contain validation message that %s is mandatory"; + + private Clock fixedClock; + private Validator validator; + + @BeforeEach + public void setUp() { + this.fixedClock = Clock.fixed(Instant.now(), ZoneId.systemDefault()); + final ValidatorFactory validatorFactory = getValidatorFactory(() -> this.fixedClock); + this.validator = validatorFactory.getValidator(); + } + + private ValidatorFactory getValidatorFactory(final ClockProvider clockProvider) { + return Validation.byDefaultProvider().configure().clockProvider(clockProvider).buildValidatorFactory(); + } + + @Nested + class CreatePolicyRequestTests { + + @Nested + class ValidUntilTests { + + @Test + void whenNull() { + + final CreatePolicyRequest requestEntity = CreatePolicyRequest.builder().validUntil(null).build(); + final Set> violations = validator.validate(requestEntity); + + assertThat(violations).isNotEmpty(); + assertMandatory(requestEntity, PROPERTY_PATH_VALID_UNTIL, violations); + } + + @Test + void whenInFuture() { + final OffsetDateTime oneSecondInFuture = OffsetDateTime.now(fixedClock).plusSeconds(1); + assertThatNoValidUntilValidationMessage( + CreatePolicyRequest.builder().validUntil(oneSecondInFuture).build()); + } + + @Test + void whenNotInFuture() { + assertThatValidUntilMustBeInTheFutureMessage(CreatePolicyRequest.builder().validUntil(now()).build()); + } + } + + @Nested + class PayloadTests { + + @Test + void whenNull() { + + final CreatePolicyRequest requestEntity = CreatePolicyRequest.builder().payload(null).build(); + final Set> violations = validator.validate(requestEntity); + + assertThat(violations).isNotEmpty(); + assertMandatory(requestEntity, PROPERTY_PATH_PAYLOAD, violations); + } + + } + } + + @Nested + class UpdatePolicyRequestTests { + + @Nested + class ValidUntilTests { + @Test + void whenNull() { + + final UpdatePolicyRequest requestEntity = UpdatePolicyRequest.builder().validUntil(null).build(); + final Set> violations = validator.validate(requestEntity); + + assertThat(violations).isNotEmpty(); + assertMandatory(requestEntity, PROPERTY_PATH_VALID_UNTIL, violations); + } + + @Test + void whenInFuture() { + final OffsetDateTime oneSecondInFuture = OffsetDateTime.now(fixedClock).plusSeconds(1); + assertThatNoValidUntilValidationMessage( + UpdatePolicyRequest.builder().validUntil(oneSecondInFuture).build()); + } + + @Test + void whenNotInFuture() { + assertThatValidUntilMustBeInTheFutureMessage(UpdatePolicyRequest.builder().validUntil(now()).build()); + } + } + } + + private void assertThatValidUntilMustBeInTheFutureMessage(final Object requestEntity) { + + final Set> violations = this.validator.validate(requestEntity); + + assertThat(violations).isNotEmpty(); + + final String propertyPath = PROPERTY_PATH_VALID_UNTIL; + assertThat(violations.stream()).as("%s should contain validation message that %s has to be in the future", + requestEntity.getClass().getSimpleName(), propertyPath) + .anyMatch(hasMessageContaining(propertyPath, MSG_MUST_BE_IN_FUTURE)); + } + + private void assertMandatory(final Object requestEntity, final String propertyPath, + final Set> violations) { + assertThat(violations.stream()).as(MSG_TEMPLATE_MANDATORY, requestEntity.getClass().getSimpleName(), + propertyPath).anyMatch(hasMessageContaining(propertyPath, "NotNull")); + } + + private void assertThatNoValidUntilValidationMessage(final Object requestEntity) { + + final Set> violations = this.validator.validate(requestEntity); + + final String propertyPath = PROPERTY_PATH_VALID_UNTIL; + assertThat(violations.stream()).as("%s should NOT contain validation message that %s has to be in the future", + requestEntity.getClass().getSimpleName(), propertyPath) + .noneMatch(hasMessageContaining(propertyPath, MSG_MUST_BE_IN_FUTURE)); + } + + private Predicate> hasMessageContaining(final String propertyPath, final String msg) { + return violation -> violation.getPropertyPath().toString().equals(propertyPath) + && violation.getMessageTemplate().contains(msg); + } + + private OffsetDateTime now() { + return OffsetDateTime.now(this.fixedClock); + } + +} \ No newline at end of file diff --git a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/models/ValidUntilValidationTest.java b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/models/ValidUntilValidationTest.java deleted file mode 100644 index e35809b4f3..0000000000 --- a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/models/ValidUntilValidationTest.java +++ /dev/null @@ -1,137 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022,2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ -package org.eclipse.tractusx.irs.policystore.models; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertAll; - -import java.time.Clock; -import java.time.Instant; -import java.time.OffsetDateTime; -import java.time.ZoneId; -import java.util.Set; -import java.util.function.Predicate; - -import jakarta.validation.ClockProvider; -import jakarta.validation.ConstraintViolation; -import jakarta.validation.Validation; -import jakarta.validation.Validator; -import jakarta.validation.ValidatorFactory; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -class ValidUntilValidationTest { - - public static final String PROPERTY_PATH_VALID_UNTIL = "validUntil"; - public static final String MSG_MUST_BE_IN_FUTURE = "must be in future"; - - private Clock fixedClock; - private Validator validator; - - @BeforeEach - public void setUp() { - this.fixedClock = Clock.fixed(Instant.now(), ZoneId.systemDefault()); - final ValidatorFactory validatorFactory = getValidatorFactory(() -> this.fixedClock); - this.validator = validatorFactory.getValidator(); - } - - private ValidatorFactory getValidatorFactory(final ClockProvider clockProvider) { - return Validation.byDefaultProvider().configure().clockProvider(clockProvider).buildValidatorFactory(); - } - - @Test - void CreatePolicyRequest_validUntil_null_shouldHaveValidationMessage() { - - final CreatePolicyRequest requestEntity = CreatePolicyRequest.builder().validUntil(null).build(); - final Set> violations = this.validator.validate(requestEntity); - - assertThat(violations).isNotEmpty(); - assertThat(violations.stream()).describedAs( - requestEntity.getClass().getSimpleName() + " should contain validation message that " - + PROPERTY_PATH_VALID_UNTIL + " is mandatory") - .anyMatch(hasValidUntilMessageContaining("NotNull")); - } - - @Test - void UpdatePolicyRequest_validUntil_null_shouldHaveValidationMessage() { - - final UpdatePolicyRequest requestEntity = UpdatePolicyRequest.builder().validUntil(null).build(); - final Set> violations = this.validator.validate(requestEntity); - - assertThat(violations).isNotEmpty(); - assertThat(violations.stream()).describedAs( - requestEntity.getClass().getSimpleName() + " should contain validation message that " - + PROPERTY_PATH_VALID_UNTIL + " is mandatory") - .anyMatch(hasValidUntilMessageContaining("NotNull")); - } - - @Test - void validUntil_inFuture_shouldNotHaveValidationMessage() { - - final OffsetDateTime now = OffsetDateTime.now(this.fixedClock); - final OffsetDateTime oneSecondInFuture = now.plusSeconds(1); - - assertAll(() -> assertThatNoValidUntilValidationMessage( - CreatePolicyRequest.builder().validUntil(oneSecondInFuture).build()), - () -> assertThatNoValidUntilValidationMessage( - UpdatePolicyRequest.builder().validUntil(oneSecondInFuture).build())); - - } - - @Test - void validUntil_notInFuture_shouldHaveValidationMessage() { - - final OffsetDateTime now = OffsetDateTime.now(this.fixedClock); - - assertAll(() -> assertThatValidUntilMustBeInTheFutureMessage( - CreatePolicyRequest.builder().validUntil(now).build()), - () -> assertThatValidUntilMustBeInTheFutureMessage( - UpdatePolicyRequest.builder().validUntil(now).build())); - - } - - private void assertThatValidUntilMustBeInTheFutureMessage(final Object requestEntity) { - - final Set> violations = this.validator.validate(requestEntity); - - assertThat(violations).isNotEmpty(); - final String msg = MSG_MUST_BE_IN_FUTURE; - assertThat(violations.stream()).describedAs( - requestEntity.getClass().getSimpleName() + " should contain validation message that " - + PROPERTY_PATH_VALID_UNTIL + " has to be in the future") - .anyMatch(hasValidUntilMessageContaining(msg)); - } - - private void assertThatNoValidUntilValidationMessage(final Object requestEntity) { - - final Set> violations = this.validator.validate(requestEntity); - - assertThat(violations.stream()).describedAs( - requestEntity.getClass().getSimpleName() + " should not contain validation message that " - + PROPERTY_PATH_VALID_UNTIL + " has to be in the future") - .noneMatch(hasValidUntilMessageContaining(MSG_MUST_BE_IN_FUTURE)); - } - - private Predicate> hasValidUntilMessageContaining(final String msg) { - return violation -> violation.getPropertyPath().toString().equals(PROPERTY_PATH_VALID_UNTIL) - && violation.getMessageTemplate().contains(msg); - } - -} \ No newline at end of file From 6fb0e3e1e1c9e9122d0bcd7e3164f9eec2990318 Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Wed, 8 May 2024 00:18:23 +0200 Subject: [PATCH 09/39] feat(impl): [#528] improve exception handling --- .../tractusx/irs/controllers/IrsExceptionHandler.java | 8 ++++++-- .../transformer/JsonObjectToIrsPolicyTransformer.java | 2 +- .../eclipse/tractusx/irs/data/JsonParseException.java | 5 +++++ .../policystore/controllers/PolicyStoreController.java | 9 ++++++++- .../irs/policystore/services/PolicyStoreService.java | 9 ++++----- 5 files changed, 24 insertions(+), 9 deletions(-) diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/controllers/IrsExceptionHandler.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/controllers/IrsExceptionHandler.java index a98edf7010..f9692476ee 100644 --- a/irs-api/src/main/java/org/eclipse/tractusx/irs/controllers/IrsExceptionHandler.java +++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/controllers/IrsExceptionHandler.java @@ -25,6 +25,7 @@ import java.util.List; import java.util.NoSuchElementException; +import java.util.UUID; import jakarta.validation.ConstraintViolationException; import lombok.extern.slf4j.Slf4j; @@ -55,7 +56,9 @@ public class IrsExceptionHandler { */ @ExceptionHandler(ResponseStatusException.class) public ResponseEntity handleResponseStatusException(final ResponseStatusException exception) { - log.info(exception.getClass().getName(), exception); + final UUID errorRef = UUID.randomUUID(); + final String messageTemplate = "%s (errorRef: %s)"; + log.info(messageTemplate.formatted(exception.getClass().getName(), errorRef), exception); final HttpStatus httpStatus = HttpStatus.valueOf(exception.getStatusCode().value()); @@ -63,7 +66,8 @@ public ResponseEntity handleResponseStatusException(final Respons .body(ErrorResponse.builder() .withStatusCode(httpStatus) .withError(httpStatus.getReasonPhrase()) - .withMessages(List.of(exception.getReason())) + .withMessages(List.of(messageTemplate.formatted(exception.getReason(), + errorRef))) .build()); } diff --git a/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/transformer/JsonObjectToIrsPolicyTransformer.java b/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/transformer/JsonObjectToIrsPolicyTransformer.java index ccb8b3ef5b..2d831e94a6 100644 --- a/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/transformer/JsonObjectToIrsPolicyTransformer.java +++ b/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/transformer/JsonObjectToIrsPolicyTransformer.java @@ -57,7 +57,7 @@ protected JsonObjectToIrsPolicyTransformer(final ObjectMapper objectMapper) { final Object result = objectMapper.readerFor(Policy.class).readValue(v.asJsonObject().toString()); builder.permissions(((Policy) result).getPermissions()); } catch (JsonProcessingException e) { - throw new JsonParseException(e); + throw new JsonParseException("Invalid policy", e); } }); diff --git a/irs-models/src/main/java/org/eclipse/tractusx/irs/data/JsonParseException.java b/irs-models/src/main/java/org/eclipse/tractusx/irs/data/JsonParseException.java index 1df61c8893..6d2f5c490c 100644 --- a/irs-models/src/main/java/org/eclipse/tractusx/irs/data/JsonParseException.java +++ b/irs-models/src/main/java/org/eclipse/tractusx/irs/data/JsonParseException.java @@ -27,6 +27,11 @@ * Exception when parsing JSON */ public class JsonParseException extends RuntimeException { + + public JsonParseException(String message, final Throwable throwable) { + super(message, throwable); + } + public JsonParseException(final Throwable cause) { super(cause); } diff --git a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/controllers/PolicyStoreController.java b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/controllers/PolicyStoreController.java index 470f714830..b73c139f7c 100644 --- a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/controllers/PolicyStoreController.java +++ b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/controllers/PolicyStoreController.java @@ -46,6 +46,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections.CollectionUtils; import org.eclipse.tractusx.irs.common.auth.IrsRoles; +import org.eclipse.tractusx.irs.data.JsonParseException; import org.eclipse.tractusx.irs.dtos.ErrorResponse; import org.eclipse.tractusx.irs.edc.client.policy.Policy; import org.eclipse.tractusx.irs.policystore.models.CreatePoliciesResponse; @@ -127,7 +128,13 @@ public class PolicyStoreController { @PreAuthorize("hasAuthority('" + IrsRoles.ADMIN_IRS + "')") public CreatePoliciesResponse registerAllowedPolicy(@Valid @RequestBody final CreatePolicyRequest request) { - final Policy registeredPolicy = service.registerPolicy(request); + final Policy registeredPolicy; + + try { + registeredPolicy = service.registerPolicy(request); + } catch (JsonParseException e) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage()); + } if (registeredPolicy == null) { throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Policy was not registered"); 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 a47ba48ea5..2d458ec331 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 @@ -78,8 +78,7 @@ public class PolicyStoreService implements AcceptedPoliciesProvider { private final Clock clock; - private static final String MISSING_REQUEST_FIELD_MESSAGE = - "Request does not contain all required fields. " + "Missing: %s"; + private static final String POLICY_INCOMPLETE_MESSAGE = "Policy does not contain all required fields. Missing: %s"; private static final String DEFAULT = "default"; @@ -126,7 +125,7 @@ public Policy registerPolicy(final CreatePolicyRequest request) { return registeredPolicy; } - public Policy doRegisterPolicy(final Policy policy, final String businessPartnersNumber) { + Policy doRegisterPolicy(final Policy policy, final String businessPartnersNumber) { validatePolicy(policy); policy.setCreatedOn(OffsetDateTime.now(clock)); log.info("Registering new policy with id {}, valid until {}", policy.getPolicyId(), policy.getValidUntil()); @@ -146,12 +145,12 @@ private void validatePolicy(final Policy policy) { if (policy.getPermissions() == null) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, - String.format(MISSING_REQUEST_FIELD_MESSAGE, "odrl:permission")); + String.format(POLICY_INCOMPLETE_MESSAGE, "odrl:permission")); } if (policy.getPermissions().stream().anyMatch(p -> p.getConstraint() == null)) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, - String.format(MISSING_REQUEST_FIELD_MESSAGE, "odrl:constraint")); + String.format(POLICY_INCOMPLETE_MESSAGE, "odrl:constraint")); } } From 225d59187251b283e4cabd65def117d185dfdb46 Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Wed, 8 May 2024 00:35:13 +0200 Subject: [PATCH 10/39] feat(impl): [#528] fix PMD error --- .../java/org/eclipse/tractusx/irs/data/JsonParseException.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/irs-models/src/main/java/org/eclipse/tractusx/irs/data/JsonParseException.java b/irs-models/src/main/java/org/eclipse/tractusx/irs/data/JsonParseException.java index 6d2f5c490c..5b2711c8e5 100644 --- a/irs-models/src/main/java/org/eclipse/tractusx/irs/data/JsonParseException.java +++ b/irs-models/src/main/java/org/eclipse/tractusx/irs/data/JsonParseException.java @@ -28,7 +28,7 @@ */ public class JsonParseException extends RuntimeException { - public JsonParseException(String message, final Throwable throwable) { + public JsonParseException(final String message, final Throwable throwable) { super(message, throwable); } From 1263ee5047f5e8d330747374e1f4efc66d63f0f6 Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Wed, 8 May 2024 19:36:28 +0200 Subject: [PATCH 11/39] feat(impl): [#528] add validators --- .../validators/PolicyIdValidator.java | 63 +++++++++++++ .../validators/PolicyValidator.java | 56 +++++++++++ .../policystore/validators/ValidPolicyId.java | 47 ++++++++++ .../validators/PolicyIdValidatorTest.java | 94 +++++++++++++++++++ .../validators/PolicyValidatorTest.java | 38 ++++++++ 5 files changed, 298 insertions(+) create mode 100644 irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdValidator.java create mode 100644 irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/PolicyValidator.java create mode 100644 irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/ValidPolicyId.java create mode 100644 irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdValidatorTest.java create mode 100644 irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyValidatorTest.java diff --git a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdValidator.java b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdValidator.java new file mode 100644 index 0000000000..e22e704074 --- /dev/null +++ b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdValidator.java @@ -0,0 +1,63 @@ +/******************************************************************************** + * Copyright (c) 2022,2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +package org.eclipse.tractusx.irs.policystore.validators; + +import java.util.UUID; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; + +/** + * Validator for list of business partner numbers (BPN). + */ +public class PolicyIdValidator implements ConstraintValidator { + + @Override + public boolean isValid(final String value, final ConstraintValidatorContext context) { + + // allow null and empty here (in order to allow flexible combination with @NotNull) + if (value == null) { + return true; + } + + if (isInvalid(value)) { + return false; + } + + return true; + } + + private static boolean isInvalid(final String policyId) { + return !isValid(policyId); + } + + private static boolean isValid(final String policyId) { + return validateUUID(policyId); + } + + static boolean validateUUID(String uuidStr) { + try { + UUID.fromString(uuidStr); + return true; + } catch (IllegalArgumentException e) { + return false; + } + } +} diff --git a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/PolicyValidator.java b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/PolicyValidator.java new file mode 100644 index 0000000000..da1cbb2495 --- /dev/null +++ b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/PolicyValidator.java @@ -0,0 +1,56 @@ +/******************************************************************************** + * Copyright (c) 2022,2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +package org.eclipse.tractusx.irs.policystore.validators; + +import java.util.Set; + +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import jakarta.validation.ValidatorFactory; +import org.eclipse.tractusx.irs.edc.client.policy.Policy; + +public class PolicyValidator { + + record PolicyId(@ValidPolicyId String policyId) { + } + + public static void validate(final Policy policy) { + + try (final ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory()) { + + final Validator validator = validatorFactory.getValidator(); + + // workaround to avoid manipulating the class policy which comes from other module + final Set> policyIdViolations = validator.validate( + new PolicyId(policy.getPolicyId())); + if (!policyIdViolations.isEmpty()) { + throw new ConstraintViolationException(policyIdViolations); + } + + final Set> policyViolations = validator.validate(policy); + if (!policyViolations.isEmpty()) { + throw new ConstraintViolationException(policyViolations); + } + } + + } +} diff --git a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/ValidPolicyId.java b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/ValidPolicyId.java new file mode 100644 index 0000000000..19a00bbe52 --- /dev/null +++ b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/ValidPolicyId.java @@ -0,0 +1,47 @@ +/******************************************************************************** + * Copyright (c) 2022,2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +package org.eclipse.tractusx.irs.policystore.validators; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; + +/** + * Annotation for validating policyId. + */ +@Documented +@Constraint(validatedBy = PolicyIdValidator.class) +@Target({ ElementType.FIELD, + ElementType.PARAMETER +}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ValidPolicyId { + + String message() default "must be a valid UUID"; + + Class[] groups() default { }; + + Class[] payload() default { }; +} diff --git a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdValidatorTest.java b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdValidatorTest.java new file mode 100644 index 0000000000..664a7519b1 --- /dev/null +++ b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdValidatorTest.java @@ -0,0 +1,94 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +package org.eclipse.tractusx.irs.policystore.validators; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.stream.Stream; + +import jakarta.validation.ConstraintValidatorContext; +import net.datafaker.Faker; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.Answers; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class PolicyIdValidatorTest { + + private static final Faker faker = new Faker(); + + @InjectMocks + private PolicyIdValidator validator; + + @Captor + private ArgumentCaptor messageCaptor; + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private ConstraintValidatorContext contextMock; + + @Test + void withEmptyString() { + assertThat(validator.isValid("", contextMock)).isFalse(); + } + + @Test + void withNull() { + assertThat(validator.isValid(null, contextMock)).isTrue(); + } + + @ParameterizedTest + @MethodSource("generateUUIDs") + void withValidPolicyId(final String validPolicyId) { + assertThat(validator.isValid(validPolicyId, contextMock)).isTrue(); + } + + private static Stream generateUUIDs() { + return Stream.generate(() -> faker.internet().uuid()).limit(10); + } + + @ParameterizedTest + @ValueSource(strings = { "a_", + "*", + "(", + ")", + "<", + ">", + "[", + "]", + "{", + "}", + "p3", + "123", + "abc", + "abc123", + "abc-def-4444" + }) + void withInvalidPolicyId(final String invalidPolicyId) { + assertThat(validator.isValid(invalidPolicyId, contextMock)).isFalse(); + } + +} diff --git a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyValidatorTest.java b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyValidatorTest.java new file mode 100644 index 0000000000..665e498c2d --- /dev/null +++ b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyValidatorTest.java @@ -0,0 +1,38 @@ +/******************************************************************************** + * Copyright (c) 2022,2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +package org.eclipse.tractusx.irs.policystore.validators; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import jakarta.validation.ConstraintViolationException; +import org.eclipse.tractusx.irs.edc.client.policy.Policy; +import org.junit.jupiter.api.Test; + +class PolicyValidatorTest { + + @Test + void invalidPolicyId() { + final Policy policy = Policy.builder().policyId("_invalid_policy_id_").build(); + assertThatThrownBy(() -> PolicyValidator.validate(policy)).isInstanceOf(ConstraintViolationException.class) + .hasMessageContaining("policyId") + .hasMessageContaining("must be a valid UUID"); + } + +} \ No newline at end of file From 3af30fefc969bf44d06fb049e9a6bad40edcca58 Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Wed, 8 May 2024 21:48:46 +0200 Subject: [PATCH 12/39] feat(impl): [#528] format code and use policy builder --- .../irs/edc/client/policy/AcceptedPoliciesProviderTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client/policy/AcceptedPoliciesProviderTest.java b/irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client/policy/AcceptedPoliciesProviderTest.java index e572a6a2da..74503814b1 100644 --- a/irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client/policy/AcceptedPoliciesProviderTest.java +++ b/irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client/policy/AcceptedPoliciesProviderTest.java @@ -49,6 +49,7 @@ void shouldReturnStoredPolicies() { assertThat(acceptedPolicies).hasSize(1); } + @Test void shouldRemoveStoredPolicies() { testee.addAcceptedPolicies(List.of(policy())); @@ -60,8 +61,9 @@ void shouldRemoveStoredPolicies() { assertThat(testee.getAcceptedPolicies("testBpn")).isEmpty(); } + @NotNull private static AcceptedPolicy policy() { - return new AcceptedPolicy(new Policy(), OffsetDateTime.now()); + return new AcceptedPolicy(Policy.builder().build(), OffsetDateTime.now()); } } \ No newline at end of file From 0e2aa48f63e317795e688b28aedb738711fdfd86 Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Wed, 8 May 2024 21:50:03 +0200 Subject: [PATCH 13/39] feat(impl): [#528] replace dummy policyIds with UUIDs --- local/testing/request-collection/IRS_Request_Collection.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/local/testing/request-collection/IRS_Request_Collection.json b/local/testing/request-collection/IRS_Request_Collection.json index 6dd22a9dba..b0992be5e7 100644 --- a/local/testing/request-collection/IRS_Request_Collection.json +++ b/local/testing/request-collection/IRS_Request_Collection.json @@ -151,7 +151,7 @@ "method": "PUT", "body": { "mimeType": "application/json", - "text": "{\n \"validUntil\": \"2025-12-12T23:59:59.999Z\",\n\t\"businessPartnerNumbers\": [\n\t\t\"BPNL00000001CRHK\"\n\t],\n\t\"policyIds\": [\n\t\t\"policy-id12\"\n\t]\n}" + "text": "{\n \"validUntil\": \"2025-12-12T23:59:59.999Z\",\n\t\"businessPartnerNumbers\": [\n\t\t\"BPNL00000001CRHK\"\n\t],\n\t\"policyIds\": [\n\t\t\"e917f5f-8dac-49ac-8d10-5b4d254d2b48\"\n\t]\n}" }, "parameters": [], "headers": [ @@ -3261,7 +3261,7 @@ "method": "POST", "body": { "mimeType": "application/json", - "text": "{\n\t\"validUntil\": \"2025-12-12T23:59:59.999Z\",\n\t\"businessPartnerNumber\": \"BPNL00000000BJTL\",\n\t\"payload\": {\n\t\t\t\"@context\": {\n\t\t\t\t\"odrl\": \"http://www.w3.org/ns/odrl/2/\"\n\t\t\t},\n\t\t\t\"@id\": \"policy-id12\",\n\t\t\t\"policy\": {\n\t\t\t\t\"odrl:permission\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"odrl:action\": \"USE\",\n\t\t\t\t\t\t\"odrl:constraint\": {\n\t\t\t\t\t\t\t\"odrl:and\": [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\"odrl:leftOperand\": \"Membership\",\n\t\t\t\t\t\t\t\t\t\"odrl:operator\": {\n\t\t\t\t\t\t\t\t\t\t\"@id\": \"odrl:eq\"\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\"odrl:rightOperand\": \"active\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\"odrl:leftOperand\": \"PURPOSE\",\n\t\t\t\t\t\t\t\t\t\"odrl:operator\": {\n\t\t\t\t\t\t\t\t\t\t\"@id\": \"odrl:eq\"\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\"odrl:rightOperand\": \"ID 3.1 Trace\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t}\n\t\t}\n}" + "text": "{\n\t\"validUntil\": \"2025-12-12T23:59:59.999Z\",\n\t\"businessPartnerNumber\": \"BPNL00000000BJTL\",\n\t\"payload\": {\n\t\t\t\"@context\": {\n\t\t\t\t\"odrl\": \"http://www.w3.org/ns/odrl/2/\"\n\t\t\t},\n\t\t\t\"@id\": \"e917f5f-8dac-49ac-8d10-5b4d254d2b48\",\n\t\t\t\"policy\": {\n\t\t\t\t\"odrl:permission\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"odrl:action\": \"USE\",\n\t\t\t\t\t\t\"odrl:constraint\": {\n\t\t\t\t\t\t\t\"odrl:and\": [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\"odrl:leftOperand\": \"Membership\",\n\t\t\t\t\t\t\t\t\t\"odrl:operator\": {\n\t\t\t\t\t\t\t\t\t\t\"@id\": \"odrl:eq\"\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\"odrl:rightOperand\": \"active\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\"odrl:leftOperand\": \"PURPOSE\",\n\t\t\t\t\t\t\t\t\t\"odrl:operator\": {\n\t\t\t\t\t\t\t\t\t\t\"@id\": \"odrl:eq\"\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\"odrl:rightOperand\": \"ID 3.1 Trace\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t}\n\t\t}\n}" }, "parameters": [], "headers": [ From fc804c351bea144a5d8ce0f8758fff500f980dba Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Wed, 8 May 2024 21:51:52 +0200 Subject: [PATCH 14/39] feat(impl): [#528] add cause --- .../irs/policystore/controllers/PolicyStoreController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/controllers/PolicyStoreController.java b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/controllers/PolicyStoreController.java index b73c139f7c..abe452ea7c 100644 --- a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/controllers/PolicyStoreController.java +++ b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/controllers/PolicyStoreController.java @@ -133,7 +133,7 @@ public CreatePoliciesResponse registerAllowedPolicy(@Valid @RequestBody final Cr try { registeredPolicy = service.registerPolicy(request); } catch (JsonParseException e) { - throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage()); + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage(), e); } if (registeredPolicy == null) { From 923199a7429d1af0f19356400898da6cd7f31856 Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Wed, 8 May 2024 21:52:49 +0200 Subject: [PATCH 15/39] feat(impl): [#528] replace dummy policyIds with UUIDs --- .../tractusx/irs/policystore/models/CreatePolicyRequest.java | 2 +- .../tractusx/irs/policystore/models/PolicyResponse.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/models/CreatePolicyRequest.java b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/models/CreatePolicyRequest.java index 8ad85f37ed..9a60c46061 100644 --- a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/models/CreatePolicyRequest.java +++ b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/models/CreatePolicyRequest.java @@ -70,7 +70,7 @@ The business partner number (BPN) for which the policy should be registered. "@context": { "odrl": "http://www.w3.org/ns/odrl/2/" }, - "@id": "policy-id", + "@id": "e917f5f-8dac-49ac-8d10-5b4d254d2b48", "@type": "PolicyDefinitionRequestDto", "policy": { "@type": "Policy", diff --git a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/models/PolicyResponse.java b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/models/PolicyResponse.java index b4c583a91a..422ffcf953 100644 --- a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/models/PolicyResponse.java +++ b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/models/PolicyResponse.java @@ -41,9 +41,9 @@ public record PolicyResponse(OffsetDateTime validUntil, Payload payload) { "@context": { "odrl": "http://www.w3.org/ns/odrl/2/" }, - "@id": "policy-id3", + "@id": "e917f5f-8dac-49ac-8d10-5b4d254d2b48", "policy": { - "policyId": "p3", + "policyId": "e917f5f-8dac-49ac-8d10-5b4d254d2b48", "createdOn": "2024-03-28T03:34:42.9454448Z", "validUntil": "2025-12-12T23:59:59.999Z", "permissions": [ From db2f1a3b694aeb1758949c7d13495561a1e49cea Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Wed, 8 May 2024 21:54:40 +0200 Subject: [PATCH 16/39] feat(impl): [#528] init with builder, use clock --- .../services/PolicyStoreService.java | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) 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 2d458ec331..31386bfd7a 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 @@ -99,10 +99,12 @@ private static final class ConfiguredDefaultPolicy { public PolicyStoreService(final DefaultAcceptedPoliciesConfig defaultAcceptedPoliciesConfig, final PolicyPersistence persistence, final EdcTransformer edcTransformer, final Clock clock) { + + this.clock = clock; + this.allowedPoliciesFromConfig = createDefaultPolicyFromConfig(defaultAcceptedPoliciesConfig); this.persistence = persistence; this.edcTransformer = edcTransformer; - this.clock = clock; } /** @@ -320,11 +322,17 @@ private List createDefaultPolicyFromConfig( new Constraint(acceptedPolicy.getLeftOperand(), new Operator(OperatorType.fromValue(acceptedPolicy.getOperator())), acceptedPolicy.getRightOperand()))); - final Policy policy = new Policy(ConfiguredDefaultPolicy.DEFAULT_POLICY_ID, OffsetDateTime.now(), - OffsetDateTime.now().plusYears(ConfiguredDefaultPolicy.DEFAULT_POLICY_LIFETIME_YEARS), - List.of(new Permission(PolicyType.USE, new Constraints(constraints, constraints)))); - return List.of(policy); + 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()); } } From 1da88e0852cb2f63cb199d03ab4c2bbae476ecd4 Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Wed, 8 May 2024 21:57:58 +0200 Subject: [PATCH 17/39] feat(impl): [#528] move more validation code to PolicyValidator and use it in PolicyStoreService reason: validation logic in one place (single responsibility) --- .../services/PolicyStoreService.java | 25 ++-------------- .../validators/PolicyValidator.java | 29 ++++++++++++++++++- 2 files changed, 31 insertions(+), 23 deletions(-) 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 31386bfd7a..28cfb1dd15 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 @@ -56,6 +56,7 @@ import org.eclipse.tractusx.irs.policystore.models.CreatePolicyRequest; import org.eclipse.tractusx.irs.policystore.models.UpdatePolicyRequest; import org.eclipse.tractusx.irs.policystore.persistence.PolicyPersistence; +import org.eclipse.tractusx.irs.policystore.validators.PolicyValidator; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import org.springframework.web.server.ResponseStatusException; @@ -78,8 +79,6 @@ public class PolicyStoreService implements AcceptedPoliciesProvider { private final Clock clock; - private static final String POLICY_INCOMPLETE_MESSAGE = "Policy does not contain all required fields. Missing: %s"; - private static final String DEFAULT = "default"; /** @@ -99,7 +98,7 @@ private static final class ConfiguredDefaultPolicy { public PolicyStoreService(final DefaultAcceptedPoliciesConfig defaultAcceptedPoliciesConfig, final PolicyPersistence persistence, final EdcTransformer edcTransformer, final Clock clock) { - + this.clock = clock; this.allowedPoliciesFromConfig = createDefaultPolicyFromConfig(defaultAcceptedPoliciesConfig); @@ -128,7 +127,7 @@ public Policy registerPolicy(final CreatePolicyRequest request) { } Policy doRegisterPolicy(final Policy policy, final String businessPartnersNumber) { - validatePolicy(policy); + PolicyValidator.validate(policy); policy.setCreatedOn(OffsetDateTime.now(clock)); log.info("Registering new policy with id {}, valid until {}", policy.getPolicyId(), policy.getValidUntil()); try { @@ -138,24 +137,6 @@ Policy doRegisterPolicy(final Policy policy, final String businessPartnersNumber } } - /** - * Checks whether policy from register policy request has all required fields - * - * @param policy policy to register - */ - private void validatePolicy(final Policy policy) { - - if (policy.getPermissions() == null) { - throw new ResponseStatusException(HttpStatus.BAD_REQUEST, - String.format(POLICY_INCOMPLETE_MESSAGE, "odrl:permission")); - } - - if (policy.getPermissions().stream().anyMatch(p -> p.getConstraint() == null)) { - throw new ResponseStatusException(HttpStatus.BAD_REQUEST, - String.format(POLICY_INCOMPLETE_MESSAGE, "odrl:constraint")); - } - } - /** * Finds policies by list of BPN. * diff --git a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/PolicyValidator.java b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/PolicyValidator.java index da1cbb2495..d498a74d4b 100644 --- a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/PolicyValidator.java +++ b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/PolicyValidator.java @@ -27,15 +27,41 @@ import jakarta.validation.Validator; import jakarta.validation.ValidatorFactory; import org.eclipse.tractusx.irs.edc.client.policy.Policy; +import org.springframework.http.HttpStatus; +import org.springframework.web.server.ResponseStatusException; +/** + * Validator for policy + */ public class PolicyValidator { + private static final String POLICY_INCOMPLETE_MESSAGE = "Policy does not contain all required fields. Missing: %s"; + record PolicyId(@ValidPolicyId String policyId) { } + /** + * Validates the policy + * + * @param policy the policy + */ public static void validate(final Policy policy) { - try (final ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory()) { + if (policy.getPolicyId() == null) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, String.format(POLICY_INCOMPLETE_MESSAGE, "@id")); + } + + if (policy.getPermissions() == null) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, + String.format(POLICY_INCOMPLETE_MESSAGE, "odrl:permission")); + } + + if (policy.getPermissions().stream().anyMatch(p -> p.getConstraint() == null)) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, + String.format(POLICY_INCOMPLETE_MESSAGE, "odrl:constraint")); + } + + try (ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory()) { final Validator validator = validatorFactory.getValidator(); @@ -50,6 +76,7 @@ public static void validate(final Policy policy) { if (!policyViolations.isEmpty()) { throw new ConstraintViolationException(policyViolations); } + } } From daaea59f481d408b06f6faad5a4fe02564b84a31 Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Wed, 8 May 2024 22:01:03 +0200 Subject: [PATCH 18/39] feat(impl): [#528] replace dummy policyIds with UUIDs --- docs/src/api/irs-api.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/src/api/irs-api.yaml b/docs/src/api/irs-api.yaml index 99d729841f..8fc7ec3812 100644 --- a/docs/src/api/irs-api.yaml +++ b/docs/src/api/irs-api.yaml @@ -815,9 +815,9 @@ paths: payload: '@context': odrl: http://www.w3.org/ns/odrl/2/ - '@id': policy-id3 + '@id': e917f5f-8dac-49ac-8d10-5b4d254d2b48 policy: - policyId: p3 + policyId: e917f5f-8dac-49ac-8d10-5b4d254d2b48 createdOn: 2024-03-28T03:34:42.9454448Z validUntil: 2025-12-12T23:59:59.999Z permissions: @@ -1805,7 +1805,7 @@ components: example: '@context': odrl: http://www.w3.org/ns/odrl/2/ - '@id': policy-id + '@id': e917f5f-8dac-49ac-8d10-5b4d254d2b48 '@type': PolicyDefinitionRequestDto policy: '@type': Policy @@ -2198,7 +2198,7 @@ components: example: '@context': odrl: http://www.w3.org/ns/odrl/2/ - '@id': policy-id + '@id': e917f5f-8dac-49ac-8d10-5b4d254d2b48 '@type': PolicyDefinitionRequestDto policy: '@type': Policy From 3306946e5f356346340defa9693119ac8bf772a0 Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Wed, 8 May 2024 22:02:03 +0200 Subject: [PATCH 19/39] feat(impl): [#528] replace old BPN pattern for CreatePolicyRequest in irs-api.yaml --- docs/src/api/irs-api.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/api/irs-api.yaml b/docs/src/api/irs-api.yaml index 8fc7ec3812..b246abc3d6 100644 --- a/docs/src/api/irs-api.yaml +++ b/docs/src/api/irs-api.yaml @@ -1796,7 +1796,7 @@ components: This parameter is optional. If not set the policy is registered for each existing BPN. example: BPNL1234567890AB - pattern: "(BPN)[LSA][\\w\\d]{10}[\\w\\d]{2}" + pattern: "(BPNL)[\\w\\d]{10}[\\w\\d]{2}" payload: type: object additionalProperties: From 84b4b41d15557891696bea9fc474c45fda175773 Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Wed, 8 May 2024 22:03:51 +0200 Subject: [PATCH 20/39] feat(impl): [#528] improve exception handling for parsing policy JSON --- .../client/transformer/JsonObjectToIrsPolicyTransformer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/transformer/JsonObjectToIrsPolicyTransformer.java b/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/transformer/JsonObjectToIrsPolicyTransformer.java index 2d831e94a6..91aa9ec9b9 100644 --- a/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/transformer/JsonObjectToIrsPolicyTransformer.java +++ b/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/transformer/JsonObjectToIrsPolicyTransformer.java @@ -56,7 +56,7 @@ protected JsonObjectToIrsPolicyTransformer(final ObjectMapper objectMapper) { try { final Object result = objectMapper.readerFor(Policy.class).readValue(v.asJsonObject().toString()); builder.permissions(((Policy) result).getPermissions()); - } catch (JsonProcessingException e) { + } catch (ClassCastException | JsonProcessingException e) { throw new JsonParseException("Invalid policy", e); } }); From 4376e76eacfb2ba0c7cbdc67f60381bae8a929b8 Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Wed, 8 May 2024 22:04:17 +0200 Subject: [PATCH 21/39] feat(impl): [#528] replace dummy policyIds with UUIDs --- .../JsonObjectToIrsPolicyTransformerTest.java | 4 +-- .../PolicyStoreControllerTest.java | 26 +++++++++++++++---- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client/transformer/JsonObjectToIrsPolicyTransformerTest.java b/irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client/transformer/JsonObjectToIrsPolicyTransformerTest.java index 8c5c83ed8c..dc8081c2b2 100644 --- a/irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client/transformer/JsonObjectToIrsPolicyTransformerTest.java +++ b/irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client/transformer/JsonObjectToIrsPolicyTransformerTest.java @@ -55,7 +55,7 @@ class JsonObjectToIrsPolicyTransformerTest { "cx-policy": "https://w3id.org/catenax/policy/", "odrl": "http://www.w3.org/ns/odrl/2/" }, - "@id": "policy-id", + "@id": "e917f5f-8dac-49ac-8d10-5b4d254d2b48", "policy": { "odrl:permission": [ { @@ -104,7 +104,7 @@ void shouldTransformJsonObjectToPolicyCorrectly() { final Policy transformed = jsonObjectToIrsPolicyTransformer.transform(jsonObject, transformerContext); // then - assertThat(transformed.getPolicyId()).isEqualTo("policy-id"); + assertThat(transformed.getPolicyId()).isEqualTo("e917f5f-8dac-49ac-8d10-5b4d254d2b48"); final Permission permission = transformed.getPermissions().get(0); assertThat(permission.getAction()).isEqualTo(PolicyType.USE); final List and = permission.getConstraint().getAnd(); diff --git a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/controllers/PolicyStoreControllerTest.java b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/controllers/PolicyStoreControllerTest.java index 7c805f2504..a7f271ac19 100644 --- a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/controllers/PolicyStoreControllerTest.java +++ b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/controllers/PolicyStoreControllerTest.java @@ -32,6 +32,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.UUID; import java.util.stream.Collectors; import jakarta.json.JsonObject; @@ -64,7 +65,7 @@ public class PolicyStoreControllerTest { "@context": { "odrl": "http://www.w3.org/ns/odrl/2/" }, - "@id": "policy-id", + "@id": "e917f5f-8dac-49ac-8d10-5b4d254d2b48", "policy": { "odrl:permission": [ { @@ -130,8 +131,13 @@ class GetPolicyTests { @Test void getPolicies() { // arrange - final List policies = List.of( - new Policy("testId", OffsetDateTime.now(), OffsetDateTime.now(), createPermissions())); + final String policyId = randomPolicyId(); + final List policies = List.of(Policy.builder() + .policyId(policyId) + .createdOn(OffsetDateTime.now()) + .validUntil(OffsetDateTime.now()) + .permissions(createPermissions()) + .build()); final String bpn = "bpn1"; when(policyStoreServiceMock.getPolicies(List.of(bpn))).thenReturn(Map.of(bpn, policies)); @@ -146,8 +152,13 @@ void getPolicies() { @Test void getAllPolicies() { // arrange - final List policies = List.of( - new Policy("testId", OffsetDateTime.now(), OffsetDateTime.now(), createPermissions())); + final String policyId = randomPolicyId(); + final List policies = List.of(Policy.builder() + .policyId(policyId) + .createdOn(OffsetDateTime.now()) + .validUntil(OffsetDateTime.now()) + .permissions(createPermissions()) + .build()); final String bpn = "bpn1"; when(policyStoreServiceMock.getPolicies(null)).thenReturn(Map.of(bpn, policies)); @@ -208,4 +219,9 @@ private Constraints createConstraints() { new Constraint("FrameworkAgreement.traceability", new Operator(OperatorType.EQ), "active"), new Constraint("PURPOSE", new Operator(OperatorType.EQ), "ID 3.1 Trace"))); } + + private static String randomPolicyId() { + return UUID.randomUUID().toString(); + } + } \ No newline at end of file From 59cb21af9461617739c582e587d1d4827c83d66f Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Wed, 8 May 2024 22:07:09 +0200 Subject: [PATCH 22/39] feat(impl): [#528] replace dummy policyIds with UUIDs and use builder --- .../persistence/PolicyPersistenceTest.java | 81 ++++++----- .../services/PolicyStoreServiceTest.java | 127 ++++++++++++++---- 2 files changed, 150 insertions(+), 58 deletions(-) diff --git a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/persistence/PolicyPersistenceTest.java b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/persistence/PolicyPersistenceTest.java index 9cbbeac40a..541f6aed4e 100644 --- a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/persistence/PolicyPersistenceTest.java +++ b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/persistence/PolicyPersistenceTest.java @@ -39,6 +39,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.UUID; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; @@ -76,7 +77,7 @@ class SavePolicyTests { @Test void save() throws BlobPersistenceException { // ARRANGE - final var policy = new Policy("test", OffsetDateTime.now(), OffsetDateTime.now(), emptyList()); + final var policy = policyBuilder(randomPolicyId()).build(); // ACT testee.save("testBpn", policy); @@ -88,7 +89,7 @@ void save() throws BlobPersistenceException { @Test void saveWithoutBpn() throws BlobPersistenceException { // ARRANGE - final var policy = new Policy("test", OffsetDateTime.now(), OffsetDateTime.now(), emptyList()); + final var policy = policyBuilder(randomPolicyId()).build(); // ACT testee.save("default", policy); @@ -100,26 +101,27 @@ void saveWithoutBpn() throws BlobPersistenceException { @Test void saveDuplicate() throws BlobPersistenceException, JsonProcessingException { // ARRANGE - final var policy = new Policy("test", OffsetDateTime.now(), OffsetDateTime.now(), emptyList()); - final var policies = List.of(policy); - when(mockPersistence.getBlob(anyString())).thenReturn(Optional.of(mapper.writeValueAsBytes(policies))); + final String policyId = randomPolicyId(); + final var policy = policyBuilder(policyId).build(); + when(mockPersistence.getBlob(anyString())).thenReturn( + Optional.of(mapper.writeValueAsBytes(List.of(policy)))); // ACT & ASSERT final ThrowableAssert.ThrowingCallable call = () -> testee.save("testBpn", policy); assertThatThrownBy(call).isInstanceOf(PolicyStoreException.class) - .hasMessageContaining("'test'") + .hasMessageContaining("'" + policyId + "'") .hasMessageContaining("already exists"); } @Test void saveWithReadError() throws BlobPersistenceException { // ARRANGE - final var policy = new Policy("test", OffsetDateTime.now(), OffsetDateTime.now(), emptyList()); when(mockPersistence.getBlob(any())).thenThrow( - new BlobPersistenceException("test", new IllegalStateException())); + new BlobPersistenceException("test exception", new IllegalStateException())); // ACT & ASSERT - final ThrowableAssert.ThrowingCallable call = () -> testee.save("testBpn", policy); + final ThrowableAssert.ThrowingCallable call = () -> testee.save("testBpn", + policyBuilder(randomPolicyId()).build()); assertThatThrownBy(call).isInstanceOf(PolicyStoreException.class) .hasMessageContaining("Unable to read policy data"); } @@ -127,12 +129,12 @@ void saveWithReadError() throws BlobPersistenceException { @Test void saveWithWriteError() throws BlobPersistenceException { // ARRANGE - final var policy = new Policy("test", OffsetDateTime.now(), OffsetDateTime.now(), emptyList()); - doThrow(new BlobPersistenceException("test", new IllegalStateException())).when(mockPersistence) - .putBlob(any(), any()); + final String policyId = randomPolicyId(); + doThrow(new BlobPersistenceException(policyId, new IllegalStateException())).when(mockPersistence) + .putBlob(any(), any()); // ACT & ASSERT - final ThrowableAssert.ThrowingCallable call = () -> testee.save("testBpn", policy); + final ThrowableAssert.ThrowingCallable call = () -> testee.save("testBpn", policyBuilder(policyId).build()); assertThatThrownBy(call).isInstanceOf(PolicyStoreException.class) .hasMessageContaining("Unable to store policy data"); } @@ -143,10 +145,9 @@ class DeletePolicyTests { @Test void delete_success() throws BlobPersistenceException, JsonProcessingException { // ARRANGE - final String policyId = "test"; - final var policy = new Policy(policyId, OffsetDateTime.now(), OffsetDateTime.now(), emptyList()); - final var policies = List.of(policy); - when(mockPersistence.getBlob(anyString())).thenReturn(Optional.of(mapper.writeValueAsBytes(policies))); + final String policyId = randomPolicyId(); + when(mockPersistence.getBlob(anyString())).thenReturn( + Optional.of(mapper.writeValueAsBytes(List.of(policyBuilder(policyId).build())))); // ACT testee.delete("testBpn", policyId); @@ -158,13 +159,17 @@ void delete_success() throws BlobPersistenceException, JsonProcessingException { @Test void delete_shouldThrowExceptionIfPolicyWithIdDoesntExists() throws BlobPersistenceException, JsonProcessingException { + // ARRANGE - final var policy = new Policy("policyId", OffsetDateTime.now(), OffsetDateTime.now(), emptyList()); + + final String policyId = randomPolicyId(); + final String notExistingPolicyId = randomPolicyId(); + when(mockPersistence.getBlob(anyString())).thenReturn( - Optional.of(mapper.writeValueAsBytes(List.of(policy)))); + Optional.of(mapper.writeValueAsBytes(List.of(policyBuilder(policyId).build())))); // ACT - assertThrows(PolicyStoreException.class, () -> testee.delete("testBpn", "notExistingPolicyId")); + assertThrows(PolicyStoreException.class, () -> testee.delete("testBpn", notExistingPolicyId)); } } @@ -173,10 +178,11 @@ class ReadPoliciesTests { @Test void readAll_withBpn() throws BlobPersistenceException, JsonProcessingException { + // ARRANGE - final var policy = new Policy("test", OffsetDateTime.now(), OffsetDateTime.now(), emptyList()); - final var policies = List.of(policy); - when(mockPersistence.getBlob(anyString())).thenReturn(Optional.of(mapper.writeValueAsBytes(policies))); + final String policyId = randomPolicyId(); + when(mockPersistence.getBlob(anyString())).thenReturn( + Optional.of(mapper.writeValueAsBytes(List.of(policyBuilder(policyId).build())))); // ACT final var readPolicies = testee.readAll("testBpn"); @@ -187,15 +193,11 @@ void readAll_withBpn() throws BlobPersistenceException, JsonProcessingException @Test void readAll_shouldReturnCorrect() throws BlobPersistenceException, JsonProcessingException { + // ARRANGE - final var policy = Policy.builder() - .policyId("test") - .createdOn(OffsetDateTime.now()) - .validUntil(OffsetDateTime.now()) - .permissions(emptyList()) - .build(); + final String policyId = randomPolicyId(); final Map blobMap = new HashMap<>(); - blobMap.put("bpn1", mapper.writeValueAsBytes(List.of(policy))); + blobMap.put("bpn1", mapper.writeValueAsBytes(List.of(policyBuilder(policyId).build()))); blobMap.put("bpn2", mapper.writeValueAsBytes(emptyList())); when(mockPersistence.getAllBlobs()).thenReturn(blobMap); @@ -211,11 +213,12 @@ void readAll_shouldReturnCorrect() throws BlobPersistenceException, JsonProcessi @Test void readAll_withBpn_withError() throws BlobPersistenceException, JsonProcessingException { + // ARRANGE - final var policy = new Policy("test", OffsetDateTime.now(), OffsetDateTime.now(), emptyList()); - final var policies = List.of(policy); + final String policyId = randomPolicyId(); final var mapperMock = mock(ObjectMapper.class); - when(mockPersistence.getBlob(anyString())).thenReturn(Optional.of(mapper.writeValueAsBytes(policies))); + when(mockPersistence.getBlob(anyString())).thenReturn( + Optional.of(mapper.writeValueAsBytes(List.of(policyBuilder(policyId).build())))); when(mapperMock.readerForListOf(Policy.class)).thenThrow(new IllegalStateException()); final var localTestee = new PolicyPersistence(mockPersistence, mapperMock); @@ -226,4 +229,16 @@ void readAll_withBpn_withError() throws BlobPersistenceException, JsonProcessing .hasMessageContaining("Could not read the policies from the store"); } } + + private static Policy.PolicyBuilder policyBuilder(final String policyId) { + return Policy.builder() + .policyId(policyId) + .createdOn(OffsetDateTime.now()) + .validUntil(OffsetDateTime.now()) + .permissions(emptyList()); + } + + private static String randomPolicyId() { + return UUID.randomUUID().toString(); + } } \ No newline at end of file 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 f2b75bbd35..c3f4cfcb12 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 @@ -39,6 +39,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.UUID; import jakarta.json.JsonObject; import org.assertj.core.api.AbstractThrowableAssert; @@ -120,7 +121,7 @@ void registerPolicy_withBpnNull_shouldStoreAsDefault() { // ASSERT verify(persistenceMock).save(eq("default"), policyCaptor.capture()); - assertThat(policyCaptor.getValue().getPolicyId()).isEqualTo("policy-id"); + assertThat(policyCaptor.getValue().getPolicyId()).isEqualTo("e917f5f-8dac-49ac-8d10-5b4d254d2b48"); assertThat(policyCaptor.getValue().getValidUntil()).isEqualTo(now); assertThat(policyCaptor.getValue().getPermissions()).hasSize(1); } @@ -141,7 +142,7 @@ void registerPolicy_success() { // ASSERT verify(persistenceMock).save(eq("BPNL00000123ABCD"), policyCaptor.capture()); - assertThat(policyCaptor.getValue().getPolicyId()).isEqualTo("policy-id"); + assertThat(policyCaptor.getValue().getPolicyId()).isEqualTo("e917f5f-8dac-49ac-8d10-5b4d254d2b48"); assertThat(policyCaptor.getValue().getValidUntil()).isEqualTo(validUntil); assertThat(policyCaptor.getValue().getPermissions()).isNotEmpty(); @@ -156,7 +157,13 @@ void doRegisterPolicy_permissionEmpty() { // ARRANGE final OffsetDateTime now = OffsetDateTime.now(clock); - final Policy policy = new Policy("testId", now, now.plusMinutes(1), emptyList()); + final String policyId = randomPolicyId(); + final Policy policy = Policy.builder() + .policyId(policyId) + .createdOn(now) + .validUntil(now.plusMinutes(1)) + .permissions(emptyList()) + .build(); // ACT testee.doRegisterPolicy(policy, BPN); @@ -170,7 +177,13 @@ void doRegisterPolicy_permissionsNull() { // ARRANGE final OffsetDateTime now = OffsetDateTime.now(clock); - final Policy policy = new Policy("testId", now, now.plusMinutes(1), null); + final String policyId = randomPolicyId(); + final Policy policy = Policy.builder() + .policyId(policyId) + .createdOn(now) + .validUntil(now.plusMinutes(1)) + .permissions(null) + .build(); // ACT final ThrowableAssert.ThrowingCallable call = () -> testee.doRegisterPolicy(policy, "A"); @@ -188,7 +201,13 @@ void doRegisterPolicy_withFilledPermissionList() { // ARRANGE final OffsetDateTime now = OffsetDateTime.now(clock); - final Policy policy = new Policy("testId", now, now.plusMinutes(1), createPermissions()); + final String policyId = randomPolicyId(); + final Policy policy = Policy.builder() + .policyId(policyId) + .createdOn(now) + .validUntil(now.plusMinutes(1)) + .permissions(createPermissions()) + .build(); // ACT testee.doRegisterPolicy(policy, BPN); @@ -207,9 +226,15 @@ void doRegisterPolicy_withFilledPermissionList() { void doRegisterPolicy_whenPolicyStoreExceptionOccurs() { // ACT - final String policyId = "testId"; + final String policyId = randomPolicyId(); final OffsetDateTime now = OffsetDateTime.now(clock); - final Policy policy = new Policy(policyId, now, now.plusMinutes(1), createPermissions()); + final Policy policy = Policy.builder() + .policyId(policyId) + .createdOn(now) + .validUntil(now.plusMinutes(1)) + .permissions(createPermissions()) + .build(); + doThrow(new PolicyStoreException("")).when(persistenceMock).save(any(), any()); // ASSERT @@ -221,6 +246,7 @@ void doRegisterPolicy_withMissingConstraintShouldThrowException() { // ARRANGE final Policy policy = Policy.builder() + .policyId(randomPolicyId()) .permissions(List.of(Permission.builder() .constraint(new Constraints(emptyList(), emptyList())) @@ -329,7 +355,12 @@ void getAcceptedPolicies_whenNoPoliciesAssociatedWithTheGivenBpn_shouldReturnThe } private Policy createPolicy(final String policyId) { - return new Policy(policyId, OffsetDateTime.now(clock), OffsetDateTime.now(clock).plusDays(1), emptyList()); + return Policy.builder() + .policyId(policyId) + .createdOn(OffsetDateTime.now(clock)) + .validUntil(OffsetDateTime.now(clock).plusDays(1)) + .permissions(emptyList()) + .build(); } private List createPermissions() { @@ -350,21 +381,32 @@ class DeletePolicyTests { @Test void deletePolicy_deleteSuccessful() { // ARRANGE - when(persistenceMock.readAll()).thenReturn(Map.of(BPN, List.of(new Policy("testId", null, null, null)))); + final String policyId = randomPolicyId(); + when(persistenceMock.readAll()).thenReturn(Map.of(BPN, List.of(Policy.builder() + .policyId(policyId) + .createdOn(null) + .validUntil(null) + .permissions(null) + .build()))); // ACT - testee.deletePolicy("testId"); + testee.deletePolicy(policyId); // ASSERT - verify(persistenceMock).delete(BPN, "testId"); + verify(persistenceMock).delete(BPN, policyId); } @Test void deletePolicy_exceptionFromPolicyPersistence_shouldReturnHttpStatus500() { // ACT - final String policyId = "testId"; - when(persistenceMock.readAll()).thenReturn(Map.of(BPN, List.of(new Policy(policyId, null, null, null)))); + final String policyId = randomPolicyId(); + when(persistenceMock.readAll()).thenReturn(Map.of(BPN, List.of(Policy.builder() + .policyId(policyId) + .createdOn(null) + .validUntil(null) + .permissions(null) + .build()))); doThrow(new PolicyStoreException("")).when(persistenceMock).delete(BPN, policyId); // ASSERT @@ -376,13 +418,17 @@ void deletePolicy_exceptionFromPolicyPersistence_shouldReturnHttpStatus500() { void deletePolicy_whenPolicyNotFound_shouldReturnHttpStatus404() { // ACT - final String policyId = "notExistingPolicyId"; - when(persistenceMock.readAll()).thenReturn(Map.of(BPN, List.of(new Policy("testId", null, null, null)))); + final String notExistingPolicyId = randomPolicyId(); + final String policyId = randomPolicyId(); + when(persistenceMock.readAll()).thenReturn( + Map.of(BPN, List.of(Policy.builder().policyId(policyId).build()))); // ASSERT - assertThatThrownBy(() -> testee.deletePolicy(policyId)).isInstanceOf(ResponseStatusException.class) - .hasMessageContaining("404 NOT_FOUND") - .hasMessageContaining(policyId); + assertThatThrownBy(() -> testee.deletePolicy(notExistingPolicyId)).isInstanceOf( + ResponseStatusException.class) + .hasMessageContaining("404 NOT_FOUND") + .hasMessageContaining( + notExistingPolicyId); } } @@ -404,7 +450,12 @@ void updatePolicies_shouldUpdateBpnAndValidUntil() { final List permissions = emptyList(); - final Policy testPolicy = new Policy(policyId, createdOn, originalValidUntil, permissions); + final Policy testPolicy = Policy.builder() + .policyId(policyId) + .createdOn(createdOn) + .validUntil(originalValidUntil) + .permissions(permissions) + .build(); when(persistenceMock.readAll()).thenReturn(Map.of(originalBpn, List.of(testPolicy))); // get policies for bpn when(persistenceMock.readAll(originalBpn)).thenReturn(List.of(testPolicy)); @@ -434,7 +485,12 @@ void updatePolicies_shouldAddPolicyToEachBpn() { // BPN1 without any policies // BPN2 with testPolicy - final Policy testPolicy = new Policy(policyId, createdOn, validUntil, permissions); + final Policy testPolicy = Policy.builder() + .policyId(policyId) + .createdOn(createdOn) + .validUntil(validUntil) + .permissions(permissions) + .build(); when(persistenceMock.readAll()).thenReturn(Map.of("bpn2", List.of(testPolicy))); when(persistenceMock.readAll("bpn2")).thenReturn(List.of(testPolicy)); @@ -468,16 +524,34 @@ void updatePolicies_shouldAssociateEachGivenPolicyWithEachGivenBpn() { final List permissions = emptyList(); - final Policy testPolicy1 = new Policy(policyId1, createdOn, originalValidUntil, permissions); - final Policy testPolicy2 = new Policy(policyId2, createdOn, originalValidUntil, permissions); + final Policy testPolicy1 = Policy.builder() + .policyId(policyId1) + .createdOn(createdOn) + .validUntil(originalValidUntil) + .permissions(permissions) + .build(); + final Policy testPolicy2 = Policy.builder() + .policyId(policyId2) + .createdOn(createdOn) + .validUntil(originalValidUntil) + .permissions(permissions) + .build(); // BPN1 without any policies // BPN2 with testPolicy1 and testPolicy2 when(persistenceMock.readAll()).thenReturn(Map.of(bpn2, List.of(testPolicy1, testPolicy2))); - when(persistenceMock.readAll(bpn2)).thenReturn( - List.of(new Policy(policyId1, createdOn, originalValidUntil, permissions), - new Policy(policyId2, createdOn, originalValidUntil, permissions))); + when(persistenceMock.readAll(bpn2)).thenReturn(List.of(Policy.builder() + .policyId(policyId1) + .createdOn(createdOn) + .validUntil(originalValidUntil) + .permissions(permissions) + .build(), Policy.builder() + .policyId(policyId2) + .createdOn(createdOn) + .validUntil(originalValidUntil) + .permissions(permissions) + .build())); // ACT testee.updatePolicies( @@ -523,4 +597,7 @@ void updatePolicies_exceptionFromPolicyPersistence_shouldReturnHttpStatus500() { } + private static String randomPolicyId() { + return UUID.randomUUID().toString(); + } } \ No newline at end of file From 14a82e8383d3cdbe0784e7df7b90a44ecbe305f8 Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Wed, 8 May 2024 22:16:42 +0200 Subject: [PATCH 23/39] feat(impl): [#528] fix test --- .../validators/PolicyValidatorTest.java | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyValidatorTest.java b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyValidatorTest.java index 665e498c2d..94ddb78b32 100644 --- a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyValidatorTest.java +++ b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyValidatorTest.java @@ -21,18 +21,39 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; +import java.util.Collections; +import java.util.List; + import jakarta.validation.ConstraintViolationException; +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.junit.jupiter.api.Test; class PolicyValidatorTest { @Test void invalidPolicyId() { - final Policy policy = Policy.builder().policyId("_invalid_policy_id_").build(); + final Policy policy = Policy.builder().policyId("_invalid_policy_id_").permissions(createPermissions()).build(); assertThatThrownBy(() -> PolicyValidator.validate(policy)).isInstanceOf(ConstraintViolationException.class) .hasMessageContaining("policyId") .hasMessageContaining("must be a valid UUID"); } + private List createPermissions() { + return List.of(Permission.builder().action(PolicyType.USE).constraint(createConstraints()).build(), + Permission.builder().action(PolicyType.ACCESS).constraint(createConstraints()).build()); + } + + private Constraints createConstraints() { + return new Constraints(Collections.emptyList(), + List.of(new Constraint("Membership", new Operator(OperatorType.EQ), "active"), + new Constraint("FrameworkAgreement.traceability", new Operator(OperatorType.EQ), "active"), + new Constraint("PURPOSE", new Operator(OperatorType.EQ), "ID 3.1 Trace"))); + } + } \ No newline at end of file From b9a120b85d72603a8c2b8023c373392b4bb81832 Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Thu, 9 May 2024 02:40:01 +0200 Subject: [PATCH 24/39] feat(impl): [#528] fix PMD warnings --- .../services/PolicyStoreService.java | 2 +- .../validators/PolicyIdValidator.java | 18 ++------ .../validators/PolicyValidator.java | 46 ++++++++++++------- 3 files changed, 34 insertions(+), 32 deletions(-) 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 28cfb1dd15..a1081fb980 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 @@ -126,7 +126,7 @@ public Policy registerPolicy(final CreatePolicyRequest request) { return registeredPolicy; } - Policy doRegisterPolicy(final Policy policy, final String businessPartnersNumber) { + /* package */ Policy doRegisterPolicy(final Policy policy, final String businessPartnersNumber) { PolicyValidator.validate(policy); policy.setCreatedOn(OffsetDateTime.now(clock)); log.info("Registering new policy with id {}, valid until {}", policy.getPolicyId(), policy.getValidUntil()); diff --git a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdValidator.java b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdValidator.java index e22e704074..c7042faf3a 100644 --- a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdValidator.java +++ b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdValidator.java @@ -32,27 +32,17 @@ public class PolicyIdValidator implements ConstraintValidator> policyIdViolations = validator.validate( - new PolicyId(policy.getPolicyId())); - if (!policyIdViolations.isEmpty()) { - throw new ConstraintViolationException(policyIdViolations); - } - - final Set> policyViolations = validator.validate(policy); - if (!policyViolations.isEmpty()) { - throw new ConstraintViolationException(policyViolations); - } - + private static void validatePolicy(final Policy policy, final Validator validator) { + final Set> policyViolations = validator.validate(policy); + if (!policyViolations.isEmpty()) { + throw new ConstraintViolationException(policyViolations); } + } + private static void validatePolicyId(final Policy policy, final Validator validator) { + final Set> policyIdViolations = validator.validate( + new PolicyId(policy.getPolicyId())); + if (!policyIdViolations.isEmpty()) { + throw new ConstraintViolationException(policyIdViolations); + } } } From fa3476426a805538e9f120ae9346504007dc75f5 Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Thu, 9 May 2024 03:30:26 +0200 Subject: [PATCH 25/39] feat(impl): [#528] validate policyIDs for update and delete --- .../controllers/PolicyStoreController.java | 3 +- .../models/UpdatePolicyRequest.java | 2 + .../validators/ListOfPolicyIds.java | 47 ++++++++++ .../validators/ListOfPolicyIdsValidator.java | 51 +++++++++++ .../validators/PolicyIdValidator.java | 2 +- ...usinessPartnerNumberListValidatorTest.java | 2 +- .../validators/PolicyIdListValidatorTest.java | 91 +++++++++++++++++++ 7 files changed, 195 insertions(+), 3 deletions(-) create mode 100644 irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/ListOfPolicyIds.java create mode 100644 irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/ListOfPolicyIdsValidator.java create mode 100644 irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdListValidatorTest.java diff --git a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/controllers/PolicyStoreController.java b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/controllers/PolicyStoreController.java index abe452ea7c..77e9285c9c 100644 --- a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/controllers/PolicyStoreController.java +++ b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/controllers/PolicyStoreController.java @@ -56,6 +56,7 @@ import org.eclipse.tractusx.irs.policystore.services.PolicyStoreService; import org.eclipse.tractusx.irs.policystore.validators.BusinessPartnerNumberListValidator; import org.eclipse.tractusx.irs.policystore.validators.ValidListOfBusinessPartnerNumbers; +import org.eclipse.tractusx.irs.policystore.validators.ValidPolicyId; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; @@ -220,7 +221,7 @@ public Map> getPolicies(// @DeleteMapping("/policies/{policyId}") @ResponseStatus(HttpStatus.OK) @PreAuthorize("hasAuthority('" + IrsRoles.ADMIN_IRS + "')") - public void deleteAllowedPolicy(@PathVariable("policyId") final String policyId) { + public void deleteAllowedPolicy(@ValidPolicyId @PathVariable("policyId") final String policyId) { service.deletePolicy(policyId); } diff --git a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/models/UpdatePolicyRequest.java b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/models/UpdatePolicyRequest.java index 6731811d33..42fae2f2f2 100644 --- a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/models/UpdatePolicyRequest.java +++ b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/models/UpdatePolicyRequest.java @@ -31,6 +31,7 @@ import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import lombok.Builder; +import org.eclipse.tractusx.irs.policystore.validators.ListOfPolicyIds; import org.eclipse.tractusx.irs.policystore.validators.ValidListOfBusinessPartnerNumbers; /** @@ -52,6 +53,7 @@ public record UpdatePolicyRequest( @Schema(description = "The IDs of the policies to be updated.") // @NotNull // @NotEmpty // + @ListOfPolicyIds // List policyIds // ) { } diff --git a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/ListOfPolicyIds.java b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/ListOfPolicyIds.java new file mode 100644 index 0000000000..46975ae19f --- /dev/null +++ b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/ListOfPolicyIds.java @@ -0,0 +1,47 @@ +/******************************************************************************** + * Copyright (c) 2022,2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +package org.eclipse.tractusx.irs.policystore.validators; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; + +/** + * Annotation for validating list of policyIDs + */ +@Documented +@Constraint(validatedBy = ListOfPolicyIdsValidator.class) +@Target({ ElementType.FIELD, + ElementType.PARAMETER +}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ListOfPolicyIds { + + String message() default "Invalid list of policyIDs"; + + Class[] groups() default { }; + + Class[] payload() default { }; +} diff --git a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/ListOfPolicyIdsValidator.java b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/ListOfPolicyIdsValidator.java new file mode 100644 index 0000000000..29277047b1 --- /dev/null +++ b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/ListOfPolicyIdsValidator.java @@ -0,0 +1,51 @@ +/******************************************************************************** + * Copyright (c) 2022,2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +package org.eclipse.tractusx.irs.policystore.validators; + +import java.util.List; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; + +/** + * Validator for list of policyIDs + */ +public class ListOfPolicyIdsValidator implements ConstraintValidator> { + + @Override + public boolean isValid(final List value, final ConstraintValidatorContext context) { + + // allow null and empty here (in order to allow flexible combination with @NotNull and @NotEmpty) + if (value == null || value.isEmpty()) { + return true; + } + + for (int index = 0; index < value.size(); index++) { + if (!PolicyIdValidator.isValid(value.get(index))) { + context.disableDefaultConstraintViolation(); + final String msg = "The policyId at index %d is invalid (should be a valid UUID)"; + context.buildConstraintViolationWithTemplate(msg.formatted(index)).addConstraintViolation(); + return false; + } + } + + return true; + } +} diff --git a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdValidator.java b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdValidator.java index c7042faf3a..ada5add0b6 100644 --- a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdValidator.java +++ b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdValidator.java @@ -38,7 +38,7 @@ public boolean isValid(final String value, final ConstraintValidatorContext cont return isNull || isValid(value); } - private static boolean isValid(final String policyId) { + public static boolean isValid(final String policyId) { return validateUUID(policyId); } diff --git a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/BusinessPartnerNumberListValidatorTest.java b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/BusinessPartnerNumberListValidatorTest.java index 16b1d0b10c..50e0f7d191 100644 --- a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/BusinessPartnerNumberListValidatorTest.java +++ b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/BusinessPartnerNumberListValidatorTest.java @@ -38,7 +38,7 @@ import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) -public class BusinessPartnerNumberListValidatorTest { +class BusinessPartnerNumberListValidatorTest { public static final String VALID_BPN_1 = "BPNL1234567890AB"; public static final String VALID_BPN_2 = "BPNL123456789012"; diff --git a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdListValidatorTest.java b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdListValidatorTest.java new file mode 100644 index 0000000000..73ec01145d --- /dev/null +++ b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdListValidatorTest.java @@ -0,0 +1,91 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +package org.eclipse.tractusx.irs.policystore.validators; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.verify; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +import jakarta.validation.ConstraintValidatorContext; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Answers; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class PolicyIdListValidatorTest { + + @InjectMocks + private ListOfPolicyIdsValidator validator; + + @Captor + private ArgumentCaptor messageCaptor; + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private ConstraintValidatorContext contextMock; + + @Test + void withEmptyListOfStrings() { + assertThat(validator.isValid(Collections.emptyList(), contextMock)).isTrue(); + } + + @Test + void withNull() { + assertThat(validator.isValid(null, contextMock)).isTrue(); + } + + @Test + void withValidListOfStrings() { + final String policyId1 = UUID.randomUUID().toString(); + final String policyId2 = UUID.randomUUID().toString(); + List validList = Arrays.asList(policyId1, policyId2); + assertThat(validator.isValid(validList, contextMock)).isTrue(); + } + + @Test + void withListContainingInvalidPolicyId() { + List invalidList = Arrays.asList(UUID.randomUUID().toString(), "_INVALID_POLICY_ID_"); + assertThat(validator.isValid(invalidList, contextMock)).isFalse(); + verify(contextMock).buildConstraintViolationWithTemplate(messageCaptor.capture()); + assertThat(messageCaptor.getValue()).contains("policyId").contains(" index 1 ").contains("invalid"); + } + + // @ParameterizedTest + // @ValueSource(strings = { "BPN", + // "BPNL", + // "BPNACB", + // "BPNA1234567890AB", + // "BPNS1234567890AB", + // "DELETE * FROM Table", + // "ERRRES" + // }) + // void withInvalidBPN(final String invalidBPN) { + // assertThat(validator.isValid(Collections.singletonList(invalidBPN), contextMock)).isFalse(); + // verify(contextMock).buildConstraintViolationWithTemplate(messageCaptor.capture()); + // assertThat(messageCaptor.getValue()).contains("BPN").contains(" index 0 ").contains("invalid"); + // } +} From 2ad9f990de2d39a1bb5f6c341052c74d6daf0610 Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Thu, 9 May 2024 03:43:59 +0200 Subject: [PATCH 26/39] feat(impl): [#528] update CHANGELOG.md --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d4b8f8652..315a0fb102 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,12 +10,16 @@ _**For better traceability add the corresponding GitHub issue number in each cha ### Fixed -- Fixed issue in EDR Token renewal #358 +- Fixed issue in EDR token renewal. #358 ### Added - Cucumber test step definitions for Policy Store API (Happy Path) including some test helper utilities. #518 +### Changed + +- Improved policy store API input validation. #528 +- ## [5.1.0] - 2024-05-06 From 6afcdd4d905ab51a4d5945ab213ff57fd111b649 Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Fri, 10 May 2024 10:20:09 +0200 Subject: [PATCH 27/39] feat(impl): [#528] update CHANGELOG.md --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 315a0fb102..23f0b63297 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,6 @@ _**For better traceability add the corresponding GitHub issue number in each cha ### Changed - Improved policy store API input validation. #528 -- ## [5.1.0] - 2024-05-06 From f88f916b1a58f0168a4e5f7cb0ff6315fde6fc84 Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Fri, 10 May 2024 11:40:32 +0200 Subject: [PATCH 28/39] feat(impl): [#528] harden ListOfPolicyIdsValidator to check for duplicates --- .../validators/ListOfPolicyIdsValidator.java | 15 +++++++ .../ListOfPolicyIdsValidatorTest.java | 40 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/ListOfPolicyIdsValidatorTest.java diff --git a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/ListOfPolicyIdsValidator.java b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/ListOfPolicyIdsValidator.java index 29277047b1..2d1e09380c 100644 --- a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/ListOfPolicyIdsValidator.java +++ b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/ListOfPolicyIdsValidator.java @@ -19,7 +19,9 @@ ********************************************************************************/ package org.eclipse.tractusx.irs.policystore.validators; +import java.util.HashSet; import java.util.List; +import java.util.Set; import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; @@ -37,6 +39,13 @@ public boolean isValid(final List value, final ConstraintValidatorContex return true; } + if (containsDuplicates(value)) { + context.disableDefaultConstraintViolation(); + context.buildConstraintViolationWithTemplate("The list of policyIds must not contain duplicates") + .addConstraintViolation(); + return false; + } + for (int index = 0; index < value.size(); index++) { if (!PolicyIdValidator.isValid(value.get(index))) { context.disableDefaultConstraintViolation(); @@ -48,4 +57,10 @@ public boolean isValid(final List value, final ConstraintValidatorContex return true; } + + /* package */ + static boolean containsDuplicates(final List strings) { + final Set set = new HashSet<>(strings); + return set.size() < strings.size(); + } } diff --git a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/ListOfPolicyIdsValidatorTest.java b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/ListOfPolicyIdsValidatorTest.java new file mode 100644 index 0000000000..90bb9f0625 --- /dev/null +++ b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/ListOfPolicyIdsValidatorTest.java @@ -0,0 +1,40 @@ +/******************************************************************************** + * Copyright (c) 2022,2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +package org.eclipse.tractusx.irs.policystore.validators; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import java.util.List; + +import org.junit.jupiter.api.Test; + +class ListOfPolicyIdsValidatorTest { + + @Test + void containsDuplicates() { + assertThat(ListOfPolicyIdsValidator.containsDuplicates(List.of("abc", "def", "abc", "ghi"))).isTrue(); + } + + @Test + void containsDuplicates_not() { + assertThat(ListOfPolicyIdsValidator.containsDuplicates(List.of("abc", "def", "ghi"))).isFalse(); + } + +} \ No newline at end of file From 4db48e93b753f795fbb455405f06d2e8ce279db0 Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Fri, 10 May 2024 14:34:45 +0200 Subject: [PATCH 29/39] feat(impl): [#528] add step definition for cucumber tests for validating error response message --- .../cucumber/E2ETestStepDefinitionsForPolicyStoreApi.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/irs-cucumber-tests/src/test/java/org/eclipse/tractusx/irs/cucumber/E2ETestStepDefinitionsForPolicyStoreApi.java b/irs-cucumber-tests/src/test/java/org/eclipse/tractusx/irs/cucumber/E2ETestStepDefinitionsForPolicyStoreApi.java index a775f3c0b0..884d5199cb 100644 --- a/irs-cucumber-tests/src/test/java/org/eclipse/tractusx/irs/cucumber/E2ETestStepDefinitionsForPolicyStoreApi.java +++ b/irs-cucumber-tests/src/test/java/org/eclipse/tractusx/irs/cucumber/E2ETestStepDefinitionsForPolicyStoreApi.java @@ -314,6 +314,11 @@ public void theCreatePolicyResponseShouldHaveStatus(final int httpStatus) { this.createPoliciesResponse.statusCode(httpStatus); } + @Then("the create policy response should have message containing {string}") + public void theCreatePolicyResponseShouldHaveMessageContaining(final String string) { + this.createPoliciesResponse.body("messages", Matchers.hasItem(Matchers.containsString(string))); + } + @Then("the update policy response should have HTTP status {int}") public void theUpdatePolicyResponseShouldHaveStatus(final int httpStatus) { this.updatePoliciesResponse.statusCode(httpStatus); From aba7f8efd9ace713b39df3bd13da745f1dd54354 Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Fri, 10 May 2024 14:35:49 +0200 Subject: [PATCH 30/39] feat(impl): [#528] add step definition for cucumber tests for validating error response message --- .../irs/policystore/validators/ListOfPolicyIdsValidator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/ListOfPolicyIdsValidator.java b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/ListOfPolicyIdsValidator.java index 2d1e09380c..fb1aba2971 100644 --- a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/ListOfPolicyIdsValidator.java +++ b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/ListOfPolicyIdsValidator.java @@ -49,7 +49,7 @@ public boolean isValid(final List value, final ConstraintValidatorContex for (int index = 0; index < value.size(); index++) { if (!PolicyIdValidator.isValid(value.get(index))) { context.disableDefaultConstraintViolation(); - final String msg = "The policyId at index %d is invalid (should be a valid UUID)"; + final String msg = "The policyId at index %d is invalid (must be a valid UUID)"; context.buildConstraintViolationWithTemplate(msg.formatted(index)).addConstraintViolation(); return false; } From a101fdd32b1896d9100b038a161d11c77a81afd2 Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Fri, 10 May 2024 16:12:40 +0200 Subject: [PATCH 31/39] feat(impl): [#528] add step definitions for cucumber tests for validating error response message --- ...2ETestStepDefinitionsForPolicyStoreApi.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/irs-cucumber-tests/src/test/java/org/eclipse/tractusx/irs/cucumber/E2ETestStepDefinitionsForPolicyStoreApi.java b/irs-cucumber-tests/src/test/java/org/eclipse/tractusx/irs/cucumber/E2ETestStepDefinitionsForPolicyStoreApi.java index 884d5199cb..5d533b1a23 100644 --- a/irs-cucumber-tests/src/test/java/org/eclipse/tractusx/irs/cucumber/E2ETestStepDefinitionsForPolicyStoreApi.java +++ b/irs-cucumber-tests/src/test/java/org/eclipse/tractusx/irs/cucumber/E2ETestStepDefinitionsForPolicyStoreApi.java @@ -316,7 +316,23 @@ public void theCreatePolicyResponseShouldHaveStatus(final int httpStatus) { @Then("the create policy response should have message containing {string}") public void theCreatePolicyResponseShouldHaveMessageContaining(final String string) { - this.createPoliciesResponse.body("messages", Matchers.hasItem(Matchers.containsString(string))); + final ValidatableResponse validatableResponse = this.createPoliciesResponse; + assertThatResponseHasMessageContaining(validatableResponse, string); + } + + @Then("the delete policy response should have message containing {string}") + public void thedeletePolicyResponseShouldHaveMessageContaining(final String string) { + assertThatResponseHasMessageContaining(this.deletePoliciesResponse, string); + } + + @Then("the update policy response should have message containing {string}") + public void theUpdatePolicyResponseShouldHaveMessageContaining(final String string) { + assertThatResponseHasMessageContaining(this.updatePoliciesResponse, string); + } + + private static void assertThatResponseHasMessageContaining(final ValidatableResponse validatableResponse, + final String string) { + validatableResponse.body("messages", Matchers.hasItem(Matchers.containsString(string))); } @Then("the update policy response should have HTTP status {int}") From 0f84aca973037abf5f9b1de1a93bfd5e04b10a96 Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Mon, 13 May 2024 12:00:43 +0200 Subject: [PATCH 32/39] feat(impl): [#528] remove obsolete uncommented test code --- .../validators/PolicyIdListValidatorTest.java | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdListValidatorTest.java b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdListValidatorTest.java index 73ec01145d..7a219a5ce9 100644 --- a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdListValidatorTest.java +++ b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdListValidatorTest.java @@ -74,18 +74,4 @@ void withListContainingInvalidPolicyId() { assertThat(messageCaptor.getValue()).contains("policyId").contains(" index 1 ").contains("invalid"); } - // @ParameterizedTest - // @ValueSource(strings = { "BPN", - // "BPNL", - // "BPNACB", - // "BPNA1234567890AB", - // "BPNS1234567890AB", - // "DELETE * FROM Table", - // "ERRRES" - // }) - // void withInvalidBPN(final String invalidBPN) { - // assertThat(validator.isValid(Collections.singletonList(invalidBPN), contextMock)).isFalse(); - // verify(contextMock).buildConstraintViolationWithTemplate(messageCaptor.capture()); - // assertThat(messageCaptor.getValue()).contains("BPN").contains(" index 0 ").contains("invalid"); - // } } From 2711640fcbf533295d6d0ad472d69c462a08e0eb Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Mon, 13 May 2024 12:22:46 +0200 Subject: [PATCH 33/39] feat(impl): [#528] fix CHANGELOG.md --- CHANGELOG.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7956787385..6679c4f3df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 _**For better traceability add the corresponding GitHub issue number in each changelog entry, please.**_ ## [Unreleased] + +### Changed + +- Improved policy store API input validation. #528 + ## [5.1.1] - 2024-05-08 @@ -17,10 +22,6 @@ _**For better traceability add the corresponding GitHub issue number in each cha ### Added - Cucumber test step definitions for Policy Store API (Happy Path) including some test helper utilities. #518 - -### Changed - -- Improved policy store API input validation. #528 ## [5.1.0] - 2024-05-06 From 67ea850fd98988635d8ce96ce4e8f0a05a0fd285 Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Mon, 13 May 2024 12:26:50 +0200 Subject: [PATCH 34/39] feat(impl): [#528] extract constraints to constants --- .../client/policy/ConstraintConstants.java | 40 +++++++++++++++ .../client/SubmodelFacadeWiremockTest.java | 50 +++++++------------ .../PolicyStoreControllerTest.java | 10 ++-- 3 files changed, 63 insertions(+), 37 deletions(-) create mode 100644 irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/policy/ConstraintConstants.java diff --git a/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/policy/ConstraintConstants.java b/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/policy/ConstraintConstants.java new file mode 100644 index 0000000000..d3fbd18648 --- /dev/null +++ b/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/policy/ConstraintConstants.java @@ -0,0 +1,40 @@ +/******************************************************************************** + * Copyright (c) 2022,2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +package org.eclipse.tractusx.irs.edc.client.policy; + +/** + * Constants for {@link Constraint}s + */ +public final class ConstraintConstants { + + private ConstraintConstants() { + // helper class + } + + public static final Constraint ACTIVE_MEMBERSHIP = new Constraint("Membership", new Operator(OperatorType.EQ), + "active"); + + public static final Constraint FRAMEWORK_AGREEMENT_TRACEABILITY_ACTIVE = new Constraint( + "FrameworkAgreement.traceability", new Operator(OperatorType.EQ), "active"); + + public static final Constraint PURPOSE_ID_3_1_TRACE = new Constraint("PURPOSE", new Operator(OperatorType.EQ), + "ID 3.1 Trace"); + +} diff --git a/irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client/SubmodelFacadeWiremockTest.java b/irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client/SubmodelFacadeWiremockTest.java index fe9a429048..12e60e9eb5 100644 --- a/irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client/SubmodelFacadeWiremockTest.java +++ b/irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client/SubmodelFacadeWiremockTest.java @@ -28,6 +28,8 @@ import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.eclipse.tractusx.irs.edc.client.policy.ConstraintConstants.ACTIVE_MEMBERSHIP; +import static org.eclipse.tractusx.irs.edc.client.policy.ConstraintConstants.FRAMEWORK_AGREEMENT_TRACEABILITY_ACTIVE; import static org.eclipse.tractusx.irs.edc.client.testutil.TestMother.createEdcTransformer; import static org.eclipse.tractusx.irs.testing.wiremock.SubmodelFacadeWiremockSupport.DATAPLANE_HOST; import static org.eclipse.tractusx.irs.testing.wiremock.SubmodelFacadeWiremockSupport.PATH_DATAPLANE_PUBLIC; @@ -67,8 +69,6 @@ import org.eclipse.tractusx.irs.edc.client.policy.Constraint; import org.eclipse.tractusx.irs.edc.client.policy.ConstraintCheckerService; 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.PolicyCheckerService; @@ -134,11 +134,10 @@ void configureSystemUnderTest(WireMockRuntimeInfo wireMockRuntimeInfo) { storage); acceptedPoliciesProvider = mock(AcceptedPoliciesProvider.class); - when(acceptedPoliciesProvider.getAcceptedPolicies("BPN")).thenReturn(List.of(new AcceptedPolicy(policy("IRS Policy", - List.of(new Permission(PolicyType.USE, new Constraints( - List.of(new Constraint("Membership", new Operator(OperatorType.EQ), "active"), - new Constraint("FrameworkAgreement.traceability", new Operator(OperatorType.EQ), - "active")), new ArrayList<>())))), OffsetDateTime.now().plusYears(1)))); + when(acceptedPoliciesProvider.getAcceptedPolicies("BPN")).thenReturn(List.of(new AcceptedPolicy( + policy("IRS Policy", List.of(new Permission(PolicyType.USE, + new Constraints(List.of(ACTIVE_MEMBERSHIP, FRAMEWORK_AGREEMENT_TRACEABILITY_ACTIVE), + new ArrayList<>())))), OffsetDateTime.now().plusYears(1)))); final PolicyCheckerService policyCheckerService = new PolicyCheckerService(acceptedPoliciesProvider, new ConstraintCheckerService()); final ContractNegotiationService contractNegotiationService = new ContractNegotiationService(controlPlaneClient, @@ -157,11 +156,9 @@ void shouldReturnAssemblyPartRelationshipAsString() givenThat(get(urlPathEqualTo(SUBMODEL_DATAPLANE_PATH)).willReturn( responseWithStatus(200).withBodyFile("singleLevelBomAsBuilt.json"))); - final List andConstraints = List.of( - new Constraint("Membership", new Operator(OperatorType.EQ), "active"), new Constraint("FrameworkAgreement.traceability", new Operator(OperatorType.EQ), "active")); + final List andConstraints = List.of(ACTIVE_MEMBERSHIP, FRAMEWORK_AGREEMENT_TRACEABILITY_ACTIVE); final ArrayList orConstraints = new ArrayList<>(); - final Permission permission = new Permission(PolicyType.USE, - new Constraints(andConstraints, orConstraints)); + final Permission permission = new Permission(PolicyType.USE, new Constraints(andConstraints, orConstraints)); final AcceptedPolicy acceptedPolicy = new AcceptedPolicy(policy("IRS Policy", List.of(permission)), OffsetDateTime.now().plusYears(1)); when(acceptedPoliciesProvider.getAcceptedPolicies(eq("bpn"))).thenReturn(List.of(acceptedPolicy)); @@ -182,11 +179,9 @@ void shouldReturnMaterialForRecyclingAsString() givenThat(get(urlPathEqualTo(SUBMODEL_DATAPLANE_PATH)).willReturn( responseWithStatus(200).withBodyFile("materialForRecycling.json"))); - final List andConstraints = List.of( - new Constraint("Membership", new Operator(OperatorType.EQ), "active"), new Constraint("FrameworkAgreement.traceability", new Operator(OperatorType.EQ), "active")); + final List andConstraints = List.of(ACTIVE_MEMBERSHIP, FRAMEWORK_AGREEMENT_TRACEABILITY_ACTIVE); final ArrayList orConstraints = new ArrayList<>(); - final Permission permission = new Permission(PolicyType.USE, - new Constraints(andConstraints, orConstraints)); + final Permission permission = new Permission(PolicyType.USE, new Constraints(andConstraints, orConstraints)); final AcceptedPolicy acceptedPolicy = new AcceptedPolicy(policy("IRS Policy", List.of(permission)), OffsetDateTime.now().plusYears(1)); when(acceptedPoliciesProvider.getAcceptedPolicies(eq("bpn"))).thenReturn(List.of(acceptedPolicy)); @@ -206,11 +201,9 @@ void shouldReturnObjectAsStringWhenResponseNotJSON() prepareNegotiation(); givenThat(get(urlPathEqualTo(SUBMODEL_DATAPLANE_PATH)).willReturn(responseWithStatus(200).withBody("test"))); - final List andConstraints = List.of( - new Constraint("Membership", new Operator(OperatorType.EQ), "active"), new Constraint("FrameworkAgreement.traceability", new Operator(OperatorType.EQ), "active")); + final List andConstraints = List.of(ACTIVE_MEMBERSHIP, FRAMEWORK_AGREEMENT_TRACEABILITY_ACTIVE); final ArrayList orConstraints = new ArrayList<>(); - final Permission permission = new Permission(PolicyType.USE, - new Constraints(andConstraints, orConstraints)); + final Permission permission = new Permission(PolicyType.USE, new Constraints(andConstraints, orConstraints)); final AcceptedPolicy acceptedPolicy = new AcceptedPolicy(policy("IRS Policy", List.of(permission)), OffsetDateTime.now().plusYears(1)); when(acceptedPoliciesProvider.getAcceptedPolicies(eq("bpn"))).thenReturn(List.of(acceptedPolicy)); @@ -226,8 +219,7 @@ void shouldReturnObjectAsStringWhenResponseNotJSON() @Test void shouldThrowExceptionWhenPoliciesAreNotAccepted() { // Arrange - final List andConstraints = List.of( - new Constraint("Membership", new Operator(OperatorType.EQ), "active")); + final List andConstraints = List.of(ACTIVE_MEMBERSHIP); final ArrayList orConstraints = new ArrayList<>(); final Permission permission = new Permission(PolicyType.USE, new Constraints(andConstraints, orConstraints)); final AcceptedPolicy acceptedPolicy = new AcceptedPolicy(policy("IRS Policy", List.of(permission)), @@ -241,8 +233,8 @@ void shouldThrowExceptionWhenPoliciesAreNotAccepted() { // Act & Assert final String errorMessage = "Consumption of asset '5a7ab616-989f-46ae-bdf2-32027b9f6ee6-31b614f5-ec14-4ed2-a509-e7b7780083e7' is not permitted as the required catalog offer policies do not comply with defined IRS policies."; assertThatExceptionOfType(UsagePolicyException.class).isThrownBy( - () -> edcSubmodelClient.getSubmodelPayload(CONNECTOR_ENDPOINT_URL, SUBMODEL_DATAPLANE_URL, ASSET_ID, "bpn") - .get()).withMessageEndingWith(errorMessage); + () -> edcSubmodelClient.getSubmodelPayload(CONNECTOR_ENDPOINT_URL, SUBMODEL_DATAPLANE_URL, ASSET_ID, + "bpn").get()).withMessageEndingWith(errorMessage); } @Test @@ -252,11 +244,9 @@ void shouldThrowExceptionWhenResponse_400() { givenThat(get(urlPathEqualTo(SUBMODEL_DATAPLANE_PATH)).willReturn( responseWithStatus(400).withBody("{ error: '400'}"))); - final List andConstraints = List.of( - new Constraint("Membership", new Operator(OperatorType.EQ), "active"), new Constraint("FrameworkAgreement.traceability", new Operator(OperatorType.EQ), "active")); + final List andConstraints = List.of(ACTIVE_MEMBERSHIP, FRAMEWORK_AGREEMENT_TRACEABILITY_ACTIVE); final ArrayList orConstraints = new ArrayList<>(); - final Permission permission = new Permission(PolicyType.USE, - new Constraints(andConstraints, orConstraints)); + final Permission permission = new Permission(PolicyType.USE, new Constraints(andConstraints, orConstraints)); final AcceptedPolicy acceptedPolicy = new AcceptedPolicy(policy("IRS Policy", List.of(permission)), OffsetDateTime.now().plusYears(1)); when(acceptedPoliciesProvider.getAcceptedPolicies(eq("bpn"))).thenReturn(List.of(acceptedPolicy)); @@ -277,11 +267,9 @@ void shouldThrowExceptionWhenResponse_500() { givenThat(get(urlPathEqualTo(SUBMODEL_DATAPLANE_PATH)).willReturn( responseWithStatus(500).withBody("{ error: '500'}"))); - final List andConstraints = List.of( - new Constraint("Membership", new Operator(OperatorType.EQ), "active"), new Constraint("FrameworkAgreement.traceability", new Operator(OperatorType.EQ), "active")); + final List andConstraints = List.of(ACTIVE_MEMBERSHIP, FRAMEWORK_AGREEMENT_TRACEABILITY_ACTIVE); final ArrayList orConstraints = new ArrayList<>(); - final Permission permission = new Permission(PolicyType.USE, - new Constraints(andConstraints, orConstraints)); + final Permission permission = new Permission(PolicyType.USE, new Constraints(andConstraints, orConstraints)); final AcceptedPolicy acceptedPolicy = new AcceptedPolicy(policy("IRS Policy", List.of(permission)), OffsetDateTime.now().plusYears(1)); when(acceptedPoliciesProvider.getAcceptedPolicies(eq("bpn"))).thenReturn(List.of(acceptedPolicy)); diff --git a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/controllers/PolicyStoreControllerTest.java b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/controllers/PolicyStoreControllerTest.java index a7f271ac19..49faea7c17 100644 --- a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/controllers/PolicyStoreControllerTest.java +++ b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/controllers/PolicyStoreControllerTest.java @@ -24,6 +24,9 @@ package org.eclipse.tractusx.irs.policystore.controllers; import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.tractusx.irs.edc.client.policy.ConstraintConstants.ACTIVE_MEMBERSHIP; +import static org.eclipse.tractusx.irs.edc.client.policy.ConstraintConstants.FRAMEWORK_AGREEMENT_TRACEABILITY_ACTIVE; +import static org.eclipse.tractusx.irs.edc.client.policy.ConstraintConstants.PURPOSE_ID_3_1_TRACE; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -37,10 +40,7 @@ import jakarta.json.JsonObject; import jakarta.servlet.http.HttpServletRequest; -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; @@ -215,9 +215,7 @@ private List createPermissions() { private Constraints createConstraints() { return new Constraints(Collections.emptyList(), - List.of(new Constraint("Membership", new Operator(OperatorType.EQ), "active"), - new Constraint("FrameworkAgreement.traceability", new Operator(OperatorType.EQ), "active"), - new Constraint("PURPOSE", new Operator(OperatorType.EQ), "ID 3.1 Trace"))); + List.of(ACTIVE_MEMBERSHIP, FRAMEWORK_AGREEMENT_TRACEABILITY_ACTIVE, PURPOSE_ID_3_1_TRACE)); } private static String randomPolicyId() { From ea44d2f50ec80d5ba1b50e97ae40735f531ac9b8 Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Mon, 13 May 2024 12:29:53 +0200 Subject: [PATCH 35/39] feat(impl): [#528] extract constraints to constants --- .../policystore/validators/PolicyValidatorTest.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyValidatorTest.java b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyValidatorTest.java index 94ddb78b32..d5e6f23006 100644 --- a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyValidatorTest.java +++ b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyValidatorTest.java @@ -25,10 +25,8 @@ import java.util.List; import jakarta.validation.ConstraintViolationException; -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.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; @@ -50,10 +48,8 @@ private List createPermissions() { } private Constraints createConstraints() { - return new Constraints(Collections.emptyList(), - List.of(new Constraint("Membership", new Operator(OperatorType.EQ), "active"), - new Constraint("FrameworkAgreement.traceability", new Operator(OperatorType.EQ), "active"), - new Constraint("PURPOSE", new Operator(OperatorType.EQ), "ID 3.1 Trace"))); + return new Constraints(Collections.emptyList(), List.of(ConstraintConstants.ACTIVE_MEMBERSHIP, + ConstraintConstants.FRAMEWORK_AGREEMENT_TRACEABILITY_ACTIVE, ConstraintConstants.PURPOSE_ID_3_1_TRACE)); } } \ No newline at end of file From 94b59775f85088369a25e8e572cd6e8c8c167f33 Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Mon, 13 May 2024 12:30:45 +0200 Subject: [PATCH 36/39] feat(impl): [#528] extract constraints to constants --- .../policystore/services/PolicyStoreServiceTest.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) 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 c3f4cfcb12..1b696109c7 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 @@ -48,10 +48,8 @@ import org.eclipse.edc.jsonld.TitaniumJsonLd; import org.eclipse.edc.spi.monitor.ConsoleMonitor; 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.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; @@ -369,10 +367,8 @@ private List createPermissions() { } private Constraints createConstraints() { - return new Constraints(Collections.emptyList(), - List.of(new Constraint("Membership", new Operator(OperatorType.EQ), "active"), - new Constraint("FrameworkAgreement.traceability", new Operator(OperatorType.EQ), "active"), - new Constraint(EXAMPLE_ACCEPTED_LEFT_OPERAND, new Operator(OperatorType.EQ), "ID 3.1 Trace"))); + return new Constraints(Collections.emptyList(), List.of(ConstraintConstants.ACTIVE_MEMBERSHIP, + ConstraintConstants.FRAMEWORK_AGREEMENT_TRACEABILITY_ACTIVE, ConstraintConstants.PURPOSE_ID_3_1_TRACE)); } @Nested From 25ced4c183b9f69dc45133bb441f7e1ca08a70be Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Mon, 13 May 2024 12:53:04 +0200 Subject: [PATCH 37/39] feat(impl): [#528] policyId allow "urn:uuid:" prefix --- .../irs/policystore/validators/PolicyIdValidator.java | 3 ++- .../policystore/validators/PolicyIdValidatorTest.java | 10 +++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdValidator.java b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdValidator.java index ada5add0b6..dd6ed59c70 100644 --- a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdValidator.java +++ b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdValidator.java @@ -23,6 +23,7 @@ import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; +import org.apache.commons.lang3.StringUtils; /** * Validator for list of business partner numbers (BPN). @@ -44,7 +45,7 @@ public static boolean isValid(final String policyId) { private static boolean validateUUID(final String uuidStr) { try { - UUID.fromString(uuidStr); + UUID.fromString(StringUtils.removeStart(uuidStr, "urn:uuid:")); return true; } catch (IllegalArgumentException e) { return false; diff --git a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdValidatorTest.java b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdValidatorTest.java index 664a7519b1..82c0e221af 100644 --- a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdValidatorTest.java +++ b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdValidatorTest.java @@ -67,7 +67,15 @@ void withValidPolicyId(final String validPolicyId) { } private static Stream generateUUIDs() { - return Stream.generate(() -> faker.internet().uuid()).limit(10); + return Stream.concat(uuidStream().limit(5), generateUUIDsWithUrnPrefix().limit(5)); + } + + private static Stream generateUUIDsWithUrnPrefix() { + return uuidStream().map(uuid -> "urn:uuid:" + uuid); + } + + private static Stream uuidStream() { + return Stream.generate(() -> faker.internet().uuid()); } @ParameterizedTest From fe55a211e4019023c738f5ca1f009a9faa8130b6 Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Mon, 13 May 2024 14:56:52 +0200 Subject: [PATCH 38/39] feat(impl): [#528] fix PMD warning --- .../irs/edc/client/policy/ConstraintConstants.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/policy/ConstraintConstants.java b/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/policy/ConstraintConstants.java index d3fbd18648..808ddb46e2 100644 --- a/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/policy/ConstraintConstants.java +++ b/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/policy/ConstraintConstants.java @@ -24,10 +24,6 @@ */ public final class ConstraintConstants { - private ConstraintConstants() { - // helper class - } - public static final Constraint ACTIVE_MEMBERSHIP = new Constraint("Membership", new Operator(OperatorType.EQ), "active"); @@ -37,4 +33,8 @@ private ConstraintConstants() { public static final Constraint PURPOSE_ID_3_1_TRACE = new Constraint("PURPOSE", new Operator(OperatorType.EQ), "ID 3.1 Trace"); + private ConstraintConstants() { + // helper class + } + } From d13dfb50e19c7d939d4e1268211842bd1609476b Mon Sep 17 00:00:00 2001 From: Matthias Fischer Date: Tue, 14 May 2024 00:53:18 +0200 Subject: [PATCH 39/39] feat(impl): [#528] relax validation of policyId to safe path parameter characters New pattern "[a-zA-Z0-9\-_~.:]+" (safe path variable characters). Reason: Avoid problems with too strict validation. --- .../validators/ListOfPolicyIdsValidator.java | 11 ++++++----- .../policystore/validators/PolicyIdValidator.java | 15 ++++----------- .../irs/policystore/validators/ValidPolicyId.java | 4 +++- .../validators/PolicyIdListValidatorTest.java | 2 +- .../validators/PolicyIdValidatorTest.java | 12 +++++------- .../validators/PolicyValidatorTest.java | 5 +++-- 6 files changed, 22 insertions(+), 27 deletions(-) diff --git a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/ListOfPolicyIdsValidator.java b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/ListOfPolicyIdsValidator.java index fb1aba2971..dc0a5a2a72 100644 --- a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/ListOfPolicyIdsValidator.java +++ b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/ListOfPolicyIdsValidator.java @@ -34,7 +34,8 @@ public class ListOfPolicyIdsValidator implements ConstraintValidator value, final ConstraintValidatorContext context) { - // allow null and empty here (in order to allow flexible combination with @NotNull and @NotEmpty) + // allow null and empty here + // (in order to allow flexible combination with @NotNull and @NotEmpty) if (value == null || value.isEmpty()) { return true; } @@ -49,8 +50,9 @@ public boolean isValid(final List value, final ConstraintValidatorContex for (int index = 0; index < value.size(); index++) { if (!PolicyIdValidator.isValid(value.get(index))) { context.disableDefaultConstraintViolation(); - final String msg = "The policyId at index %d is invalid (must be a valid UUID)"; - context.buildConstraintViolationWithTemplate(msg.formatted(index)).addConstraintViolation(); + final String msg = "The policyId at index %d is invalid (%s)"; + context.buildConstraintViolationWithTemplate(msg.formatted(index, ValidPolicyId.DEFAULT_MESSAGE)) + .addConstraintViolation(); return false; } } @@ -58,8 +60,7 @@ public boolean isValid(final List value, final ConstraintValidatorContex return true; } - /* package */ - static boolean containsDuplicates(final List strings) { + protected static boolean containsDuplicates(final List strings) { final Set set = new HashSet<>(strings); return set.size() < strings.size(); } diff --git a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdValidator.java b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdValidator.java index dd6ed59c70..201c1de214 100644 --- a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdValidator.java +++ b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdValidator.java @@ -19,17 +19,18 @@ ********************************************************************************/ package org.eclipse.tractusx.irs.policystore.validators; -import java.util.UUID; +import java.util.regex.Pattern; import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; -import org.apache.commons.lang3.StringUtils; /** * Validator for list of business partner numbers (BPN). */ public class PolicyIdValidator implements ConstraintValidator { + private static final Pattern PATTERN_SAFE_PATH_VARIABLE_CHARACTERS = Pattern.compile("[a-zA-Z0-9\\-_~.:]+"); + @Override public boolean isValid(final String value, final ConstraintValidatorContext context) { @@ -40,15 +41,7 @@ public boolean isValid(final String value, final ConstraintValidatorContext cont } public static boolean isValid(final String policyId) { - return validateUUID(policyId); + return PATTERN_SAFE_PATH_VARIABLE_CHARACTERS.matcher(policyId).matches(); } - private static boolean validateUUID(final String uuidStr) { - try { - UUID.fromString(StringUtils.removeStart(uuidStr, "urn:uuid:")); - return true; - } catch (IllegalArgumentException e) { - return false; - } - } } diff --git a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/ValidPolicyId.java b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/ValidPolicyId.java index 19a00bbe52..82a9e78bdc 100644 --- a/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/ValidPolicyId.java +++ b/irs-policy-store/src/main/java/org/eclipse/tractusx/irs/policystore/validators/ValidPolicyId.java @@ -39,7 +39,9 @@ @Retention(RetentionPolicy.RUNTIME) public @interface ValidPolicyId { - String message() default "must be a valid UUID"; + String DEFAULT_MESSAGE = "must only contain safe URL path variable characters"; + + String message() default DEFAULT_MESSAGE; Class[] groups() default { }; diff --git a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdListValidatorTest.java b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdListValidatorTest.java index 7a219a5ce9..a4317c007e 100644 --- a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdListValidatorTest.java +++ b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdListValidatorTest.java @@ -68,7 +68,7 @@ void withValidListOfStrings() { @Test void withListContainingInvalidPolicyId() { - List invalidList = Arrays.asList(UUID.randomUUID().toString(), "_INVALID_POLICY_ID_"); + List invalidList = Arrays.asList(UUID.randomUUID().toString(), "###INVALID_POLICY_ID###"); assertThat(validator.isValid(invalidList, contextMock)).isFalse(); verify(contextMock).buildConstraintViolationWithTemplate(messageCaptor.capture()); assertThat(messageCaptor.getValue()).contains("policyId").contains(" index 1 ").contains("invalid"); diff --git a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdValidatorTest.java b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdValidatorTest.java index 82c0e221af..cec91fe2b4 100644 --- a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdValidatorTest.java +++ b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyIdValidatorTest.java @@ -79,8 +79,7 @@ private static Stream uuidStream() { } @ParameterizedTest - @ValueSource(strings = { "a_", - "*", + @ValueSource(strings = { "*", "(", ")", "<", @@ -89,11 +88,10 @@ private static Stream uuidStream() { "]", "{", "}", - "p3", - "123", - "abc", - "abc123", - "abc-def-4444" + "a/policyId", + "a\\policyId", + "my?policyId", + "my#policyId" }) void withInvalidPolicyId(final String invalidPolicyId) { assertThat(validator.isValid(invalidPolicyId, contextMock)).isFalse(); diff --git a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyValidatorTest.java b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyValidatorTest.java index d5e6f23006..cbcaddeea8 100644 --- a/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyValidatorTest.java +++ b/irs-policy-store/src/test/java/org/eclipse/tractusx/irs/policystore/validators/PolicyValidatorTest.java @@ -36,10 +36,11 @@ class PolicyValidatorTest { @Test void invalidPolicyId() { - final Policy policy = Policy.builder().policyId("_invalid_policy_id_").permissions(createPermissions()).build(); + final Policy policy = Policy.builder().policyId("?invalid_policy_id#").permissions(createPermissions()).build(); assertThatThrownBy(() -> PolicyValidator.validate(policy)).isInstanceOf(ConstraintViolationException.class) .hasMessageContaining("policyId") - .hasMessageContaining("must be a valid UUID"); + .hasMessageContaining( + "must only contain safe URL path variable characters"); } private List createPermissions() {