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

org.openmetadata.service.jdbi3.PolicyRepository Maven / Gradle / Ivy

There is a newer version: 1.5.11
Show newest version
/*
 *  Copyright 2021 Collate
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *  http://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.
 */

package org.openmetadata.service.jdbi3;

import static java.lang.Boolean.FALSE;
import static org.openmetadata.common.utils.CommonUtil.nullOrEmpty;
import static org.openmetadata.schema.type.MetadataOperation.EDIT_ALL;
import static org.openmetadata.schema.type.MetadataOperation.VIEW_ALL;
import static org.openmetadata.service.Entity.ALL_RESOURCES;
import static org.openmetadata.service.Entity.FIELD_DESCRIPTION;
import static org.openmetadata.service.Entity.POLICY;
import static org.openmetadata.service.security.policyevaluator.OperationContext.isEditOperation;
import static org.openmetadata.service.security.policyevaluator.OperationContext.isViewOperation;
import static org.openmetadata.service.util.EntityUtil.getRuleField;
import static org.openmetadata.service.util.EntityUtil.ruleMatch;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import javax.ws.rs.BadRequestException;
import lombok.extern.slf4j.Slf4j;
import org.jdbi.v3.sqlobject.transaction.Transaction;
import org.openmetadata.schema.entity.policies.Policy;
import org.openmetadata.schema.entity.policies.accessControl.Rule;
import org.openmetadata.schema.type.EntityReference;
import org.openmetadata.schema.type.MetadataOperation;
import org.openmetadata.schema.type.Relationship;
import org.openmetadata.service.Entity;
import org.openmetadata.service.exception.CatalogExceptionMessage;
import org.openmetadata.service.resources.policies.PolicyResource;
import org.openmetadata.service.security.policyevaluator.CompiledRule;
import org.openmetadata.service.util.EntityUtil.Fields;

@Slf4j
public class PolicyRepository extends EntityRepository {
  public static final String ENABLED = "enabled";

  public PolicyRepository() {
    super(
        PolicyResource.COLLECTION_PATH,
        POLICY,
        Policy.class,
        Entity.getCollectionDAO().policyDAO(),
        "",
        "");
  }

  @Override
  public void setFields(Policy policy, Fields fields) {
    policy.setTeams(fields.contains("teams") ? getTeams(policy) : policy.getTeams());
    policy.withRoles(fields.contains("roles") ? getRoles(policy) : policy.getRoles());
  }

  @Override
  public void clearFields(Policy policy, Fields fields) {
    policy.setTeams(fields.contains("teams") ? policy.getTeams() : null);
    policy.withRoles(fields.contains("roles") ? policy.getRoles() : null);
  }

  /* Get all the teams that use this policy */
  private List getTeams(Policy policy) {
    return findFrom(policy.getId(), POLICY, Relationship.HAS, Entity.TEAM);
  }

  /* Get all the roles that use this policy */
  private List getRoles(Policy policy) {
    return findFrom(policy.getId(), POLICY, Relationship.HAS, Entity.ROLE);
  }

  @Override
  public void prepare(Policy policy, boolean update) {
    validateRules(policy);
  }

  @Override
  public void storeEntity(Policy policy, boolean update) {
    store(policy, update);
  }

  @Override
  public void storeRelationships(Policy policy) {
    // No relationships to store beyond what is stored in the super class
  }

  @Override
  public PolicyUpdater getUpdater(Policy original, Policy updated, Operation operation) {
    return new PolicyUpdater(original, updated, operation);
  }

  @Override
  protected void preDelete(Policy entity, String updateBy) {
    if (FALSE.equals(entity.getAllowDelete())) {
      throw new IllegalArgumentException(
          CatalogExceptionMessage.systemEntityDeleteNotAllowed(entity.getName(), Entity.POLICY));
    }
  }

  public void validateRules(Policy policy) {
    List rules = policy.getRules();
    if (nullOrEmpty(rules)) {
      throw new IllegalArgumentException(CatalogExceptionMessage.EMPTY_RULES_IN_POLICY);
    }

    // Validate all the expressions in the rule
    for (Rule rule : rules) {
      CompiledRule.validateExpression(rule.getCondition(), Boolean.class);
      rule.getResources().sort(String.CASE_INSENSITIVE_ORDER);
      rule.getOperations().sort(Comparator.comparing(MetadataOperation::value));

      // Remove redundant resources
      rule.setResources(filterRedundantResources(rule.getResources()));

      // Remove redundant operations
      rule.setOperations(filterRedundantOperations(rule.getOperations()));
    }
    rules.sort(Comparator.comparing(Rule::getName));
  }

  public static List filterRedundantResources(List resources) {
    // If ALL_RESOURCES are in the resource list, remove redundant resources specifically mentioned
    boolean containsAllResources = resources.stream().anyMatch(ALL_RESOURCES::equalsIgnoreCase);
    return containsAllResources
        ? new ArrayList<>(List.of(ALL_RESOURCES))
        : new ArrayList<>(resources);
  }

  public static List filterRedundantOperations(
      List operations) {
    // If VIEW_ALL is in the operation list, remove all the other specific view operations that are
    // redundant
    boolean containsViewAll = operations.stream().anyMatch(o -> o.equals(VIEW_ALL));
    if (containsViewAll) {
      operations =
          operations.stream().filter(o -> o.equals(VIEW_ALL) || !isViewOperation(o)).toList();
    }

    // If EDIT_ALL is in the operation list, remove all the other specific edit operations that are
    // redundant
    boolean containsEditAll = operations.stream().anyMatch(o -> o.equals(EDIT_ALL));
    if (containsEditAll) {
      operations =
          operations.stream().filter(o -> o.equals(EDIT_ALL) || !isEditOperation(o)).toList();
    }
    return new ArrayList<>(operations);
  }

  /** Handles entity updated from PUT and POST operation. */
  public class PolicyUpdater extends EntityUpdater {
    public PolicyUpdater(Policy original, Policy updated, Operation operation) {
      super(original, updated, operation);
    }

    @Transaction
    @Override
    public void entitySpecificUpdate() {
      recordChange(ENABLED, original.getEnabled(), updated.getEnabled());
      updateRules(original.getRules(), updated.getRules());
    }

    private void updateRules(List origRules, List updatedRules) {
      // Check if the Rules have unique names
      if (!nullOrEmpty(updatedRules)) {
        Set ruleNames =
            updatedRules.stream().map(Rule::getName).collect(Collectors.toSet());

        if (ruleNames.size() != updatedRules.size()) {
          throw new BadRequestException(
              "Policy contains duplicate Rules. Please use unique name for Rules.");
        }
      }

      // Record change description
      List deletedRules = new ArrayList<>();
      List addedRules = new ArrayList<>();

      recordListChange("rules", origRules, updatedRules, addedRules, deletedRules, ruleMatch);

      // Record changes based on updatedRule
      for (Rule updated : updatedRules) {
        Rule stored =
            origRules.stream().filter(c -> ruleMatch.test(c, updated)).findAny().orElse(null);
        if (stored == null) { // New Rule added
          continue;
        }

        updateRuleDescription(stored, updated);
        updateRuleEffect(stored, updated);
        updateRuleOperations(stored, updated);
        updateRuleResources(stored, updated);
        updateRuleCondition(stored, updated);
      }
    }

    private void updateRuleDescription(Rule stored, Rule updated) {
      String ruleField = getRuleField(stored, FIELD_DESCRIPTION);
      recordChange(ruleField, stored.getDescription(), updated.getDescription());
    }

    private void updateRuleEffect(Rule stored, Rule updated) {
      String ruleField = getRuleField(stored, "effect");
      recordChange(ruleField, stored.getEffect(), updated.getEffect());
    }

    private void updateRuleOperations(Rule stored, Rule updated) {
      String ruleField = getRuleField(stored, "operations");
      recordChange(ruleField, stored.getOperations(), updated.getOperations());
    }

    private void updateRuleResources(Rule stored, Rule updated) {
      String ruleField = getRuleField(stored, "resources");
      recordChange(ruleField, stored.getResources(), updated.getResources());
    }

    private void updateRuleCondition(Rule stored, Rule updated) {
      String ruleField = getRuleField(stored, "condition");
      recordChange(ruleField, stored.getCondition(), updated.getCondition());
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy