Skip to content

Commit

Permalink
Merge pull request #322 from bci-oss/feature/286-implement-crud-acces…
Browse files Browse the repository at this point in the history
…s-management-api__04-add-rule-validators

feat: Implementation of CRUD API for Access management APIs - Add rule validations
  • Loading branch information
tunacicek authored Feb 19, 2024
2 parents 00f678d + 484ba05 commit 390806d
Show file tree
Hide file tree
Showing 15 changed files with 842 additions and 53 deletions.
14 changes: 14 additions & 0 deletions access-control-service-sql-impl/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>

<!-- Tests -->
<dependency>
Expand All @@ -125,6 +129,16 @@
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-el</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

}
Original file line number Diff line number Diff line change
Expand Up @@ -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<AccessRulePolicyValue> accessRules;

private Set<SpecificAssetId> mandatorySpecificAssetIds;

@JsonIgnore
@Valid
@Size( min = 1, groups = { OnCreate.class, OnUpdate.class } )
@NotNull( groups = { OnCreate.class, OnUpdate.class } )
public Set<SpecificAssetId> 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() );
}

Expand All @@ -67,40 +80,23 @@ public Set<String> 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<String> 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 );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<AccessRulePolicyValue> values) {
import jakarta.validation.Valid;

@ValidAccessRulePolicyValue( groups = { OnCreate.class, OnUpdate.class } )
public record AccessRulePolicyValue(String attribute, PolicyOperator operator, String value, @Valid Set<AccessRulePolicyValue> values) {

@JsonIgnore
public boolean hasSingleValue() {
return Optional.ofNullable( value ).isPresent();
return (operator != null && operator.isSingleValued()) || Optional.ofNullable( value ).isPresent();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -44,4 +47,9 @@ public static PolicyOperator forValue( final String value ) {
public String getValue() {
return value;
}

@JsonIgnore
public boolean isSingleValued() {
return singleValued;
}
}
Original file line number Diff line number Diff line change
@@ -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<ValidAccessRulePolicyValue, AccessRulePolicyValue> {

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;
}

}
Loading

0 comments on commit 390806d

Please sign in to comment.