All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sap.cloud.security.ams.dcn.engine.CompatibilityEvaluationNodeFactory Maven / Gradle / Ivy

Go to download

Client Library for integrating Jakarta EE applications with SAP Authorization Management Service (AMS)

The newest version!
/************************************************************************
 * © 2019-2024 SAP SE or an SAP affiliate company. All rights reserved. *
 ************************************************************************/
package com.sap.cloud.security.ams.dcn.engine;

import static com.sap.cloud.security.ams.dcl.client.el.Call.QualifiedNames.IN;
import static com.sap.cloud.security.ams.dcl.client.el.Call.QualifiedNames.OR;

import com.sap.cloud.security.ams.dcl.client.el.AttributeName;
import com.sap.cloud.security.ams.dcl.client.el.Call;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;

/**
 * {@link EvaluationNodeFactory} implementation for creating {@link EvaluationNode} instances that
 * are compatible with OPA behaviour. This class deviates from the base implementations in {@link
 * AbstractEvaluationNodeFactory} by providing compatibility transformations for the {@link
 * EvaluationNode#toCondition()} implementation of many node types.
 */
public class CompatibilityEvaluationNodeFactory extends AbstractEvaluationNodeFactory {

  @Override
  public AllOfNode allOfNode(Collection nodes) {
    return super.allOfNode(nodes)
        .writtenBy(
            (subNodes) -> {
              Set commonConditions = new HashSet<>();
              List> eqConditionsFromInOps = new ArrayList<>();
              subNodes.forEach(
                  node -> {
                    if (node instanceof BinaryOperatorNode operatorNode && operatorNode.is(IN)) {
                      Optional rightValue = operatorNode.rhs().value();
                      if (rightValue.isPresent()) {
                        EvaluationNode lhs = operatorNode.lhs();
                        eqConditionsFromInOps.add(
                            ((Collection) rightValue.get())
                                .stream()
                                    .map(value -> equalsNode(lhs, valueNode(value)).toCondition())
                                    .toList());
                        return;
                      }
                    }
                    commonConditions.add(node.toCondition());
                  });

              AtomicReference>> allTuples =
                  new AtomicReference<>(new ArrayList<>());
              allTuples.get().add(commonConditions);
              eqConditionsFromInOps.forEach(
                  eqConditionsOfSingleInOp ->
                      allTuples.set(
                          eqConditionsOfSingleInOp.stream()
                              .flatMap(
                                  eqCondition ->
                                      allTuples.get().stream()
                                          .map(
                                              tuple -> {
                                                Set newTuple = new HashSet<>(tuple);
                                                newTuple.add(eqCondition);
                                                return newTuple;
                                              }))
                              .toList()));

              return switch (allTuples.get().size()) {
                case 0 -> false;
                case 1 ->
                    switch (allTuples.get().iterator().next().size()) {
                      case 0 -> true;
                      case 1 -> allTuples.get().iterator().next().iterator().next();
                      default ->
                          Call.createFrom(
                              Call.QualifiedNames.AND, allTuples.get().iterator().next());
                    };
                default ->
                    Call.createFrom(
                        OR,
                        allTuples.get().stream()
                            .map(
                                tuple ->
                                    switch (tuple.size()) {
                                      case 0 ->
                                          true; // impossible, multiple tuples are always created by
                                        // adding elements
                                      case 1 -> tuple.iterator().next();
                                      default -> Call.createFrom(Call.QualifiedNames.AND, tuple);
                                    })
                            .toList());
              };
            });
  }

  @Override
  public AnyOfNode anyOfNode(Collection nodes) {
    return super.anyOfNode(nodes)
        .writtenBy(
            (subNodes) -> {
              Set subConditions = new HashSet<>();
              for (EvaluationNode node : subNodes) {
                Object condition = node.toCondition();
                if (condition instanceof Call call && call.getQualifiedName().equals(OR)) {
                  subConditions.addAll(call.getArguments());
                } else {
                  subConditions.add(condition);
                }
              }

              return switch (subConditions.size()) {
                case 0 -> false;
                case 1 -> subConditions.iterator().next();
                default -> Call.createFrom(OR, subConditions);
              };
            });
  }

  @Override
  public BinaryOperatorNode inNode(EvaluationNode lhs, EvaluationNode rhs) {
    return super.inNode(lhs, rhs)
        .writtenBy(
            (left, right) -> {
              Object leftCondition = left.toCondition();
              Optional rightValue = right.value();
              if (rightValue.isPresent()) {
                List eqCalls =
                    ((Collection) rightValue.get())
                        .stream()
                            .map(value -> Call.create(Call.QualifiedNames.EQ, leftCondition, value))
                            .toList();
                return switch (eqCalls.size()) {
                  case 0 -> false;
                  case 1 -> eqCalls.get(0);
                  default -> Call.createFrom(OR, eqCalls);
                };
              }

              return Call.create(IN, leftCondition, right.toCondition());
            });
  }

  @Override
  public BinaryOperatorNode notInNode(EvaluationNode lhs, EvaluationNode rhs) {
    return super.notInNode(lhs, rhs)
        .writtenBy(
            (left, right) -> {
              Object leftCondition = left.toCondition();
              Optional rightValue = right.value();
              if (rightValue.isPresent()) {
                List neCalls =
                    ((Collection) rightValue.get())
                        .stream()
                            .map(value -> Call.create(Call.QualifiedNames.NE, leftCondition, value))
                            .toList();
                return switch (neCalls.size()) {
                  case 0 -> true;
                  case 1 -> neCalls.get(0);
                  default -> Call.createFrom(Call.QualifiedNames.AND, neCalls);
                };
              }

              return Call.create(Call.QualifiedNames.NOT_IN, leftCondition, right.toCondition());
            });
  }

  @Override
  public BinaryOperatorNode lessThanOrEqualsNode(EvaluationNode lhs, EvaluationNode rhs) {
    return super.lessThanOrEqualsNode(lhs, rhs)
        .writtenBy(
            (left, right) -> {
              if (left.equals(right)) {
                return isNotNullNode(left).toCondition();
              }
              return Call.create(Call.QualifiedNames.LE, left.toCondition(), right.toCondition());
            });
  }

  @Override
  public BinaryOperatorNode equalsNode(EvaluationNode lhs, EvaluationNode rhs) {
    return super.equalsNode(lhs, rhs)
        .writtenBy(
            (left, right) -> {
              if (left.equals(right)) {
                return isNotNullNode(left).toCondition();
              }
              Object leftCondition = left.toCondition();
              Object rightCondition = right.toCondition();
              if (rightCondition instanceof AttributeName rightName) {
                if (left.value().isPresent()
                    || (leftCondition instanceof AttributeName leftName
                        && leftName.compareTo(rightName) > 0)) {
                  return Call.create(Call.QualifiedNames.EQ, rightCondition, leftCondition);
                }
              }
              return Call.create(Call.QualifiedNames.EQ, leftCondition, rightCondition);
            });
  }

  @Override
  public BinaryOperatorNode notEqualsNode(EvaluationNode lhs, EvaluationNode rhs) {
    return super.notEqualsNode(lhs, rhs)
        .writtenBy(
            (left, right) -> {
              Object leftCondition = left.toCondition();
              Object rightCondition = right.toCondition();
              if (rightCondition instanceof AttributeName rightName) {
                if (left.value().isPresent()
                    || (leftCondition instanceof AttributeName leftName
                        && leftName.compareTo(rightName) > 0)) {
                  return Call.create(Call.QualifiedNames.NE, rightCondition, leftCondition);
                }
              }
              return Call.create(Call.QualifiedNames.NE, leftCondition, rightCondition);
            });
  }

  @Override
  public BinaryOperatorNode greaterThanOrEqualsNode(EvaluationNode lhs, EvaluationNode rhs) {
    return super.greaterThanOrEqualsNode(lhs, rhs)
        .writtenBy(
            (left, right) -> {
              if (left.equals(right)) {
                return isNotNullNode(left).toCondition();
              }
              return Call.create(Call.QualifiedNames.GE, left.toCondition(), right.toCondition());
            });
  }

  @Override
  public TernaryOperatorNode betweenNode(
      EvaluationNode lhs, EvaluationNode lowerBound, EvaluationNode upperBound) {
    return super.betweenNode(lhs, lowerBound, upperBound)
        .writtenBy(
            (left, lower, upper) -> {
              if (left.equals(lower) && lower.equals(upper)) {
                return isNotNullNode(left).toCondition();
              }
              return Call.create(
                  Call.QualifiedNames.AND,
                  Call.create(Call.QualifiedNames.GE, left.toCondition(), lower.toCondition()),
                  Call.create(Call.QualifiedNames.LE, left.toCondition(), upper.toCondition()));
            });
  }

  @Override
  public TernaryOperatorNode notBetweenNode(
      EvaluationNode lhs, EvaluationNode lowerBound, EvaluationNode upperBound) {
    return super.notBetweenNode(lhs, lowerBound, upperBound)
        .writtenBy(
            (left, lower, upper) ->
                Call.create(
                    Call.QualifiedNames.OR,
                    Call.create(Call.QualifiedNames.LT, left.toCondition(), lower.toCondition()),
                    Call.create(Call.QualifiedNames.GT, left.toCondition(), upper.toCondition())));
  }
}