diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/errors/ValidationErrorCode.java b/agents-common/src/main/java/org/apache/ranger/plugin/errors/ValidationErrorCode.java index 83cae22d89..976dc79446 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/errors/ValidationErrorCode.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/errors/ValidationErrorCode.java @@ -67,6 +67,13 @@ public enum ValidationErrorCode { SERVICE_DEF_VALIDATION_ERR_INVALID_SERVICE_RESOURCE_LEVELS(2021, "Resource-def levels are not in increasing order in an hierarchy"), SERVICE_DEF_VALIDATION_ERR_NOT_LOWERCASE_NAME(2022, "{0}:[{1}] Invalid resource name. Resource name should consist of only lowercase, hyphen or underscore characters"), SERVICE_DEF_VALIDATION_ERR_INVALID_MANADORY_VALUE_FOR_SERVICE_RESOURCE(2023, "{0} cannot be mandatory because {1}(parent) is not mandatory"), + SERVICE_DEF_VALIDATION_ERR_CONSTRAINT_UNKNOWN_RESOURCE(2026, "resource[{0}] of constraint def[{1}] is not defined on the service def"), + SERVICE_DEF_VALIDATION_ERR_CONSTRAINT_MISSING_ANCESTOR(2027, "constraint def[{0}] lists resource[{1}] without ancestor[{2}]"), + SERVICE_DEF_VALIDATION_ERR_CONSTRAINT_UNKNOWN_ACCESS_TYPE(2028, "access type[{0}] of constraint def[{1}] is not defined on the service def"), + SERVICE_DEF_VALIDATION_ERR_CONSTRAINT_PARAM_MISSING_TYPE(2029, "type of constraint def[{0}] parameter[{1}] was null/empty"), + SERVICE_DEF_VALIDATION_ERR_CONSTRAINT_PARAM_INVALID_TYPE(2030, "type[{0}] of constraint def[{1}] parameter[{2}] is not among valid types: {3}"), + SERVICE_DEF_VALIDATION_ERR_CONSTRAINT_PARAM_UNKNOWN_ENUM(2031, "subtype[{0}] of constraint def[{1}] parameter[{2}] was not among defined enums[{3}]"), + SERVICE_DEF_VALIDATION_ERR_CONSTRAINT_PARAM_SUBTYPE_NOT_EMPTY(2032, "subtype[{0}] of constraint def[{1}] parameter[{2}] must be empty when type is [{3}]"), // POLICY VALIDATION POLICY_VALIDATION_ERR_UNSUPPORTED_ACTION(3001, "Internal error: method signature isValid(Long) is only supported for DELETE"), diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefValidator.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefValidator.java index 1f0c99d29f..0a40b7e9d1 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefValidator.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefValidator.java @@ -27,6 +27,8 @@ import org.apache.ranger.plugin.model.RangerPolicy; import org.apache.ranger.plugin.model.RangerServiceDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerAccessTypeDef; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerConstraintDef; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerConstraintParamDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerDataMaskTypeDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerEnumDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerEnumElementDef; @@ -50,7 +52,8 @@ public class RangerServiceDefValidator extends RangerValidator { private static final Logger LOG = LoggerFactory.getLogger(RangerServiceDefValidator.class); - private static final Set VALID_CONFIG_TYPES = ImmutableSet.of("bool", "enum", "int", "string", "password", "path"); + private static final Set VALID_CONFIG_TYPES = ImmutableSet.of("bool", "enum", "int", "string", "password", "path"); + private static final Set VALID_CONSTRAINT_PARAM_TYPES = ImmutableSet.of("string", "int", "bool", "enum", "user", "group", "role"); public RangerServiceDefValidator(ServiceStore store) { super(store); @@ -220,6 +223,7 @@ boolean isValid(final RangerServiceDef serviceDef, final Action action, final Li } valid = isValidPolicyConditions(serviceDef.getId(), serviceDef.getPolicyConditions(), failures, action) && valid; + valid = isValidConstraintDefs(serviceDef, failures) && valid; valid = isValidDataMaskTypes(serviceDef.getId(), serviceDef.getDataMaskDef().getMaskTypes(), failures, action) && valid; } @@ -532,6 +536,270 @@ boolean isValidPolicyConditions(Long serviceDefId, List failures) { + LOG.debug("==> RangerServiceDefValidator.isValidConstraintDefs({}, {})", serviceDef, failures); + + boolean valid = true; + + if (serviceDef == null || CollectionUtils.isEmpty(serviceDef.getConstraintDefs())) { + LOG.debug("constraint def collection was null/empty. Ok."); + } else { + Map resourceDefs = resourceDefMap(serviceDef.getResources()); + Set accessTypes = accessTypeNames(serviceDef.getAccessTypes()); + Map enumDefs = getEnumDefMap(serviceDef.getEnums()); + Set ids = new HashSet<>(); + Set names = new HashSet<>(); + + for (RangerConstraintDef constraintDef : serviceDef.getConstraintDefs()) { + if (constraintDef == null) { + failures.add(new ValidationFailureDetailsBuilder() + .field("constraint def") + .isMissing() + .becauseOf("constraint def was null") + .build()); + + valid = false; + + continue; + } + + String constraintName = constraintDef.getName(); + + valid = isUnique(constraintDef.getItemId(), ids, "constraint def itemId", "constraint defs", failures) && valid; + valid = isUnique(constraintName, names, "constraint def name", "constraint defs", failures) && valid; + valid = isValidConstraintResources(constraintDef, resourceDefs, failures) && valid; + valid = isValidConstraintAccessTypes(constraintDef, accessTypes, failures) && valid; + valid = isValidConstraintParams(constraintDef, enumDefs, failures) && valid; + } + } + + LOG.debug("<== RangerServiceDefValidator.isValidConstraintDefs({}, {}): {}", serviceDef, failures, valid); + + return valid; + } + + private boolean isValidConstraintResources(RangerConstraintDef constraintDef, Map resourceDefs, List failures) { + boolean valid = true; + List resources = constraintDef.getResources(); + String constraintName = constraintDef.getName(); + + if (CollectionUtils.isEmpty(resources)) { + return true; + } + + Set listed = new HashSet<>(); + + for (String resourceName : resources) { + if (StringUtils.isNotBlank(resourceName)) { + listed.add(resourceName); + } + } + + for (String resourceName : resources) { + if (StringUtils.isBlank(resourceName) || !resourceDefs.containsKey(resourceName)) { + ValidationErrorCode error = ValidationErrorCode.SERVICE_DEF_VALIDATION_ERR_CONSTRAINT_UNKNOWN_RESOURCE; + + failures.add(new ValidationFailureDetailsBuilder() + .field("constraint def resource") + .subField(StringUtils.isBlank(resourceName) ? constraintName : resourceName) + .isSemanticallyIncorrect() + .errorCode(error.getErrorCode()) + .becauseOf(error.getMessage(resourceName, constraintName)) + .build()); + + valid = false; + + continue; + } + + if (!hasConstraintAncestors(resourceName, listed, resourceDefs, constraintName, failures)) { + valid = false; + } + } + + return valid; + } + + private boolean hasConstraintAncestors(String resourceName, Set listed, Map resourceDefs, String constraintName, List failures) { + Set seen = new HashSet<>(); + RangerResourceDef resourceDef = resourceDefs.get(resourceName); + String parent = resourceDef == null ? null : resourceDef.getParent(); + + seen.add(resourceName); + + while (StringUtils.isNotBlank(parent)) { + if (!seen.add(parent)) { + break; + } + + if (!listed.contains(parent)) { + ValidationErrorCode error = ValidationErrorCode.SERVICE_DEF_VALIDATION_ERR_CONSTRAINT_MISSING_ANCESTOR; + + failures.add(new ValidationFailureDetailsBuilder() + .field("constraint def ancestor") + .subField(resourceName) + .isSemanticallyIncorrect() + .errorCode(error.getErrorCode()) + .becauseOf(error.getMessage(constraintName, resourceName, parent)) + .build()); + + return false; + } + + RangerResourceDef parentDef = resourceDefs.get(parent); + + parent = parentDef == null ? null : parentDef.getParent(); + } + + return true; + } + + private boolean isValidConstraintAccessTypes(RangerConstraintDef constraintDef, Set accessTypes, List failures) { + boolean valid = true; + List constraintAccessTypes = constraintDef.getAccessTypes(); + + if (CollectionUtils.isEmpty(constraintAccessTypes)) { + return true; + } + + String constraintName = constraintDef.getName(); + + for (String accessType : constraintAccessTypes) { + if (StringUtils.isBlank(accessType) || !accessTypes.contains(accessType)) { + ValidationErrorCode error = ValidationErrorCode.SERVICE_DEF_VALIDATION_ERR_CONSTRAINT_UNKNOWN_ACCESS_TYPE; + + failures.add(new ValidationFailureDetailsBuilder() + .field("constraint def access type") + .subField(StringUtils.isBlank(accessType) ? constraintName : accessType) + .isSemanticallyIncorrect() + .errorCode(error.getErrorCode()) + .becauseOf(error.getMessage(accessType, constraintName)) + .build()); + + valid = false; + } + } + + return valid; + } + + private boolean isValidConstraintParams(RangerConstraintDef constraintDef, Map enumDefs, List failures) { + boolean valid = true; + List parameters = constraintDef.getParameters(); + String constraintName = constraintDef.getName(); + + if (CollectionUtils.isEmpty(parameters)) { + return true; + } + + Set paramNames = new HashSet<>(); + + for (RangerConstraintParamDef parameter : parameters) { + if (parameter == null) { + failures.add(new ValidationFailureDetailsBuilder() + .field("constraint def parameter") + .subField(constraintName) + .isMissing() + .becauseOf("constraint def parameter was null") + .build()); + + valid = false; + + continue; + } + + String paramName = parameter.getName(); + + valid = isUnique(paramName, constraintName, paramNames, "constraint def parameter name", "constraint def parameters", failures) && valid; + + String type = parameter.getType(); + + if (StringUtils.isBlank(type)) { + ValidationErrorCode error = ValidationErrorCode.SERVICE_DEF_VALIDATION_ERR_CONSTRAINT_PARAM_MISSING_TYPE; + + failures.add(new ValidationFailureDetailsBuilder() + .field("constraint def parameter type") + .subField(paramName) + .isMissing() + .errorCode(error.getErrorCode()) + .becauseOf(error.getMessage(constraintName, paramName)) + .build()); + + valid = false; + } else if (!VALID_CONSTRAINT_PARAM_TYPES.contains(type)) { + ValidationErrorCode error = ValidationErrorCode.SERVICE_DEF_VALIDATION_ERR_CONSTRAINT_PARAM_INVALID_TYPE; + + failures.add(new ValidationFailureDetailsBuilder() + .field("constraint def parameter type") + .subField(paramName) + .isSemanticallyIncorrect() + .errorCode(error.getErrorCode()) + .becauseOf(error.getMessage(type, constraintName, paramName, VALID_CONSTRAINT_PARAM_TYPES)) + .build()); + + valid = false; + } else if ("enum".equals(type)) { + String subType = parameter.getSubType(); + + if (StringUtils.isBlank(subType) || !enumDefs.containsKey(subType)) { + ValidationErrorCode error = ValidationErrorCode.SERVICE_DEF_VALIDATION_ERR_CONSTRAINT_PARAM_UNKNOWN_ENUM; + + failures.add(new ValidationFailureDetailsBuilder() + .field("constraint def parameter subtype") + .subField(paramName) + .isSemanticallyIncorrect() + .errorCode(error.getErrorCode()) + .becauseOf(error.getMessage(subType, constraintName, paramName, enumDefs.keySet())) + .build()); + + valid = false; + } + } else if (StringUtils.isNotBlank(parameter.getSubType())) { + ValidationErrorCode error = ValidationErrorCode.SERVICE_DEF_VALIDATION_ERR_CONSTRAINT_PARAM_SUBTYPE_NOT_EMPTY; + + failures.add(new ValidationFailureDetailsBuilder() + .field("constraint def parameter subtype") + .subField(paramName) + .isSemanticallyIncorrect() + .errorCode(error.getErrorCode()) + .becauseOf(error.getMessage(parameter.getSubType(), constraintName, paramName, type)) + .build()); + + valid = false; + } + } + + return valid; + } + + private Map resourceDefMap(List resources) { + Map resourceDefs = new HashMap<>(); + + if (resources != null) { + for (RangerResourceDef resourceDef : resources) { + if (resourceDef != null && StringUtils.isNotBlank(resourceDef.getName())) { + resourceDefs.put(resourceDef.getName(), resourceDef); + } + } + } + + return resourceDefs; + } + + private Set accessTypeNames(List accessTypeDefs) { + Set accessTypes = new HashSet<>(); + + if (accessTypeDefs != null) { + for (RangerAccessTypeDef accessTypeDef : accessTypeDefs) { + if (accessTypeDef != null && StringUtils.isNotBlank(accessTypeDef.getName())) { + accessTypes.add(accessTypeDef.getName()); + } + } + } + + return accessTypes; + } + boolean isValidConfigs(List configs, List enumDefs, List failures) { LOG.debug("==> RangerServiceDefValidator.isValidConfigs({}, {}, {})", configs, enumDefs, failures); diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceDefValidator.java b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceDefValidator.java index a178f3174d..10098b8530 100644 --- a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceDefValidator.java +++ b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceDefValidator.java @@ -19,8 +19,11 @@ package org.apache.ranger.plugin.model.validation; +import org.apache.ranger.plugin.errors.ValidationErrorCode; import org.apache.ranger.plugin.model.RangerServiceDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerAccessTypeDef; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerConstraintDef; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerConstraintParamDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerEnumDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerEnumElementDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerPolicyConditionDef; @@ -32,11 +35,14 @@ import org.junit.jupiter.api.Test; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.Date; import java.util.List; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -662,4 +668,158 @@ public final void test_isValidConfigs_enumTypeBranches() { assertFalse(validator.isValidConfigs(configs, enumDefs, failures)); utils.checkFailureForSemanticError(failures, "config def subtype", "enumCfg"); } + + @Test + public final void test_isValidConstraintDefs() { + assertTrue(validator.isValidConstraintDefs(new RangerServiceDef(), failures)); + assertTrue(failures.isEmpty()); + + RangerServiceDef def = constraintServiceDef(); + RangerConstraintDef valid = constraintDef(1L, "requireApproval", Arrays.asList("database", "udf"), Arrays.asList("execute")); + List validParams = new ArrayList<>(); + validParams.add(param("approver", "user", null, true)); + validParams.add(param("approverGroup", "group", null, false)); + validParams.add(param("approverRole", "role", null, false)); + validParams.add(param("mode", "enum", "authType", false)); + valid.setParameters(validParams); + def.setConstraintDefs(Collections.singletonList(valid)); + assertTrue(validator.isValidConstraintDefs(def, failures)); + assertTrue(failures.isEmpty()); + + RangerConstraintDef emptyResources = constraintDef(1L, "requireApproval", new ArrayList<>(), Arrays.asList("execute")); + emptyResources.setParameters(Collections.singletonList(param("approver", "user", null, true))); + def.setConstraintDefs(Collections.singletonList(emptyResources)); + assertTrue(validator.isValidConstraintDefs(def, failures)); + assertTrue(failures.isEmpty()); + + assertFalse(validator.isValidConfigType("user", "approver", failures)); + utils.checkFailureForSemanticError(failures, "config def type", "approver"); + failures.clear(); + assertFalse(validator.isValidConfigType("group", "approverGroup", failures)); + utils.checkFailureForSemanticError(failures, "config def type", "approverGroup"); + failures.clear(); + assertFalse(validator.isValidConfigType("role", "approverRole", failures)); + utils.checkFailureForSemanticError(failures, "config def type", "approverRole"); + } + + @Test + public final void test_isValid_rejectsInvalidConstraintDef() { + RangerServiceDef def = constraintServiceDef(); + def.setName("agent"); + def.setDisplayName("Agent"); + def.setConstraintDefs(Collections.singletonList(constraintDef(1L, "requireApproval", Arrays.asList("missing"), Arrays.asList("select")))); + + assertFalse(validator.isValid(def, Action.CREATE, failures)); + assertHasErrorCode(ValidationErrorCode.SERVICE_DEF_VALIDATION_ERR_CONSTRAINT_UNKNOWN_RESOURCE.getErrorCode()); + } + + @Test + public final void test_isValidConstraintDefs_failures() { + RangerServiceDef def = constraintServiceDef(); + List constraintDefs = new ArrayList<>(); + constraintDefs.add(null); + + RangerConstraintDef missingAncestor = constraintDef(1L, "maxRows", Arrays.asList("udf"), Arrays.asList("select")); + missingAncestor.setParameters(Collections.singletonList(param("rowCount", "int", null, true))); + constraintDefs.add(missingAncestor); + + RangerConstraintDef invalid = constraintDef(1L, " ", Arrays.asList("missing", ""), Arrays.asList("drop", " ")); + List params = new ArrayList<>(); + params.add(param("rowCount", "password", null, true)); + params.add(param("rowCount", "int", null, false)); + params.add(null); + params.add(param("mode", "enum", "no-such-enum", false)); + params.add(param("kind", "enum", " ", false)); + params.add(param("approver", "user", "authType", true)); + params.add(param("limit", null, null, false)); + invalid.setParameters(params); + constraintDefs.add(invalid); + + RangerConstraintDef duplicateName = constraintDef(2L, "maxRows", Arrays.asList("database", "table"), Arrays.asList("select")); + duplicateName.setParameters(Collections.singletonList(param("rowCount", "int", null, true))); + constraintDefs.add(duplicateName); + + def.setConstraintDefs(constraintDefs); + assertFalse(validator.isValidConstraintDefs(def, failures)); + utils.checkFailureForMissingValue(failures, "constraint def"); + utils.checkFailureForMissingValue(failures, "constraint def name"); + utils.checkFailureForSemanticError(failures, "constraint def itemId", "1"); + utils.checkFailureForSemanticError(failures, "constraint def name", "maxRows"); + utils.checkFailureForSemanticError(failures, "constraint def ancestor", "udf"); + utils.checkFailureForSemanticError(failures, "constraint def resource", "missing"); + utils.checkFailureForSemanticError(failures, "constraint def access type", "drop"); + utils.checkFailureForSemanticError(failures, "constraint def parameter type", "rowCount"); + utils.checkFailureForSemanticError(failures, "constraint def parameter name", "rowCount"); + utils.checkFailureForMissingValue(failures, "constraint def parameter"); + utils.checkFailureForSemanticError(failures, "constraint def parameter subtype", "mode"); + utils.checkFailureForSemanticError(failures, "constraint def parameter subtype", "kind"); + utils.checkFailureForSemanticError(failures, "constraint def parameter subtype", "approver"); + utils.checkFailureForMissingValue(failures, "constraint def parameter type", "limit"); + assertHasErrorCode(ValidationErrorCode.SERVICE_DEF_VALIDATION_ERR_CONSTRAINT_UNKNOWN_RESOURCE.getErrorCode()); + assertHasErrorCode(ValidationErrorCode.SERVICE_DEF_VALIDATION_ERR_CONSTRAINT_MISSING_ANCESTOR.getErrorCode()); + assertHasErrorCode(ValidationErrorCode.SERVICE_DEF_VALIDATION_ERR_CONSTRAINT_UNKNOWN_ACCESS_TYPE.getErrorCode()); + assertHasErrorCode(ValidationErrorCode.SERVICE_DEF_VALIDATION_ERR_CONSTRAINT_PARAM_MISSING_TYPE.getErrorCode()); + assertHasErrorCode(ValidationErrorCode.SERVICE_DEF_VALIDATION_ERR_CONSTRAINT_PARAM_INVALID_TYPE.getErrorCode()); + assertHasErrorCode(ValidationErrorCode.SERVICE_DEF_VALIDATION_ERR_CONSTRAINT_PARAM_UNKNOWN_ENUM.getErrorCode()); + assertHasErrorCode(ValidationErrorCode.SERVICE_DEF_VALIDATION_ERR_CONSTRAINT_PARAM_SUBTYPE_NOT_EMPTY.getErrorCode()); + } + + private void assertHasErrorCode(int errorCode) { + for (ValidationFailureDetails failure : failures) { + if (failure.errorCode == errorCode) { + return; + } + } + + fail("expected error code " + errorCode + " in " + failures); + } + + private RangerServiceDef constraintServiceDef() { + RangerServiceDef def = new RangerServiceDef(); + def.setResources(utils.createResourceDefs(new Object[][] { + {"database", Boolean.FALSE, Boolean.FALSE, Boolean.TRUE, null, null, 10}, + {"table", Boolean.FALSE, Boolean.FALSE, Boolean.FALSE, null, "database", 20}, + {"udf", Boolean.FALSE, Boolean.FALSE, Boolean.FALSE, null, "database", 20}, + {"column", Boolean.FALSE, Boolean.FALSE, Boolean.FALSE, null, "table", 30} + })); + + RangerAccessTypeDef select = new RangerAccessTypeDef(); + select.setName("select"); + RangerAccessTypeDef execute = new RangerAccessTypeDef(); + execute.setName("execute"); + List accessTypes = new ArrayList<>(); + accessTypes.add(select); + accessTypes.add(execute); + def.setAccessTypes(accessTypes); + + RangerEnumDef enumDef = new RangerEnumDef(); + enumDef.setName("authType"); + List enums = new ArrayList<>(); + enums.add(enumDef); + def.setEnums(enums); + + return def; + } + + private RangerConstraintDef constraintDef(Long itemId, String name, List resources, List accessTypes) { + RangerConstraintDef constraintDef = new RangerConstraintDef(); + + constraintDef.setItemId(itemId); + constraintDef.setName(name); + constraintDef.setResources(resources); + constraintDef.setAccessTypes(accessTypes); + + return constraintDef; + } + + private RangerConstraintParamDef param(String name, String type, String subType, boolean mandatory) { + RangerConstraintParamDef paramDef = new RangerConstraintParamDef(); + + paramDef.setName(name); + paramDef.setType(type); + paramDef.setSubType(subType); + paramDef.setMandatory(mandatory); + + return paramDef; + } } diff --git a/dev-support/checkstyle-suppressions.xml b/dev-support/checkstyle-suppressions.xml index f20f609e94..b82da722ad 100644 --- a/dev-support/checkstyle-suppressions.xml +++ b/dev-support/checkstyle-suppressions.xml @@ -93,4 +93,5 @@ + diff --git a/security-admin/db/mysql/optimized/current/ranger_core_db_mysql.sql b/security-admin/db/mysql/optimized/current/ranger_core_db_mysql.sql index 8ecc59f4aa..0818c85903 100644 --- a/security-admin/db/mysql/optimized/current/ranger_core_db_mysql.sql +++ b/security-admin/db/mysql/optimized/current/ranger_core_db_mysql.sql @@ -533,6 +533,7 @@ CREATE TABLE `x_service_def` ( `rb_key_description` varchar(1024) DEFAULT NULL, `is_enabled` tinyint DEFAULT 1, `def_options` VARCHAR(1024) DEFAULT NULL NULL, +`def_text` MEDIUMTEXT NULL DEFAULT NULL, primary key (`id`), KEY `x_service_def_added_by_id` (`added_by_id`), KEY `x_service_def_upd_by_id` (`upd_by_id`), @@ -1975,6 +1976,7 @@ INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('076',UTC_TIMESTAMP(),'Ranger 3.0.0',UTC_TIMESTAMP(),'localhost','Y'); INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('077',UTC_TIMESTAMP(),'Ranger 3.0.0',UTC_TIMESTAMP(),'localhost','Y'); INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('078',UTC_TIMESTAMP(),'Ranger 3.0.0',UTC_TIMESTAMP(),'localhost','Y'); +INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('079',UTC_TIMESTAMP(),'Ranger 3.0.0',UTC_TIMESTAMP(),'localhost','Y'); INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('DB_PATCHES',UTC_TIMESTAMP(),'Ranger 1.0.0',UTC_TIMESTAMP(),'localhost','Y'); INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('J10001',UTC_TIMESTAMP(),'Ranger 1.0.0',UTC_TIMESTAMP(),'localhost','Y'); @@ -2029,4 +2031,5 @@ INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('J10064',UTC_TIMESTAMP(),'Ranger 3.0.0',UTC_TIMESTAMP(),'localhost','Y'); INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('J10065',UTC_TIMESTAMP(),'Ranger 3.0.0',UTC_TIMESTAMP(),'localhost','Y'); INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('J10066',UTC_TIMESTAMP(),'Ranger 3.0.0',UTC_TIMESTAMP(),'localhost','Y'); +INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('J10067',UTC_TIMESTAMP(),'Ranger 3.0.0',UTC_TIMESTAMP(),'localhost','Y'); INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('JAVA_PATCHES',UTC_TIMESTAMP(),'Ranger 1.0.0',UTC_TIMESTAMP(),'localhost','Y'); diff --git a/security-admin/db/mysql/patches/079-add-def_text-to-x_service_def.sql b/security-admin/db/mysql/patches/079-add-def_text-to-x_service_def.sql new file mode 100644 index 0000000000..7acdb89053 --- /dev/null +++ b/security-admin/db/mysql/patches/079-add-def_text-to-x_service_def.sql @@ -0,0 +1,30 @@ +-- 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. + +drop procedure if exists add_def_text_in_x_service_def; + +delimiter ;; +create procedure add_def_text_in_x_service_def() begin + +if not exists (select * from information_schema.columns where table_schema=database() and table_name = 'x_service_def' and column_name='def_text') then + ALTER TABLE x_service_def ADD def_text MEDIUMTEXT NULL DEFAULT NULL; +end if; + +end;; + +delimiter ; +call add_def_text_in_x_service_def(); + +drop procedure if exists add_def_text_in_x_service_def; diff --git a/security-admin/db/oracle/optimized/current/ranger_core_db_oracle.sql b/security-admin/db/oracle/optimized/current/ranger_core_db_oracle.sql index ddca10bb5d..ec8dfff613 100644 --- a/security-admin/db/oracle/optimized/current/ranger_core_db_oracle.sql +++ b/security-admin/db/oracle/optimized/current/ranger_core_db_oracle.sql @@ -662,6 +662,7 @@ rb_key_label VARCHAR(1024) DEFAULT NULL NULL, rb_key_description VARCHAR(1024) DEFAULT NULL NULL, is_enabled NUMBER(1) DEFAULT '1' NULL, def_options VARCHAR(1024) DEFAULT NULL NULL, +def_text CLOB DEFAULT NULL NULL, PRIMARY KEY (id), CONSTRAINT x_service_def_FK_added_by_id FOREIGN KEY (added_by_id) REFERENCES x_portal_user (id), CONSTRAINT x_service_def_FK_upd_by_id FOREIGN KEY (upd_by_id) REFERENCES x_portal_user (id) @@ -2164,6 +2165,7 @@ INSERT INTO x_db_version_h (id,version,inst_at,inst_by,updated_at,updated_by,act INSERT INTO x_db_version_h (id,version,inst_at,inst_by,updated_at,updated_by,active) VALUES (X_DB_VERSION_H_SEQ.nextval, '076',sys_extract_utc(systimestamp),'Ranger 3.0.0',sys_extract_utc(systimestamp),'localhost','Y'); INSERT INTO x_db_version_h (id,version,inst_at,inst_by,updated_at,updated_by,active) VALUES (X_DB_VERSION_H_SEQ.nextval, '077',sys_extract_utc(systimestamp),'Ranger 3.0.0',sys_extract_utc(systimestamp),'localhost','Y'); INSERT INTO x_db_version_h (id,version,inst_at,inst_by,updated_at,updated_by,active) VALUES (X_DB_VERSION_H_SEQ.nextval, '078',sys_extract_utc(systimestamp),'Ranger 3.0.0',sys_extract_utc(systimestamp),'localhost','Y'); +INSERT INTO x_db_version_h (id,version,inst_at,inst_by,updated_at,updated_by,active) VALUES (X_DB_VERSION_H_SEQ.nextval, '079',sys_extract_utc(systimestamp),'Ranger 3.0.0',sys_extract_utc(systimestamp),'localhost','Y'); INSERT INTO x_db_version_h (id,version,inst_at,inst_by,updated_at,updated_by,active) VALUES (X_DB_VERSION_H_SEQ.nextval, 'DB_PATCHES',sys_extract_utc(systimestamp),'Ranger 1.0.0',sys_extract_utc(systimestamp),'localhost','Y'); INSERT INTO x_user_module_perm (id,user_id,module_id,create_time,update_time,added_by_id,upd_by_id,is_allowed) VALUES (X_USER_MODULE_PERM_SEQ.nextval,getXportalUIdByLoginId('admin'),getModulesIdByName('Reports'),sys_extract_utc(systimestamp),sys_extract_utc(systimestamp),getXportalUIdByLoginId('admin'),getXportalUIdByLoginId('admin'),1); @@ -2253,5 +2255,6 @@ INSERT INTO x_db_version_h (id,version,inst_at,inst_by,updated_at,updated_by,act INSERT INTO x_db_version_h (id,version,inst_at,inst_by,updated_at,updated_by,active) VALUES (X_DB_VERSION_H_SEQ.nextval,'J10064',sys_extract_utc(systimestamp),'Ranger 3.0.0',sys_extract_utc(systimestamp),'localhost','Y'); INSERT INTO x_db_version_h (id,version,inst_at,inst_by,updated_at,updated_by,active) VALUES (X_DB_VERSION_H_SEQ.nextval,'J10065',sys_extract_utc(systimestamp),'Ranger 3.0.0',sys_extract_utc(systimestamp),'localhost','Y'); INSERT INTO x_db_version_h (id,version,inst_at,inst_by,updated_at,updated_by,active) VALUES (X_DB_VERSION_H_SEQ.nextval,'J10066',sys_extract_utc(systimestamp),'Ranger 3.0.0',sys_extract_utc(systimestamp),'localhost','Y'); +INSERT INTO x_db_version_h (id,version,inst_at,inst_by,updated_at,updated_by,active) VALUES (X_DB_VERSION_H_SEQ.nextval,'J10067',sys_extract_utc(systimestamp),'Ranger 3.0.0',sys_extract_utc(systimestamp),'localhost','Y'); INSERT INTO x_db_version_h (id,version,inst_at,inst_by,updated_at,updated_by,active) VALUES (X_DB_VERSION_H_SEQ.nextval,'JAVA_PATCHES',sys_extract_utc(systimestamp),'Ranger 1.0.0',sys_extract_utc(systimestamp),'localhost','Y'); commit; diff --git a/security-admin/db/oracle/patches/079-add-def_text-to-x_service_def.sql b/security-admin/db/oracle/patches/079-add-def_text-to-x_service_def.sql new file mode 100644 index 0000000000..70d084fcae --- /dev/null +++ b/security-admin/db/oracle/patches/079-add-def_text-to-x_service_def.sql @@ -0,0 +1,24 @@ +-- 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. + +DECLARE + v_count number:=0; +BEGIN + select count(*) into v_count from user_tab_cols where table_name='X_SERVICE_DEF' and column_name='DEF_TEXT'; + if (v_count = 0) then + execute immediate 'ALTER TABLE x_service_def ADD def_text CLOB DEFAULT NULL NULL'; + end if; + commit; +END;/ diff --git a/security-admin/db/postgres/optimized/current/ranger_core_db_postgres.sql b/security-admin/db/postgres/optimized/current/ranger_core_db_postgres.sql index 654d19aa73..019ab57820 100644 --- a/security-admin/db/postgres/optimized/current/ranger_core_db_postgres.sql +++ b/security-admin/db/postgres/optimized/current/ranger_core_db_postgres.sql @@ -549,6 +549,7 @@ rb_key_label VARCHAR(1024) DEFAULT NULL NULL, rb_key_description VARCHAR(1024) DEFAULT NULL NULL, is_enabled BOOLEAN DEFAULT '1' NULL, def_options VARCHAR(1024) DEFAULT NULL NULL, +def_text TEXT DEFAULT NULL NULL, PRIMARY KEY(id), CONSTRAINT x_service_def_FK_added_by_id FOREIGN KEY(added_by_id) REFERENCES x_portal_user(id), CONSTRAINT x_service_def_FK_upd_by_id FOREIGN KEY(upd_by_id) REFERENCES x_portal_user(id) @@ -2078,6 +2079,7 @@ INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('076',current_timestamp,'Ranger 3.0.0',current_timestamp,'localhost','Y'); INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('077',current_timestamp,'Ranger 3.0.0',current_timestamp,'localhost','Y'); INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('078',current_timestamp,'Ranger 3.0.0',current_timestamp,'localhost','Y'); +INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('079',current_timestamp,'Ranger 3.0.0',current_timestamp,'localhost','Y'); INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('DB_PATCHES',current_timestamp,'Ranger 1.0.0',current_timestamp,'localhost','Y'); INSERT INTO x_user_module_perm (user_id,module_id,create_time,update_time,added_by_id,upd_by_id,is_allowed) VALUES @@ -2186,6 +2188,7 @@ INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('J10064',current_timestamp,'Ranger 3.0.0',current_timestamp,'localhost','Y'); INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('J10065',current_timestamp,'Ranger 3.0.0',current_timestamp,'localhost','Y'); INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('J10066',current_timestamp,'Ranger 3.0.0',current_timestamp,'localhost','Y'); +INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('J10067',current_timestamp,'Ranger 3.0.0',current_timestamp,'localhost','Y'); INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('JAVA_PATCHES',current_timestamp,'Ranger 1.0.0',current_timestamp,'localhost','Y'); DROP VIEW IF EXISTS vx_principal; diff --git a/security-admin/db/postgres/patches/079-add-def_text-to-x_service_def.sql b/security-admin/db/postgres/patches/079-add-def_text-to-x_service_def.sql new file mode 100644 index 0000000000..30e74e03e1 --- /dev/null +++ b/security-admin/db/postgres/patches/079-add-def_text-to-x_service_def.sql @@ -0,0 +1,32 @@ +-- 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. + +select 'delimiter start'; +CREATE OR REPLACE FUNCTION add_def_text_in_x_service_def() +RETURNS void AS $$ +DECLARE + v_column_exists integer := 0; +BEGIN + select count(*) into v_column_exists from pg_attribute where attrelid in(select oid from pg_class where relname='x_service_def') and attname='def_text'; + IF v_column_exists = 0 THEN + ALTER TABLE x_service_def ADD COLUMN def_text TEXT DEFAULT NULL NULL; + END IF; +END; +$$ LANGUAGE plpgsql; +select 'delimiter end'; + +select add_def_text_in_x_service_def(); +select 'delimiter end'; +commit; diff --git a/security-admin/db/sqlanywhere/optimized/current/ranger_core_db_sqlanywhere.sql b/security-admin/db/sqlanywhere/optimized/current/ranger_core_db_sqlanywhere.sql index 678fe343b9..cac26327a5 100644 --- a/security-admin/db/sqlanywhere/optimized/current/ranger_core_db_sqlanywhere.sql +++ b/security-admin/db/sqlanywhere/optimized/current/ranger_core_db_sqlanywhere.sql @@ -497,6 +497,7 @@ create table dbo.x_service_def( rb_key_description varchar(1024) DEFAULT NULL NULL, is_enabled tinyint DEFAULT 1 NULL, def_options varchar(1024) DEFAULT NULL NULL, + def_text text DEFAULT NULL NULL, CONSTRAINT x_service_def_PK_id PRIMARY KEY CLUSTERED(id) ) GO @@ -2256,6 +2257,7 @@ GO INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('077',CURRENT_TIMESTAMP,'Ranger 3.0.0',CURRENT_TIMESTAMP,'localhost','Y'); GO INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('078',CURRENT_TIMESTAMP,'Ranger 3.0.0',CURRENT_TIMESTAMP,'localhost','Y'); +INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('079',CURRENT_TIMESTAMP,'Ranger 3.0.0',CURRENT_TIMESTAMP,'localhost','Y'); GO INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('DB_PATCHES',CURRENT_TIMESTAMP,'Ranger 1.0.0',CURRENT_TIMESTAMP,'localhost','Y'); GO @@ -2420,6 +2422,7 @@ GO INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('J10065',CURRENT_TIMESTAMP,'Ranger 3.0.0',CURRENT_TIMESTAMP,'localhost','Y'); GO INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('J10066',CURRENT_TIMESTAMP,'Ranger 3.0.0',CURRENT_TIMESTAMP,'localhost','Y'); +INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('J10067',CURRENT_TIMESTAMP,'Ranger 3.0.0',CURRENT_TIMESTAMP,'localhost','Y'); GO INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('JAVA_PATCHES',CURRENT_TIMESTAMP,'Ranger 1.0.0',CURRENT_TIMESTAMP,'localhost','Y'); GO diff --git a/security-admin/db/sqlanywhere/patches/079-add-def_text-to-x_service_def.sql b/security-admin/db/sqlanywhere/patches/079-add-def_text-to-x_service_def.sql new file mode 100644 index 0000000000..a50014bb56 --- /dev/null +++ b/security-admin/db/sqlanywhere/patches/079-add-def_text-to-x_service_def.sql @@ -0,0 +1,20 @@ +-- 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. + +IF NOT EXISTS(select * from SYS.SYSCOLUMNS where tname = 'x_service_def' and cname = 'def_text') THEN + ALTER TABLE dbo.x_service_def ADD def_text text DEFAULT NULL NULL; +END IF; +GO +exit diff --git a/security-admin/db/sqlserver/optimized/current/ranger_core_db_sqlserver.sql b/security-admin/db/sqlserver/optimized/current/ranger_core_db_sqlserver.sql index 68c864e74e..fef115ed7d 100644 --- a/security-admin/db/sqlserver/optimized/current/ranger_core_db_sqlserver.sql +++ b/security-admin/db/sqlserver/optimized/current/ranger_core_db_sqlserver.sql @@ -1497,6 +1497,7 @@ CREATE TABLE [dbo].[x_service_def]( [rb_key_description] [varchar](1024) DEFAULT NULL NULL, [is_enabled] [tinyint] DEFAULT 1 NULL, [def_options] [varchar](1024) DEFAULT NULL NULL, + [def_text] [nvarchar](max) DEFAULT NULL NULL, PRIMARY KEY CLUSTERED ( [id] ASC @@ -4538,6 +4539,7 @@ INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('076',CURRENT_TIMESTAMP,'Ranger 3.0.0',CURRENT_TIMESTAMP,'localhost','Y'); INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('077',CURRENT_TIMESTAMP,'Ranger 3.0.0',CURRENT_TIMESTAMP,'localhost','Y'); INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('078',CURRENT_TIMESTAMP,'Ranger 3.0.0',CURRENT_TIMESTAMP,'localhost','Y'); +INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('079',CURRENT_TIMESTAMP,'Ranger 3.0.0',CURRENT_TIMESTAMP,'localhost','Y'); INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('DB_PATCHES',CURRENT_TIMESTAMP,'Ranger 1.0.0',CURRENT_TIMESTAMP,'localhost','Y'); INSERT INTO x_user_module_perm (user_id,module_id,create_time,update_time,added_by_id,upd_by_id,is_allowed) VALUES (dbo.getXportalUIdByLoginId('admin'),dbo.getModulesIdByName('Reports'),CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,dbo.getXportalUIdByLoginId('admin'),dbo.getXportalUIdByLoginId('admin'),1); INSERT INTO x_user_module_perm (user_id,module_id,create_time,update_time,added_by_id,upd_by_id,is_allowed) VALUES (dbo.getXportalUIdByLoginId('admin'),dbo.getModulesIdByName('Resource Based Policies'),CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,dbo.getXportalUIdByLoginId('admin'),dbo.getXportalUIdByLoginId('admin'),1); @@ -4626,5 +4628,6 @@ INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('J10064',CURRENT_TIMESTAMP,'Ranger 3.0.0',CURRENT_TIMESTAMP,'localhost','Y'); INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('J10065',CURRENT_TIMESTAMP,'Ranger 3.0.0',CURRENT_TIMESTAMP,'localhost','Y'); INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('J10066',CURRENT_TIMESTAMP,'Ranger 3.0.0',CURRENT_TIMESTAMP,'localhost','Y'); +INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('J10067',CURRENT_TIMESTAMP,'Ranger 3.0.0',CURRENT_TIMESTAMP,'localhost','Y'); INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('JAVA_PATCHES',CURRENT_TIMESTAMP,'Ranger 1.0.0',CURRENT_TIMESTAMP,'localhost','Y'); GO diff --git a/security-admin/db/sqlserver/patches/079-add-def_text-to-x_service_def.sql b/security-admin/db/sqlserver/patches/079-add-def_text-to-x_service_def.sql new file mode 100644 index 0000000000..51e9063b45 --- /dev/null +++ b/security-admin/db/sqlserver/patches/079-add-def_text-to-x_service_def.sql @@ -0,0 +1,22 @@ +-- 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. + +GO +IF NOT EXISTS(select * from INFORMATION_SCHEMA.columns where table_name = 'x_service_def' and column_name = 'def_text') +BEGIN + ALTER TABLE [dbo].[x_service_def] ADD [def_text] [nvarchar](max) DEFAULT NULL NULL; +END +GO +exit diff --git a/security-admin/src/main/java/org/apache/ranger/biz/ServiceDBStore.java b/security-admin/src/main/java/org/apache/ranger/biz/ServiceDBStore.java index 0d3f5bc1bc..83e32d3ff7 100644 --- a/security-admin/src/main/java/org/apache/ranger/biz/ServiceDBStore.java +++ b/security-admin/src/main/java/org/apache/ranger/biz/ServiceDBStore.java @@ -115,6 +115,7 @@ import org.apache.ranger.plugin.model.RangerService; import org.apache.ranger.plugin.model.RangerServiceDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerAccessTypeDef; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerConstraintDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerContextEnricherDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerDataMaskDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerDataMaskTypeDef; @@ -467,6 +468,7 @@ public RangerServiceDef createServiceDef(RangerServiceDef serviceDef) throws Exc List enums = serviceDef.getEnums(); RangerDataMaskDef dataMaskDef = serviceDef.getDataMaskDef(); RangerRowFilterDef rowFilterDef = serviceDef.getRowFilterDef(); + List constraintDefs = serviceDef.getConstraintDefs(); List dataMaskTypes = dataMaskDef == null || dataMaskDef.getMaskTypes() == null ? new ArrayList<>() : dataMaskDef.getMaskTypes(); List dataMaskAccessTypes = dataMaskDef == null || dataMaskDef.getAccessTypes() == null ? new ArrayList<>() : dataMaskDef.getAccessTypes(); List dataMaskResources = dataMaskDef == null || dataMaskDef.getResources() == null ? new ArrayList<>() : dataMaskDef.getResources(); @@ -698,6 +700,8 @@ public RangerServiceDef createServiceDef(RangerServiceDef serviceDef) throws Exc } } + updateServiceDefText(createdSvcDef, constraintDefs); + RangerServiceDef createdServiceDef = serviceDefService.getPopulatedViewObject(createdSvcDef); dataHistService.createObjectDataHistory(createdServiceDef, RangerDataHistService.ACTION_CREATE); @@ -739,6 +743,7 @@ public RangerServiceDef updateServiceDef(RangerServiceDef serviceDef) throws Exc List enums = serviceDef.getEnums() != null ? serviceDef.getEnums() : new ArrayList<>(); RangerDataMaskDef dataMaskDef = serviceDef.getDataMaskDef(); RangerRowFilterDef rowFilterDef = serviceDef.getRowFilterDef(); + List constraintDefs = serviceDef.getConstraintDefs(); RangerServiceDefHelper defHelper = new RangerServiceDefHelper(serviceDef, false); defHelper.patchServiceDefWithDefaultValues(); @@ -753,6 +758,8 @@ public RangerServiceDef updateServiceDef(RangerServiceDef serviceDef) throws Exc updateChildObjectsOfServiceDef(createdSvcDef, configs, resources, accessTypes, policyConditions, contextEnrichers, enums, dataMaskDef, rowFilterDef); + updateServiceDefText(createdSvcDef, constraintDefs); + RangerServiceDef updatedSvcDef = getServiceDef(serviceDefId); dataHistService.createObjectDataHistory(updatedSvcDef, RangerDataHistService.ACTION_UPDATE); @@ -3248,6 +3255,17 @@ boolean hasServiceConfigForPluginChanged(List dbConfigMaps, return ret; } + // Writes x_service_def.def_text after the child rows. constraintDefs are stored only in this JSON. + private void updateServiceDefText(XXServiceDef xServiceDef, List constraintDefs) { + if (xServiceDef == null) { + return; + } + + xServiceDef.setDefText(serviceDefService.buildDefText(xServiceDef, constraintDefs)); + + daoMgr.getXXServiceDef().update(xServiceDef); + } + private void updateChildObjectsOfServiceDef(XXServiceDef createdSvcDef, List configs, List resources, List accessTypes, List policyConditions, List contextEnrichers, diff --git a/security-admin/src/main/java/org/apache/ranger/entity/XXServiceDefBase.java b/security-admin/src/main/java/org/apache/ranger/entity/XXServiceDefBase.java index ba86de242d..567d5ace0d 100644 --- a/security-admin/src/main/java/org/apache/ranger/entity/XXServiceDefBase.java +++ b/security-admin/src/main/java/org/apache/ranger/entity/XXServiceDefBase.java @@ -93,6 +93,14 @@ public abstract class XXServiceDefBase extends XXDBBase implements Serializable @Column(name = "def_options") protected String defOptions; + /** + * defText of the XXServiceDef + *
    + *
+ */ + @Column(name = "def_text") + protected String defText; + /** * rbKeyLabel of the XXServiceDef *
    @@ -238,6 +246,24 @@ public void setDefOptions(String options) { this.defOptions = options; } + /** + * Returns the value for the member attribute defText + * + * @return String - value of member attribute defText . + */ + public String getDefText() { + return this.defText; + } + + /** + * This method sets the value to the member attribute defText . + * + * @param defText Value to set member attribute defText + */ + public void setDefText(String defText) { + this.defText = defText; + } + /** * Returns the value for the member attribute rbKeyLabel * @@ -332,6 +358,7 @@ public boolean equals(Object obj) { XXServiceDefBase other = (XXServiceDefBase) obj; return Objects.equals(description, other.description) && + Objects.equals(defText, other.defText) && Objects.equals(guid, other.guid) && Objects.equals(implClassName, other.implClassName) && Objects.equals(label, other.label) && diff --git a/security-admin/src/main/java/org/apache/ranger/patch/PatchForServiceDefJson_J10067.java b/security-admin/src/main/java/org/apache/ranger/patch/PatchForServiceDefJson_J10067.java new file mode 100644 index 0000000000..d95a5a0a16 --- /dev/null +++ b/security-admin/src/main/java/org/apache/ranger/patch/PatchForServiceDefJson_J10067.java @@ -0,0 +1,134 @@ +/* + * 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.patch; + +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.ranger.db.RangerDaoManager; +import org.apache.ranger.entity.XXServiceDef; +import org.apache.ranger.service.RangerServiceDefService; +import org.apache.ranger.util.CLIUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.support.TransactionTemplate; + +import java.util.List; + +@Component +public class PatchForServiceDefJson_J10067 extends BaseLoader { + private static final Logger logger = LoggerFactory.getLogger(PatchForServiceDefJson_J10067.class); + + @Autowired + RangerDaoManager daoMgr; + + @Autowired + RangerServiceDefService serviceDefService; + + @Autowired + @Qualifier(value = "transactionManager") + PlatformTransactionManager txManager; + + public static void main(String[] args) { + logger.info("main()"); + try { + PatchForServiceDefJson_J10067 loader = (PatchForServiceDefJson_J10067) CLIUtil.getBean(PatchForServiceDefJson_J10067.class); + + loader.init(); + + while (loader.isMoreToProcess()) { + loader.load(); + } + + logger.info("Load complete. Exiting!!!"); + System.exit(0); + } catch (Exception e) { + logger.error("Error loading", e); + System.exit(1); + } + } + + @Override + public void init() throws Exception { + // Do Nothing + } + + @Override + public void printStats() { + logger.info("PatchForServiceDefJson_J10067"); + } + + @Override + public void execLoad() { + logger.info("==> PatchForServiceDefJson_J10067.execLoad()"); + + try { + updateServiceDefText(); + } catch (Exception e) { + logger.error("Error while applying PatchForServiceDefJson_J10067", e); + throw new RuntimeException("PatchForServiceDefJson_J10067 failed", e); + } + + logger.info("<== PatchForServiceDefJson_J10067.execLoad()"); + } + + private void updateServiceDefText() { + List serviceDefs = daoMgr.getXXServiceDef().getAll(); + + if (CollectionUtils.isEmpty(serviceDefs)) { + logger.info("No service-defs found"); + + return; + } + + TransactionTemplate txTemplate = new TransactionTemplate(txManager); + + txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); + + for (XXServiceDef xServiceDef : serviceDefs) { + Long serviceDefId = xServiceDef.getId(); + + txTemplate.execute(status -> { + XXServiceDef dbServiceDef = daoMgr.getXXServiceDef().getById(serviceDefId); + + if (dbServiceDef != null && StringUtils.isEmpty(dbServiceDef.getDefText())) { + // constraintDefs were not stored before def_text existed, so there are none to carry over + String defText = serviceDefService.buildDefText(dbServiceDef, null); + + if (StringUtils.isEmpty(defText)) { + throw new IllegalStateException("failed to build JSON for service-def(id=" + serviceDefId + ", name=" + dbServiceDef.getName() + ")"); + } + + dbServiceDef.setDefText(defText); + + daoMgr.getXXServiceDef().update(dbServiceDef); + + logger.info("Stored JSON for service-def(id={}, name={})", serviceDefId, dbServiceDef.getName()); + } else if (dbServiceDef != null) { + logger.info("JSON already present for service-def(id={}, name={}); skipping", serviceDefId, dbServiceDef.getName()); + } + + return null; + }); + } + } +} diff --git a/security-admin/src/main/java/org/apache/ranger/service/RangerServiceDefServiceBase.java b/security-admin/src/main/java/org/apache/ranger/service/RangerServiceDefServiceBase.java index 1b93b2c84b..3edaf67787 100644 --- a/security-admin/src/main/java/org/apache/ranger/service/RangerServiceDefServiceBase.java +++ b/security-admin/src/main/java/org/apache/ranger/service/RangerServiceDefServiceBase.java @@ -19,6 +19,7 @@ import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.ranger.authorization.utils.JsonUtils; import org.apache.ranger.common.AppConstants; import org.apache.ranger.common.GUIDUtil; import org.apache.ranger.common.JSONUtil; @@ -41,6 +42,7 @@ import org.apache.ranger.plugin.model.RangerServiceDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerAccessTypeDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerAccessTypeDef.AccessTypeCategory; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerConstraintDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerContextEnricherDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerDataMaskDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerDataMaskTypeDef; @@ -557,6 +559,39 @@ protected T mapViewToEntityBean(V vObj, T xObj, int operationContext) { @Override protected V mapEntityToViewBean(V vObj, T xObj) { + return mapServiceDefColumns(vObj, xObj); + } + + @Override + protected V populateViewBean(T xServiceDef) { + V serviceDef = super.populateViewBean(xServiceDef); + + if (!applyDefText(serviceDef, xServiceDef.getDefText())) { + loadChildTables(serviceDef, xServiceDef); + } + + serviceDef.setMarkerAccessTypes(ServiceDefUtil.getMarkerAccessTypes(serviceDef.getAccessTypes())); + addImplicitConditionExpressionIfNeeded(serviceDef); + ServiceDefUtil.normalize(serviceDef); + + return serviceDef; + } + + // JSON for x_service_def.def_text, built after the child rows are written. + // Marker access types, the implicit condition, and read-time options are left out. + public String buildDefText(T xServiceDef, List constraintDefs) { + V serviceDef = createViewObject(); + + serviceDef.setId(xServiceDef.getId()); + mapServiceDefColumns(serviceDef, xServiceDef); + loadChildTables(serviceDef, xServiceDef); + serviceDef.setConstraintDefs(constraintDefs); + serviceDef.setMarkerAccessTypes(null); + + return JsonUtils.objectToJson(serviceDef); + } + + private V mapServiceDefColumns(V vObj, T xObj) { vObj.setGuid(xObj.getGuid()); vObj.setVersion(xObj.getVersion()); vObj.setName(xObj.getName()); @@ -572,9 +607,33 @@ protected V mapEntityToViewBean(V vObj, T xObj) { return vObj; } - @Override - protected V populateViewBean(T xServiceDef) { - V serviceDef = super.populateViewBean(xServiceDef); + private boolean applyDefText(V serviceDef, String defText) { + if (StringUtils.isEmpty(defText)) { + return false; + } + + V fromJson = JsonUtils.jsonToObject(defText, tViewClass); + + if (fromJson == null) { + LOG.warn("failed to read def_text of service-def(id={}, name={}); loading it from child tables", serviceDef.getId(), serviceDef.getName()); + + return false; + } + + serviceDef.setConfigs(fromJson.getConfigs()); + serviceDef.setResources(fromJson.getResources()); + serviceDef.setAccessTypes(fromJson.getAccessTypes()); + serviceDef.setPolicyConditions(fromJson.getPolicyConditions()); + serviceDef.setConstraintDefs(fromJson.getConstraintDefs()); + serviceDef.setContextEnrichers(fromJson.getContextEnrichers()); + serviceDef.setEnums(fromJson.getEnums()); + serviceDef.setDataMaskDef(fromJson.getDataMaskDef()); + serviceDef.setRowFilterDef(fromJson.getRowFilterDef()); + + return true; + } + + private void loadChildTables(V serviceDef, T xServiceDef) { Long serviceDefId = xServiceDef.getId(); List xConfigs = daoMgr.getXXServiceConfigDef().findByServiceDefId(serviceDefId); @@ -619,8 +678,6 @@ protected V populateViewBean(T xServiceDef) { serviceDef.setAccessTypes(accessTypes); } - serviceDef.setMarkerAccessTypes(ServiceDefUtil.getMarkerAccessTypes(serviceDef.getAccessTypes())); - List xPolicyConditions = daoMgr.getXXPolicyConditionDef().findByServiceDefId(serviceDefId); if (!stringUtil.isEmpty(xPolicyConditions)) { @@ -713,12 +770,6 @@ protected V populateViewBean(T xServiceDef) { serviceDef.setDataMaskDef(dataMaskDef); serviceDef.setRowFilterDef(rowFilterDef); - - addImplicitConditionExpressionIfNeeded(serviceDef); - - ServiceDefUtil.normalize(serviceDef); - - return serviceDef; } protected Map jsonStringToMap(String jsonStr) { diff --git a/security-admin/src/test/java/org/apache/ranger/biz/TestServiceDBStore.java b/security-admin/src/test/java/org/apache/ranger/biz/TestServiceDBStore.java index 5a36e7f297..1eec973569 100644 --- a/security-admin/src/test/java/org/apache/ranger/biz/TestServiceDBStore.java +++ b/security-admin/src/test/java/org/apache/ranger/biz/TestServiceDBStore.java @@ -116,6 +116,7 @@ import org.apache.ranger.plugin.model.RangerService; import org.apache.ranger.plugin.model.RangerServiceDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerAccessTypeDef; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerConstraintDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerContextEnricherDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerEnumDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerPolicyConditionDef; @@ -284,7 +285,11 @@ public void test11createServiceDef() throws Exception { List xResourceDefs = new ArrayList<>(); xResourceDefs.add(xResourceDef); - RangerServiceDef serviceDef = new RangerServiceDef(); + RangerServiceDef serviceDef = new RangerServiceDef(); + List constraintDefs = Collections.singletonList(constraintDef()); + + serviceDef.setConstraintDefs(constraintDefs); + Mockito.when(serviceDefService.create(serviceDef)).thenReturn(serviceDef); Mockito.when(daoMgr.getXXServiceDef()).thenReturn(xServiceDefDao); @@ -323,6 +328,8 @@ public void test11createServiceDef() throws Exception { Assertions.assertEquals(dbServiceDef.getResources(), serviceDef.getResources()); Mockito.verify(serviceDefService).getPopulatedViewObject(xServiceDef); Mockito.verify(serviceDefService).create(serviceDef); + Mockito.verify(serviceDefService).buildDefText(xServiceDef, constraintDefs); + Mockito.verify(xServiceDefDao).update(xServiceDef); Mockito.verify(daoMgr).getXXServiceConfigDef(); Mockito.verify(daoMgr).getXXEnumDef(); Mockito.verify(daoMgr).getXXAccessTypeDef(); @@ -342,8 +349,11 @@ public void test12updateServiceDef() throws Exception { XXDataMaskTypeDefDao xDataMaskDefDao = Mockito.mock(XXDataMaskTypeDefDao.class); XXServiceDao xServiceDao = Mockito.mock(XXServiceDao.class); - RangerServiceDef rangerServiceDef = rangerServiceDef(); - Long serviceDefId = rangerServiceDef.getId(); + RangerServiceDef rangerServiceDef = rangerServiceDef(); + Long serviceDefId = rangerServiceDef.getId(); + List constraintDefs = Collections.singletonList(constraintDef()); + + rangerServiceDef.setConstraintDefs(constraintDefs); List svcConfDefList = new ArrayList<>(); XXServiceConfigDef serviceConfigDefObj = new XXServiceConfigDef(); @@ -444,6 +454,9 @@ public void test12updateServiceDef() throws Exception { Assertions.assertEquals(dbServiceDef.getConfigs(), rangerServiceDef.getConfigs()); Assertions.assertEquals(dbServiceDef.getVersion(), rangerServiceDef.getVersion()); Assertions.assertEquals(dbServiceDef.getResources(), rangerServiceDef.getResources()); + // update() returns a different object, so this checks the request's constraintDefs were kept + Mockito.verify(serviceDefService).buildDefText(xServiceDef, constraintDefs); + Mockito.verify(xServiceDefDao).update(xServiceDef); } @Test @@ -6961,6 +6974,25 @@ private RangerServiceDef rangerServiceDef() { return rangerServiceDef; } + private RangerConstraintDef constraintDef() { + RangerServiceDef.RangerConstraintParamDef approver = new RangerServiceDef.RangerConstraintParamDef(); + + approver.setItemId(1L); + approver.setName("approver"); + approver.setType("group"); + approver.setMandatory(true); + + RangerConstraintDef constraintDef = new RangerConstraintDef(); + + constraintDef.setItemId(1L); + constraintDef.setName("requireApproval"); + constraintDef.setResources(Collections.singletonList("tool")); + constraintDef.setAccessTypes(Collections.singletonList("execute")); + constraintDef.setParameters(Collections.singletonList(approver)); + + return constraintDef; + } + private RangerService rangerService() { Map configs = new HashMap<>(); configs.put("username", "servicemgr"); diff --git a/security-admin/src/test/java/org/apache/ranger/patch/TestPatchForServiceDefJson_J10067.java b/security-admin/src/test/java/org/apache/ranger/patch/TestPatchForServiceDefJson_J10067.java new file mode 100644 index 0000000000..b306d823fb --- /dev/null +++ b/security-admin/src/test/java/org/apache/ranger/patch/TestPatchForServiceDefJson_J10067.java @@ -0,0 +1,113 @@ +/* + * 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.patch; + +import org.apache.ranger.db.RangerDaoManager; +import org.apache.ranger.db.XXServiceDefDao; +import org.apache.ranger.entity.XXServiceDef; +import org.apache.ranger.service.RangerServiceDefService; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionStatus; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; + +@ExtendWith(MockitoExtension.class) +public class TestPatchForServiceDefJson_J10067 { + private RangerDaoManager daoMgr; + private XXServiceDefDao xServiceDefDao; + private RangerServiceDefService serviceDefService; + private PatchForServiceDefJson_J10067 patch; + + @BeforeEach + public void setUp() { + daoMgr = Mockito.mock(RangerDaoManager.class); + xServiceDefDao = Mockito.mock(XXServiceDefDao.class); + serviceDefService = Mockito.mock(RangerServiceDefService.class); + + PlatformTransactionManager txManager = Mockito.mock(PlatformTransactionManager.class); + + Mockito.lenient().when(txManager.getTransaction(Mockito.any())).thenReturn(Mockito.mock(TransactionStatus.class)); + Mockito.when(daoMgr.getXXServiceDef()).thenReturn(xServiceDefDao); + + patch = new PatchForServiceDefJson_J10067(); + patch.daoMgr = daoMgr; + patch.serviceDefService = serviceDefService; + patch.txManager = txManager; + } + + @Test + public void testStoresJsonOnlyForServiceDefsWithoutIt() { + XXServiceDef withoutJson = serviceDef(1L, "hive", null); + XXServiceDef withJson = serviceDef(2L, "hdfs", "{\"name\":\"hdfs\"}"); + + Mockito.when(xServiceDefDao.getAll()).thenReturn(new ArrayList<>(Arrays.asList(withoutJson, withJson))); + Mockito.when(xServiceDefDao.getById(1L)).thenReturn(withoutJson); + Mockito.when(xServiceDefDao.getById(2L)).thenReturn(withJson); + Mockito.when(serviceDefService.buildDefText(withoutJson, null)).thenReturn("{\"name\":\"hive\"}"); + + patch.execLoad(); + + Assertions.assertEquals("{\"name\":\"hive\"}", withoutJson.getDefText()); + Assertions.assertEquals("{\"name\":\"hdfs\"}", withJson.getDefText()); + Mockito.verify(xServiceDefDao).update(withoutJson); + Mockito.verify(xServiceDefDao, Mockito.never()).update(withJson); + Mockito.verify(serviceDefService, Mockito.never()).buildDefText(Mockito.eq(withJson), Mockito.any()); + } + + @Test + public void testFailsWhenJsonCannotBeBuilt() { + XXServiceDef withoutJson = serviceDef(1L, "hive", null); + + Mockito.when(xServiceDefDao.getAll()).thenReturn(new ArrayList<>(Collections.singletonList(withoutJson))); + Mockito.when(xServiceDefDao.getById(1L)).thenReturn(withoutJson); + Mockito.when(serviceDefService.buildDefText(withoutJson, null)).thenReturn(null); + + Assertions.assertThrows(RuntimeException.class, () -> patch.execLoad()); + Mockito.verify(xServiceDefDao, Mockito.never()).update(Mockito.any()); + } + + @Test + public void testNoServiceDefs() { + Mockito.when(xServiceDefDao.getAll()).thenReturn(new ArrayList<>()); + + patch.execLoad(); + patch.printStats(); + + Mockito.verify(xServiceDefDao, Mockito.never()).update(Mockito.any()); + } + + private static XXServiceDef serviceDef(Long id, String name, String defText) { + XXServiceDef ret = new XXServiceDef(); + + ret.setId(id); + ret.setName(name); + ret.setDefText(defText); + + return ret; + } +} diff --git a/security-admin/src/test/java/org/apache/ranger/service/TestRangerServiceDefService.java b/security-admin/src/test/java/org/apache/ranger/service/TestRangerServiceDefService.java index 41d0be2d98..090d2ba634 100644 --- a/security-admin/src/test/java/org/apache/ranger/service/TestRangerServiceDefService.java +++ b/security-admin/src/test/java/org/apache/ranger/service/TestRangerServiceDefService.java @@ -18,8 +18,10 @@ import org.apache.commons.lang3.StringUtils; import org.apache.ranger.authorization.hadoop.config.RangerAdminConfig; +import org.apache.ranger.authorization.utils.JsonUtils; import org.apache.ranger.biz.RangerBizUtil; import org.apache.ranger.common.ContextUtil; +import org.apache.ranger.common.JSONUtil; import org.apache.ranger.common.PropertiesUtil; import org.apache.ranger.common.StringUtil; import org.apache.ranger.common.UserSessionBase; @@ -50,6 +52,7 @@ import org.apache.ranger.entity.XXServiceDefWithAssignedId; import org.apache.ranger.plugin.model.RangerServiceDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerAccessTypeDef; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerConstraintDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerContextEnricherDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerEnumDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerPolicyConditionDef; @@ -71,6 +74,7 @@ import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashMap; @@ -85,6 +89,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -911,6 +916,202 @@ public void testValidateForUpdate() throws NoSuchMethodException { } } + @Test + public void test6PopulateViewBeanLoadsConstraintDefs() throws Exception { + serviceDefService.jsonUtil = new JSONUtil(); + + RangerConstraintDef constraintDef = new RangerConstraintDef(); + constraintDef.setItemId(4L); + constraintDef.setName("requireApproval"); + constraintDef.setResources(Arrays.asList("database", "udf")); + constraintDef.setAccessTypes(Collections.singletonList("select")); + RangerServiceDef.RangerConstraintParamDef approver = new RangerServiceDef.RangerConstraintParamDef(); + approver.setItemId(1L); + approver.setName("approver"); + approver.setType("user"); + approver.setMandatory(true); + constraintDef.getParameters().add(approver); + + RangerServiceDef jsonBody = new RangerServiceDef(); + jsonBody.setName("json-name"); + jsonBody.getConstraintDefs().add(constraintDef); + + XXServiceDef serviceDef = serviceDef(); + serviceDef.setAddedByUserId(null); + serviceDef.setUpdatedByUserId(null); + serviceDef.setName("hdfs"); + serviceDef.setDefOptions("{}"); + serviceDef.setDefText(serviceDefService.objectToJson(jsonBody)); + + RangerServiceDef loaded = serviceDefService.populateViewBean(serviceDef); + + assertNotNull(loaded.getConstraintDefs()); + assertEquals(serviceDef.getName(), loaded.getName()); + assertEquals(1, loaded.getConstraintDefs().size()); + assertEquals(Long.valueOf(4L), loaded.getConstraintDefs().get(0).getItemId()); + assertEquals("requireApproval", loaded.getConstraintDefs().get(0).getName()); + assertEquals(Arrays.asList("database", "udf"), loaded.getConstraintDefs().get(0).getResources()); + assertEquals(Collections.singletonList("select"), loaded.getConstraintDefs().get(0).getAccessTypes()); + assertEquals("approver", loaded.getConstraintDefs().get(0).getParameters().get(0).getName()); + assertEquals("user", loaded.getConstraintDefs().get(0).getParameters().get(0).getType()); + } + + @Test + public void testPopulateViewBeanReadsDefText() { + mockBaseFieldsForPopulateViewBean(); + + RangerResourceDef tool = new RangerResourceDef(); + tool.setItemId(1L); + tool.setName("tool"); + tool.setLevel(10); + + RangerAccessTypeDef execute = new RangerAccessTypeDef(); + execute.setItemId(1L); + execute.setName("execute"); + + RangerServiceDef stored = new RangerServiceDef(); + stored.setName("stale-name-in-json"); + stored.setResources(Collections.singletonList(tool)); + stored.setAccessTypes(Collections.singletonList(execute)); + stored.setConstraintDefs(Collections.singletonList(constraintDef())); + + XXServiceDef xServiceDef = serviceDef(); + xServiceDef.setName("agent"); + xServiceDef.setDefOptions("{}"); + xServiceDef.setDefText(JsonUtils.objectToJson(stored)); + + RangerServiceDef loaded = serviceDefService.populateViewBean(xServiceDef); + + assertEquals("agent", loaded.getName()); + assertEquals(1, loaded.getResources().size()); + assertEquals("tool", loaded.getResources().get(0).getName()); + assertEquals("execute", loaded.getAccessTypes().get(0).getName()); + assertEquals(1, loaded.getConstraintDefs().size()); + assertEquals("requireApproval", loaded.getConstraintDefs().get(0).getName()); + assertEquals(Collections.singletonList("tool"), loaded.getConstraintDefs().get(0).getResources()); + assertEquals("approver", loaded.getConstraintDefs().get(0).getParameters().get(0).getName()); + verify(daoMgr, never()).getXXResourceDef(); + verify(daoMgr, never()).getXXAccessTypeDef(); + verify(daoMgr, never()).getXXServiceConfigDef(); + } + + @Test + public void testPopulateViewBeanFallsBackToChildTables() { + mockBaseFieldsForPopulateViewBean(); + mockEmptyChildTables(Id); + + XXServiceDef xServiceDef = serviceDef(); + xServiceDef.setDefOptions("{}"); + xServiceDef.setDefText("not-a-json"); + + RangerServiceDef loaded = serviceDefService.populateViewBean(xServiceDef); + + assertNotNull(loaded); + assertTrue(loaded.getConstraintDefs().isEmpty()); + verify(daoMgr).getXXResourceDef(); + verify(daoMgr).getXXAccessTypeDef(); + } + + @Test + public void testBuildDefTextIncludesConstraintDefsAndNoDerivedValues() { + serviceDefService.stringUtil = new StringUtil(); + serviceDefService.jsonUtil = new JSONUtil(); + + mockEmptyChildTables(Id); + + XXServiceDef xServiceDef = serviceDef(); + xServiceDef.setName("agent"); + xServiceDef.setDefOptions("{\"custom\":\"value\"}"); + + String defText = serviceDefService.buildDefText(xServiceDef, Collections.singletonList(constraintDef())); + + assertNotNull(defText); + assertFalse(defText.contains("markerAccessTypes")); + assertFalse(defText.contains(RangerServiceDef.OPTION_ENABLE_DENY_AND_EXCEPTIONS_IN_POLICIES)); + + RangerServiceDef stored = JsonUtils.jsonToObject(defText, RangerServiceDef.class); + + assertNotNull(stored); + assertEquals("agent", stored.getName()); + assertEquals("value", stored.getOptions().get("custom")); + assertEquals(1, stored.getConstraintDefs().size()); + assertEquals("requireApproval", stored.getConstraintDefs().get(0).getName()); + assertEquals(Arrays.asList("execute"), stored.getConstraintDefs().get(0).getAccessTypes()); + } + + @Test + public void testBuildDefTextWithoutConstraintDefs() { + serviceDefService.stringUtil = new StringUtil(); + + mockEmptyChildTables(Id); + + String defText = serviceDefService.buildDefText(serviceDef(), null); + RangerServiceDef stored = JsonUtils.jsonToObject(defText, RangerServiceDef.class); + + assertNotNull(stored); + assertTrue(stored.getConstraintDefs() == null || stored.getConstraintDefs().isEmpty()); + } + + private void mockBaseFieldsForPopulateViewBean() { + serviceDefService.bizUtil = Mockito.mock(RangerBizUtil.class); + serviceDefService.stringUtil = new StringUtil(); + serviceDefService.jsonUtil = new JSONUtil(); + + XXPortalUserDao xPortalUserDao = Mockito.mock(XXPortalUserDao.class); + XXPortalUser portalUser = new XXPortalUser(); + + portalUser.setId(Id); + portalUser.setFirstName("Admin"); + + Mockito.lenient().when(daoMgr.getXXPortalUser()).thenReturn(xPortalUserDao); + Mockito.lenient().when(xPortalUserDao.getById(Id)).thenReturn(portalUser); + } + + private void mockEmptyChildTables(Long serviceDefId) { + XXServiceConfigDefDao xServiceConfigDefDao = Mockito.mock(XXServiceConfigDefDao.class); + XXResourceDefDao xResourceDefDao = Mockito.mock(XXResourceDefDao.class); + XXAccessTypeDefDao xAccessTypeDefDao = Mockito.mock(XXAccessTypeDefDao.class); + XXAccessTypeDefGrantsDao xAccessTypeDefGrantsDao = Mockito.mock(XXAccessTypeDefGrantsDao.class); + XXPolicyConditionDefDao xPolicyConditionDefDao = Mockito.mock(XXPolicyConditionDefDao.class); + XXContextEnricherDefDao xContextEnricherDefDao = Mockito.mock(XXContextEnricherDefDao.class); + XXEnumDefDao xEnumDefDao = Mockito.mock(XXEnumDefDao.class); + XXDataMaskTypeDefDao xDataMaskTypeDefDao = Mockito.mock(XXDataMaskTypeDefDao.class); + + Mockito.when(daoMgr.getXXServiceConfigDef()).thenReturn(xServiceConfigDefDao); + Mockito.when(xServiceConfigDefDao.findByServiceDefId(serviceDefId)).thenReturn(new ArrayList<>()); + Mockito.when(daoMgr.getXXResourceDef()).thenReturn(xResourceDefDao); + Mockito.when(xResourceDefDao.findByServiceDefId(serviceDefId)).thenReturn(new ArrayList<>()); + Mockito.when(daoMgr.getXXAccessTypeDef()).thenReturn(xAccessTypeDefDao); + Mockito.when(xAccessTypeDefDao.findByServiceDefId(serviceDefId)).thenReturn(new ArrayList<>()); + Mockito.when(daoMgr.getXXAccessTypeDefGrants()).thenReturn(xAccessTypeDefGrantsDao); + Mockito.when(xAccessTypeDefGrantsDao.findImpliedGrantsByServiceDefId(serviceDefId)).thenReturn(Collections.emptyMap()); + Mockito.when(daoMgr.getXXPolicyConditionDef()).thenReturn(xPolicyConditionDefDao); + Mockito.when(xPolicyConditionDefDao.findByServiceDefId(serviceDefId)).thenReturn(new ArrayList<>()); + Mockito.when(daoMgr.getXXContextEnricherDef()).thenReturn(xContextEnricherDefDao); + Mockito.when(xContextEnricherDefDao.findByServiceDefId(serviceDefId)).thenReturn(new ArrayList<>()); + Mockito.when(daoMgr.getXXEnumDef()).thenReturn(xEnumDefDao); + Mockito.when(xEnumDefDao.findByServiceDefId(serviceDefId)).thenReturn(new ArrayList<>()); + Mockito.when(daoMgr.getXXDataMaskTypeDef()).thenReturn(xDataMaskTypeDefDao); + Mockito.when(xDataMaskTypeDefDao.findByServiceDefId(serviceDefId)).thenReturn(new ArrayList<>()); + } + + private RangerConstraintDef constraintDef() { + RangerServiceDef.RangerConstraintParamDef approver = new RangerServiceDef.RangerConstraintParamDef(); + approver.setItemId(1L); + approver.setName("approver"); + approver.setType("group"); + approver.setMandatory(true); + + RangerConstraintDef ret = new RangerConstraintDef(); + ret.setItemId(1L); + ret.setName("requireApproval"); + ret.setResources(Collections.singletonList("tool")); + ret.setAccessTypes(Collections.singletonList("execute")); + ret.setParameters(Collections.singletonList(approver)); + + return ret; + } + private boolean hasActionMatchesCondition(RangerServiceDef serviceDef) { if (serviceDef.getPolicyConditions() == null) { return false; diff --git a/security-admin/src/test/java/org/apache/ranger/service/TestRangerServiceDefServiceBase.java b/security-admin/src/test/java/org/apache/ranger/service/TestRangerServiceDefServiceBase.java index b58f047c75..a2d73b7507 100644 --- a/security-admin/src/test/java/org/apache/ranger/service/TestRangerServiceDefServiceBase.java +++ b/security-admin/src/test/java/org/apache/ranger/service/TestRangerServiceDefServiceBase.java @@ -38,6 +38,8 @@ import org.apache.ranger.entity.XXServiceDef; import org.apache.ranger.plugin.model.RangerServiceDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerAccessTypeDef; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerConstraintDef; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerConstraintParamDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerContextEnricherDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerEnumDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerEnumElementDef; @@ -62,9 +64,12 @@ import javax.ws.rs.WebApplicationException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Date; +import java.util.HashMap; import java.util.List; +import java.util.Map; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -589,6 +594,94 @@ public void test21searchRangerServiceDefs() { Assertions.assertNotNull(dbRangerServiceDefList); } + @Test + public void test22mapViewToEntityBeanLeavesDefTextUnset() { + RangerServiceDef view = rangerServiceDef(); + view.setGuid("column-guid"); + view.setName("hdfs"); + view.getConstraintDefs().add(constraintDef()); + + XXServiceDef stored = rangerServiceDefService.mapViewToEntityBean(view, serviceDef(), 1); + + Assertions.assertNull(stored.getDefText()); + } + + @Test + public void test23populateViewBeanReadsDefTextWithoutOverwritingColumns() throws Exception { + JSONUtil jsonUtil = new JSONUtil(); + rangerServiceDefService.jsonUtil = jsonUtil; + + RangerServiceDef jsonBody = new RangerServiceDef(); + jsonBody.setName("json-name"); + jsonBody.setGuid("json-guid"); + jsonBody.setLabel("json-label"); + jsonBody.setDescription("json-description"); + Map jsonOptions = new HashMap<>(); + jsonOptions.put("fromJson", "yes"); + jsonBody.setOptions(jsonOptions); + jsonBody.getConstraintDefs().add(constraintDef()); + + XXServiceDef row = serviceDef(); + row.setAddedByUserId(null); + row.setUpdatedByUserId(null); + row.setName("hdfs"); + row.setGuid("column-guid"); + row.setLabel("column-label"); + row.setDescription("column-description"); + row.setDefOptions("{\"stored\":\"yes\"}"); + row.setDefText(jsonUtil.writeObjectAsString(jsonBody)); + + RangerServiceDef loaded = rangerServiceDefService.populateViewBean(row); + + Assertions.assertEquals("hdfs", loaded.getName()); + Assertions.assertEquals("column-guid", loaded.getGuid()); + Assertions.assertEquals("column-label", loaded.getLabel()); + Assertions.assertEquals("column-description", loaded.getDescription()); + Assertions.assertEquals("yes", loaded.getOptions().get("stored")); + Assertions.assertNull(loaded.getOptions().get("fromJson")); + Assertions.assertEquals(1, loaded.getConstraintDefs().size()); + Assertions.assertEquals("requireApproval", loaded.getConstraintDefs().get(0).getName()); + Assertions.assertEquals(Arrays.asList("database", "udf"), loaded.getConstraintDefs().get(0).getResources()); + Assertions.assertEquals(Collections.singletonList("select"), loaded.getConstraintDefs().get(0).getAccessTypes()); + Assertions.assertEquals("approver", loaded.getConstraintDefs().get(0).getParameters().get(0).getName()); + Assertions.assertEquals("user", loaded.getConstraintDefs().get(0).getParameters().get(0).getType()); + Assertions.assertEquals("authType", loaded.getConstraintDefs().get(0).getParameters().get(1).getSubType()); + } + + private RangerConstraintDef constraintDef() { + RangerConstraintParamDef approver = new RangerConstraintParamDef(); + approver.setItemId(1L); + approver.setName("approver"); + approver.setType("user"); + approver.setMandatory(true); + + RangerConstraintParamDef mode = new RangerConstraintParamDef(); + mode.setItemId(2L); + mode.setName("mode"); + mode.setType("enum"); + mode.setSubType("authType"); + mode.setMandatory(false); + mode.setDefaultValue("simple"); + + List resources = new ArrayList<>(); + resources.add("database"); + resources.add("udf"); + List accessTypes = new ArrayList<>(); + accessTypes.add("select"); + List parameters = new ArrayList<>(); + parameters.add(approver); + parameters.add(mode); + + RangerConstraintDef constraintDef = new RangerConstraintDef(); + constraintDef.setItemId(4L); + constraintDef.setName("requireApproval"); + constraintDef.setResources(resources); + constraintDef.setAccessTypes(accessTypes); + constraintDef.setParameters(parameters); + + return constraintDef; + } + private RangerServiceDef rangerServiceDef() { List configs = new ArrayList<>(); List resources = new ArrayList<>();