org.apache.ranger.plugin.model.validation.RangerPolicyValidator.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.ranger.plugin.model.validation.RangerPolicyValidator.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.apache.ranger.plugin.model.validation;

import java.util.*;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ranger.plugin.errors.ValidationErrorCode;
import org.apache.ranger.plugin.model.RangerPolicy;
import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItem;
import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemAccess;
import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource;
import org.apache.ranger.plugin.model.RangerPolicyResourceSignature;
import org.apache.ranger.plugin.model.RangerService;
import org.apache.ranger.plugin.model.RangerServiceDef;
import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef;
import org.apache.ranger.plugin.store.ServiceStore;

public class RangerPolicyValidator extends RangerValidator {

    private static final Log LOG = LogFactory.getLog(RangerPolicyValidator.class);

    public RangerPolicyValidator(ServiceStore store) {
        super(store);
    }

    public void validate(RangerPolicy policy, Action action, boolean isAdmin) throws Exception {
        if (LOG.isDebugEnabled()) {
            LOG.debug(String.format("==> RangerPolicyValidator.validate(%s, %s, %s)", policy, action, isAdmin));
        }

        List<ValidationFailureDetails> failures = new ArrayList<ValidationFailureDetails>();
        boolean valid = isValid(policy, action, isAdmin, failures);
        String message = "";
        try {
            if (!valid) {
                message = serializeFailures(failures);
                throw new Exception(message);
            }
        } finally {
            if (LOG.isDebugEnabled()) {
                LOG.debug(String.format("<== RangerPolicyValidator.validate(%s, %s, %s): %s, reason[%s]", policy,
                        action, isAdmin, valid, message));
            }
        }
    }

    @Override
    boolean isValid(Long id, Action action, List<ValidationFailureDetails> failures) {
        if (LOG.isDebugEnabled()) {
            LOG.debug(String.format("==> RangerPolicyValidator.isValid(%s, %s, %s)", id, action, failures));
        }

        boolean valid = true;
        if (action != Action.DELETE) {
            ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_UNSUPPORTED_ACTION;
            failures.add(new ValidationFailureDetailsBuilder().isAnInternalError().becauseOf(error.getMessage())
                    .errorCode(error.getErrorCode()).build());
            valid = false;
        } else if (id == null) {
            ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_MISSING_FIELD;
            failures.add(new ValidationFailureDetailsBuilder().becauseOf("policy id was null/missing").field("id")
                    .isMissing().errorCode(error.getErrorCode()).becauseOf(error.getMessage("id")).build());
            valid = false;
        } else if (getPolicy(id) == null) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("No policy found for id[" + id + "]! ok!");
            }
        }

        if (LOG.isDebugEnabled()) {
            LOG.debug(String.format("<== RangerPolicyValidator.isValid(%s, %s, %s): %s", id, action, failures,
                    valid));
        }
        return valid;
    }

    boolean isValid(RangerPolicy policy, Action action, boolean isAdmin, List<ValidationFailureDetails> failures) {
        if (LOG.isDebugEnabled()) {
            LOG.debug(String.format("==> RangerPolicyValidator.isValid(%s, %s, %s, %s)", policy, action, isAdmin,
                    failures));
        }

        if (!(action == Action.CREATE || action == Action.UPDATE)) {
            throw new IllegalArgumentException("isValid(RangerPolicy, ...) is only supported for create/update");
        }
        boolean valid = true;
        if (policy == null) {
            ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_NULL_POLICY_OBJECT;
            failures.add(new ValidationFailureDetailsBuilder().field("policy").isMissing()
                    .becauseOf(error.getMessage()).errorCode(error.getErrorCode()).build());
            valid = false;
        } else {
            Long id = policy.getId();
            RangerPolicy existingPolicy = null;

            if (action == Action.UPDATE) { // id is ignored for CREATE
                if (id == null) {
                    ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_MISSING_FIELD;
                    failures.add(new ValidationFailureDetailsBuilder().field("id").isMissing()
                            .becauseOf(error.getMessage("id")).errorCode(error.getErrorCode()).build());
                    valid = false;
                }

                existingPolicy = getPolicy(id);

                if (existingPolicy == null) {
                    ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_INVALID_POLICY_ID;
                    failures.add(new ValidationFailureDetailsBuilder().field("id").isSemanticallyIncorrect()
                            .becauseOf(error.getMessage(id)).errorCode(error.getErrorCode()).build());
                    valid = false;
                }
            }
            String policyName = policy.getName();
            String serviceName = policy.getService();
            if (StringUtils.isBlank(policyName)) {
                ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_MISSING_FIELD;
                failures.add(new ValidationFailureDetailsBuilder().field("name").isMissing()
                        .becauseOf(error.getMessage("name")).errorCode(error.getErrorCode()).build());
                valid = false;
            } else {
                List<RangerPolicy> policies = getPolicies(serviceName, policyName);
                if (CollectionUtils.isNotEmpty(policies)) {
                    if (policies.size() > 1) {
                        ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_POLICY_NAME_MULTIPLE_POLICIES_WITH_SAME_NAME;
                        failures.add(new ValidationFailureDetailsBuilder().field("name").isAnInternalError()
                                .becauseOf(error.getMessage(policyName)).errorCode(error.getErrorCode()).build());
                        valid = false;
                    } else if (action == Action.CREATE) { // size == 1
                        ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_POLICY_NAME_CONFLICT;
                        failures.add(new ValidationFailureDetailsBuilder().field("policy name")
                                .isSemanticallyIncorrect()
                                .becauseOf(error.getMessage(policies.iterator().next().getId(), serviceName))
                                .errorCode(error.getErrorCode()).build());
                        valid = false;
                    } else if (!policies.iterator().next().getId().equals(id)) { // size == 1 && action == UPDATE
                        ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_POLICY_NAME_CONFLICT;
                        failures.add(new ValidationFailureDetailsBuilder().field("id/name")
                                .isSemanticallyIncorrect()
                                .becauseOf(error.getMessage(policies.iterator().next().getId(), serviceName))
                                .errorCode(error.getErrorCode()).build());
                        valid = false;
                    }
                }
            }
            RangerService service = null;
            boolean serviceNameValid = false;
            if (StringUtils.isBlank(serviceName)) {
                ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_MISSING_FIELD;
                failures.add(new ValidationFailureDetailsBuilder().field("service name").isMissing()
                        .becauseOf(error.getMessage("service name")).errorCode(error.getErrorCode()).build());
                valid = false;
            } else {
                service = getService(serviceName);
                if (service == null) {
                    ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_INVALID_SERVICE_NAME;
                    failures.add(new ValidationFailureDetailsBuilder().field("service name")
                            .isSemanticallyIncorrect().becauseOf(error.getMessage(serviceName))
                            .errorCode(error.getErrorCode()).build());
                    valid = false;
                } else {
                    serviceNameValid = true;
                }
            }

            if (existingPolicy != null) {
                if (!StringUtils.equalsIgnoreCase(existingPolicy.getService(), policy.getService())) {
                    ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_POLICY_UPDATE_MOVE_SERVICE_NOT_ALLOWED;
                    failures.add(new ValidationFailureDetailsBuilder().field("service name")
                            .isSemanticallyIncorrect().becauseOf(error.getMessage(policy.getId(),
                                    existingPolicy.getService(), policy.getService()))
                            .errorCode(error.getErrorCode()).build());
                    valid = false;
                }

                int existingPolicyType = existingPolicy.getPolicyType() == null ? RangerPolicy.POLICY_TYPE_ACCESS
                        : existingPolicy.getPolicyType();
                int policyType = policy.getPolicyType() == null ? RangerPolicy.POLICY_TYPE_ACCESS
                        : policy.getPolicyType();

                if (existingPolicyType != policyType) {
                    ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_POLICY_TYPE_CHANGE_NOT_ALLOWED;
                    failures.add(
                            new ValidationFailureDetailsBuilder().field("policy type").isSemanticallyIncorrect()
                                    .becauseOf(error.getMessage(policy.getId(), existingPolicyType, policyType))
                                    .errorCode(error.getErrorCode()).build());
                    valid = false;
                }
            }

            boolean isAuditEnabled = getIsAuditEnabled(policy);
            String serviceDefName = null;
            RangerServiceDef serviceDef = null;
            int policyItemsCount = 0;

            int policyType = policy.getPolicyType() == null ? RangerPolicy.POLICY_TYPE_ACCESS
                    : policy.getPolicyType();
            switch (policyType) {
            case RangerPolicy.POLICY_TYPE_DATAMASK:
                if (CollectionUtils.isNotEmpty(policy.getDataMaskPolicyItems()))
                    policyItemsCount += policy.getDataMaskPolicyItems().size();
                break;
            case RangerPolicy.POLICY_TYPE_ROWFILTER:
                if (CollectionUtils.isNotEmpty(policy.getRowFilterPolicyItems()))
                    policyItemsCount += policy.getRowFilterPolicyItems().size();
                break;
            default:
                if (CollectionUtils.isNotEmpty(policy.getPolicyItems())) {
                    policyItemsCount += policy.getPolicyItems().size();
                }
                if (CollectionUtils.isNotEmpty(policy.getDenyPolicyItems())) {
                    policyItemsCount += policy.getDenyPolicyItems().size();
                }
                break;
            }

            if (policyItemsCount == 0 && !isAuditEnabled) {
                ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_MISSING_POLICY_ITEMS;
                failures.add(new ValidationFailureDetailsBuilder().field("policy items").isMissing()
                        .becauseOf(error.getMessage()).errorCode(error.getErrorCode()).build());
                valid = false;
            } else if (service != null) {
                serviceDefName = service.getType();
                serviceDef = getServiceDef(serviceDefName);
                if (serviceDef == null) {
                    ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_MISSING_SERVICE_DEF;
                    failures.add(new ValidationFailureDetailsBuilder().field("policy service def")
                            .isAnInternalError().becauseOf(error.getMessage(serviceDefName, serviceName))
                            .errorCode(error.getErrorCode()).build());
                    valid = false;
                } else {
                    valid = isValidPolicyItems(policy.getPolicyItems(), failures, serviceDef) && valid;
                    valid = isValidPolicyItems(policy.getDenyPolicyItems(), failures, serviceDef) && valid;
                    valid = isValidPolicyItems(policy.getAllowExceptions(), failures, serviceDef) && valid;
                    valid = isValidPolicyItems(policy.getDenyExceptions(), failures, serviceDef) && valid;
                }
            }

            if (serviceNameValid) { // resource checks can't be done meaningfully otherwise
                valid = isValidResources(policy, failures, action, isAdmin, serviceDef) && valid;
            }
        }

        if (LOG.isDebugEnabled()) {
            LOG.debug(String.format("<== RangerPolicyValidator.isValid(%s, %s, %s, %s): %s", policy, action,
                    isAdmin, failures, valid));
        }
        return valid;
    }

    boolean isValidResources(RangerPolicy policy, final List<ValidationFailureDetails> failures, Action action,
            boolean isAdmin, final RangerServiceDef serviceDef) {

        if (LOG.isDebugEnabled()) {
            LOG.debug(String.format("==> RangerPolicyValidator.isValidResources(%s, %s, %s, %s, %s)", policy,
                    failures, action, isAdmin, serviceDef));
        }

        boolean valid = true;
        Map<String, RangerPolicyResource> resourceMap = policy.getResources();
        if (resourceMap != null) { // following checks can't be done meaningfully otherwise
            valid = isPolicyResourceUnique(policy, failures, action) && valid;
            if (serviceDef != null) { // following checks can't be done meaningfully otherwise
                valid = isValidResourceNames(policy, failures, serviceDef) && valid;
                valid = isValidResourceValues(resourceMap, failures, serviceDef) && valid;
                valid = isValidResourceFlags(resourceMap, failures, serviceDef.getResources(), serviceDef.getName(),
                        policy.getName(), isAdmin) && valid;
            }
        }

        if (LOG.isDebugEnabled()) {
            LOG.debug(String.format("<== RangerPolicyValidator.isValidResources(%s, %s, %s, %s, %s): %s", policy,
                    failures, action, isAdmin, serviceDef, valid));
        }
        return valid;
    }

    boolean isPolicyResourceUnique(RangerPolicy policy, final List<ValidationFailureDetails> failures,
            Action action) {

        if (LOG.isDebugEnabled()) {
            LOG.debug(String.format("==> RangerPolicyValidator.isPolicyResourceUnique(%s, %s, %s)", policy,
                    failures, action));
        }

        boolean valid = true;
        if (!Boolean.TRUE.equals(policy.getIsEnabled())) {
            LOG.debug("Policy is disabled. Skipping resource uniqueness validation.");
        } else {
            RangerPolicyResourceSignature policySignature = _factory.createPolicyResourceSignature(policy);
            String signature = policySignature.getSignature();
            List<RangerPolicy> policies = getPoliciesForResourceSignature(policy.getService(), signature);
            if (CollectionUtils.isNotEmpty(policies)) {
                ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_DUPLICATE_POLICY_RESOURCE;
                RangerPolicy matchedPolicy = policies.iterator().next();
                // there shouldn't be a matching policy for create.  During update only match should be to itself
                if (action == Action.CREATE || (action == Action.UPDATE
                        && (policies.size() > 1 || !matchedPolicy.getId().equals(policy.getId())))) {
                    failures.add(new ValidationFailureDetailsBuilder().field("resources").isSemanticallyIncorrect()
                            .becauseOf(error.getMessage(matchedPolicy.getName(), policy.getService()))
                            .errorCode(error.getErrorCode()).build());
                    valid = false;
                }
            }
        }

        if (LOG.isDebugEnabled()) {
            LOG.debug(String.format("<== RangerPolicyValidator.isPolicyResourceUnique(%s, %s, %s): %s", policy,
                    failures, action, valid));
        }
        return valid;
    }

    boolean isValidResourceNames(final RangerPolicy policy, final List<ValidationFailureDetails> failures,
            final RangerServiceDef serviceDef) {

        if (LOG.isDebugEnabled()) {
            LOG.debug(String.format("==> RangerPolicyValidator.isValidResourceNames(%s, %s, %s)", policy, failures,
                    serviceDef));
        }

        boolean valid = true;
        Set<String> policyResources = getPolicyResources(policy);

        RangerServiceDefHelper defHelper = new RangerServiceDefHelper(serviceDef);
        Set<List<RangerResourceDef>> hierarchies = defHelper.getResourceHierarchies(policy.getPolicyType()); // this can be empty but not null!
        if (hierarchies.isEmpty()) {
            LOG.warn(
                    "RangerPolicyValidator.isValidResourceNames: serviceDef does not have any resource hierarchies, possibly due to a old/migrated service def!  Skipping this check!");
        } else {
            /*
             * A policy is for a single hierarchy however, it doesn't specify which one.  So we have to guess which hierarchy(s) it possibly be for.  First, see if the policy could be for
             * any of the known hierarchies?  A candidate hierarchy is one whose resource levels are a superset of those in the policy.
             * Why?  What we want to catch at this stage is policies that straddles multiple hierarchies, e.g. db, udf and column for a hive policy.
             * This has the side effect of catch spurious levels specified on the policy, e.g. having a level "blah" on a hive policy.
             */
            Set<List<RangerResourceDef>> candidateHierarchies = filterHierarchies_hierarchyHasAllPolicyResources(
                    policyResources, hierarchies, defHelper);
            if (candidateHierarchies.isEmpty()) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug(String.format(
                            "No compatible resource hierarchies found: resource[%s], service-def[%s], valid-resource-hierarchies[%s]",
                            policyResources.toString(), serviceDef.getName(),
                            toStringHierarchies_all(hierarchies, defHelper)));
                }
                ValidationErrorCode error;
                if (hierarchies.size() == 1) { // we can give a simpler message for single hierarchy service-defs which is the majority of cases
                    error = ValidationErrorCode.POLICY_VALIDATION_ERR_INVALID_RESOURCE_NO_COMPATIBLE_HIERARCHY_SINGLE;
                } else {
                    error = ValidationErrorCode.POLICY_VALIDATION_ERR_INVALID_RESOURCE_NO_COMPATIBLE_HIERARCHY;
                }
                failures.add(new ValidationFailureDetailsBuilder().field("policy resources")
                        .subField("incompatible").isSemanticallyIncorrect()
                        .becauseOf(error.getMessage(serviceDef.getName(),
                                toStringHierarchies_all(hierarchies, defHelper)))
                        .errorCode(error.getErrorCode()).build());
                valid = false;
            } else {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("isValidResourceNames: Found [" + candidateHierarchies.size()
                            + "] compatible hierarchies: "
                            + toStringHierarchies_all(candidateHierarchies, defHelper));
                }
                /*
                 * Among the candidate hierarchies there should be at least one for which policy specifies all of the mandatory resources.  Note that there could be multiple
                 * hierarchies that meet that criteria, e.g. a hive policy that specified only DB.  It is not clear if it belongs to DB->UDF or DB->TBL->COL hierarchy.
                 * However, if both UDF and TBL were required then we can detect that policy does not specify mandatory levels for any of the candidate hierarchies.
                 */
                Set<List<RangerResourceDef>> validHierarchies = filterHierarchies_mandatoryResourcesSpecifiedInPolicy(
                        policyResources, candidateHierarchies, defHelper);
                if (validHierarchies.isEmpty()) {
                    ValidationErrorCode error;
                    if (candidateHierarchies.size() == 1) { // we can provide better message if there is a single candidate hierarchy
                        error = ValidationErrorCode.POLICY_VALIDATION_ERR_INVALID_RESOURCE_MISSING_MANDATORY_SINGLE;
                    } else {
                        error = ValidationErrorCode.POLICY_VALIDATION_ERR_INVALID_RESOURCE_MISSING_MANDATORY;
                    }
                    failures.add(new ValidationFailureDetailsBuilder().field("policy resources")
                            .subField("missing mandatory").isSemanticallyIncorrect()
                            .becauseOf(error.getMessage(serviceDef.getName(),
                                    toStringHierarchies_mandatory(candidateHierarchies, defHelper)))
                            .errorCode(error.getErrorCode()).build());
                    valid = false;
                } else {
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("isValidResourceNames: Found hierarchies with all mandatory fields specified: "
                                + toStringHierarchies_mandatory(validHierarchies, defHelper));
                    }
                }
            }
        }

        if (LOG.isDebugEnabled()) {
            LOG.debug(String.format("<== RangerPolicyValidator.isValidResourceNames(%s, %s, %s): %s", policy,
                    failures, serviceDef, valid));
        }
        return valid;
    }

    /**
     * String representation of mandatory resources of all the hierarchies suitable of showing to user.  Mandatory resources within a hierarchy are not ordered per the hierarchy.
     * @param hierarchies
     * @param defHelper
     * @return
     */
    String toStringHierarchies_mandatory(Set<List<RangerResourceDef>> hierarchies,
            RangerServiceDefHelper defHelper) {

        // helper function skipping sanity checks of getting null arguments passed
        StringBuilder builder = new StringBuilder();
        for (List<RangerResourceDef> aHierarchy : hierarchies) {
            builder.append(defHelper.getMandatoryResourceNames(aHierarchy));
            builder.append(" ");
        }
        return builder.toString();
    }

    /**
     * String representation of all resources of all hierarchies.  Resources within a hierarchy are ordered per the hierarchy.
     * @param hierarchies
     * @param defHelper
     * @return
     */
    String toStringHierarchies_all(Set<List<RangerResourceDef>> hierarchies, RangerServiceDefHelper defHelper) {

        // helper function skipping sanity checks of getting null arguments passed
        StringBuilder builder = new StringBuilder();
        for (List<RangerResourceDef> aHierarchy : hierarchies) {
            builder.append(defHelper.getAllResourceNamesOrdered(aHierarchy));
            builder.append(" ");
        }
        return builder.toString();
    }

    /**
     * Returns the subset of all hierarchies that are a superset of the policy's resources.
     * @param policyResources
     * @param hierarchies
     * @return
     */
    Set<List<RangerResourceDef>> filterHierarchies_hierarchyHasAllPolicyResources(Set<String> policyResources,
            Set<List<RangerResourceDef>> hierarchies, RangerServiceDefHelper defHelper) {

        // helper function skipping sanity checks of getting null arguments passed
        Set<List<RangerResourceDef>> result = new HashSet<List<RangerResourceDef>>(hierarchies.size());
        for (List<RangerResourceDef> aHierarchy : hierarchies) {
            Set<String> hierarchyResources = defHelper.getAllResourceNames(aHierarchy);
            if (hierarchyResources.containsAll(policyResources)) {
                result.add(aHierarchy);
            }
        }
        return result;
    }

    /**
     * Returns the subset of hierarchies all of whose mandatory resources were found in policy's resource set.  candidate hierarchies are expected to have passed
     * <code>filterHierarchies_hierarchyHasAllPolicyResources</code> check first.
     * @param policyResources
     * @param hierarchies
     * @param defHelper
     * @return
     */
    Set<List<RangerResourceDef>> filterHierarchies_mandatoryResourcesSpecifiedInPolicy(Set<String> policyResources,
            Set<List<RangerResourceDef>> hierarchies, RangerServiceDefHelper defHelper) {

        // helper function skipping sanity checks of getting null arguments passed
        Set<List<RangerResourceDef>> result = new HashSet<List<RangerResourceDef>>(hierarchies.size());
        for (List<RangerResourceDef> aHierarchy : hierarchies) {
            Set<String> mandatoryResources = defHelper.getMandatoryResourceNames(aHierarchy);
            if (policyResources.containsAll(mandatoryResources)) {
                result.add(aHierarchy);
            }
        }
        return result;
    }

    boolean isValidResourceFlags(final Map<String, RangerPolicyResource> inputPolicyResources,
            final List<ValidationFailureDetails> failures, final List<RangerResourceDef> resourceDefs,
            final String serviceDefName, final String policyName, boolean isAdmin) {
        if (LOG.isDebugEnabled()) {
            LOG.debug(String.format("==> RangerPolicyValidator.isValidResourceFlags(%s, %s, %s, %s, %s, %s)",
                    inputPolicyResources, failures, resourceDefs, serviceDefName, policyName, isAdmin));
        }

        boolean valid = true;
        if (resourceDefs == null) {
            LOG.debug("isValidResourceFlags: service Def is null");
        } else {
            Map<String, RangerPolicyResource> policyResources = getPolicyResourceWithLowerCaseKeys(
                    inputPolicyResources);
            for (RangerResourceDef resourceDef : resourceDefs) {
                if (resourceDef == null) {
                    ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_NULL_RESOURCE_DEF;
                    failures.add(new ValidationFailureDetailsBuilder().field("resource-def").isAnInternalError()
                            .becauseOf(error.getMessage(serviceDefName)).errorCode(error.getErrorCode()).build());
                    valid = false;
                } else if (StringUtils.isBlank(resourceDef.getName())) {
                    ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_MISSING_RESOURCE_DEF_NAME;
                    failures.add(new ValidationFailureDetailsBuilder().field("resource-def-name")
                            .isAnInternalError().becauseOf(error.getMessage(serviceDefName))
                            .errorCode(error.getErrorCode()).build());
                    valid = false;
                } else {
                    String resourceName = resourceDef.getName().toLowerCase();
                    RangerPolicyResource policyResource = policyResources.get(resourceName);
                    if (policyResource == null) {
                        if (LOG.isDebugEnabled()) {
                            LOG.debug("a policy-resource object for resource[" + resourceName + "] on policy ["
                                    + policyName + "] was null");
                        }
                    } else {
                        boolean excludesSupported = Boolean.TRUE.equals(resourceDef.getExcludesSupported()); // could be null
                        boolean policyResourceIsExcludes = Boolean.TRUE.equals(policyResource.getIsExcludes()); // could be null
                        if (policyResourceIsExcludes && !excludesSupported) {
                            ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_EXCLUDES_NOT_SUPPORTED;
                            failures.add(
                                    new ValidationFailureDetailsBuilder().field("isExcludes").subField(resourceName)
                                            .isSemanticallyIncorrect().becauseOf(error.getMessage(resourceName))
                                            .errorCode(error.getErrorCode()).build());
                            valid = false;
                        }
                        if (policyResourceIsExcludes && !isAdmin) {
                            ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_EXCLUDES_REQUIRES_ADMIN;
                            failures.add(new ValidationFailureDetailsBuilder().field("isExcludes")
                                    .subField("isAdmin").isSemanticallyIncorrect().becauseOf(error.getMessage())
                                    .errorCode(error.getErrorCode()).build());
                            valid = false;
                        }
                        boolean recursiveSupported = Boolean.TRUE.equals(resourceDef.getRecursiveSupported());
                        boolean policyIsRecursive = Boolean.TRUE.equals(policyResource.getIsRecursive());
                        if (policyIsRecursive && !recursiveSupported) {
                            ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_RECURSIVE_NOT_SUPPORTED;
                            failures.add(new ValidationFailureDetailsBuilder().field("isRecursive")
                                    .subField(resourceName).isSemanticallyIncorrect()
                                    .becauseOf(error.getMessage(resourceName)).errorCode(error.getErrorCode())
                                    .build());
                            valid = false;
                        }
                    }
                }
            }
        }

        if (LOG.isDebugEnabled()) {
            LOG.debug(String.format("<== RangerPolicyValidator.isValidResourceFlags(%s, %s, %s, %s, %s, %s): %s",
                    inputPolicyResources, failures, resourceDefs, serviceDefName, policyName, isAdmin, valid));
        }
        return valid;
    }

    boolean isValidResourceValues(Map<String, RangerPolicyResource> resourceMap,
            List<ValidationFailureDetails> failures, RangerServiceDef serviceDef) {
        if (LOG.isDebugEnabled()) {
            LOG.debug(String.format("==> RangerPolicyValidator.isValidResourceValues(%s, %s, %s)", resourceMap,
                    failures, serviceDef));
        }

        boolean valid = true;
        Map<String, String> validationRegExMap = getValidationRegExes(serviceDef);
        for (Map.Entry<String, RangerPolicyResource> entry : resourceMap.entrySet()) {
            String name = entry.getKey();
            RangerPolicyResource policyResource = entry.getValue();
            if (policyResource != null) {
                if (CollectionUtils.isNotEmpty(policyResource.getValues())) {
                    Set<String> resources = new HashSet<String>(policyResource.getValues());
                    for (String aValue : resources) {
                        if (StringUtils.isBlank(aValue)) {
                            policyResource.getValues().remove(aValue);
                        }
                    }
                }

                if (CollectionUtils.isEmpty(policyResource.getValues())) {
                    ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_MISSING_RESOURCE_LIST;
                    if (LOG.isDebugEnabled()) {
                        LOG.debug(String.format(
                                "Resource list was empty or contains null: value[%s], resource-name[%s], service-def-name[%s]",
                                policyResource.getValues(), name, serviceDef.getName()));
                    }
                    failures.add(new ValidationFailureDetailsBuilder().field("resource-values").subField(name)
                            .isMissing().becauseOf(error.getMessage(name)).errorCode(error.getErrorCode()).build());
                    valid = false;
                }

                if (validationRegExMap.containsKey(name)
                        && CollectionUtils.isNotEmpty(policyResource.getValues())) {
                    String regEx = validationRegExMap.get(name);
                    for (String aValue : policyResource.getValues()) {
                        if (!aValue.matches(regEx)) {
                            if (LOG.isDebugEnabled()) {
                                LOG.debug(String.format(
                                        "Resource failed regex check: value[%s], resource-name[%s], regEx[%s], service-def-name[%s]",
                                        aValue, name, regEx, serviceDef.getName()));
                            }
                            ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_INVALID_RESOURCE_VALUE_REGEX;
                            failures.add(
                                    new ValidationFailureDetailsBuilder().field("resource-values").subField(name)
                                            .isSemanticallyIncorrect().becauseOf(error.getMessage(aValue, name))
                                            .errorCode(error.getErrorCode()).build());
                            valid = false;
                        }
                    }
                }
            }
        }

        if (LOG.isDebugEnabled()) {
            LOG.debug(String.format("<== RangerPolicyValidator.isValidResourceValues(%s, %s, %s): %s", resourceMap,
                    failures, serviceDef, valid));
        }
        return valid;
    }

    boolean isValidPolicyItems(List<RangerPolicyItem> policyItems, List<ValidationFailureDetails> failures,
            RangerServiceDef serviceDef) {
        if (LOG.isDebugEnabled()) {
            LOG.debug(String.format("==> RangerPolicyValidator.isValid(%s, %s, %s)", policyItems, failures,
                    serviceDef));
        }

        boolean valid = true;
        if (CollectionUtils.isEmpty(policyItems)) {
            LOG.debug("policy items collection was null/empty");
        } else {
            for (RangerPolicyItem policyItem : policyItems) {
                if (policyItem == null) {
                    ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_NULL_POLICY_ITEM;
                    failures.add(new ValidationFailureDetailsBuilder().field("policy item").isMissing()
                            .becauseOf(error.getMessage()).errorCode(error.getErrorCode()).build());
                    valid = false;
                } else {
                    // we want to go through all elements even though one may be bad so all failures are captured
                    valid = isValidPolicyItem(policyItem, failures, serviceDef) && valid;
                }
            }
        }

        if (LOG.isDebugEnabled()) {
            LOG.debug(String.format("<== RangerPolicyValidator.isValid(%s, %s, %s): %s", policyItems, failures,
                    serviceDef, valid));
        }
        return valid;
    }

    boolean isValidPolicyItem(RangerPolicyItem policyItem, List<ValidationFailureDetails> failures,
            RangerServiceDef serviceDef) {
        if (LOG.isDebugEnabled()) {
            LOG.debug(String.format("==> RangerPolicyValidator.isValid(%s, %s, %s)", policyItem, failures,
                    serviceDef));
        }

        boolean valid = true;
        if (policyItem == null) {
            LOG.debug("policy item was null!");
        } else {
            // access items collection can't be empty (unless delegated admin is true) and should be otherwise valid
            if (CollectionUtils.isEmpty(policyItem.getAccesses())) {
                if (!Boolean.TRUE.equals(policyItem.getDelegateAdmin())) {
                    ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_MISSING_FIELD;
                    failures.add(new ValidationFailureDetailsBuilder().field("policy item accesses").isMissing()
                            .becauseOf(error.getMessage("policy item accesses")).errorCode(error.getErrorCode())
                            .build());
                    valid = false;
                } else {
                    LOG.debug("policy item collection was null but delegated admin is true. Ok");
                }
            } else {
                valid = isValidItemAccesses(policyItem.getAccesses(), failures, serviceDef) && valid;
            }
            // both users and user-groups collections can't be empty
            if (CollectionUtils.isEmpty(policyItem.getUsers()) && CollectionUtils.isEmpty(policyItem.getGroups())) {
                ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_MISSING_USER_AND_GROUPS;
                failures.add(new ValidationFailureDetailsBuilder().field("policy item users/user-groups")
                        .isMissing().becauseOf(error.getMessage()).errorCode(error.getErrorCode()).build());
                valid = false;
            }
        }

        if (LOG.isDebugEnabled()) {
            LOG.debug(String.format("<== RangerPolicyValidator.isValid(%s, %s, %s): %s", policyItem, failures,
                    serviceDef, valid));
        }
        return valid;
    }

    boolean isValidItemAccesses(List<RangerPolicyItemAccess> accesses, List<ValidationFailureDetails> failures,
            RangerServiceDef serviceDef) {
        if (LOG.isDebugEnabled()) {
            LOG.debug(
                    String.format("==> RangerPolicyValidator.isValid(%s, %s, %s)", accesses, failures, serviceDef));
        }

        boolean valid = true;
        if (CollectionUtils.isEmpty(accesses)) {
            LOG.debug("policy item accesses collection was null/empty!");
        } else {
            Set<String> accessTypes = getAccessTypes(serviceDef);
            for (RangerPolicyItemAccess access : accesses) {
                if (access == null) {
                    ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_NULL_POLICY_ITEM_ACCESS;
                    failures.add(new ValidationFailureDetailsBuilder().field("policy item access").isMissing()
                            .becauseOf(error.getMessage()).errorCode(error.getErrorCode()).build());
                    valid = false;
                } else {
                    // we want to go through all elements even though one may be bad so all failures are captured
                    valid = isValidPolicyItemAccess(access, failures, accessTypes) && valid;
                }
            }
        }

        if (LOG.isDebugEnabled()) {
            LOG.debug(String.format("<== RangerPolicyValidator.isValid(%s, %s, %s): %b", accesses, failures,
                    serviceDef, valid));
        }
        return valid;
    }

    boolean isValidPolicyItemAccess(RangerPolicyItemAccess access, List<ValidationFailureDetails> failures,
            Set<String> accessTypes) {
        if (LOG.isDebugEnabled()) {
            LOG.debug(String.format("==> RangerPolicyValidator.isValidPolicyItemAccess(%s, %s, %s)", access,
                    failures, accessTypes));
        }

        boolean valid = true;
        if (CollectionUtils.isEmpty(accessTypes)) { // caller should firewall this argument!
            LOG.debug("isValidPolicyItemAccess: accessTypes was null!");
        } else if (access == null) {
            LOG.debug("isValidPolicyItemAccess: policy item access was null!");
        } else {
            String accessType = access.getType();
            if (StringUtils.isBlank(accessType)) {
                ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_MISSING_FIELD;
                failures.add(new ValidationFailureDetailsBuilder().field("policy item access type").isMissing()
                        .becauseOf(error.getMessage("policy item access type")).errorCode(error.getErrorCode())
                        .build());
                valid = false;
            } else if (!accessTypes.contains(accessType.toLowerCase())) {
                ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_POLICY_ITEM_ACCESS_TYPE_INVALID;
                failures.add(new ValidationFailureDetailsBuilder().field("policy item access type")
                        .isSemanticallyIncorrect().becauseOf(error.getMessage(accessType, accessTypes))
                        .errorCode(error.getErrorCode()).build());
                valid = false;
            }
            Boolean isAllowed = access.getIsAllowed();
            // it can be null (which is treated as allowed) but not false
            if (isAllowed != null && isAllowed == false) {
                ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_POLICY_ITEM_ACCESS_TYPE_DENY;
                failures.add(new ValidationFailureDetailsBuilder().field("policy item access type allowed")
                        .isSemanticallyIncorrect().becauseOf(error.getMessage()).errorCode(error.getErrorCode())
                        .build());
                valid = false;
            }
        }

        if (LOG.isDebugEnabled()) {
            LOG.debug(String.format("<== RangerPolicyValidator.isValidPolicyItemAccess(%s, %s, %s): %s", access,
                    failures, accessTypes, valid));
        }
        return valid;
    }
}