From 484ba05b01883af86e3bb71fbca87916aa26fb18 Mon Sep 17 00:00:00 2001 From: Istvan Zoltan Nagy Date: Fri, 16 Feb 2024 13:15:51 +0100 Subject: [PATCH] Implementation of CRUD API for Access management APIs - Add rule validations - Adds custom validator implementations - Configures validation groups using JSR-303 annotations - Adds tests to verify that validation is working - Fixes internal logic of policy values to allow validation rules to enforce validation instead of runtime exceptions --- access-control-service-sql-impl/pom.xml | 14 ++ .../accesscontrol/sql/model/AccessRule.java | 34 +++- .../sql/model/AccessRulePolicy.java | 74 ++++--- .../model/policy/AccessRulePolicyValue.java | 11 +- .../sql/model/policy/PolicyOperator.java | 14 +- .../AccessRulePolicyValueValidator.java | 107 ++++++++++ .../sql/validation/OnCreate.java | 23 +++ .../sql/validation/OnUpdate.java | 23 +++ .../ValidAccessRulePolicyValue.java | 41 ++++ .../sql/validation/ValidValidityPeriod.java | 42 ++++ .../validation/ValidityPeriodValidator.java | 60 ++++++ .../validation/AbstractValidationTest.java | 78 +++++++ .../AccessRulePolicyValidationTest.java | 191 ++++++++++++++++++ .../validation/AccessRuleValidationTest.java | 171 ++++++++++++++++ pom.xml | 12 ++ 15 files changed, 842 insertions(+), 53 deletions(-) create mode 100644 access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/AccessRulePolicyValueValidator.java create mode 100644 access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/OnCreate.java create mode 100644 access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/OnUpdate.java create mode 100644 access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/ValidAccessRulePolicyValue.java create mode 100644 access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/ValidValidityPeriod.java create mode 100644 access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/ValidityPeriodValidator.java create mode 100644 access-control-service-sql-impl/src/test/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/AbstractValidationTest.java create mode 100644 access-control-service-sql-impl/src/test/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/AccessRulePolicyValidationTest.java create mode 100644 access-control-service-sql-impl/src/test/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/AccessRuleValidationTest.java diff --git a/access-control-service-sql-impl/pom.xml b/access-control-service-sql-impl/pom.xml index d921c61d..cebcb6cd 100644 --- a/access-control-service-sql-impl/pom.xml +++ b/access-control-service-sql-impl/pom.xml @@ -106,6 +106,10 @@ io.swagger.core.v3 swagger-annotations + + org.apache.commons + commons-lang3 + @@ -125,6 +129,16 @@ mockito-core test + + org.hibernate.validator + hibernate-validator + test + + + org.apache.tomcat.embed + tomcat-embed-el + test + diff --git a/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/model/AccessRule.java b/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/model/AccessRule.java index daa73ad3..017f8094 100644 --- a/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/model/AccessRule.java +++ b/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/model/AccessRule.java @@ -23,6 +23,9 @@ import java.time.Instant; import org.eclipse.tractusx.semantics.accesscontrol.sql.model.converter.AccessRulePolicyConverter; +import org.eclipse.tractusx.semantics.accesscontrol.sql.validation.OnCreate; +import org.eclipse.tractusx.semantics.accesscontrol.sql.validation.OnUpdate; +import org.eclipse.tractusx.semantics.accesscontrol.sql.validation.ValidValidityPeriod; import jakarta.persistence.Column; import jakarta.persistence.Convert; @@ -32,44 +35,57 @@ import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.Lob; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Null; import lombok.Data; //@Entity -//@Table(name = "access_control_rule") +//@Table( name = "ACCESS_RULE" ) @Data +@ValidValidityPeriod( groups = { OnCreate.class, OnUpdate.class } ) public class AccessRule { public enum PolicyType { AAS } + @Null( groups = OnCreate.class ) + @NotNull( groups = OnUpdate.class ) @Id @GeneratedValue( strategy = GenerationType.AUTO ) - @Column( name = "id", nullable = false, updatable = false ) + @Column( name = "ID", nullable = false, updatable = false ) private Long id; - @Column( name = "tid", nullable = false, updatable = false, length = 36 ) + @NotNull( groups = { OnCreate.class, OnUpdate.class } ) + @Column( name = "TID", nullable = false, updatable = false, length = 36 ) private String tid; - @Column( name = "target_tenant", nullable = false, updatable = false, length = 36 ) + @NotNull( groups = { OnCreate.class, OnUpdate.class } ) + @NotBlank( groups = { OnCreate.class, OnUpdate.class } ) + @Column( name = "TARGET_TENANT", nullable = false, updatable = false, length = 36 ) private String targetTenant; + @NotNull( groups = { OnCreate.class, OnUpdate.class } ) @Enumerated( EnumType.STRING ) - @Column( name = "policy_type", nullable = false, length = 10 ) + @Column( name = "POLICY_TYPE", nullable = false, length = 10 ) private PolicyType policyType; + @Valid + @NotNull( groups = { OnCreate.class, OnUpdate.class } ) @Lob - @Column( name = "policy", nullable = false ) + @Column( name = "POLICY", nullable = false ) @Convert( converter = AccessRulePolicyConverter.class ) private AccessRulePolicy policy; - @Column( name = "description", length = 256 ) + @Column( name = "DESCRIPTION", length = 256 ) private String description; - @Column( name = "valid_from" ) + @Column( name = "VALID_FROM" ) private Instant validFrom; - @Column( name = "valid_to" ) + @Column( name = "VALID_TO" ) private Instant validTo; } diff --git a/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/model/AccessRulePolicy.java b/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/model/AccessRulePolicy.java index a82cf612..80e02ae0 100644 --- a/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/model/AccessRulePolicy.java +++ b/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/model/AccessRulePolicy.java @@ -20,39 +20,52 @@ package org.eclipse.tractusx.semantics.accesscontrol.sql.model; +import java.util.Collection; +import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import org.eclipse.tractusx.semantics.accesscontrol.api.model.SpecificAssetId; import org.eclipse.tractusx.semantics.accesscontrol.sql.model.policy.AccessRulePolicyValue; +import org.eclipse.tractusx.semantics.accesscontrol.sql.validation.OnCreate; +import org.eclipse.tractusx.semantics.accesscontrol.sql.validation.OnUpdate; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; import lombok.Data; @Data public class AccessRulePolicy { - static final String BPN_RULE_NAME = "bpn"; - static final String MANDATORY_SPECIFIC_ASSET_IDS_RULE_NAME = "mandatorySpecificAssetIds"; - static final String VISIBLE_SPECIFIC_ASSET_ID_NAMES_RULE_NAME = "visibleSpecificAssetIdNames"; - static final String VISIBLE_SEMANTIC_IDS_RULE_NAME = "visibleSemanticIds"; + public static final String BPN_RULE_NAME = "bpn"; + public static final String MANDATORY_SPECIFIC_ASSET_IDS_RULE_NAME = "mandatorySpecificAssetIds"; + public static final String VISIBLE_SPECIFIC_ASSET_ID_NAMES_RULE_NAME = "visibleSpecificAssetIdNames"; + public static final String VISIBLE_SEMANTIC_IDS_RULE_NAME = "visibleSemanticIds"; + @Valid + @NotNull( groups = { OnCreate.class, OnUpdate.class } ) + @Size( min = 4, groups = { OnCreate.class, OnUpdate.class } ) @JsonProperty( "accessRules" ) private Set accessRules; + private Set mandatorySpecificAssetIds; + @JsonIgnore + @Valid + @Size( min = 1, groups = { OnCreate.class, OnUpdate.class } ) + @NotNull( groups = { OnCreate.class, OnUpdate.class } ) public Set getMandatorySpecificAssetIds() { return accessRules.stream().filter( rule -> MANDATORY_SPECIFIC_ASSET_IDS_RULE_NAME.equals( rule.attribute() ) ) - .flatMap( rule -> { - assertMultiValued( rule, MANDATORY_SPECIFIC_ASSET_IDS_RULE_NAME ); - return rule.values().stream(); - } ) - .map( idValue -> { - assertSingleValued( idValue, MANDATORY_SPECIFIC_ASSET_IDS_RULE_NAME + ".*" ); - return new SpecificAssetId( idValue.attribute(), idValue.value() ); - } ) + .map( AccessRulePolicyValue::values ) + .filter( Objects::nonNull ) + .flatMap( Collection::stream ) + .map( idValue -> new SpecificAssetId( idValue.attribute(), idValue.value() ) ) .collect( Collectors.toSet() ); } @@ -67,40 +80,23 @@ public Set getVisibleSemanticIds() { } @JsonIgnore + @NotNull( groups = { OnCreate.class, OnUpdate.class } ) + @NotBlank( groups = { OnCreate.class, OnUpdate.class } ) public String getBpn() { - return getStringValueOfRule( BPN_RULE_NAME ); + return accessRules.stream().filter( rule -> BPN_RULE_NAME.equals( rule.attribute() ) ) + .map( this::getAccessRulePolicyValueStringFunction ) + .filter( Objects::nonNull ) + .findAny().orElse( null ); } private Set getStringValuesOfRule( final String ruleName ) { return accessRules.stream().filter( rule -> ruleName.equals( rule.attribute() ) ) - .flatMap( rule -> { - assertMultiValued( rule, ruleName ); - return rule.values().stream(); - } ) - .map( idValue -> getAccessRulePolicyValueStringFunction( idValue, ruleName ) ) + .flatMap( rule -> rule.values().stream() ) + .map( this::getAccessRulePolicyValueStringFunction ) .collect( Collectors.toSet() ); } - private String getStringValueOfRule( final String ruleName ) { - return accessRules.stream().filter( rule -> ruleName.equals( rule.attribute() ) ) - .map( idValue -> getAccessRulePolicyValueStringFunction( idValue, ruleName ) ) - .findAny().orElse( null ); - } - - private String getAccessRulePolicyValueStringFunction( final AccessRulePolicyValue idValue, final String ruleName ) { - assertSingleValued( idValue, ruleName ); - return idValue.value(); - } - - private void assertSingleValued( final AccessRulePolicyValue idValue, final String path ) { - if ( !idValue.hasSingleValue() ) { - throw new IllegalStateException( "Entry of " + path + " must have single value!" ); - } - } - - private void assertMultiValued( AccessRulePolicyValue idValue, String path ) { - if ( idValue.hasSingleValue() ) { - throw new IllegalStateException( "Entry of " + path + " must have multiple values!" ); - } + private String getAccessRulePolicyValueStringFunction( final AccessRulePolicyValue idValue ) { + return Optional.ofNullable( idValue ).map( AccessRulePolicyValue::value ).orElse( null ); } } diff --git a/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/model/policy/AccessRulePolicyValue.java b/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/model/policy/AccessRulePolicyValue.java index 90a15b19..2b8459b5 100644 --- a/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/model/policy/AccessRulePolicyValue.java +++ b/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/model/policy/AccessRulePolicyValue.java @@ -22,12 +22,19 @@ import java.util.Optional; import java.util.Set; +import org.eclipse.tractusx.semantics.accesscontrol.sql.validation.OnCreate; +import org.eclipse.tractusx.semantics.accesscontrol.sql.validation.OnUpdate; +import org.eclipse.tractusx.semantics.accesscontrol.sql.validation.ValidAccessRulePolicyValue; + import com.fasterxml.jackson.annotation.JsonIgnore; -public record AccessRulePolicyValue(String attribute, PolicyOperator operator, String value, Set values) { +import jakarta.validation.Valid; + +@ValidAccessRulePolicyValue( groups = { OnCreate.class, OnUpdate.class } ) +public record AccessRulePolicyValue(String attribute, PolicyOperator operator, String value, @Valid Set values) { @JsonIgnore public boolean hasSingleValue() { - return Optional.ofNullable( value ).isPresent(); + return (operator != null && operator.isSingleValued()) || Optional.ofNullable( value ).isPresent(); } } diff --git a/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/model/policy/PolicyOperator.java b/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/model/policy/PolicyOperator.java index 26fa0e3e..54817cb0 100644 --- a/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/model/policy/PolicyOperator.java +++ b/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/model/policy/PolicyOperator.java @@ -22,17 +22,20 @@ import java.util.Arrays; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonValue; public enum PolicyOperator { - EQUALS( "eq" ), - INCLUDES("includes"); + EQUALS( "eq", true ), + INCLUDES( "includes", false ); private final String value; + private final boolean singleValued; - PolicyOperator( String value ) { + PolicyOperator( String value, boolean singleValued ) { this.value = value; + this.singleValued = singleValued; } @JsonCreator @@ -44,4 +47,9 @@ public static PolicyOperator forValue( final String value ) { public String getValue() { return value; } + + @JsonIgnore + public boolean isSingleValued() { + return singleValued; + } } diff --git a/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/AccessRulePolicyValueValidator.java b/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/AccessRulePolicyValueValidator.java new file mode 100644 index 00000000..f0794bec --- /dev/null +++ b/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/AccessRulePolicyValueValidator.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 2024 Robert Bosch Manufacturing Solutions GmbH and others + * + * 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.semantics.accesscontrol.sql.validation; + +import java.util.Objects; + +import org.apache.commons.lang3.StringUtils; +import org.eclipse.tractusx.semantics.accesscontrol.sql.model.policy.AccessRulePolicyValue; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; + +public class AccessRulePolicyValueValidator implements ConstraintValidator { + + private static final String ATTRIBUTE = "attribute"; + private static final String VALUE = "value"; + private static final String OPERATOR = "operator"; + private static final String VALUES = "values"; + private String message; + + @Override + public void initialize( ValidAccessRulePolicyValue constraintAnnotation ) { + ConstraintValidator.super.initialize( constraintAnnotation ); + this.message = constraintAnnotation.message(); + } + + @Override + public boolean isValid( AccessRulePolicyValue accessRulePolicyValue, ConstraintValidatorContext constraintValidatorContext ) { + var valid = true; + if ( StringUtils.isBlank( accessRulePolicyValue.attribute() ) ) { + constraintValidatorContext.buildConstraintViolationWithTemplate( "attribute must not be null or blank." ) + .addPropertyNode( ATTRIBUTE ).addConstraintViolation(); + valid = false; + } + if ( accessRulePolicyValue.operator() == null ) { + constraintValidatorContext.buildConstraintViolationWithTemplate( "Operator must not be null." ) + .addPropertyNode( OPERATOR ).addConstraintViolation(); + valid = false; + } + if ( accessRulePolicyValue.hasSingleValue() ) { + boolean validatedSingleValue = validateSingleValue( accessRulePolicyValue, constraintValidatorContext ); + valid = valid && validatedSingleValue; + } else { + boolean validatedMultiValues = validateMultiValues( accessRulePolicyValue, constraintValidatorContext ); + valid = valid && validatedMultiValues; + } + if ( !valid ) { + constraintValidatorContext.buildConstraintViolationWithTemplate( message ).addConstraintViolation(); + } + return valid; + } + + private boolean validateSingleValue( AccessRulePolicyValue accessRulePolicyValue, ConstraintValidatorContext constraintValidatorContext ) { + boolean valid = true; + if ( StringUtils.isBlank( accessRulePolicyValue.value() ) ) { + constraintValidatorContext.buildConstraintViolationWithTemplate( "Value must not be null or blank if the policy hasSingleValue() is true." ) + .addPropertyNode( VALUE ).addConstraintViolation(); + valid = false; + } + if ( accessRulePolicyValue.operator() != null && !accessRulePolicyValue.operator().isSingleValued() ) { + constraintValidatorContext.buildConstraintViolationWithTemplate( "Operator must be single valued if the policy hasSingleValue() is true." ) + .addPropertyNode( OPERATOR ).addConstraintViolation(); + valid = false; + } + return valid; + } + + private boolean validateMultiValues( AccessRulePolicyValue accessRulePolicyValue, ConstraintValidatorContext constraintValidatorContext ) { + boolean valid = true; + if ( accessRulePolicyValue.values() == null ) { + constraintValidatorContext.buildConstraintViolationWithTemplate( + "Values must not be null if the policy hasSingleValue() is false." ) + .addPropertyNode( VALUES ).addConstraintViolation(); + valid = false; + } else { + if ( accessRulePolicyValue.values().stream().anyMatch( Objects::isNull ) ) { + constraintValidatorContext.buildConstraintViolationWithTemplate( "Values cannot contain null entries if the policy hasSingleValue() is false." ) + .addPropertyNode( VALUES ).addConstraintViolation(); + valid = false; + } + if ( accessRulePolicyValue.operator() != null && accessRulePolicyValue.operator().isSingleValued() ) { + constraintValidatorContext.buildConstraintViolationWithTemplate( "Operator must not be single valued if the policy hasSingleValue() is false." ) + .addPropertyNode( OPERATOR ).addConstraintViolation(); + valid = false; + } + } + return valid; + } + +} diff --git a/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/OnCreate.java b/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/OnCreate.java new file mode 100644 index 00000000..165ba452 --- /dev/null +++ b/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/OnCreate.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2024 Robert Bosch Manufacturing Solutions GmbH and others + * + * 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.semantics.accesscontrol.sql.validation; + +public interface OnCreate { +} diff --git a/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/OnUpdate.java b/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/OnUpdate.java new file mode 100644 index 00000000..7ce1f3de --- /dev/null +++ b/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/OnUpdate.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2024 Robert Bosch Manufacturing Solutions GmbH and others + * + * 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.semantics.accesscontrol.sql.validation; + +public interface OnUpdate { +} diff --git a/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/ValidAccessRulePolicyValue.java b/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/ValidAccessRulePolicyValue.java new file mode 100644 index 00000000..37088b21 --- /dev/null +++ b/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/ValidAccessRulePolicyValue.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2024 Robert Bosch Manufacturing Solutions GmbH and others + * + * 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.semantics.accesscontrol.sql.validation; + +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; + +@Retention( RetentionPolicy.RUNTIME ) +@Target( ElementType.TYPE ) +@Constraint( validatedBy = AccessRulePolicyValueValidator.class ) +@Documented +public @interface ValidAccessRulePolicyValue { + String message() default "Invalid rule policy."; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/ValidValidityPeriod.java b/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/ValidValidityPeriod.java new file mode 100644 index 00000000..864d2fd4 --- /dev/null +++ b/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/ValidValidityPeriod.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2024 Robert Bosch Manufacturing Solutions GmbH and others + * + * 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.semantics.accesscontrol.sql.validation; + +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; + +@Retention( RetentionPolicy.RUNTIME ) +@Target( ElementType.TYPE ) +@Constraint( validatedBy = ValidityPeriodValidator.class ) +@Documented +public @interface ValidValidityPeriod { + + String message() default "Invalid validity period."; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/ValidityPeriodValidator.java b/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/ValidityPeriodValidator.java new file mode 100644 index 00000000..a4094095 --- /dev/null +++ b/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/ValidityPeriodValidator.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2024 Robert Bosch Manufacturing Solutions GmbH and others + * + * 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.semantics.accesscontrol.sql.validation; + +import java.time.Instant; +import java.util.Optional; + +import org.eclipse.tractusx.semantics.accesscontrol.sql.model.AccessRule; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; + +public class ValidityPeriodValidator implements ConstraintValidator { + private String message; + + @Override + public void initialize( ValidValidityPeriod constraintAnnotation ) { + ConstraintValidator.super.initialize( constraintAnnotation ); + this.message = constraintAnnotation.message(); + } + + @Override + public boolean isValid( AccessRule rule, ConstraintValidatorContext constraintValidatorContext ) { + var valid = true; + final var from = Optional.ofNullable( rule.getValidFrom() ) + .map( Instant::toEpochMilli ) + .orElse( 0L ); + final var to = Optional.ofNullable( rule.getValidTo() ) + .map( Instant::toEpochMilli ) + .orElse( Long.MAX_VALUE ); + if ( from >= to ) { + constraintValidatorContext.buildConstraintViolationWithTemplate( "ValidFrom must be earlier than validTo!" ) + .addPropertyNode( "validFrom" ).addConstraintViolation(); + constraintValidatorContext.buildConstraintViolationWithTemplate( "ValidTo must be later than validFrom!" ) + .addPropertyNode( "validTo" ).addConstraintViolation(); + valid = false; + } + if ( !valid ) { + constraintValidatorContext.buildConstraintViolationWithTemplate( message ).addConstraintViolation(); + } + return valid; + } +} diff --git a/access-control-service-sql-impl/src/test/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/AbstractValidationTest.java b/access-control-service-sql-impl/src/test/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/AbstractValidationTest.java new file mode 100644 index 00000000..ddda1d6c --- /dev/null +++ b/access-control-service-sql-impl/src/test/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/AbstractValidationTest.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2024 Robert Bosch Manufacturing Solutions GmbH and others + * + * 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.semantics.accesscontrol.sql.validation; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import org.eclipse.tractusx.semantics.accesscontrol.sql.model.AccessRulePolicy; +import org.eclipse.tractusx.semantics.accesscontrol.sql.model.policy.AccessRulePolicyValue; +import org.eclipse.tractusx.semantics.accesscontrol.sql.model.policy.PolicyOperator; + +import jakarta.validation.ConstraintViolation; + +public abstract class AbstractValidationTest { + + protected static final String BPNA = "BPN00000A"; + + protected static final Instant NOW = Instant.now(); + protected static final Instant ONE_SECOND_AGO = NOW.minus( 1, ChronoUnit.SECONDS ); + + protected AccessRulePolicy getAccessRulePolicy( String bpn, Map mandatorySaId, Set visibleSaId, Set semanticIds ) { + AccessRulePolicy policy = new AccessRulePolicy(); + Set rules = new HashSet<>(); + rules.add( new AccessRulePolicyValue( AccessRulePolicy.BPN_RULE_NAME, PolicyOperator.EQUALS, bpn, null ) ); + if ( mandatorySaId != null ) { + rules.add( new AccessRulePolicyValue( AccessRulePolicy.MANDATORY_SPECIFIC_ASSET_IDS_RULE_NAME, PolicyOperator.INCLUDES, null, + mandatorySaId.entrySet().stream() + .map( entry -> new AccessRulePolicyValue( entry.getKey(), PolicyOperator.EQUALS, entry.getValue(), null ) ) + .collect( Collectors.toSet() ) + ) ); + } + if ( visibleSaId != null ) { + rules.add( new AccessRulePolicyValue( AccessRulePolicy.VISIBLE_SPECIFIC_ASSET_ID_NAMES_RULE_NAME, PolicyOperator.INCLUDES, null, + visibleSaId.stream() + .map( value -> new AccessRulePolicyValue( "name", PolicyOperator.EQUALS, value, null ) ) + .collect( Collectors.toSet() ) + ) ); + } + if ( semanticIds != null ) { + rules.add( new AccessRulePolicyValue( AccessRulePolicy.VISIBLE_SEMANTIC_IDS_RULE_NAME, PolicyOperator.INCLUDES, null, + semanticIds.stream() + .map( value -> new AccessRulePolicyValue( "name", PolicyOperator.EQUALS, value, null ) ) + .collect( Collectors.toSet() ) + ) ); + } + policy.setAccessRules( rules ); + return policy; + } + + protected Map> mapViolations( Set> actual ) { + return actual.stream() + .collect( Collectors.groupingBy( violation -> violation.getPropertyPath().toString() ) ) + .entrySet().stream() + .collect( Collectors.toMap( Map.Entry::getKey, + entry -> entry.getValue().stream().map( ConstraintViolation::getMessageTemplate ).collect( Collectors.toSet() ) ) ); + } +} diff --git a/access-control-service-sql-impl/src/test/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/AccessRulePolicyValidationTest.java b/access-control-service-sql-impl/src/test/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/AccessRulePolicyValidationTest.java new file mode 100644 index 00000000..459b2d5c --- /dev/null +++ b/access-control-service-sql-impl/src/test/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/AccessRulePolicyValidationTest.java @@ -0,0 +1,191 @@ +/******************************************************************************* + * Copyright (c) 2024 Robert Bosch Manufacturing Solutions GmbH and others + * + * 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.semantics.accesscontrol.sql.validation; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Map; +import java.util.Set; + +import org.eclipse.tractusx.semantics.accesscontrol.sql.model.AccessRulePolicy; +import org.eclipse.tractusx.semantics.accesscontrol.sql.model.policy.AccessRulePolicyValue; +import org.eclipse.tractusx.semantics.accesscontrol.sql.model.policy.PolicyOperator; +import org.junit.jupiter.api.Test; + +import jakarta.validation.ConstraintViolation; +import jakarta.validation.Validation; +import jakarta.validation.ValidatorFactory; + +class AccessRulePolicyValidationTest extends AbstractValidationTest { + + @Test + void testGetMandatorySpecificAssetIdsWithZeroIdsExpectViolation() { + try ( ValidatorFactory factory = Validation.buildDefaultValidatorFactory() ) { + final var underTest = factory.getValidator(); + AccessRulePolicy policy = getAccessRulePolicy( BPNA, Map.of(), null, null ); + + Set> actual = underTest.validate( policy, OnCreate.class ); + + assertThat( actual ).hasSize( 2 ); + Map> violations = mapViolations( actual ); + assertThat( violations ) + .containsEntry( "accessRules", Set.of( "{jakarta.validation.constraints.Size.message}" ) ) + .containsEntry( AccessRulePolicy.MANDATORY_SPECIFIC_ASSET_IDS_RULE_NAME, Set.of( "{jakarta.validation.constraints.Size.message}" ) ); + } + } + + @Test + void testGetMandatorySpecificAssetIdsWithNullSetExpectViolation() { + try ( ValidatorFactory factory = Validation.buildDefaultValidatorFactory() ) { + final var underTest = factory.getValidator(); + AccessRulePolicy policy = getAccessRulePolicy( BPNA, null, null, null ); + policy.getAccessRules() + .add( new AccessRulePolicyValue( AccessRulePolicy.MANDATORY_SPECIFIC_ASSET_IDS_RULE_NAME, PolicyOperator.INCLUDES, null, null ) ); + + Set> actual = underTest.validate( policy, OnCreate.class ); + + assertThat( actual ).hasSize( 4 ); + Map> violations = mapViolations( actual ); + assertThat( violations ) + .containsEntry( "accessRules", Set.of( "{jakarta.validation.constraints.Size.message}" ) ) + .containsEntry( "accessRules[]", Set.of( "Invalid rule policy." ) ) + .containsEntry( "accessRules[].values", Set.of( "Values must not be null if the policy hasSingleValue() is false." ) ) + .containsEntry( AccessRulePolicy.MANDATORY_SPECIFIC_ASSET_IDS_RULE_NAME, Set.of( "{jakarta.validation.constraints.Size.message}" ) ); + } + } + + @Test + void testGetMandatorySpecificAssetIdsWithMissingMandatoryIdsValuesExpectViolation() { + try ( ValidatorFactory factory = Validation.buildDefaultValidatorFactory() ) { + final var underTest = factory.getValidator(); + AccessRulePolicy policy = getAccessRulePolicy( BPNA, null, null, null ); + + Set> actual = underTest.validate( policy, OnCreate.class ); + + assertThat( actual ).hasSize( 2 ); + Map> violations = mapViolations( actual ); + assertThat( violations ) + .containsEntry( "accessRules", Set.of( "{jakarta.validation.constraints.Size.message}" ) ) + .containsEntry( AccessRulePolicy.MANDATORY_SPECIFIC_ASSET_IDS_RULE_NAME, Set.of( "{jakarta.validation.constraints.Size.message}" ) ); + } + } + + @Test + void testGetBpnWithMissingValueExpectViolation() { + try ( ValidatorFactory factory = Validation.buildDefaultValidatorFactory() ) { + final var underTest = factory.getValidator(); + AccessRulePolicy policy = new AccessRulePolicy(); + policy.setAccessRules( Set.of( + new AccessRulePolicyValue( AccessRulePolicy.MANDATORY_SPECIFIC_ASSET_IDS_RULE_NAME, PolicyOperator.INCLUDES, null, Set.of( + new AccessRulePolicyValue( "name", PolicyOperator.EQUALS, "value", null ) + ) ) + ) ); + + Set> actual = underTest.validate( policy, OnCreate.class ); + + assertThat( actual ).hasSize( 3 ); + Map> violations = mapViolations( actual ); + assertThat( violations ) + .containsEntry( "accessRules", Set.of( "{jakarta.validation.constraints.Size.message}" ) ) + .containsEntry( AccessRulePolicy.BPN_RULE_NAME, + Set.of( "{jakarta.validation.constraints.NotNull.message}", "{jakarta.validation.constraints.NotBlank.message}" ) ); + } + } + + @Test + void testGetBpnWithNullValueExpectViolation() { + try ( ValidatorFactory factory = Validation.buildDefaultValidatorFactory() ) { + final var underTest = factory.getValidator(); + AccessRulePolicy policy = getAccessRulePolicy( null, Map.of( "name", "value" ), null, null ); + + Set> actual = underTest.validate( policy, OnCreate.class ); + + assertThat( actual ).hasSize( 5 ); + Map> violations = mapViolations( actual ); + assertThat( violations ) + .containsEntry( "accessRules", Set.of( "{jakarta.validation.constraints.Size.message}" ) ) + .containsEntry( AccessRulePolicy.BPN_RULE_NAME, + Set.of( "{jakarta.validation.constraints.NotNull.message}", "{jakarta.validation.constraints.NotBlank.message}" ) ); + } + } + + @Test + void testGetVisibleSpecificAssetIdsWithNullSetExpectViolation() { + try ( ValidatorFactory factory = Validation.buildDefaultValidatorFactory() ) { + final var underTest = factory.getValidator(); + AccessRulePolicy policy = getAccessRulePolicy( BPNA, Map.of( "name", "value" ), null, null ); + policy.getAccessRules() + .add( new AccessRulePolicyValue( AccessRulePolicy.VISIBLE_SPECIFIC_ASSET_ID_NAMES_RULE_NAME, PolicyOperator.INCLUDES, null, null ) ); + + Set> actual = underTest.validate( policy, OnCreate.class ); + + assertThat( actual ).hasSize( 3 ); + Map> violations = mapViolations( actual ); + assertThat( violations ) + .containsEntry( "accessRules", Set.of( "{jakarta.validation.constraints.Size.message}" ) ) + .containsEntry( "accessRules[]", Set.of( "Invalid rule policy." ) ) + .containsEntry( "accessRules[].values", Set.of( "Values must not be null if the policy hasSingleValue() is false." ) ); + } + } + + @Test + void testGetVisibleSemanticIdsWithNullSetExpectViolation() { + try ( ValidatorFactory factory = Validation.buildDefaultValidatorFactory() ) { + final var underTest = factory.getValidator(); + AccessRulePolicy policy = getAccessRulePolicy( BPNA, Map.of( "name", "value" ), null, null ); + policy.getAccessRules() + .add( new AccessRulePolicyValue( AccessRulePolicy.VISIBLE_SEMANTIC_IDS_RULE_NAME, PolicyOperator.INCLUDES, null, null ) ); + + Set> actual = underTest.validate( policy, OnCreate.class ); + + assertThat( actual ).hasSize( 3 ); + Map> violations = mapViolations( actual ); + assertThat( violations ) + .containsEntry( "accessRules", Set.of( "{jakarta.validation.constraints.Size.message}" ) ) + .containsEntry( "accessRules[]", Set.of( "Invalid rule policy." ) ) + .containsEntry( "accessRules[].values", Set.of( "Values must not be null if the policy hasSingleValue() is false." ) ); + } + } + + @Test + void testGetOptionalIdsWithEmptySetExpectSuccess() { + try ( ValidatorFactory factory = Validation.buildDefaultValidatorFactory() ) { + final var underTest = factory.getValidator(); + AccessRulePolicy policy = getAccessRulePolicy( BPNA, Map.of( "name", "value" ), Set.of(), Set.of() ); + + Set> actual = underTest.validate( policy, OnCreate.class ); + + assertThat( actual ).isEmpty(); + } + } + + @Test + void testFullyPopulatedWithEmptySetExpectSuccess() { + try ( ValidatorFactory factory = Validation.buildDefaultValidatorFactory() ) { + final var underTest = factory.getValidator(); + AccessRulePolicy policy = getAccessRulePolicy( BPNA, Map.of( "name", "value" ), Set.of( "name" ), Set.of( "semanticId" ) ); + + Set> actual = underTest.validate( policy, OnCreate.class ); + + assertThat( actual ).isEmpty(); + } + } + +} \ No newline at end of file diff --git a/access-control-service-sql-impl/src/test/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/AccessRuleValidationTest.java b/access-control-service-sql-impl/src/test/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/AccessRuleValidationTest.java new file mode 100644 index 00000000..b05b6ef8 --- /dev/null +++ b/access-control-service-sql-impl/src/test/java/org/eclipse/tractusx/semantics/accesscontrol/sql/validation/AccessRuleValidationTest.java @@ -0,0 +1,171 @@ +/******************************************************************************* + * Copyright (c) 2024 Robert Bosch Manufacturing Solutions GmbH and others + * + * 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.semantics.accesscontrol.sql.validation; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Map; +import java.util.Set; + +import org.eclipse.tractusx.semantics.accesscontrol.sql.model.AccessRule; +import org.eclipse.tractusx.semantics.accesscontrol.sql.model.AccessRulePolicy; +import org.junit.jupiter.api.Test; + +import jakarta.validation.ConstraintViolation; +import jakarta.validation.Validation; +import jakarta.validation.ValidatorFactory; + +class AccessRuleValidationTest extends AbstractValidationTest { + + @Test + void testCreateWithNullsExpectViolation() { + try ( ValidatorFactory factory = Validation.buildDefaultValidatorFactory() ) { + final var underTest = factory.getValidator(); + AccessRule rule = new AccessRule(); + + Set> actual = underTest.validate( rule, OnCreate.class ); + + assertThat( actual ).hasSize( 5 ); + Map> violations = mapViolations( actual ); + assertThat( violations ) + .containsEntry( "policyType", Set.of( "{jakarta.validation.constraints.NotNull.message}" ) ) + .containsEntry( "targetTenant", + Set.of( "{jakarta.validation.constraints.NotNull.message}", "{jakarta.validation.constraints.NotBlank.message}" ) ) + .containsEntry( "tid", Set.of( "{jakarta.validation.constraints.NotNull.message}" ) ) + .containsEntry( "policy", Set.of( "{jakarta.validation.constraints.NotNull.message}" ) ); + } + } + + @Test + void testCreateWithInvalidValuesExpectViolation() { + try ( ValidatorFactory factory = Validation.buildDefaultValidatorFactory() ) { + final var underTest = factory.getValidator(); + AccessRulePolicy policy = getAccessRulePolicy( BPNA, Map.of( "name", "value" ), Set.of( "name" ), Set.of( "semanticId" ) ); + AccessRule rule = new AccessRule(); + rule.setId( 1L ); + rule.setTid( " " ); + rule.setTargetTenant( " " ); + rule.setPolicyType( AccessRule.PolicyType.AAS ); + rule.setPolicy( policy ); + rule.setDescription( "" ); + rule.setValidFrom( NOW ); + rule.setValidTo( ONE_SECOND_AGO ); + + Set> actual = underTest.validate( rule, OnCreate.class ); + + assertThat( actual ).hasSize( 5 ); + Map> violations = mapViolations( actual ); + assertThat( violations ) + .containsEntry( "", Set.of( "Invalid validity period." ) ) + .containsEntry( "id", Set.of( "{jakarta.validation.constraints.Null.message}" ) ) + .containsEntry( "targetTenant", Set.of( "{jakarta.validation.constraints.NotBlank.message}" ) ) + .containsEntry( "validFrom", Set.of( "ValidFrom must be earlier than validTo!" ) ) + .containsEntry( "validTo", Set.of( "ValidTo must be later than validFrom!" ) ); + } + } + + @Test + void testCreateWithValidValuesExpectSuccess() { + try ( ValidatorFactory factory = Validation.buildDefaultValidatorFactory() ) { + final var underTest = factory.getValidator(); + AccessRulePolicy policy = getAccessRulePolicy( BPNA, Map.of( "name", "value" ), Set.of( "name" ), Set.of( "semanticId" ) ); + AccessRule rule = new AccessRule(); + rule.setTid( BPNA ); + rule.setTargetTenant( BPNA ); + rule.setPolicyType( AccessRule.PolicyType.AAS ); + rule.setPolicy( policy ); + rule.setDescription( "description" ); + rule.setValidFrom( ONE_SECOND_AGO ); + rule.setValidTo( NOW ); + + Set> actual = underTest.validate( rule, OnCreate.class ); + + assertThat( actual ).isEmpty(); + } + } + + @Test + void testUpdateWithNullsExpectViolation() { + try ( ValidatorFactory factory = Validation.buildDefaultValidatorFactory() ) { + final var underTest = factory.getValidator(); + AccessRule rule = new AccessRule(); + + Set> actual = underTest.validate( rule, OnUpdate.class ); + + assertThat( actual ).hasSize( 6 ); + Map> violations = mapViolations( actual ); + assertThat( violations ) + .containsEntry( "id", Set.of( "{jakarta.validation.constraints.NotNull.message}" ) ) + .containsEntry( "policyType", Set.of( "{jakarta.validation.constraints.NotNull.message}" ) ) + .containsEntry( "targetTenant", + Set.of( "{jakarta.validation.constraints.NotNull.message}", "{jakarta.validation.constraints.NotBlank.message}" ) ) + .containsEntry( "tid", Set.of( "{jakarta.validation.constraints.NotNull.message}" ) ) + .containsEntry( "policy", Set.of( "{jakarta.validation.constraints.NotNull.message}" ) ); + } + } + + @Test + void testUpdateWithInvalidValuesExpectViolation() { + try ( ValidatorFactory factory = Validation.buildDefaultValidatorFactory() ) { + final var underTest = factory.getValidator(); + AccessRulePolicy policy = getAccessRulePolicy( BPNA, Map.of( "name", "value" ), Set.of( "name" ), Set.of( "semanticId" ) ); + AccessRule rule = new AccessRule(); + rule.setId( 1L ); + rule.setTid( " " ); + rule.setTargetTenant( " " ); + rule.setPolicyType( AccessRule.PolicyType.AAS ); + rule.setPolicy( policy ); + rule.setDescription( "" ); + rule.setValidFrom( NOW ); + rule.setValidTo( ONE_SECOND_AGO ); + + Set> actual = underTest.validate( rule, OnUpdate.class ); + + assertThat( actual ).hasSize( 4 ); + Map> violations = mapViolations( actual ); + assertThat( violations ) + .containsEntry( "", Set.of( "Invalid validity period." ) ) + .containsEntry( "targetTenant", Set.of( "{jakarta.validation.constraints.NotBlank.message}" ) ) + .containsEntry( "validFrom", Set.of( "ValidFrom must be earlier than validTo!" ) ) + .containsEntry( "validTo", Set.of( "ValidTo must be later than validFrom!" ) ); + } + } + + @Test + void testUpdateWithValidValuesExpectSuccess() { + try ( ValidatorFactory factory = Validation.buildDefaultValidatorFactory() ) { + final var underTest = factory.getValidator(); + AccessRulePolicy policy = getAccessRulePolicy( BPNA, Map.of( "name", "value" ), Set.of( "name" ), Set.of( "semanticId" ) ); + AccessRule rule = new AccessRule(); + rule.setId( 1L ); + rule.setTid( BPNA ); + rule.setTargetTenant( BPNA ); + rule.setPolicyType( AccessRule.PolicyType.AAS ); + rule.setPolicy( policy ); + rule.setDescription( "description" ); + rule.setValidFrom( ONE_SECOND_AGO ); + rule.setValidTo( NOW ); + + Set> actual = underTest.validate( rule, OnUpdate.class ); + + assertThat( actual ).isEmpty(); + } + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index d8654a6a..637b4b00 100644 --- a/pom.xml +++ b/pom.xml @@ -300,6 +300,18 @@ ${junit.version} test + + org.hibernate.validator + hibernate-validator + 8.0.1.Final + test + + + org.apache.tomcat.embed + tomcat-embed-el + 10.1.18 + test +