Java tutorial
/* * 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; } }