diff --git a/agents-audit/dest-auditserver/pom.xml b/agents-audit/dest-auditserver/pom.xml index 41aa1baee1f..d512ebbae95 100644 --- a/agents-audit/dest-auditserver/pom.xml +++ b/agents-audit/dest-auditserver/pom.xml @@ -74,6 +74,12 @@ + + org.junit.jupiter + junit-jupiter + ${junit.jupiter.version} + test + org.slf4j log4j-over-slf4j diff --git a/agents-audit/dest-auditserver/src/main/java/org/apache/ranger/audit/destination/RangerAuditServerDestination.java b/agents-audit/dest-auditserver/src/main/java/org/apache/ranger/audit/destination/RangerAuditServerDestination.java index a7eacb999e3..4734e6617d4 100644 --- a/agents-audit/dest-auditserver/src/main/java/org/apache/ranger/audit/destination/RangerAuditServerDestination.java +++ b/agents-audit/dest-auditserver/src/main/java/org/apache/ranger/audit/destination/RangerAuditServerDestination.java @@ -27,6 +27,7 @@ import org.apache.ranger.audit.model.AuthzAuditEvent; import org.apache.ranger.audit.provider.MiscUtil; import org.apache.ranger.plugin.authn.DefaultJwtProvider; +import org.apache.ranger.plugin.util.PluginHeaderAuthConfig; import org.apache.ranger.plugin.util.RangerRESTClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -98,6 +99,12 @@ public void init(Properties props, String propPrefix) { this.restClient.setMaxRetryAttempts(maxRetryAttempts); this.restClient.setRetryIntervalMs(retryIntervalMs); + Map spiffeHeaders = PluginHeaderAuthConfig.buildSpiffeAuthHeaders(props, propPrefix); + if (!spiffeHeaders.isEmpty()) { + this.restClient.setTrustedAuthHeaders(spiffeHeaders); + LOG.debug("SPIFFE header authentication enabled for audit-server destination"); + } + LOG.info("<== RangerAuditServerDestination:init()"); } diff --git a/agents-audit/dest-auditserver/src/test/java/org/apache/ranger/audit/destination/RangerAuditServerDestinationTest.java b/agents-audit/dest-auditserver/src/test/java/org/apache/ranger/audit/destination/RangerAuditServerDestinationTest.java new file mode 100644 index 00000000000..42082f51e49 --- /dev/null +++ b/agents-audit/dest-auditserver/src/test/java/org/apache/ranger/audit/destination/RangerAuditServerDestinationTest.java @@ -0,0 +1,57 @@ +/* + * 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.audit.destination; + +import org.apache.ranger.plugin.util.PluginHeaderAuthConfig; +import org.junit.jupiter.api.Test; + +import java.util.Map; +import java.util.Properties; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class RangerAuditServerDestinationTest { + private static final String AUDIT_DEST_PREFIX = "xasecure.audit.destination.auditserver"; + + @Test + public void buildSpiffeAuthHeadersUsesAuditDestinationPrefix() { + Properties props = new Properties(); + props.setProperty(AUDIT_DEST_PREFIX + ".authn.header.enabled", "true"); + props.setProperty(AUDIT_DEST_PREFIX + ".authn.header.spiffe", "X-Spiffe-Id"); + props.setProperty(AUDIT_DEST_PREFIX + ".authn.spiffe.value", + "spiffe://example.com/ns/default/sa/hive"); + + Map headers = PluginHeaderAuthConfig.buildSpiffeAuthHeaders(props, AUDIT_DEST_PREFIX); + + assertEquals(1, headers.size()); + assertEquals("spiffe://example.com/ns/default/sa/hive", headers.get("X-Spiffe-Id")); + } + + @Test + public void buildSpiffeAuthHeadersEmptyWhenAuditDestinationDisabled() { + Properties props = new Properties(); + props.setProperty(AUDIT_DEST_PREFIX + ".authn.header.enabled", "false"); + props.setProperty(AUDIT_DEST_PREFIX + ".authn.spiffe.value", + "spiffe://example.com/ns/default/sa/hive"); + + Map headers = PluginHeaderAuthConfig.buildSpiffeAuthHeaders(props, AUDIT_DEST_PREFIX); + + assertTrue(headers.isEmpty()); + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/audit/partition/AuditPartitionPlanAdminConfig.java b/agents-common/src/main/java/org/apache/ranger/audit/partition/AuditPartitionPlanAdminConfig.java new file mode 100644 index 00000000000..67e1c4518c3 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/audit/partition/AuditPartitionPlanAdminConfig.java @@ -0,0 +1,40 @@ +/* + * 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.audit.partition; + +import org.apache.commons.lang3.StringUtils; +import org.apache.hadoop.conf.Configuration; + +/** Reads Admin site configuration for audit partition plan allocation. */ +public final class AuditPartitionPlanAdminConfig { + private AuditPartitionPlanAdminConfig() { + } + + public static int resolvePartitionsPerPlugin(String pluginId, Configuration config) { + if (config == null || StringUtils.isBlank(pluginId)) { + return AuditPartitionPlanConstants.DEFAULT_PARTITIONS_PER_PLUGIN; + } + String overrideKey = AuditPartitionPlanConstants.PROP_ADMIN_PLUGIN_PARTITION_OVERRIDE_PREFIX + pluginId.trim(); + if (StringUtils.isNotBlank(config.get(overrideKey))) { + return config.getInt(overrideKey, AuditPartitionPlanConstants.DEFAULT_PARTITIONS_PER_PLUGIN); + } + return config.getInt(AuditPartitionPlanConstants.PROP_ADMIN_PARTITIONS_PER_PLUGIN, AuditPartitionPlanConstants.DEFAULT_PARTITIONS_PER_PLUGIN); + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/audit/partition/AuditPartitionPlanConstants.java b/agents-common/src/main/java/org/apache/ranger/audit/partition/AuditPartitionPlanConstants.java new file mode 100644 index 00000000000..38c20c6547f --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/audit/partition/AuditPartitionPlanConstants.java @@ -0,0 +1,37 @@ +/* + * 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.audit.partition; + +public final class AuditPartitionPlanConstants { + public static final int INITIAL_PLAN_VERSION = 1; + public static final String DEFAULT_AUDIT_TOPIC = "ranger_audits"; + + /** Default partition slots allocated when a plugin is first promoted from buffer. */ + public static final int DEFAULT_PARTITIONS_PER_PLUGIN = 3; + + /** Admin site: {@code ranger-admin-default-site.xml} / {@code ranger-admin-site.xml}. */ + public static final String PROP_ADMIN_PARTITIONS_PER_PLUGIN = "ranger.admin.audit.partition.plan.partitions.per.plugin"; + + /** Per-plugin override prefix, e.g. {@code ...plugin.partition.overrides.hiveServer2}. */ + public static final String PROP_ADMIN_PLUGIN_PARTITION_OVERRIDE_PREFIX = "ranger.admin.audit.partition.plan.plugin.partition.overrides."; + + private AuditPartitionPlanConstants() { + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/audit/partition/PartitionPlanAllocator.java b/agents-common/src/main/java/org/apache/ranger/audit/partition/PartitionPlanAllocator.java new file mode 100644 index 00000000000..a8507d2e115 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/audit/partition/PartitionPlanAllocator.java @@ -0,0 +1,297 @@ +/* + * 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.audit.partition; + +import org.apache.commons.lang3.StringUtils; +import org.apache.ranger.audit.partition.exception.PartitionPlanException; +import org.apache.ranger.audit.partition.model.BufferEntry; +import org.apache.ranger.audit.partition.model.PartitionPlan; +import org.apache.ranger.audit.partition.model.PluginEntry; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import static java.util.Objects.requireNonNull; + +/** Append-only plan updates for Admin-managed audit partition routing. */ +public final class PartitionPlanAllocator { + private PartitionPlanAllocator() { + } + + /** + * Onboard a Ranger service repo under a plugin type. Promotes the plugin from buffer when needed, + * otherwise adds the service to an existing plugin entry. + */ + public static PartitionPlan onboardService(PartitionPlan current, String pluginId, String serviceName, int partitionCount, String updatedBy) { + requireMutationInputs(current, pluginId, partitionCount, updatedBy); + if (StringUtils.isBlank(serviceName)) { + throw new PartitionPlanException("serviceName is required"); + } + String trimmedService = serviceName.trim(); + PluginEntry existing = current.getPlugins().get(pluginId); + if (existing != null) { + return addServiceToPlugin(current, pluginId, trimmedService, updatedBy); + } + return promotePlugin(current, pluginId, partitionCount, updatedBy, trimmedService); + } + + /** Adds a service repo to an already-promoted plugin without changing partition assignment. */ + public static PartitionPlan addServiceToPlugin(PartitionPlan current, String pluginId, String serviceName, String updatedBy) { + if (current == null) { + throw new PartitionPlanException("Current plan is required"); + } + PartitionPlanValidator.validate(current); + if (StringUtils.isBlank(pluginId) || StringUtils.isBlank(serviceName) || StringUtils.isBlank(updatedBy)) { + throw new PartitionPlanException("pluginId, serviceName, and updatedBy are required"); + } + PluginEntry existing = current.getPlugins().get(pluginId); + if (existing == null) { + throw new PartitionPlanException("Plugin '" + pluginId + "' is not configured; promote it first"); + } + String trimmedService = serviceName.trim(); + if (existing.getServices().contains(trimmedService)) { + return current; + } + ensureServiceNotAssignedElsewhere(current.getPlugins(), pluginId, trimmedService); + + Map plugins = new LinkedHashMap<>(current.getPlugins()); + plugins.put(pluginId, existing.addService(trimmedService)); + return commitPlanUpdate(current, updatedBy, current.getTopicPartitionCount(), plugins, current.getBuffer().getPartitions()); + } + + /** Removes a service repo from whichever plugin currently owns it. */ + public static PartitionPlan removeService(PartitionPlan current, String serviceName, String updatedBy) { + if (current == null) { + throw new PartitionPlanException("Current plan is required"); + } + PartitionPlanValidator.validate(current); + if (StringUtils.isBlank(serviceName) || StringUtils.isBlank(updatedBy)) { + throw new PartitionPlanException("serviceName and updatedBy are required"); + } + String trimmedService = serviceName.trim(); + String owningPluginId = findPluginForService(current.getPlugins(), trimmedService); + if (owningPluginId == null) { + return current; + } + + PluginEntry existing = requireNonNull(current.getPlugins().get(owningPluginId)); + List remainingServices = new ArrayList<>(existing.getServices()); + remainingServices.remove(trimmedService); + + Map plugins = new LinkedHashMap<>(current.getPlugins()); + plugins.put(owningPluginId, existing.withServices(remainingServices)); + return commitPlanUpdate(current, updatedBy, current.getTopicPartitionCount(), plugins, current.getBuffer().getPartitions()); + } + + public static PartitionPlan promotePlugin(PartitionPlan current, String pluginId, int partitionCount, String updatedBy) { + return promotePlugin(current, pluginId, partitionCount, updatedBy, null); + } + + /** + * Give a plugin its own partitions. Uses buffer IDs first; adds new tail IDs when buffer is too small. + * Optionally attaches {@code serviceName} to the new plugin entry. + */ + public static PartitionPlan promotePlugin(PartitionPlan current, String pluginId, int partitionCount, String updatedBy, String serviceName) { + requireMutationInputs(current, pluginId, partitionCount, updatedBy); + if (current.getPlugins().containsKey(pluginId)) { + assertPromoteNotConflicting(current, pluginId, partitionCount, serviceName); + throw new PartitionPlanException("Plugin '" + pluginId + "' already has dedicated partitions"); + } + if (StringUtils.isNotBlank(serviceName)) { + ensureServiceNotAssignedElsewhere(current.getPlugins(), pluginId, serviceName.trim()); + } + + List remainingBuffer = new ArrayList<>(current.getBuffer().getPartitions()); + List newPluginIds = takeFromBuffer(remainingBuffer, partitionCount); + int topicPartitionCount = current.getTopicPartitionCount(); + int additionalNeeded = partitionCount - newPluginIds.size(); + if (additionalNeeded > 0) { + topicPartitionCount = appendTailPartitions(newPluginIds, topicPartitionCount, additionalNeeded, collectAssignedPartitionIds(current)); + } + + List services = StringUtils.isNotBlank(serviceName) ? List.of(serviceName.trim()) : List.of(); + Map plugins = addPluginAssignment(current, pluginId, newPluginIds, services); + return commitPlanUpdate(current, updatedBy, topicPartitionCount, plugins, remainingBuffer); + } + + /** Add more partitions to an existing plugin by appending new tail IDs only. */ + public static PartitionPlan scalePlugin(PartitionPlan current, String pluginId, int additionalPartitions, String updatedBy) { + requireMutationInputs(current, pluginId, additionalPartitions, updatedBy); + if (!current.getPlugins().containsKey(pluginId)) { + throw new PartitionPlanException("Plugin '" + pluginId + "' is not configured; promote it first"); + } + + List pluginIds = new ArrayList<>(current.getPlugins().get(pluginId).getPartitions()); + int topicPartitionCount = appendTailPartitions(pluginIds, current.getTopicPartitionCount(), additionalPartitions, collectAssignedPartitionIds(current)); + + Map plugins = addPluginAssignment(current, pluginId, pluginIds, current.getPlugins().get(pluginId).getServices()); + return commitPlanUpdate(current, updatedBy, topicPartitionCount, plugins, current.getBuffer().getPartitions()); + } + + public static boolean isOnboardAlreadyApplied(PartitionPlan current, String pluginId, String serviceName, int partitionCount) { + if (current == null || StringUtils.isBlank(serviceName)) { + return false; + } + PluginEntry existing = current.getPlugins().get(pluginId); + if (existing == null) { + return false; + } + return existing.getPartitions().size() == partitionCount && existing.getServices().contains(serviceName.trim()); + } + + public static boolean isPromoteAlreadyApplied(PartitionPlan current, String pluginId, int partitionCount) { + if (current == null) { + return false; + } + PluginEntry existing = current.getPlugins().get(pluginId); + return existing != null && existing.getPartitions().size() == partitionCount; + } + + /** Applies a merged plan with append-only checks against the current plan. */ + public static PartitionPlan replacePlan(PartitionPlan current, PartitionPlan proposed) { + if (current == null || proposed == null) { + throw new PartitionPlanException("Current and proposed plans are required"); + } + if (!StringUtils.equals(current.getTopic(), proposed.getTopic())) { + throw new PartitionPlanException("Proposed topic must match current topic"); + } + PartitionPlan next = proposed.toBuilder().version(current.getVersion() + 1).build(); + PartitionPlanValidator.validate(next); + PartitionPlanValidator.validateAppendOnly(current, next); + return next; + } + + /** Updates audit POST allow-list metadata; bumps version only when the map changes. */ + public static PartitionPlan updateServiceAllowedUsers(PartitionPlan current, Map> serviceAllowedUsers, String updatedBy) { + if (current == null) { + throw new PartitionPlanException("Current plan is required"); + } + PartitionPlanValidator.validate(current); + if (StringUtils.isBlank(updatedBy)) { + throw new PartitionPlanException("updatedBy is required"); + } + + Map> normalized = PolicyDownloadAuthUsersUtil.normalizeServiceAllowedUsers(serviceAllowedUsers); + if (Objects.equals(current.getServiceAllowedUsers(), normalized)) { + return current; + } + + PartitionPlan next = current.toBuilder() + .version(current.getVersion() + 1) + .serviceAllowedUsers(normalized) + .updatedAt(Instant.now().toString()) + .updatedBy(updatedBy) + .build(); + PartitionPlanValidator.validate(next); + PartitionPlanValidator.validateAppendOnly(current, next); + return next; + } + + private static List takeFromBuffer(List bufferIds, int count) { + List taken = new ArrayList<>(Math.min(count, bufferIds.size())); + while (taken.size() < count && !bufferIds.isEmpty()) { + taken.add(bufferIds.remove(0)); + } + return taken; + } + + private static int appendTailPartitions(List target, int topicPartitionCount, int count, Set assigned) { + int nextId = assigned.isEmpty() ? 1 : assigned.stream().mapToInt(Integer::intValue).max().orElse(0) + 1; + for (int i = 0; i < count; i++) { + target.add(nextId++); + assigned.add(target.get(target.size() - 1)); + } + return topicPartitionCount + count; + } + + private static Set collectAssignedPartitionIds(PartitionPlan plan) { + Set assigned = new HashSet<>(plan.getBuffer().getPartitions()); + for (PluginEntry entry : plan.getPlugins().values()) { + assigned.addAll(entry.getPartitions()); + } + return assigned; + } + + private static Map addPluginAssignment(PartitionPlan current, String pluginId, List partitionIds, List services) { + Map plugins = new LinkedHashMap<>(current.getPlugins()); + plugins.put(pluginId, new PluginEntry(partitionIds, services)); + return plugins; + } + + private static PartitionPlan commitPlanUpdate(PartitionPlan current, String updatedBy, int topicPartitionCount, Map plugins, List bufferIds) { + PartitionPlan next = current.toBuilder() + .version(current.getVersion() + 1) + .topicPartitionCount(topicPartitionCount) + .plugins(plugins) + .buffer(new BufferEntry(bufferIds)) + .updatedAt(Instant.now().toString()) + .updatedBy(updatedBy) + .build(); + PartitionPlanValidator.validate(next); + PartitionPlanValidator.validateAppendOnly(current, next); + return next; + } + + private static String findPluginForService(Map plugins, String serviceName) { + for (Map.Entry entry : plugins.entrySet()) { + if (entry.getValue().getServices().contains(serviceName)) { + return entry.getKey(); + } + } + return null; + } + + private static void ensureServiceNotAssignedElsewhere(Map plugins, String pluginId, String serviceName) { + for (Map.Entry entry : plugins.entrySet()) { + if (!entry.getKey().equals(pluginId) && entry.getValue().getServices().contains(serviceName)) { + throw new PartitionPlanException("Service '" + serviceName + "' is already assigned to plugin '" + entry.getKey() + "'"); + } + } + } + + private static void assertPromoteNotConflicting(PartitionPlan current, String pluginId, int partitionCount, String serviceName) { + PluginEntry existing = requireNonNull(current.getPlugins().get(pluginId)); + if (existing.getPartitions().size() != partitionCount) { + throw new PartitionPlanException("Plugin '" + pluginId + "' already has " + existing.getPartitions().size() + " dedicated partition(s); requested " + partitionCount); + } + if (StringUtils.isNotBlank(serviceName) && existing.getServices().contains(serviceName.trim())) { + return; + } + if (StringUtils.isNotBlank(serviceName)) { + throw new PartitionPlanException("Plugin '" + pluginId + "' already has dedicated partitions"); + } + } + + private static void requireMutationInputs(PartitionPlan current, String pluginId, int partitionCount, String updatedBy) { + if (current == null) { + throw new PartitionPlanException("Current plan is required"); + } + PartitionPlanValidator.validate(current); + if (StringUtils.isBlank(pluginId) || partitionCount < 1 || StringUtils.isBlank(updatedBy)) { + throw new PartitionPlanException("pluginId, partitionCount, and updatedBy are required"); + } + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/audit/partition/PartitionPlanRoutingUtils.java b/agents-common/src/main/java/org/apache/ranger/audit/partition/PartitionPlanRoutingUtils.java new file mode 100644 index 00000000000..65849f756b5 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/audit/partition/PartitionPlanRoutingUtils.java @@ -0,0 +1,70 @@ +/* + * 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.audit.partition; + +import org.apache.commons.lang3.StringUtils; + +/** Converts Admin-managed plan partition ids to Kafka producer partition indices. */ +public final class PartitionPlanRoutingUtils { + private PartitionPlanRoutingUtils() { + } + + /** + * Plan partition ids are 1-based logical ids ({@code 1..topicPartitionCount}). + * Kafka partition indices are 0-based. + */ + public static int toKafkaPartitionIndex(int plannedPartitionId) { + if (plannedPartitionId < 1) { + return 0; + } + return plannedPartitionId - 1; + } + + /** + * Returns a non-negative slot index in {@code [0, slotCount)} for hash-based buffer routing. + * Uses {@link Math#floorMod(int, int)} so {@code Integer.MIN_VALUE} hash codes are safe. + */ + public static int hashToSlotIndex(String key, int slotCount) { + if (slotCount <= 0) { + return 0; + } + if (StringUtils.isBlank(key)) { + return 0; + } + return Math.floorMod(key.hashCode(), slotCount); + } + + /** + * Returns the Kafka partition index for a planned id, clamped to the effective topic size when metadata lags. + */ + public static int resolveKafkaPartitionIndex(int plannedPartitionId, int effectiveTopicPartitionCount) { + if (effectiveTopicPartitionCount <= 0) { + return 0; + } + int kafkaIndex = toKafkaPartitionIndex(plannedPartitionId); + if (kafkaIndex < 0) { + return 0; + } + if (kafkaIndex >= effectiveTopicPartitionCount) { + return effectiveTopicPartitionCount - 1; + } + return kafkaIndex; + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/audit/partition/PartitionPlanValidator.java b/agents-common/src/main/java/org/apache/ranger/audit/partition/PartitionPlanValidator.java new file mode 100644 index 00000000000..79b4dc95f1b --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/audit/partition/PartitionPlanValidator.java @@ -0,0 +1,144 @@ +/* + * 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.audit.partition; + +import org.apache.commons.lang3.StringUtils; +import org.apache.ranger.audit.partition.exception.PartitionPlanException; +import org.apache.ranger.audit.partition.model.PartitionPlan; +import org.apache.ranger.audit.partition.model.PluginEntry; + +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** Checks partition plan shape and append-only updates. */ +public final class PartitionPlanValidator { + private PartitionPlanValidator() { + } + + public static void validate(PartitionPlan plan) { + validate(plan, null); + } + + /** + * When kafkaPartitionCount is set, it must be at least plan.topicPartitionCount. + * Extra live Kafka partitions (e.g. after static-mode migration) are allowed. + */ + public static void validate(PartitionPlan plan, Integer kafkaPartitionCount) { + if (plan == null || StringUtils.isBlank(plan.getTopic()) || plan.getVersion() < AuditPartitionPlanConstants.INITIAL_PLAN_VERSION || plan.getTopicPartitionCount() < 1) { + throw new PartitionPlanException("Invalid partition plan"); + } + if (kafkaPartitionCount != null && kafkaPartitionCount < plan.getTopicPartitionCount()) { + throw new PartitionPlanException("Kafka topic has fewer partitions than plan requires"); + } + + Set assigned = new HashSet<>(); + registerPartitions(plan.getBuffer().getPartitions(), assigned, true); + for (Map.Entry entry : plan.getPlugins().entrySet()) { + if (StringUtils.isBlank(entry.getKey())) { + throw new PartitionPlanException("Plugin id is required"); + } + registerPartitions(entry.getValue().getPartitions(), assigned, false); + } + if (assigned.size() != plan.getTopicPartitionCount()) { + throw new PartitionPlanException("topicPartitionCount must equal the union of all assigned partition ids"); + } + validateServiceUniqueness(plan.getPlugins()); + validateServiceAllowedUsers(plan.getServiceAllowedUsers()); + } + + /** + * When a service repo is listed in {@code serviceAllowedUsers}, it must have at least one + * allowed short username (from Admin {@code policy.download.auth.users}). + */ + public static void validateServiceAllowedUsers(Map> serviceAllowedUsers) { + if (serviceAllowedUsers == null || serviceAllowedUsers.isEmpty()) { + return; + } + for (Map.Entry> entry : serviceAllowedUsers.entrySet()) { + if (StringUtils.isBlank(entry.getKey())) { + throw new PartitionPlanException("Service repo name is required"); + } + List users = entry.getValue(); + if (users == null || users.isEmpty()) { + throw new PartitionPlanException( + "allowedUsers must not be empty for service '" + entry.getKey().trim() + "'"); + } + } + } + + /** Each Ranger service repo name may appear in at most one plugin entry. */ + public static void validateServiceUniqueness(Map plugins) { + if (plugins == null || plugins.isEmpty()) { + return; + } + Set seenServices = new HashSet<>(); + for (Map.Entry entry : plugins.entrySet()) { + for (String serviceName : entry.getValue().getServices()) { + if (!seenServices.add(serviceName)) { + throw new PartitionPlanException("Service '" + serviceName + "' is assigned to more than one plugin"); + } + } + } + } + + /** New plan must only add tail partitions; existing plugin lists stay unchanged in order. */ + public static void validateAppendOnly(PartitionPlan current, PartitionPlan proposed) { + if (current == null || proposed == null) { + throw new PartitionPlanException("Current and proposed plans are required"); + } + if (proposed.getTopicPartitionCount() < current.getTopicPartitionCount() || proposed.getVersion() != current.getVersion() + 1) { + throw new PartitionPlanException("Plan must grow partition count and increment version by one"); + } + + for (Map.Entry entry : current.getPlugins().entrySet()) { + String pluginId = entry.getKey(); + List before = entry.getValue().getPartitions(); + PluginEntry afterEntry = proposed.getPlugins().get(pluginId); + if (afterEntry == null) { + throw new PartitionPlanException("Append-only violation for plugin '" + pluginId + "'"); + } + List after = afterEntry.getPartitions(); + if (after.size() < before.size()) { + throw new PartitionPlanException("Append-only violation for plugin '" + pluginId + "'"); + } + for (int i = 0; i < before.size(); i++) { + if (!before.get(i).equals(after.get(i))) { + throw new PartitionPlanException("Append-only violation for plugin '" + pluginId + "' at index " + i); + } + } + } + } + + private static void registerPartitions(List partitionIds, Set assigned, boolean allowEmpty) { + if (partitionIds.isEmpty()) { + if (allowEmpty) { + return; + } + throw new PartitionPlanException("Plugin partition list must not be empty"); + } + for (int partitionId : partitionIds) { + if (partitionId < 1 || !assigned.add(partitionId)) { + throw new PartitionPlanException("Invalid or duplicate partition id: " + partitionId); + } + } + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/audit/partition/PolicyDownloadAuthUsersUtil.java b/agents-common/src/main/java/org/apache/ranger/audit/partition/PolicyDownloadAuthUsersUtil.java new file mode 100644 index 00000000000..08d6844f288 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/audit/partition/PolicyDownloadAuthUsersUtil.java @@ -0,0 +1,96 @@ +/* + * 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.audit.partition; + +import org.apache.commons.lang3.StringUtils; +import org.apache.ranger.plugin.model.RangerService; + +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** Parses {@code policy.download.auth.users} service config for audit ingestor authorization. */ +public final class PolicyDownloadAuthUsersUtil { + public static final String CONFIG_NAME = "policy.download.auth.users"; + + /** + * Ingestor site key pattern: {@code ranger.audit.ingestor.service..allowed.users}. + * Values originate from Admin {@link #CONFIG_NAME}; partition plan {@code serviceAllowedUsers} + * keys use the same {@code } names (Policy Manager service name). + */ + public static final String INGESTOR_ALLOWED_USERS_SUFFIX = "allowed.users"; + + private PolicyDownloadAuthUsersUtil() { + } + + public static List parseUsers(RangerService service) { + if (service == null || service.getConfigs() == null) { + return Collections.emptyList(); + } + return parseUsers(service.getConfigs().get(CONFIG_NAME)); + } + + public static List parseUsers(String configValue) { + if (StringUtils.isBlank(configValue)) { + return Collections.emptyList(); + } + return Arrays.stream(configValue.split(",")) + .map(String::trim) + .filter(StringUtils::isNotBlank) + .filter(user -> !"*".equals(user)) + .collect(Collectors.toList()); + } + + public static Map> normalizeServiceAllowedUsers(Map> serviceAllowedUsers) { + if (serviceAllowedUsers == null || serviceAllowedUsers.isEmpty()) { + return Collections.emptyMap(); + } + Map> normalized = new LinkedHashMap<>(); + for (Map.Entry> entry : serviceAllowedUsers.entrySet()) { + if (StringUtils.isBlank(entry.getKey())) { + continue; + } + List users = entry.getValue() == null ? Collections.emptyList() : parseUsers(String.join(",", entry.getValue())); + if (users.isEmpty()) { + continue; + } + normalized.put(entry.getKey().trim(), List.copyOf(users)); + } + return Collections.unmodifiableMap(normalized); + } + + /** Converts plan allow-list to ingestor lookup map; skips repos with no users (same as static site config). */ + public static Map> toAllowedUserSets(Map> serviceAllowedUsers) { + Map> normalized = normalizeServiceAllowedUsers(serviceAllowedUsers); + if (normalized.isEmpty()) { + return Collections.emptyMap(); + } + Map> allowed = new LinkedHashMap<>(); + for (Map.Entry> entry : normalized.entrySet()) { + allowed.put(entry.getKey(), new LinkedHashSet<>(entry.getValue())); + } + return Collections.unmodifiableMap(allowed); + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/audit/partition/exception/PartitionPlanException.java b/agents-common/src/main/java/org/apache/ranger/audit/partition/exception/PartitionPlanException.java new file mode 100644 index 00000000000..92726af30fa --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/audit/partition/exception/PartitionPlanException.java @@ -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. + */ + +package org.apache.ranger.audit.partition.exception; + +public class PartitionPlanException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public PartitionPlanException(String message) { + super(message); + } + + public PartitionPlanException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/audit/partition/model/BufferEntry.java b/agents-common/src/main/java/org/apache/ranger/audit/partition/model/BufferEntry.java new file mode 100644 index 00000000000..bc90262aaca --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/audit/partition/model/BufferEntry.java @@ -0,0 +1,73 @@ +/* + * 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.audit.partition.model; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.io.Serializable; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +@JsonAutoDetect(getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE, fieldVisibility = Visibility.ANY) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class BufferEntry implements Serializable { + private static final long serialVersionUID = 1L; + + private final List partitions; + + @JsonCreator + public BufferEntry(@JsonProperty("partitions") List partitions) { + if (partitions == null || partitions.isEmpty()) { + this.partitions = Collections.emptyList(); + } else { + this.partitions = List.copyOf(partitions); + } + } + + public static BufferEntry empty() { + return new BufferEntry(Collections.emptyList()); + } + + public List getPartitions() { + return partitions; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + BufferEntry other = (BufferEntry) obj; + return Objects.equals(partitions, other.partitions); + } + + @Override + public int hashCode() { + return Objects.hash(partitions); + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/audit/partition/model/PartitionPlan.java b/agents-common/src/main/java/org/apache/ranger/audit/partition/model/PartitionPlan.java new file mode 100644 index 00000000000..ffcff2ad492 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/audit/partition/model/PartitionPlan.java @@ -0,0 +1,260 @@ +/* + * 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.audit.partition.model; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.apache.ranger.audit.partition.PartitionPlanValidator; +import org.apache.ranger.audit.partition.exception.PartitionPlanException; +import org.apache.ranger.authorization.utils.JsonUtils; + +import java.io.Serializable; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +@JsonAutoDetect(getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE, fieldVisibility = Visibility.ANY) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class PartitionPlan implements Serializable { + private static final long serialVersionUID = 1L; + + private final String topic; + private final int version; + private final int topicPartitionCount; + private final String updatedAt; + private final String updatedBy; + private final Map plugins; + private final BufferEntry buffer; + /** Per-repo audit POST allow-list ({@code policy.download.auth.users}); each listed repo needs ≥1 user. */ + private final Map> serviceAllowedUsers; + + @JsonCreator + public PartitionPlan(@JsonProperty("topic") String topic, @JsonProperty("version") int version, @JsonProperty("topicPartitionCount") int topicPartitionCount, @JsonProperty("updatedAt") String updatedAt, @JsonProperty("updatedBy") String updatedBy, @JsonProperty("plugins") Map plugins, @JsonProperty("buffer") BufferEntry buffer, @JsonProperty("serviceAllowedUsers") Map> serviceAllowedUsers) { + this.topic = topic; + this.version = version; + this.topicPartitionCount = topicPartitionCount; + this.updatedAt = updatedAt; + this.updatedBy = updatedBy; + this.plugins = copyPlugins(plugins); + this.buffer = buffer != null ? buffer : BufferEntry.empty(); + this.serviceAllowedUsers = copyServiceAllowedUsers(serviceAllowedUsers); + } + + public String getTopic() { + return topic; + } + + public int getVersion() { + return version; + } + + public int getTopicPartitionCount() { + return topicPartitionCount; + } + + public String getUpdatedAt() { + return updatedAt; + } + + public String getUpdatedBy() { + return updatedBy; + } + + public Map getPlugins() { + return plugins; + } + + public BufferEntry getBuffer() { + return buffer; + } + + public Map> getServiceAllowedUsers() { + return serviceAllowedUsers; + } + + /** Compares routing payload; ignores version, updatedAt, updatedBy, and serviceAllowedUsers. */ + public boolean sameContentAs(PartitionPlan other) { + if (other == null) { + return false; + } + return topicPartitionCount == other.topicPartitionCount + && Objects.equals(topic, other.topic) + && Objects.equals(plugins, other.plugins) + && Objects.equals(buffer, other.buffer); + } + + public Builder toBuilder() { + return new Builder(this); + } + + public static Builder builder() { + return new Builder(); + } + + public String toJson() { + String json = JsonUtils.objectToJson(this); + if (json == null) { + throw new PartitionPlanException("Failed to serialize partition plan"); + } + return json; + } + + public static PartitionPlan fromJson(String json) { + try { + PartitionPlan plan = JsonUtils.jsonToObject(json, PartitionPlan.class); + PartitionPlanValidator.validate(plan); + return plan; + } catch (PartitionPlanException e) { + throw e; + } catch (Exception e) { + throw new PartitionPlanException("Failed to deserialize partition plan", e); + } + } + + @Override + public boolean equals(Object otherPartitionPlanObj) { + if (this == otherPartitionPlanObj) { + return true; + } + if (otherPartitionPlanObj == null || getClass() != otherPartitionPlanObj.getClass()) { + return false; + } + PartitionPlan otherPartitionPlan = (PartitionPlan) otherPartitionPlanObj; + return version == otherPartitionPlan.version + && topicPartitionCount == otherPartitionPlan.topicPartitionCount + && Objects.equals(topic, otherPartitionPlan.topic) + && Objects.equals(updatedAt, otherPartitionPlan.updatedAt) + && Objects.equals(updatedBy, otherPartitionPlan.updatedBy) + && Objects.equals(plugins, otherPartitionPlan.plugins) + && Objects.equals(buffer, otherPartitionPlan.buffer) + && Objects.equals(serviceAllowedUsers, otherPartitionPlan.serviceAllowedUsers); + } + + @Override + public int hashCode() { + return Objects.hash(topic, version, topicPartitionCount, updatedAt, updatedBy, plugins, buffer, serviceAllowedUsers); + } + + @Override + public String toString() { + return "PartitionPlan{topic='" + topic + "', version=" + version + ", topicPartitionCount=" + topicPartitionCount + ", plugins=" + plugins.keySet() + ", bufferSize=" + buffer.getPartitions().size() + ", serviceAllowedUsers=" + serviceAllowedUsers.keySet() + '}'; + } + + private static Map copyPlugins(Map plugins) { + if (plugins == null || plugins.isEmpty()) { + return Collections.emptyMap(); + } + return Collections.unmodifiableMap(new LinkedHashMap<>(plugins)); + } + + private static Map> copyServiceAllowedUsers(Map> serviceAllowedUsers) { + if (serviceAllowedUsers == null || serviceAllowedUsers.isEmpty()) { + return Collections.emptyMap(); + } + Map> copy = new LinkedHashMap<>(); + for (Map.Entry> entry : serviceAllowedUsers.entrySet()) { + if (entry.getKey() == null || entry.getKey().isBlank()) { + continue; + } + List users = entry.getValue() == null ? Collections.emptyList() : List.copyOf(entry.getValue()); + copy.put(entry.getKey().trim(), users); + } + return Collections.unmodifiableMap(copy); + } + + public static final class Builder { + private String topic; + private int version = 1; + private int topicPartitionCount; + private String updatedAt; + private String updatedBy; + private Map plugins = new LinkedHashMap<>(); + private BufferEntry buffer = BufferEntry.empty(); + private Map> serviceAllowedUsers = new LinkedHashMap<>(); + + private Builder() { + } + + private Builder(PartitionPlan plan) { + this.topic = plan.topic; + this.version = plan.version; + this.topicPartitionCount = plan.topicPartitionCount; + this.updatedAt = plan.updatedAt; + this.updatedBy = plan.updatedBy; + this.plugins = new LinkedHashMap<>(plan.plugins); + this.buffer = plan.buffer; + this.serviceAllowedUsers = new LinkedHashMap<>(plan.serviceAllowedUsers); + } + + public Builder topic(String topic) { + this.topic = topic; + return this; + } + + public Builder version(int version) { + this.version = version; + return this; + } + + public Builder topicPartitionCount(int topicPartitionCount) { + this.topicPartitionCount = topicPartitionCount; + return this; + } + + public Builder updatedAt(String updatedAt) { + this.updatedAt = updatedAt; + return this; + } + + public Builder updatedBy(String updatedBy) { + this.updatedBy = updatedBy; + return this; + } + + public Builder plugins(Map plugins) { + this.plugins = plugins == null ? new LinkedHashMap<>() : new LinkedHashMap<>(plugins); + return this; + } + + public Builder putPlugin(String pluginId, PluginEntry entry) { + this.plugins.put(pluginId, entry); + return this; + } + + public Builder buffer(BufferEntry buffer) { + this.buffer = buffer != null ? buffer : BufferEntry.empty(); + return this; + } + + public Builder serviceAllowedUsers(Map> serviceAllowedUsers) { + this.serviceAllowedUsers = serviceAllowedUsers == null ? new LinkedHashMap<>() : new LinkedHashMap<>(serviceAllowedUsers); + return this; + } + + public PartitionPlan build() { + return new PartitionPlan(topic, version, topicPartitionCount, updatedAt, updatedBy, plugins, buffer, serviceAllowedUsers); + } + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/audit/partition/model/PluginEntry.java b/agents-common/src/main/java/org/apache/ranger/audit/partition/model/PluginEntry.java new file mode 100644 index 00000000000..5419ae48310 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/audit/partition/model/PluginEntry.java @@ -0,0 +1,122 @@ +/* + * 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.audit.partition.model; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Objects; + +@JsonAutoDetect(getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE, fieldVisibility = Visibility.ANY) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class PluginEntry implements Serializable { + private static final long serialVersionUID = 1L; + + private final List partitions; + private final List services; + + @JsonCreator + public PluginEntry(@JsonProperty("partitions") List partitions, @JsonProperty("services") List services) { + this.partitions = copyPartitions(partitions); + this.services = copyServices(services); + } + + public static PluginEntry ofPartitions(int... partitionIds) { + List ids = new ArrayList<>(partitionIds.length); + for (int id : partitionIds) { + ids.add(id); + } + return new PluginEntry(ids, Collections.emptyList()); + } + + public static PluginEntry empty() { + return new PluginEntry(Collections.emptyList(), Collections.emptyList()); + } + + public List getPartitions() { + return partitions; + } + + public List getServices() { + return services; + } + + public PluginEntry withPartitions(List newPartitions) { + return new PluginEntry(newPartitions, services); + } + + public PluginEntry withServices(List newServices) { + return new PluginEntry(partitions, newServices); + } + + public PluginEntry addService(String serviceName) { + if (serviceName == null || serviceName.isBlank()) { + return this; + } + LinkedHashSet merged = new LinkedHashSet<>(services); + merged.add(serviceName.trim()); + return new PluginEntry(partitions, List.copyOf(merged)); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + PluginEntry other = (PluginEntry) obj; + return Objects.equals(partitions, other.partitions) && Objects.equals(services, other.services); + } + + @Override + public int hashCode() { + return Objects.hash(partitions, services); + } + + private static List copyPartitions(List partitions) { + if (partitions == null || partitions.isEmpty()) { + return Collections.emptyList(); + } + return List.copyOf(partitions); + } + + private static List copyServices(List services) { + if (services == null || services.isEmpty()) { + return Collections.emptyList(); + } + LinkedHashSet unique = new LinkedHashSet<>(); + for (String service : services) { + if (service != null && !service.isBlank()) { + unique.add(service.trim()); + } + } + return List.copyOf(unique); + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRESTClient.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRESTClient.java index d4d49523bd2..94046a549ef 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRESTClient.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRESTClient.java @@ -60,6 +60,8 @@ import java.security.SecureRandom; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; +import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Random; @@ -143,6 +145,7 @@ public String getMethod() { private volatile Client cookieAuthClient; private JwtProvider jwtProvider; private volatile String authHeader; + private volatile Map trustedAuthHeaders = Collections.emptyMap(); public RangerRESTClient(String url, String sslConfigFileName, Configuration config) { this(url, sslConfigFileName, config, getPropertyPrefix(config)); @@ -215,6 +218,19 @@ public void setRetryIntervalMs(int retryIntervalMs) { this.retryIntervalMs = retryIntervalMs; } + /** + * Trusted HTTP headers for SPIFFE or other header-based auth. + * Applied to every REST request from this client. + */ + public void setTrustedAuthHeaders(Map headers) { + if (headers == null || headers.isEmpty()) { + trustedAuthHeaders = Collections.emptyMap(); + } else { + trustedAuthHeaders = Collections.unmodifiableMap(new LinkedHashMap<>(headers)); + } + resetClient(); + } + public void setBasicAuthInfo(String username, String password) { setBasicAuthFilter(username, password); } @@ -494,9 +510,17 @@ private Invocation.Builder createInvocationBuilder(int currentIndex, String rela builder = builder.cookie(sessionId); } + applyTrustedAuthHeaders(builder); + return builder; } + private void applyTrustedAuthHeaders(Invocation.Builder builder) { + for (Map.Entry entry : trustedAuthHeaders.entrySet()) { + builder.header(entry.getKey(), entry.getValue()); + } + } + private Response performRequest(HttpMethod method, String relativeUrl, Map params, Object requestBody, Cookie sessionId) throws Exception { Response finalResponse = null; int startIndex = this.lastKnownActiveUrlIndex; diff --git a/agents-common/src/test/java/org/apache/ranger/audit/partition/AuditPartitionPlanAdminConfigTest.java b/agents-common/src/test/java/org/apache/ranger/audit/partition/AuditPartitionPlanAdminConfigTest.java new file mode 100644 index 00000000000..8d94f30a913 --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/audit/partition/AuditPartitionPlanAdminConfigTest.java @@ -0,0 +1,47 @@ +/* + * 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.audit.partition; + +import org.apache.hadoop.conf.Configuration; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class AuditPartitionPlanAdminConfigTest { + @Test + public void testDefaultPartitionsPerPlugin() { + Configuration config = new Configuration(false); + assertEquals(3, AuditPartitionPlanAdminConfig.resolvePartitionsPerPlugin("hdfs", config)); + } + + @Test + public void testGlobalDefaultFromSiteProperty() { + Configuration config = new Configuration(false); + config.set(AuditPartitionPlanConstants.PROP_ADMIN_PARTITIONS_PER_PLUGIN, "6"); + assertEquals(6, AuditPartitionPlanAdminConfig.resolvePartitionsPerPlugin("hdfs", config)); + } + + @Test + public void testPerPluginOverride() { + Configuration config = new Configuration(false); + config.set(AuditPartitionPlanConstants.PROP_ADMIN_PARTITIONS_PER_PLUGIN, "3"); + config.set(AuditPartitionPlanConstants.PROP_ADMIN_PLUGIN_PARTITION_OVERRIDE_PREFIX + "hiveServer2", "9"); + assertEquals(3, AuditPartitionPlanAdminConfig.resolvePartitionsPerPlugin("hdfs", config)); + assertEquals(9, AuditPartitionPlanAdminConfig.resolvePartitionsPerPlugin("hiveServer2", config)); + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/audit/partition/PartitionPlanAllocatorTest.java b/agents-common/src/test/java/org/apache/ranger/audit/partition/PartitionPlanAllocatorTest.java new file mode 100644 index 00000000000..7baa39120c3 --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/audit/partition/PartitionPlanAllocatorTest.java @@ -0,0 +1,164 @@ +/* + * 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.audit.partition; + +import org.apache.ranger.audit.partition.exception.PartitionPlanException; +import org.apache.ranger.audit.partition.model.PartitionPlan; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertIterableEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class PartitionPlanAllocatorTest { + private PartitionPlan initialPlan; + + @BeforeEach + public void setUp() { + initialPlan = PartitionPlanTestSupport.preAssignedPlan(); + } + + @Test + public void testPromotePluginFromBuffer() { + PartitionPlan next = PartitionPlanAllocator.promotePlugin(initialPlan, "trino", 3, "ops"); + + assertEquals(2, next.getVersion()); + assertEquals(9, next.getTopicPartitionCount()); + assertIterableEquals(List.of(7, 8, 9), next.getPlugins().get("trino").getPartitions()); + assertIterableEquals(List.of(), next.getBuffer().getPartitions()); + assertIterableEquals(List.of(1, 2, 3), next.getPlugins().get("hdfs").getPartitions()); + assertIterableEquals(List.of(4, 5, 6), next.getPlugins().get("hiveServer2").getPartitions()); + } + + @Test + public void testPromotePluginGrowsTopicWhenBufferInsufficient() { + PartitionPlan seed = PartitionPlanTestSupport.seedPlan(); + PartitionPlan next = PartitionPlanAllocator.promotePlugin(seed, "trino", 12, "ops"); + + assertEquals(2, next.getVersion()); + assertEquals(12, next.getTopicPartitionCount()); + assertIterableEquals(List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12), next.getPlugins().get("trino").getPartitions()); + assertIterableEquals(List.of(), next.getBuffer().getPartitions()); + } + + @Test + public void testOnboardServicePromotesPluginAndAddsService() { + PartitionPlan seed = PartitionPlanTestSupport.seedPlan(); + PartitionPlan next = PartitionPlanAllocator.onboardService(seed, "hiveServer2", "dev_hive", 6, "admin"); + + assertEquals(2, next.getVersion()); + assertEquals(9, next.getTopicPartitionCount()); + assertIterableEquals(List.of(1, 2, 3, 4, 5, 6), next.getPlugins().get("hiveServer2").getPartitions()); + assertIterableEquals(List.of("dev_hive"), next.getPlugins().get("hiveServer2").getServices()); + assertIterableEquals(List.of(7, 8, 9), next.getBuffer().getPartitions()); + } + + @Test + public void testOnboardServiceAddsToExistingPlugin() { + PartitionPlan promoted = PartitionPlanAllocator.onboardService(PartitionPlanTestSupport.seedPlan(), "hiveServer2", "dev_hive", 6, "admin"); + PartitionPlan next = PartitionPlanAllocator.onboardService(promoted, "hiveServer2", "prod_hive", 6, "admin"); + + assertEquals(3, next.getVersion()); + assertIterableEquals(List.of("dev_hive", "prod_hive"), next.getPlugins().get("hiveServer2").getServices()); + assertIterableEquals(List.of(1, 2, 3, 4, 5, 6), next.getPlugins().get("hiveServer2").getPartitions()); + } + + @Test + public void testRemoveService() { + PartitionPlan onboarded = PartitionPlanAllocator.onboardService(PartitionPlanTestSupport.seedPlan(), "hiveServer2", "dev_hive", 6, "admin"); + PartitionPlan next = PartitionPlanAllocator.removeService(onboarded, "dev_hive", "admin"); + + assertEquals(3, next.getVersion()); + assertIterableEquals(List.of(), next.getPlugins().get("hiveServer2").getServices()); + } + + @Test + public void testScalePluginAppendsTailOnly() { + PartitionPlan promoted = PartitionPlanAllocator.promotePlugin(initialPlan, "trino", 3, "ops"); + PartitionPlan scaled = PartitionPlanAllocator.scalePlugin(promoted, "hiveServer2", 3, "ops"); + + assertEquals(3, scaled.getVersion()); + assertEquals(12, scaled.getTopicPartitionCount()); + assertIterableEquals(List.of(4, 5, 6, 10, 11, 12), scaled.getPlugins().get("hiveServer2").getPartitions()); + assertIterableEquals(List.of(1, 2, 3), scaled.getPlugins().get("hdfs").getPartitions()); + assertIterableEquals(List.of(7, 8, 9), scaled.getPlugins().get("trino").getPartitions()); + } + + @Test + public void testPromoteAlreadyConfiguredPluginFails() { + PartitionPlanException error = assertThrows(PartitionPlanException.class, + () -> PartitionPlanAllocator.promotePlugin(initialPlan, "hdfs", 1, "ops")); + assertTrue(error.getMessage().contains("requested 1")); + } + + @Test + public void testIsPromoteAlreadyAppliedWhenPluginAndCountMatch() { + PartitionPlan promoted = PartitionPlanAllocator.promotePlugin(initialPlan, "trino", 3, "ops"); + + assertTrue(PartitionPlanAllocator.isPromoteAlreadyApplied(promoted, "trino", 3)); + assertFalse(PartitionPlanAllocator.isPromoteAlreadyApplied(promoted, "trino", 5)); + } + + @Test + public void testIsOnboardAlreadyAppliedWhenServiceAndPluginMatch() { + PartitionPlan onboarded = PartitionPlanAllocator.onboardService(PartitionPlanTestSupport.seedPlan(), "hiveServer2", "dev_hive", 6, "admin"); + + assertTrue(PartitionPlanAllocator.isOnboardAlreadyApplied(onboarded, "hiveServer2", "dev_hive", 6)); + assertFalse(PartitionPlanAllocator.isOnboardAlreadyApplied(onboarded, "hiveServer2", "prod_hive", 6)); + } + + @Test + public void testPromoteConflictWhenPartitionCountDiffers() { + PartitionPlan promoted = PartitionPlanAllocator.promotePlugin(initialPlan, "trino", 3, "ops"); + + PartitionPlanException error = assertThrows(PartitionPlanException.class, + () -> PartitionPlanAllocator.promotePlugin(promoted, "trino", 5, "ops")); + + assertTrue(error.getMessage().contains("requested 5")); + } + + @Test + public void testScaleUnknownPluginFails() { + assertThrows(PartitionPlanException.class, () -> PartitionPlanAllocator.scalePlugin(initialPlan, "trino", 2, "ops")); + } + + @Test + public void testUpdateServiceAllowedUsersBumpsVersionWhenMapChanges() { + PartitionPlan next = PartitionPlanAllocator.updateServiceAllowedUsers( + initialPlan, Map.of("dev_hive", List.of("hive")), "admin"); + + assertEquals(2, next.getVersion()); + assertIterableEquals(List.of("hive"), next.getServiceAllowedUsers().get("dev_hive")); + } + + @Test + public void testUpdateServiceAllowedUsersIsNoOpWhenUnchanged() { + PartitionPlan withUsers = PartitionPlanAllocator.updateServiceAllowedUsers( + initialPlan, Map.of("dev_hive", List.of("hive")), "admin"); + PartitionPlan unchanged = PartitionPlanAllocator.updateServiceAllowedUsers( + withUsers, Map.of("dev_hive", List.of("hive")), "admin"); + + assertEquals(withUsers, unchanged); + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/audit/partition/PartitionPlanRoutingUtilsTest.java b/agents-common/src/test/java/org/apache/ranger/audit/partition/PartitionPlanRoutingUtilsTest.java new file mode 100644 index 00000000000..edce4104cfa --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/audit/partition/PartitionPlanRoutingUtilsTest.java @@ -0,0 +1,43 @@ +/* + * 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.audit.partition; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class PartitionPlanRoutingUtilsTest { + @Test + public void testToKafkaPartitionIndexUsesZeroBasedMapping() { + assertEquals(0, PartitionPlanRoutingUtils.toKafkaPartitionIndex(1)); + assertEquals(8, PartitionPlanRoutingUtils.toKafkaPartitionIndex(9)); + } + + @Test + public void testResolveKafkaPartitionIndexClampsToTopicSize() { + assertEquals(8, PartitionPlanRoutingUtils.resolveKafkaPartitionIndex(9, 9)); + assertEquals(8, PartitionPlanRoutingUtils.resolveKafkaPartitionIndex(12, 9)); + } + + @Test + public void testHashToSlotIndexUsesFloorModForMinHashCode() { + String minHashKey = "polygenelubricants"; + assertEquals(Integer.MIN_VALUE, minHashKey.hashCode()); + assertEquals(2, PartitionPlanRoutingUtils.hashToSlotIndex(minHashKey, 5)); + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/audit/partition/PartitionPlanTestSupport.java b/agents-common/src/test/java/org/apache/ranger/audit/partition/PartitionPlanTestSupport.java new file mode 100644 index 00000000000..836f48f0083 --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/audit/partition/PartitionPlanTestSupport.java @@ -0,0 +1,62 @@ +/* + * 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.audit.partition; + +import org.apache.ranger.audit.partition.model.BufferEntry; +import org.apache.ranger.audit.partition.model.PartitionPlan; +import org.apache.ranger.audit.partition.model.PluginEntry; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +final class PartitionPlanTestSupport { + private PartitionPlanTestSupport() { + } + + static PartitionPlan seedPlan() { + return PartitionPlan.builder() + .topic(AuditPartitionPlanConstants.DEFAULT_AUDIT_TOPIC) + .version(AuditPartitionPlanConstants.INITIAL_PLAN_VERSION) + .topicPartitionCount(9) + .buffer(new BufferEntry(partitionRange(1, 9))) + .build(); + } + + static PartitionPlan preAssignedPlan() { + Map plugins = new LinkedHashMap<>(); + plugins.put("hdfs", PluginEntry.ofPartitions(1, 2, 3)); + plugins.put("hiveServer2", PluginEntry.ofPartitions(4, 5, 6)); + return PartitionPlan.builder() + .topic(AuditPartitionPlanConstants.DEFAULT_AUDIT_TOPIC) + .version(AuditPartitionPlanConstants.INITIAL_PLAN_VERSION) + .topicPartitionCount(9) + .plugins(plugins) + .buffer(new BufferEntry(partitionRange(7, 9))) + .build(); + } + + private static List partitionRange(int startInclusive, int endInclusive) { + List ids = new ArrayList<>(); + for (int id = startInclusive; id <= endInclusive; id++) { + ids.add(id); + } + return ids; + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/audit/partition/PartitionPlanValidatorTest.java b/agents-common/src/test/java/org/apache/ranger/audit/partition/PartitionPlanValidatorTest.java new file mode 100644 index 00000000000..f39e462adc8 --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/audit/partition/PartitionPlanValidatorTest.java @@ -0,0 +1,154 @@ +/* + * 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.audit.partition; + +import org.apache.ranger.audit.partition.exception.PartitionPlanException; +import org.apache.ranger.audit.partition.model.BufferEntry; +import org.apache.ranger.audit.partition.model.PartitionPlan; +import org.apache.ranger.audit.partition.model.PluginEntry; +import org.junit.jupiter.api.Test; + +import java.util.LinkedHashMap; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class PartitionPlanValidatorTest { + @Test + public void testValidateAcceptsSeedPlan() { + PartitionPlan plan = PartitionPlanTestSupport.seedPlan(); + assertDoesNotThrow(() -> PartitionPlanValidator.validate(plan, 9)); + } + + @Test + public void testValidateAcceptsPreAssignedPlan() { + PartitionPlan plan = PartitionPlanTestSupport.preAssignedPlan(); + assertDoesNotThrow(() -> PartitionPlanValidator.validate(plan)); + } + + @Test + public void testValidateRejectsDuplicatePartitionIds() { + Map plugins = new LinkedHashMap<>(); + plugins.put("hdfs", PluginEntry.ofPartitions(1, 2)); + PartitionPlan plan = PartitionPlan.builder() + .topic("ranger_audits") + .version(1) + .topicPartitionCount(3) + .plugins(plugins) + .buffer(new BufferEntry(java.util.List.of(2, 3))) + .build(); + assertThrows(PartitionPlanException.class, () -> PartitionPlanValidator.validate(plan)); + } + + @Test + public void testValidateRejectsUnionSizeMismatch() { + PartitionPlan plan = PartitionPlan.builder() + .topic("ranger_audits") + .version(1) + .topicPartitionCount(10) + .buffer(new BufferEntry(java.util.List.of(1, 2, 3, 4, 5, 6, 7, 8, 9))) + .build(); + assertThrows(PartitionPlanException.class, () -> PartitionPlanValidator.validate(plan)); + } + + @Test + public void testValidateRejectsZeroBasedPartitionId() { + PartitionPlan plan = PartitionPlan.builder() + .topic("ranger_audits") + .version(1) + .topicPartitionCount(1) + .buffer(new BufferEntry(java.util.List.of(0))) + .build(); + assertThrows(PartitionPlanException.class, () -> PartitionPlanValidator.validate(plan)); + } + + @Test + public void testValidateRejectsDuplicateServiceAssignment() { + Map plugins = new LinkedHashMap<>(); + plugins.put("hdfs", new PluginEntry(java.util.List.of(1, 2, 3), java.util.List.of("dev_hdfs"))); + plugins.put("hiveServer2", new PluginEntry(java.util.List.of(4, 5, 6), java.util.List.of("dev_hdfs"))); + PartitionPlan plan = PartitionPlan.builder() + .topic("ranger_audits") + .version(1) + .topicPartitionCount(9) + .plugins(plugins) + .buffer(new BufferEntry(java.util.List.of(7, 8, 9))) + .build(); + assertThrows(PartitionPlanException.class, () -> PartitionPlanValidator.validate(plan)); + } + + @Test + public void testValidateRejectsKafkaPartitionCountBelowPlan() { + PartitionPlan plan = PartitionPlanTestSupport.seedPlan(); + assertThrows(PartitionPlanException.class, () -> PartitionPlanValidator.validate(plan, 5)); + } + + @Test + public void testValidateAcceptsKafkaPartitionCountAbovePlan() { + PartitionPlan plan = PartitionPlanTestSupport.seedPlan(); + assertDoesNotThrow(() -> PartitionPlanValidator.validate(plan, 30)); + } + + @Test + public void testValidateAppendOnlyRejectsReshuffle() { + PartitionPlan current = PartitionPlanTestSupport.preAssignedPlan(); + Map reshuffled = new LinkedHashMap<>(); + reshuffled.put("hdfs", PluginEntry.ofPartitions(1, 2, 3, 4)); + reshuffled.put("hiveServer2", PluginEntry.ofPartitions(5, 6)); + PartitionPlan proposed = PartitionPlan.builder() + .topic("ranger_audits") + .version(2) + .topicPartitionCount(9) + .plugins(reshuffled) + .buffer(new BufferEntry(java.util.List.of(7, 8, 9))) + .build(); + assertThrows(PartitionPlanException.class, () -> PartitionPlanValidator.validateAppendOnly(current, proposed)); + } + + @Test + public void testValidateAppendOnlyAcceptsTailGrowth() { + PartitionPlan current = PartitionPlanTestSupport.preAssignedPlan(); + Map grown = new LinkedHashMap<>(); + grown.put("hdfs", PluginEntry.ofPartitions(1, 2, 3)); + grown.put("hiveServer2", PluginEntry.ofPartitions(4, 5, 6, 10, 11, 12)); + PartitionPlan proposed = PartitionPlan.builder() + .topic("ranger_audits") + .version(2) + .topicPartitionCount(12) + .plugins(grown) + .buffer(new BufferEntry(java.util.List.of(7, 8, 9))) + .build(); + assertDoesNotThrow(() -> PartitionPlanValidator.validateAppendOnly(current, proposed)); + } + + @Test + public void testValidateRejectsEmptyServiceAllowedUsers() { + Map> allowlists = new LinkedHashMap<>(); + allowlists.put("dev_hive", java.util.Collections.emptyList()); + PartitionPlan plan = PartitionPlan.builder() + .topic("ranger_audits") + .version(1) + .topicPartitionCount(9) + .plugins(PartitionPlanTestSupport.preAssignedPlan().getPlugins()) + .buffer(new BufferEntry(java.util.List.of(7, 8, 9))) + .serviceAllowedUsers(allowlists) + .build(); + assertThrows(PartitionPlanException.class, () -> PartitionPlanValidator.validate(plan)); + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/audit/partition/PolicyDownloadAuthUsersUtilTest.java b/agents-common/src/test/java/org/apache/ranger/audit/partition/PolicyDownloadAuthUsersUtilTest.java new file mode 100644 index 00000000000..649303446a8 --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/audit/partition/PolicyDownloadAuthUsersUtilTest.java @@ -0,0 +1,75 @@ +/* + * 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.audit.partition; + +import org.apache.ranger.plugin.model.RangerService; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertIterableEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class PolicyDownloadAuthUsersUtilTest { + @Test + public void testParseUsersFromConfigValue() { + assertIterableEquals(List.of("hive", "hive2"), PolicyDownloadAuthUsersUtil.parseUsers(" hive, hive2 ")); + } + + @Test + public void testParseUsersIgnoresWildcard() { + assertTrue(PolicyDownloadAuthUsersUtil.parseUsers("*").isEmpty()); + } + + @Test + public void testParseUsersFromService() { + RangerService service = new RangerService(); + Map configs = new HashMap<>(); + configs.put(PolicyDownloadAuthUsersUtil.CONFIG_NAME, "hdfs"); + service.setConfigs(configs); + + assertIterableEquals(List.of("hdfs"), PolicyDownloadAuthUsersUtil.parseUsers(service)); + } + + @Test + public void testNormalizeServiceAllowedUsersSkipsEmptyEntries() { + Map> input = new LinkedHashMap<>(); + input.put("dev_hive", List.of("hive")); + input.put("dev_empty", List.of()); + input.put("dev_wildcard", List.of("*")); + + Map> normalized = PolicyDownloadAuthUsersUtil.normalizeServiceAllowedUsers(input); + + assertEquals(1, normalized.size()); + assertIterableEquals(List.of("hive"), normalized.get("dev_hive")); + } + + @Test + public void testToAllowedUserSets() { + Map> input = Map.of("dev_hive", List.of("hive", "hive2")); + + Map> allowed = PolicyDownloadAuthUsersUtil.toAllowedUserSets(input); + + assertEquals(Set.of("hive", "hive2"), allowed.get("dev_hive")); + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/audit/partition/model/PartitionPlanJsonTest.java b/agents-common/src/test/java/org/apache/ranger/audit/partition/model/PartitionPlanJsonTest.java new file mode 100644 index 00000000000..fdd03058dca --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/audit/partition/model/PartitionPlanJsonTest.java @@ -0,0 +1,73 @@ +/* + * 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.audit.partition.model; + +import org.apache.ranger.audit.partition.AuditPartitionPlanConstants; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertIterableEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class PartitionPlanJsonTest { + @Test + public void testRoundTripSeedJson() { + String seedJson = "{\"version\":1,\"topic\":\"ranger_audits\",\"topicPartitionCount\":9,\"plugins\":{},\"buffer\":{\"partitions\":[1,2,3,4,5,6,7,8,9]}}"; + PartitionPlan plan = PartitionPlan.fromJson(seedJson); + + assertEquals(AuditPartitionPlanConstants.DEFAULT_AUDIT_TOPIC, plan.getTopic()); + assertEquals(1, plan.getVersion()); + assertEquals(9, plan.getTopicPartitionCount()); + assertEquals(0, plan.getPlugins().size()); + assertEquals(9, plan.getBuffer().getPartitions().size()); + + String roundTrip = plan.toJson(); + assertNotNull(roundTrip); + PartitionPlan parsedAgain = PartitionPlan.fromJson(roundTrip); + assertEquals(plan, parsedAgain); + } + + @Test + public void testRoundTripOnboardedPluginJson() { + String json = "{\"version\":2,\"topic\":\"ranger_audits\",\"topicPartitionCount\":9," + + "\"plugins\":{\"hiveServer2\":{\"partitions\":[1,2,3,4,5,6],\"services\":[\"dev_hive\",\"prod_hive\"]}}," + + "\"buffer\":{\"partitions\":[7,8,9]}}"; + PartitionPlan plan = PartitionPlan.fromJson(json); + + assertEquals(2, plan.getVersion()); + assertEquals(2, plan.getPlugins().get("hiveServer2").getServices().size()); + assertEquals(6, plan.getPlugins().get("hiveServer2").getPartitions().size()); + } + + @Test + public void testRoundTripServiceAllowedUsersJson() { + String json = "{\"version\":3,\"topic\":\"ranger_audits\",\"topicPartitionCount\":9," + + "\"plugins\":{},\"buffer\":{\"partitions\":[1,2,3,4,5,6,7,8,9]}," + + "\"serviceAllowedUsers\":{\"dev_hive\":[\"hive\"],\"dev_ozone\":[\"om\"]}}"; + PartitionPlan plan = PartitionPlan.fromJson(json); + + assertEquals(3, plan.getVersion()); + assertIterableEquals(List.of("hive"), plan.getServiceAllowedUsers().get("dev_hive")); + assertIterableEquals(List.of("om"), plan.getServiceAllowedUsers().get("dev_ozone")); + + PartitionPlan parsedAgain = PartitionPlan.fromJson(plan.toJson()); + assertEquals(plan, parsedAgain); + } +} diff --git a/common-utils/src/main/java/org/apache/ranger/plugin/util/PluginHeaderAuthConfig.java b/common-utils/src/main/java/org/apache/ranger/plugin/util/PluginHeaderAuthConfig.java new file mode 100644 index 00000000000..0f4e77b15eb --- /dev/null +++ b/common-utils/src/main/java/org/apache/ranger/plugin/util/PluginHeaderAuthConfig.java @@ -0,0 +1,169 @@ +/* + * 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.util; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +/** + * Outbound trusted-header auth for audit-server and other REST clients. + * + *

Properties are read under a caller-supplied prefix (audit destination example): + *

+ * xasecure.audit.destination.auditserver.authn.header.enabled=true
+ * xasecure.audit.destination.auditserver.authn.header.spiffe=X-Spiffe-Id
+ * 
+ * SPIFFE ID value is resolved via {@link SpiffeIdentityResolver} under the same + * prefix (explicit value, identity file, or {@code SPIFFE_ID} environment variable). + */ +public final class PluginHeaderAuthConfig { + public static final String RANGER_CONFIG_PREFIX = "ranger."; + public static final String PROP_HEADER_AUTH_ENABLED = "authn.header.enabled"; + public static final String PROP_HEADER_SPIFFE = "authn.header.spiffe"; + public static final String DEFAULT_SPIFFE_HEADER_NAME = "X-Spiffe-Id"; + + private static final Logger LOG = + LoggerFactory.getLogger(PluginHeaderAuthConfig.class); + + private PluginHeaderAuthConfig() { + // to block instantiation + } + + /** + * Builds the {@code ranger.} config prefix for a service type. + * + * @param serviceType Ranger service type (e.g. {@code hive}) + * @return the config prefix, or {@code null} when {@code serviceType} is blank + */ + public static String configPrefixForServiceType(final String serviceType) { + if (StringUtils.isBlank(serviceType)) { + return null; + } + + return RANGER_CONFIG_PREFIX + serviceType.trim(); + } + + /** + * Finds the first {@code ranger..authn.header.enabled=true} + * prefix in {@code props}. + * + * @param props plugin or site configuration properties + * @return the matching config prefix, or {@code null} when none is enabled + */ + public static String resolveEnabledConfigPrefix(final Properties props) { + if (props == null || props.isEmpty()) { + return null; + } + + String suffix = "." + PROP_HEADER_AUTH_ENABLED; + + for (String key : props.stringPropertyNames()) { + if (!key.startsWith(RANGER_CONFIG_PREFIX) || !key.endsWith(suffix)) { + continue; + } + + String prefix = key.substring(0, key.length() - suffix.length()); + + if (isHeaderAuthEnabled(props, prefix)) { + return prefix; + } + } + + return null; + } + + /** + * Returns whether trusted header auth is enabled for the given config prefix. + * + * @param props plugin or site configuration properties + * @param configPrefix prefix such as {@code ranger.hive} + * @return {@code true} when header auth is enabled + */ + public static boolean isHeaderAuthEnabled(final Properties props, + final String configPrefix) { + if (props == null || StringUtils.isBlank(configPrefix)) { + return false; + } + + return Boolean.parseBoolean( + props.getProperty(configPrefix + "." + PROP_HEADER_AUTH_ENABLED, + "false")); + } + + /** + * Builds SPIFFE header(s) for outbound REST calls when header auth is enabled. + * + * @param props plugin or site configuration properties + * @param configPrefix prefix such as {@code xasecure.audit.destination.auditserver} + * @return immutable header map; empty when auth is disabled or misconfigured + */ + public static Map buildSpiffeAuthHeaders(final Properties props, + final String configPrefix) { + if (!isHeaderAuthEnabled(props, configPrefix)) { + return Collections.emptyMap(); + } + + List headerNames = SpiffeIdUtil.parseHeaderNames( + resolveSpiffeHeaderName(props, configPrefix)); + String spiffeId = SpiffeIdentityResolver.resolve(props, configPrefix); + + if (headerNames.isEmpty()) { + LOG.warn("Plugin header auth enabled for {} but no SPIFFE header " + + "name is configured", configPrefix); + return Collections.emptyMap(); + } + + if (StringUtils.isBlank(spiffeId)) { + LOG.warn("Plugin header auth enabled for {} but no SPIFFE ID could " + + "be resolved", configPrefix); + return Collections.emptyMap(); + } + + if (!SpiffeIdUtil.isValidSpiffeId(spiffeId)) { + LOG.warn("Resolved SPIFFE ID for {} is not well-formed", configPrefix); + return Collections.emptyMap(); + } + + Map headers = new LinkedHashMap<>(); + + for (String headerName : headerNames) { + headers.put(headerName, spiffeId.trim()); + } + + return Collections.unmodifiableMap(headers); + } + + private static String resolveSpiffeHeaderName(final Properties props, + final String configPrefix) { + String headerName = props != null + ? StringUtils.trimToNull( + props.getProperty(configPrefix + "." + PROP_HEADER_SPIFFE)) + : null; + + return headerName != null ? headerName : DEFAULT_SPIFFE_HEADER_NAME; + } +} diff --git a/common-utils/src/main/java/org/apache/ranger/plugin/util/SpiffeIdentityResolver.java b/common-utils/src/main/java/org/apache/ranger/plugin/util/SpiffeIdentityResolver.java new file mode 100644 index 00000000000..1df60d408ce --- /dev/null +++ b/common-utils/src/main/java/org/apache/ranger/plugin/util/SpiffeIdentityResolver.java @@ -0,0 +1,117 @@ +/* + * 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.util; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.Properties; + +/** + * Resolves a workload SPIFFE ID from plugin/site configuration. + * + *

Resolution order: explicit {@code authn.spiffe.value}, identity file + * ({@code authn.spiffe.file} or the default SPIRE path), then {@code SPIFFE_ID} + * environment variable. + */ +public final class SpiffeIdentityResolver { + public static final String PROP_SPIFFE_VALUE = "authn.spiffe.value"; + public static final String PROP_SPIFFE_FILE = "authn.spiffe.file"; + public static final String ENV_SPIFFE_ID = "SPIFFE_ID"; + public static final String DEFAULT_SPIFFE_IDENTITY_FILE = + "/var/run/secrets/spiffe.io/identity/spiffe"; + + private static final Logger LOG = + LoggerFactory.getLogger(SpiffeIdentityResolver.class); + + private SpiffeIdentityResolver() { + // to block instantiation + } + + /** + * Resolves the SPIFFE ID for the given config prefix. + * + * @param props plugin or site configuration properties + * @param configPrefix prefix such as {@code ranger.hive} + * @return the resolved SPIFFE ID, or {@code null} when unavailable + */ + public static String resolve(final Properties props, final String configPrefix) { + if (props == null || StringUtils.isBlank(configPrefix)) { + return null; + } + + String value = StringUtils.trimToNull( + props.getProperty(configPrefix + "." + PROP_SPIFFE_VALUE)); + + if (value != null) { + return value; + } + + String filePath = StringUtils.trimToNull( + props.getProperty(configPrefix + "." + PROP_SPIFFE_FILE)); + + if (filePath == null) { + filePath = DEFAULT_SPIFFE_IDENTITY_FILE; + } + + value = readFirstLine(filePath); + + if (value != null) { + return value; + } + + return StringUtils.trimToNull(System.getenv(ENV_SPIFFE_ID)); + } + + private static String readFirstLine(final String filePath) { + if (StringUtils.isBlank(filePath)) { + return null; + } + + try { + Path path = Paths.get(filePath.trim()); + + if (!Files.isRegularFile(path)) { + return null; + } + + List lines = Files.readAllLines(path, StandardCharsets.UTF_8); + + for (String line : lines) { + String trimmed = StringUtils.trimToNull(line); + + if (trimmed != null) { + return trimmed; + } + } + } catch (IOException ex) { + LOG.debug("Unable to read SPIFFE identity from file {}", filePath, ex); + } + + return null; + } +} diff --git a/common-utils/src/test/java/org/apache/ranger/plugin/util/PluginHeaderAuthConfigTest.java b/common-utils/src/test/java/org/apache/ranger/plugin/util/PluginHeaderAuthConfigTest.java new file mode 100644 index 00000000000..b2bd167ac3b --- /dev/null +++ b/common-utils/src/test/java/org/apache/ranger/plugin/util/PluginHeaderAuthConfigTest.java @@ -0,0 +1,90 @@ +/* + * 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.util; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; +import java.util.Properties; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class PluginHeaderAuthConfigTest { + private static final String VALID_SPIFFE = + "spiffe://prod-cluster.k8s.example.com/ns/ranger/sa/om"; + + @Test + public void resolveEnabledConfigPrefixFindsOzonePrefix() { + Properties props = new Properties(); + props.setProperty("ranger.ozone.authn.header.enabled", "true"); + + assertEquals("ranger.ozone", PluginHeaderAuthConfig.resolveEnabledConfigPrefix(props)); + } + + @Test + public void buildSpiffeAuthHeadersUsesConfiguredHeaderName() { + Properties props = new Properties(); + props.setProperty("ranger.ozone.authn.header.enabled", "true"); + props.setProperty("ranger.ozone.authn.header.spiffe", "X-Spiffe-Id"); + props.setProperty("ranger.ozone.authn.spiffe.value", VALID_SPIFFE); + + Map headers = PluginHeaderAuthConfig.buildSpiffeAuthHeaders(props, "ranger.ozone"); + + assertEquals(VALID_SPIFFE, headers.get("X-Spiffe-Id")); + } + + @Test + public void buildSpiffeAuthHeadersEmptyWhenDisabled() { + Properties props = new Properties(); + props.setProperty("ranger.ozone.authn.header.enabled", "false"); + props.setProperty("ranger.ozone.authn.spiffe.value", VALID_SPIFFE); + + assertTrue(PluginHeaderAuthConfig.buildSpiffeAuthHeaders(props, "ranger.ozone").isEmpty()); + } + + @Test + public void resolveSpiffeIdFromFile(@TempDir Path tempDir) throws Exception { + Path spiffeFile = tempDir.resolve("spiffe"); + Files.writeString(spiffeFile, VALID_SPIFFE + "\n", StandardCharsets.UTF_8); + + Properties props = new Properties(); + props.setProperty("ranger.hive.authn.spiffe.file", spiffeFile.toString()); + + assertEquals(VALID_SPIFFE, SpiffeIdentityResolver.resolve(props, "ranger.hive")); + } + + @Test + public void isHeaderAuthEnabledFalseForMissingPrefix() { + assertFalse(PluginHeaderAuthConfig.isHeaderAuthEnabled(new Properties(), "ranger.ozone")); + } + + @Test + public void resolveEnabledConfigPrefixNullWhenDisabled() { + Properties props = new Properties(); + props.setProperty("ranger.hive.authn.header.enabled", "false"); + + assertNull(PluginHeaderAuthConfig.resolveEnabledConfigPrefix(props)); + } +} 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 46f53a73b0c..c8a6b067c54 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 @@ -597,7 +597,7 @@ CREATE TABLE IF NOT EXISTS `x_ranger_global_state`( `upd_by_id` bigint(20) NULL DEFAULT NULL, `version` bigint(20) NULL DEFAULT NULL, `state_name` varchar(255) NOT NULL, -`app_data` varchar(255) NULL DEFAULT NULL, +`app_data` TEXT NULL DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `x_ranger_global_state_UK_state_name`(`state_name`), CONSTRAINT `x_ranger_global_state_FK_added_by_id` FOREIGN KEY (`added_by_id`) REFERENCES `x_portal_user` (`id`), @@ -1806,6 +1806,7 @@ DECLARE adminID bigint; DECLARE keyadminID bigint; DECLARE rangerusersyncID bigint; DECLARE rangertagsyncID bigint; +DECLARE rangerauditserverID bigint; DECLARE moduleIdReports bigint; DECLARE moduleIdResourceBasedPolicies bigint; DECLARE moduleIdAudit bigint; @@ -1819,11 +1820,13 @@ INSERT INTO x_portal_user(create_time,update_time,added_by_id,upd_by_id,first_na INSERT INTO x_portal_user(create_time,update_time,added_by_id,upd_by_id,first_name,last_name,pub_scr_name,login_id,password,email,status,user_src,notes) VALUES (UTC_TIMESTAMP(),UTC_TIMESTAMP(),NULL,NULL,'rangerusersync','','rangerusersync','rangerusersync','70b8374d3dfe0325aaa5002a688c7e3b','rangerusersync',1,0,NULL); INSERT INTO x_portal_user(create_time,update_time,added_by_id,upd_by_id,first_name,last_name,pub_scr_name,login_id,password,email,status,user_src,notes) VALUES (UTC_TIMESTAMP(),UTC_TIMESTAMP(),NULL,NULL,'keyadmin','','keyadmin','keyadmin','a05f34d2dce2b4688fa82e82a89ba958','keyadmin',1,0,NULL); INSERT INTO x_portal_user(create_time,update_time,added_by_id,upd_by_id,first_name,last_name,pub_scr_name,login_id,password,email,status,user_src,notes) VALUES (UTC_TIMESTAMP(),UTC_TIMESTAMP(),NULL,NULL,'rangertagsync','','rangertagsync','rangertagsync','f5820e1229418dcf2575908f2c493da5','rangertagsync',1,0,NULL); +INSERT INTO x_portal_user(create_time,update_time,added_by_id,upd_by_id,first_name,last_name,pub_scr_name,login_id,password,email,status,user_src,notes) VALUES (UTC_TIMESTAMP(),UTC_TIMESTAMP(),NULL,NULL,'rangerauditserver','','rangerauditserver','rangerauditserver','9c8f4e2b1a0d6e3f7b5c4a8291d0e6f3','rangerauditserver',1,0,NULL); call getXportalUIdByLoginId('admin', adminID); call getXportalUIdByLoginId('keyadmin', keyadminID); call getXportalUIdByLoginId('rangerusersync', rangerusersyncID); call getXportalUIdByLoginId('rangertagsync', rangertagsyncID); +call getXportalUIdByLoginId('rangerauditserver', rangerauditserverID); INSERT INTO `x_modules_master` (`create_time`,`update_time`,`added_by_id`,`upd_by_id`,`module`,`url`) VALUES (UTC_TIMESTAMP(),UTC_TIMESTAMP(),adminID,adminID,'Resource Based Policies',''),(UTC_TIMESTAMP(),UTC_TIMESTAMP(),adminID,adminID,'Users/Groups',''),(UTC_TIMESTAMP(),UTC_TIMESTAMP(),adminID,adminID,'Reports',''),(UTC_TIMESTAMP(),UTC_TIMESTAMP(),adminID,adminID,'Audit',''),(UTC_TIMESTAMP(),UTC_TIMESTAMP(),adminID,adminID,'Key Manager',''),(UTC_TIMESTAMP(),UTC_TIMESTAMP(),adminID,adminID,'Tag Based Policies',''); INSERT INTO `x_modules_master` (`create_time`,`update_time`,`added_by_id`,`upd_by_id`,`module`,`url`) VALUES (UTC_TIMESTAMP(),UTC_TIMESTAMP(),adminID,adminID,'Security Zone',''); @@ -1847,6 +1850,8 @@ INSERT INTO x_portal_user_role(create_time,update_time,added_by_id,upd_by_id,use INSERT INTO x_user(create_time,update_time,added_by_id,upd_by_id,user_name,descr,status) values (UTC_TIMESTAMP(), UTC_TIMESTAMP(),NULL,NULL,'keyadmin','keyadmin',0); INSERT INTO x_portal_user_role(create_time,update_time,added_by_id,upd_by_id,user_id,user_role,status) VALUES (UTC_TIMESTAMP(),UTC_TIMESTAMP(),NULL,NULL,rangertagsyncID,'ROLE_SYS_ADMIN',1); INSERT INTO x_user(create_time,update_time,added_by_id,upd_by_id,user_name,descr,status) values (UTC_TIMESTAMP(), UTC_TIMESTAMP(),NULL,NULL,'rangertagsync','rangertagsync',0); +INSERT INTO x_portal_user_role(create_time,update_time,added_by_id,upd_by_id,user_id,user_role,status) VALUES (UTC_TIMESTAMP(),UTC_TIMESTAMP(),NULL,NULL,rangerauditserverID,'ROLE_ADMIN_AUDITOR',1); +INSERT INTO x_user(create_time,update_time,added_by_id,upd_by_id,user_name,descr,status) values (UTC_TIMESTAMP(), UTC_TIMESTAMP(),NULL,NULL,'rangerauditserver','Ranger audit server machine user',0); INSERT INTO x_security_zone(id, create_time, update_time, added_by_id, upd_by_id, version, name, jsonData, description) VALUES (1, UTC_TIMESTAMP(),UTC_TIMESTAMP(), adminID, adminID, 1, ' ', '', 'Unzoned zone'); @@ -1878,6 +1883,7 @@ INSERT INTO x_user_module_perm (user_id,module_id,create_time,update_time,added_ INSERT INTO x_ranger_global_state (create_time,update_time,added_by_id,upd_by_id,version,state_name,app_data) VALUES (UTC_TIMESTAMP(),UTC_TIMESTAMP(),adminID,adminID,1,'RangerRole','{"Version":"1"}'); INSERT INTO x_ranger_global_state (create_time,update_time,added_by_id,upd_by_id,version,state_name,app_data) VALUES (UTC_TIMESTAMP(),UTC_TIMESTAMP(),adminID,adminID,1,'RangerUserStore','{"Version":"1"}'); INSERT INTO x_ranger_global_state (create_time,update_time,added_by_id,upd_by_id,version,state_name,app_data) VALUES (UTC_TIMESTAMP(),UTC_TIMESTAMP(),adminID,adminID,1,'RangerSecurityZone','{"Version":"1"}'); +INSERT INTO x_ranger_global_state (create_time,update_time,added_by_id,upd_by_id,version,state_name,app_data) VALUES (UTC_TIMESTAMP(),UTC_TIMESTAMP(),adminID,adminID,1,'RangerAuditPartitionPlan','{"version":1,"topic":"ranger_audits","topicPartitionCount":9,"plugins":{},"buffer":{"partitions":[1,2,3,4,5,6,7,8,9]}}'); END $$ DELIMITER ; @@ -1953,6 +1959,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 ('075',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 ('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 ('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'); diff --git a/security-admin/db/mysql/patches/078-audit-partition-plan-global-state.sql b/security-admin/db/mysql/patches/078-audit-partition-plan-global-state.sql new file mode 100644 index 00000000000..57a33ac5de5 --- /dev/null +++ b/security-admin/db/mysql/patches/078-audit-partition-plan-global-state.sql @@ -0,0 +1,76 @@ +-- 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. + +DELIMITER $$ +DROP PROCEDURE IF EXISTS getXportalUIdByLoginId$$ +CREATE PROCEDURE `getXportalUIdByLoginId`(IN input_val VARCHAR(100), OUT myid BIGINT) +BEGIN +SET myid = 0; +SELECT x_portal_user.id INTO myid FROM x_portal_user WHERE x_portal_user.login_id = input_val; +END $$ + +DELIMITER ; + +DROP PROCEDURE IF EXISTS patch_audit_partition_plan_global_state; + +DELIMITER ;; +CREATE PROCEDURE patch_audit_partition_plan_global_state() +BEGIN + DECLARE adminID BIGINT; + DECLARE auditServerID BIGINT; + DECLARE planJson TEXT DEFAULT '{"version":1,"topic":"ranger_audits","topicPartitionCount":9,"plugins":{},"buffer":{"partitions":[1,2,3,4,5,6,7,8,9]}}'; + + IF EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = DATABASE() AND table_name = 'x_ranger_global_state' AND column_name = 'state_name' + ) THEN + IF EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = DATABASE() AND table_name = 'x_ranger_global_state' + AND column_name = 'app_data' AND data_type = 'varchar' + ) THEN + ALTER TABLE x_ranger_global_state MODIFY app_data TEXT DEFAULT NULL; + END IF; + + IF NOT EXISTS (SELECT 1 FROM x_portal_user WHERE login_id = 'rangerauditserver') THEN + INSERT INTO x_portal_user(create_time, update_time, added_by_id, upd_by_id, first_name, last_name, pub_scr_name, login_id, password, email, status, user_src, notes) + VALUES (UTC_TIMESTAMP(), UTC_TIMESTAMP(), NULL, NULL, 'rangerauditserver', '', 'rangerauditserver', 'rangerauditserver', '9c8f4e2b1a0d6e3f7b5c4a8291d0e6f3', 'rangerauditserver', 1, 0, NULL); + END IF; + + CALL getXportalUIdByLoginId('admin', adminID); + CALL getXportalUIdByLoginId('rangerauditserver', auditServerID); + + IF auditServerID IS NOT NULL AND NOT EXISTS ( + SELECT 1 FROM x_portal_user_role WHERE user_id = auditServerID AND user_role = 'ROLE_ADMIN_AUDITOR' + ) THEN + INSERT INTO x_portal_user_role(create_time, update_time, added_by_id, upd_by_id, user_id, user_role, status) + VALUES (UTC_TIMESTAMP(), UTC_TIMESTAMP(), NULL, NULL, auditServerID, 'ROLE_ADMIN_AUDITOR', 1); + END IF; + + IF auditServerID IS NOT NULL AND NOT EXISTS (SELECT 1 FROM x_user WHERE user_name = 'rangerauditserver') THEN + INSERT INTO x_user(create_time, update_time, added_by_id, upd_by_id, user_name, descr, status) + VALUES (UTC_TIMESTAMP(), UTC_TIMESTAMP(), NULL, NULL, 'rangerauditserver', 'Ranger audit server machine user', 0); + END IF; + + IF NOT EXISTS (SELECT 1 FROM x_ranger_global_state WHERE state_name = 'RangerAuditPartitionPlan') THEN + INSERT INTO x_ranger_global_state (create_time, update_time, added_by_id, upd_by_id, version, state_name, app_data) + VALUES (UTC_TIMESTAMP(), UTC_TIMESTAMP(), adminID, adminID, 1, 'RangerAuditPartitionPlan', planJson); + END IF; + END IF; +END;; + +DELIMITER ; +CALL patch_audit_partition_plan_global_state(); +DROP PROCEDURE IF EXISTS patch_audit_partition_plan_global_state; 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 006c047881a..aa4b45e2e19 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 @@ -719,7 +719,7 @@ added_by_id NUMBER(20) DEFAULT NULL NULL, upd_by_id NUMBER(20) DEFAULT NULL NULL, version NUMBER(20) DEFAULT NULL NULL, state_name varchar(255) NOT NULL, -app_data varchar(255) DEFAULT NULL NULL, +app_data CLOB DEFAULT NULL NULL, primary key (id), CONSTRAINT x_rngr_glbl_state_UK_statename UNIQUE(state_name), CONSTRAINT x_rngr_glbl_state_FK_addedbyid FOREIGN KEY (added_by_id) REFERENCES x_portal_user (id), @@ -2069,6 +2069,10 @@ INSERT INTO x_portal_user(ID,CREATE_TIME,UPDATE_TIME,FIRST_NAME,LAST_NAME,PUB_SC INSERT INTO x_portal_user_role(id,create_time,update_time,user_id,user_role,status) VALUES(X_PORTAL_USER_ROLE_SEQ.nextval,sys_extract_utc(systimestamp),sys_extract_utc(systimestamp),getXportalUIdByLoginId('rangertagsync'),'ROLE_SYS_ADMIN',1); INSERT INTO x_user(id,create_time,update_time,user_name,descr,status) values (X_USER_SEQ.nextval,sys_extract_utc(systimestamp),sys_extract_utc(systimestamp),'rangertagsync','rangertagsync',0); +INSERT INTO x_portal_user(ID,CREATE_TIME,UPDATE_TIME,FIRST_NAME,LAST_NAME,PUB_SCR_NAME,LOGIN_ID,PASSWORD,EMAIL,STATUS,USER_SRC) VALUES(X_PORTAL_USER_SEQ.nextval,sys_extract_utc(systimestamp),sys_extract_utc(systimestamp),'rangerauditserver',NULL,'rangerauditserver','rangerauditserver','9c8f4e2b1a0d6e3f7b5c4a8291d0e6f3','rangerauditserver',1,0); +INSERT INTO x_portal_user_role(id,create_time,update_time,user_id,user_role,status) VALUES(X_PORTAL_USER_ROLE_SEQ.nextval,sys_extract_utc(systimestamp),sys_extract_utc(systimestamp),getXportalUIdByLoginId('rangerauditserver'),'ROLE_ADMIN_AUDITOR',1); +INSERT INTO x_user(id,create_time,update_time,user_name,descr,status) values (X_USER_SEQ.nextval,sys_extract_utc(systimestamp),sys_extract_utc(systimestamp),'rangerauditserver','Ranger audit server machine user',0); + INSERT INTO x_modules_master VALUES(X_MODULES_MASTER_SEQ.NEXTVAL,sys_extract_utc(systimestamp),sys_extract_utc(systimestamp),getXportalUIdByLoginId('admin'),getXportalUIdByLoginId('admin'),'Resource Based Policies',''); INSERT INTO x_modules_master VALUES(X_MODULES_MASTER_SEQ.NEXTVAL,sys_extract_utc(systimestamp),sys_extract_utc(systimestamp),getXportalUIdByLoginId('admin'),getXportalUIdByLoginId('admin'),'Users/Groups',''); INSERT INTO x_modules_master VALUES(X_MODULES_MASTER_SEQ.NEXTVAL,sys_extract_utc(systimestamp),sys_extract_utc(systimestamp),getXportalUIdByLoginId('admin'),getXportalUIdByLoginId('admin'),'Reports',''); @@ -2145,6 +2149,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, '075',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, '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, '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); @@ -2177,6 +2182,7 @@ INSERT INTO x_user_module_perm (id,user_id,module_id,create_time,update_time,add INSERT INTO x_ranger_global_state (id,create_time,update_time,added_by_id,upd_by_id,version,state_name,app_data) VALUES (X_RANGER_GLOBAL_STATE_SEQ.nextval,sys_extract_utc(systimestamp),sys_extract_utc(systimestamp),getXportalUIdByLoginId('admin'),getXportalUIdByLoginId('admin'),1,'RangerRole','{"Version":"1"}'); INSERT INTO x_ranger_global_state (id,create_time,update_time,added_by_id,upd_by_id,version,state_name,app_data) VALUES (X_RANGER_GLOBAL_STATE_SEQ.nextval,sys_extract_utc(systimestamp),sys_extract_utc(systimestamp),getXportalUIdByLoginId('admin'),getXportalUIdByLoginId('admin'),1,'RangerUserStore','{"Version":"1"}'); INSERT INTO x_ranger_global_state (id,create_time,update_time,added_by_id,upd_by_id,version,state_name,app_data) VALUES (X_RANGER_GLOBAL_STATE_SEQ.nextval,sys_extract_utc(systimestamp),sys_extract_utc(systimestamp),getXportalUIdByLoginId('admin'),getXportalUIdByLoginId('admin'),1,'RangerSecurityZone','{"Version":"1"}'); +INSERT INTO x_ranger_global_state (id,create_time,update_time,added_by_id,upd_by_id,version,state_name,app_data) VALUES (X_RANGER_GLOBAL_STATE_SEQ.nextval,sys_extract_utc(systimestamp),sys_extract_utc(systimestamp),getXportalUIdByLoginId('admin'),getXportalUIdByLoginId('admin'),1,'RangerAuditPartitionPlan','{"version":1,"topic":"ranger_audits","topicPartitionCount":9,"plugins":{},"buffer":{"partitions":[1,2,3,4,5,6,7,8,9]}}'); INSERT INTO x_db_version_h (id,version,inst_at,inst_by,updated_at,updated_by,active) VALUES (X_DB_VERSION_H_SEQ.nextval,'J10001',sys_extract_utc(systimestamp),'Ranger 1.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,'J10002',sys_extract_utc(systimestamp),'Ranger 1.0.0',sys_extract_utc(systimestamp),'localhost','Y'); diff --git a/security-admin/db/oracle/patches/078-audit-partition-plan-global-state.sql b/security-admin/db/oracle/patches/078-audit-partition-plan-global-state.sql new file mode 100644 index 00000000000..ea1494a568c --- /dev/null +++ b/security-admin/db/oracle/patches/078-audit-partition-plan-global-state.sql @@ -0,0 +1,83 @@ +-- 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. + +CREATE OR REPLACE FUNCTION getXportalUIdByLoginId(input_val IN VARCHAR2) +RETURN NUMBER IS +BEGIN +DECLARE + myid Number := 0; +BEGIN + SELECT x_portal_user.id INTO myid FROM x_portal_user WHERE x_portal_user.login_id = input_val; + RETURN myid; +END; +END; +/ + +DECLARE + t_count number := 0; + v_admin_id number; + v_audit_user_id number; + v_plan_count number := 0; + v_user_count number := 0; + v_role_count number := 0; + v_xuser_count number := 0; + v_plan_json CLOB := '{"version":1,"topic":"ranger_audits","topicPartitionCount":9,"plugins":{},"buffer":{"partitions":[1,2,3,4,5,6,7,8,9]}}'; + sql_stmt VARCHAR2(4000); +BEGIN + SELECT count(*) INTO t_count FROM user_tables WHERE table_name = 'X_RANGER_GLOBAL_STATE'; + IF (t_count > 0) THEN + BEGIN + EXECUTE IMMEDIATE 'ALTER TABLE x_ranger_global_state MODIFY (app_data CLOB)'; + EXCEPTION + WHEN OTHERS THEN + NULL; + END; + + v_admin_id := getXportalUIdByLoginId('admin'); + + SELECT count(*) INTO v_user_count FROM x_portal_user WHERE login_id = 'rangerauditserver'; + IF (v_user_count = 0) THEN + sql_stmt := 'INSERT INTO x_portal_user (id, create_time, update_time, first_name, last_name, pub_scr_name, login_id, password, email, status, user_src) VALUES (X_PORTAL_USER_SEQ.nextval, sys_extract_utc(systimestamp), sys_extract_utc(systimestamp), :1, NULL, :2, :3, :4, :5, 1, 0)'; + EXECUTE IMMEDIATE sql_stmt USING 'rangerauditserver', 'rangerauditserver', 'rangerauditserver', '9c8f4e2b1a0d6e3f7b5c4a8291d0e6f3', 'rangerauditserver'; + COMMIT; + END IF; + + v_audit_user_id := getXportalUIdByLoginId('rangerauditserver'); + + IF (v_audit_user_id IS NOT NULL AND v_audit_user_id > 0) THEN + SELECT count(*) INTO v_role_count FROM x_portal_user_role WHERE user_id = v_audit_user_id AND user_role = 'ROLE_ADMIN_AUDITOR'; + IF (v_role_count = 0) THEN + sql_stmt := 'INSERT INTO x_portal_user_role (id, create_time, update_time, user_id, user_role, status) VALUES (X_PORTAL_USER_ROLE_SEQ.nextval, sys_extract_utc(systimestamp), sys_extract_utc(systimestamp), :1, :2, 1)'; + EXECUTE IMMEDIATE sql_stmt USING v_audit_user_id, 'ROLE_ADMIN_AUDITOR'; + COMMIT; + END IF; + + SELECT count(*) INTO v_xuser_count FROM x_user WHERE user_name = 'rangerauditserver'; + IF (v_xuser_count = 0) THEN + sql_stmt := 'INSERT INTO x_user (id, create_time, update_time, user_name, status, descr) VALUES (X_USER_SEQ.nextval, sys_extract_utc(systimestamp), sys_extract_utc(systimestamp), :1, 0, :2)'; + EXECUTE IMMEDIATE sql_stmt USING 'rangerauditserver', 'Ranger audit server machine user'; + COMMIT; + END IF; + END IF; + + SELECT count(*) INTO v_plan_count FROM x_ranger_global_state WHERE state_name = 'RangerAuditPartitionPlan'; + IF (v_plan_count = 0) THEN + sql_stmt := 'INSERT INTO x_ranger_global_state (id, create_time, update_time, added_by_id, upd_by_id, version, state_name, app_data) VALUES (X_RANGER_GLOBAL_STATE_SEQ.nextval, sys_extract_utc(systimestamp), sys_extract_utc(systimestamp), :1, :2, 1, :3, :4)'; + EXECUTE IMMEDIATE sql_stmt USING v_admin_id, v_admin_id, 'RangerAuditPartitionPlan', v_plan_json; + COMMIT; + END IF; + END IF; +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 8aa20a8dde4..bd41e42493d 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 @@ -607,7 +607,7 @@ added_by_id BIGINT DEFAULT NULL NULL, upd_by_id BIGINT DEFAULT NULL NULL, version BIGINT DEFAULT NULL NULL, state_name varchar(255) NOT NULL, -app_data varchar(255) DEFAULT NULL NULL, +app_data TEXT DEFAULT NULL NULL, primary key (id), CONSTRAINT x_ranger_global_state_UK_state_name UNIQUE (state_name), CONSTRAINT x_ranger_global_state_FK_added_by_id FOREIGN KEY (added_by_id) REFERENCES x_portal_user (id), @@ -1997,6 +1997,10 @@ INSERT INTO x_portal_user(CREATE_TIME,UPDATE_TIME,FIRST_NAME,LAST_NAME,PUB_SCR_N INSERT INTO x_portal_user_role(CREATE_TIME,UPDATE_TIME,USER_ID,USER_ROLE,STATUS)VALUES(current_timestamp,current_timestamp,getXportalUIdByLoginId('rangertagsync'),'ROLE_SYS_ADMIN',1); INSERT INTO x_user(CREATE_TIME,UPDATE_TIME,user_name,status,descr)VALUES(current_timestamp,current_timestamp,'rangertagsync',0,'rangertagsync'); +INSERT INTO x_portal_user(CREATE_TIME,UPDATE_TIME,FIRST_NAME,LAST_NAME,PUB_SCR_NAME,LOGIN_ID,PASSWORD,EMAIL,STATUS)VALUES(current_timestamp,current_timestamp,'rangerauditserver','','rangerauditserver','rangerauditserver','9c8f4e2b1a0d6e3f7b5c4a8291d0e6f3','rangerauditserver',1); +INSERT INTO x_portal_user_role(CREATE_TIME,UPDATE_TIME,USER_ID,USER_ROLE,STATUS)VALUES(current_timestamp,current_timestamp,getXportalUIdByLoginId('rangerauditserver'),'ROLE_ADMIN_AUDITOR',1); +INSERT INTO x_user(CREATE_TIME,UPDATE_TIME,user_name,status,descr)VALUES(current_timestamp,current_timestamp,'rangerauditserver',0,'Ranger audit server machine user'); + INSERT INTO x_modules_master(create_time,update_time,added_by_id,upd_by_id,module,url) VALUES(current_timestamp,current_timestamp,getXportalUIdByLoginId('admin'),getXportalUIdByLoginId('admin'),'Tag Based Policies',''); INSERT INTO x_modules_master(create_time,update_time,added_by_id,upd_by_id,module,url) VALUES(current_timestamp,current_timestamp,getXportalUIdByLoginId('admin'),getXportalUIdByLoginId('admin'),'Security Zone',''); INSERT INTO x_modules_master(create_time,update_time,added_by_id,upd_by_id,module,url) VALUES(current_timestamp,current_timestamp,getXportalUIdByLoginId('admin'),getXportalUIdByLoginId('admin'),'Governed Data Sharing',''); @@ -2059,6 +2063,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 ('075',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 ('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 ('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 @@ -2110,6 +2115,7 @@ INSERT INTO x_user_module_perm (user_id,module_id,create_time,update_time,added_ INSERT INTO x_ranger_global_state (create_time,update_time,added_by_id,upd_by_id,version,state_name,app_data) VALUES (current_timestamp,current_timestamp,getXportalUIdByLoginId('admin'),getXportalUIdByLoginId('admin'),1,'RangerRole','{"Version":"1"}'); INSERT INTO x_ranger_global_state (create_time,update_time,added_by_id,upd_by_id,version,state_name,app_data) VALUES (current_timestamp,current_timestamp,getXportalUIdByLoginId('admin'),getXportalUIdByLoginId('admin'),1,'RangerUserStore','{"Version":"1"}'); INSERT INTO x_ranger_global_state (create_time,update_time,added_by_id,upd_by_id,version,state_name,app_data) VALUES (current_timestamp,current_timestamp,getXportalUIdByLoginId('admin'),getXportalUIdByLoginId('admin'),1,'RangerSecurityZone','{"Version":"1"}'); +INSERT INTO x_ranger_global_state (create_time,update_time,added_by_id,upd_by_id,version,state_name,app_data) VALUES (current_timestamp,current_timestamp,getXportalUIdByLoginId('admin'),getXportalUIdByLoginId('admin'),1,'RangerAuditPartitionPlan','{"version":1,"topic":"ranger_audits","topicPartitionCount":9,"plugins":{},"buffer":{"partitions":[1,2,3,4,5,6,7,8,9]}}'); INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('J10001',current_timestamp,'Ranger 1.0.0',current_timestamp,'localhost','Y'); INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('J10002',current_timestamp,'Ranger 1.0.0',current_timestamp,'localhost','Y'); diff --git a/security-admin/db/postgres/patches/078-audit-partition-plan-global-state.sql b/security-admin/db/postgres/patches/078-audit-partition-plan-global-state.sql new file mode 100644 index 00000000000..99bcf6da6b8 --- /dev/null +++ b/security-admin/db/postgres/patches/078-audit-partition-plan-global-state.sql @@ -0,0 +1,67 @@ +-- 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 patch_audit_partition_plan_global_state() +RETURNS void AS $$ +DECLARE + v_column_is_varchar integer := 0; + v_admin_id bigint; + v_audit_user_id bigint; + v_plan_json text := '{"version":1,"topic":"ranger_audits","topicPartitionCount":9,"plugins":{},"buffer":{"partitions":[1,2,3,4,5,6,7,8,9]}}'; +BEGIN + IF EXISTS (SELECT 1 FROM pg_class WHERE relname = 'x_ranger_global_state') THEN + SELECT count(*) INTO v_column_is_varchar + FROM pg_attribute + WHERE attrelid = (SELECT oid FROM pg_class WHERE relname = 'x_ranger_global_state') + AND attname = 'app_data' + AND atttypid = (SELECT oid FROM pg_type WHERE typname = 'varchar'); + + IF v_column_is_varchar > 0 THEN + ALTER TABLE x_ranger_global_state ALTER COLUMN app_data TYPE TEXT; + END IF; + + SELECT getXportalUIdByLoginId('admin') INTO v_admin_id; + + IF NOT EXISTS (SELECT 1 FROM x_portal_user WHERE login_id = 'rangerauditserver') THEN + INSERT INTO x_portal_user(create_time, update_time, first_name, last_name, pub_scr_name, login_id, password, email, status) + VALUES (current_timestamp, current_timestamp, 'rangerauditserver', '', 'rangerauditserver', 'rangerauditserver', '9c8f4e2b1a0d6e3f7b5c4a8291d0e6f3', 'rangerauditserver', 1); + END IF; + + SELECT getXportalUIdByLoginId('rangerauditserver') INTO v_audit_user_id; + + IF v_audit_user_id IS NOT NULL AND NOT EXISTS ( + SELECT 1 FROM x_portal_user_role WHERE user_id = v_audit_user_id AND user_role = 'ROLE_ADMIN_AUDITOR' + ) THEN + INSERT INTO x_portal_user_role(create_time, update_time, user_id, user_role, status) + VALUES (current_timestamp, current_timestamp, v_audit_user_id, 'ROLE_ADMIN_AUDITOR', 1); + END IF; + + IF v_audit_user_id IS NOT NULL AND NOT EXISTS (SELECT 1 FROM x_user WHERE user_name = 'rangerauditserver') THEN + INSERT INTO x_user(create_time, update_time, user_name, status, descr) + VALUES (current_timestamp, current_timestamp, 'rangerauditserver', 0, 'Ranger audit server machine user'); + END IF; + + IF NOT EXISTS (SELECT 1 FROM x_ranger_global_state WHERE state_name = 'RangerAuditPartitionPlan') THEN + INSERT INTO x_ranger_global_state (create_time, update_time, added_by_id, upd_by_id, version, state_name, app_data) + VALUES (current_timestamp, current_timestamp, v_admin_id, v_admin_id, 1, 'RangerAuditPartitionPlan', v_plan_json); + END IF; + END IF; +END; +$$ LANGUAGE plpgsql; +select 'delimiter end'; + +select patch_audit_partition_plan_global_state(); +select 'delimiter end'; 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 df950c85ce6..231e899dde1 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 @@ -545,7 +545,7 @@ CREATE TABLE dbo.x_ranger_global_state( upd_by_id bigint DEFAULT NULL NULL, version bigint DEFAULT NULL NULL, state_name varchar(255) NOT NULL, - app_data varchar(255) DEFAULT NULL NULL, + app_data LONG VARCHAR DEFAULT NULL NULL, CONSTRAINT x_ranger_global_state_PK_id PRIMARY KEY CLUSTERED(id), CONSTRAINT x_ranger_global_state_UK_state_name UNIQUE NONCLUSTERED(state_name) ) @@ -2132,6 +2132,12 @@ INSERT INTO x_portal_user_role(create_time,update_time,added_by_id,upd_by_id,use GO INSERT INTO x_user(create_time,update_time,added_by_id,upd_by_id,user_name,descr,status) values (CURRENT_TIMESTAMP, CURRENT_TIMESTAMP,NULL,NULL,'rangertagsync','rangertagsync',0); GO +INSERT INTO x_portal_user(create_time,update_time,added_by_id,upd_by_id,first_name,last_name,pub_scr_name,login_id,password,email,status,user_src,notes) VALUES (CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,NULL,NULL,'rangerauditserver','','rangerauditserver','rangerauditserver','9c8f4e2b1a0d6e3f7b5c4a8291d0e6f3','rangerauditserver',1,0,NULL); +GO +INSERT INTO x_portal_user_role(create_time,update_time,added_by_id,upd_by_id,user_id,user_role,status) VALUES (CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,NULL,NULL,dbo.getXportalUIdByLoginId('rangerauditserver'),'ROLE_ADMIN_AUDITOR',1); +GO +INSERT INTO x_user(create_time,update_time,added_by_id,upd_by_id,user_name,descr,status) values (CURRENT_TIMESTAMP, CURRENT_TIMESTAMP,NULL,NULL,'rangerauditserver','Ranger audit server machine user',0); +GO INSERT INTO x_security_zone(create_time, update_time, added_by_id, upd_by_id, version, name, jsonData, description) VALUES (CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, dbo.getXportalUIdByLoginId('admin'), dbo.getXportalUIdByLoginId('admin'), 1, ' ', '', 'Unzoned zone'); GO INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('CORE_DB_SCHEMA',CURRENT_TIMESTAMP,'Ranger 1.0.0',CURRENT_TIMESTAMP,'localhost','Y'); @@ -2236,6 +2242,8 @@ INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active 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'); +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 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); @@ -2290,6 +2298,8 @@ INSERT INTO x_ranger_global_state (create_time,update_time,added_by_id,upd_by_id GO INSERT INTO x_ranger_global_state (create_time,update_time,added_by_id,upd_by_id,version,state_name,app_data) VALUES (CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,getXportalUIdByLoginId('admin'),getXportalUIdByLoginId('admin'),1,'RangerSecurityZone','{"Version":"1"}'); GO +INSERT INTO x_ranger_global_state (create_time,update_time,added_by_id,upd_by_id,version,state_name,app_data) VALUES (CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,getXportalUIdByLoginId('admin'),getXportalUIdByLoginId('admin'),1,'RangerAuditPartitionPlan','{"version":1,"topic":"ranger_audits","topicPartitionCount":9,"plugins":{},"buffer":{"partitions":[1,2,3,4,5,6,7,8,9]}}'); +GO INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('J10001',CURRENT_TIMESTAMP,'Ranger 1.0.0',CURRENT_TIMESTAMP,'localhost','Y'); GO INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('J10002',CURRENT_TIMESTAMP,'Ranger 1.0.0',CURRENT_TIMESTAMP,'localhost','Y'); diff --git a/security-admin/db/sqlanywhere/patches/078-audit-partition-plan-global-state.sql b/security-admin/db/sqlanywhere/patches/078-audit-partition-plan-global-state.sql new file mode 100644 index 00000000000..d45a273c89a --- /dev/null +++ b/security-admin/db/sqlanywhere/patches/078-audit-partition-plan-global-state.sql @@ -0,0 +1,55 @@ +-- 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. + +CREATE OR REPLACE FUNCTION dbo.getXportalUIdByLoginId (input_val CHAR(60)) +RETURNS INTEGER +BEGIN + DECLARE myid INTEGER; + SELECT x_portal_user.id INTO myid FROM x_portal_user WHERE x_portal_user.login_id = input_val; + RETURN (myid); +END; +GO + +BEGIN + DECLARE planJson LONG VARCHAR DEFAULT '{"version":1,"topic":"ranger_audits","topicPartitionCount":9,"plugins":{},"buffer":{"partitions":[1,2,3,4,5,6,7,8,9]}}'; + + IF EXISTS(SELECT * FROM SYS.SYSCOLUMNS WHERE tname = 'x_ranger_global_state' AND cname = 'state_name') THEN + IF EXISTS(SELECT * FROM SYS.SYSCOLUMNS WHERE tname = 'x_ranger_global_state' AND cname = 'app_data' AND coltype = 'varchar') THEN + ALTER TABLE dbo.x_ranger_global_state MODIFY app_data LONG VARCHAR DEFAULT NULL; + END IF; + + IF NOT EXISTS(SELECT * FROM x_portal_user WHERE login_id = 'rangerauditserver') THEN + INSERT INTO x_portal_user(create_time, update_time, added_by_id, upd_by_id, first_name, last_name, pub_scr_name, login_id, password, email, status, user_src, notes) + VALUES (CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, 'rangerauditserver', '', 'rangerauditserver', 'rangerauditserver', '9c8f4e2b1a0d6e3f7b5c4a8291d0e6f3', 'rangerauditserver', 1, 0, NULL); + END IF; + + IF NOT EXISTS(SELECT * FROM x_portal_user_role WHERE user_id = getXportalUIdByLoginId('rangerauditserver') AND user_role = 'ROLE_ADMIN_AUDITOR') THEN + INSERT INTO x_portal_user_role(create_time, update_time, added_by_id, upd_by_id, user_id, user_role, status) + VALUES (CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, getXportalUIdByLoginId('rangerauditserver'), 'ROLE_ADMIN_AUDITOR', 1); + END IF; + + IF NOT EXISTS(SELECT * FROM x_user WHERE user_name = 'rangerauditserver') THEN + INSERT INTO x_user(create_time, update_time, added_by_id, upd_by_id, user_name, descr, status) + VALUES (CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, 'rangerauditserver', 'Ranger audit server machine user', 0); + END IF; + + IF NOT EXISTS(SELECT * FROM x_ranger_global_state WHERE state_name = 'RangerAuditPartitionPlan') THEN + INSERT INTO x_ranger_global_state(create_time, update_time, added_by_id, upd_by_id, version, state_name, app_data) + VALUES (CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, getXportalUIdByLoginId('admin'), getXportalUIdByLoginId('admin'), 1, 'RangerAuditPartitionPlan', planJson); + END IF; + END IF; +END +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 315a3bc50a1..0be2cdd1cd5 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 @@ -1564,7 +1564,7 @@ CREATE TABLE [dbo].[x_ranger_global_state]( [upd_by_id] [bigint] DEFAULT NULL NULL, [version] [bigint] DEFAULT NULL NULL, [state_name] [varchar](255) NOT NULL, - [app_data] [varchar](255) DEFAULT NULL NULL, + [app_data] NVARCHAR(MAX) DEFAULT NULL NULL, PRIMARY KEY CLUSTERED ( [id] ASC @@ -4456,6 +4456,9 @@ insert into x_user (CREATE_TIME,UPDATE_TIME,user_name,status,descr) values (CURR insert into x_portal_user (CREATE_TIME,UPDATE_TIME,FIRST_NAME,LAST_NAME,PUB_SCR_NAME,LOGIN_ID,PASSWORD,EMAIL,STATUS) values (CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,'rangertagsync','','rangertagsync','rangertagsync','f5820e1229418dcf2575908f2c493da5','rangertagsync',1); insert into x_portal_user_role (CREATE_TIME,UPDATE_TIME,USER_ID,USER_ROLE,STATUS) values (CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,dbo.getXportalUIdByLoginId('rangertagsync'),'ROLE_SYS_ADMIN',1); insert into x_user (CREATE_TIME,UPDATE_TIME,user_name,status,descr) values (CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,'rangertagsync',0,'rangertagsync'); +insert into x_portal_user (CREATE_TIME,UPDATE_TIME,FIRST_NAME,LAST_NAME,PUB_SCR_NAME,LOGIN_ID,PASSWORD,EMAIL,STATUS) values (CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,'rangerauditserver','','rangerauditserver','rangerauditserver','9c8f4e2b1a0d6e3f7b5c4a8291d0e6f3','rangerauditserver',1); +insert into x_portal_user_role (CREATE_TIME,UPDATE_TIME,USER_ID,USER_ROLE,STATUS) values (CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,dbo.getXportalUIdByLoginId('rangerauditserver'),'ROLE_ADMIN_AUDITOR',1); +insert into x_user (CREATE_TIME,UPDATE_TIME,user_name,status,descr) values (CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,'rangerauditserver',0,'Ranger audit server machine user'); INSERT INTO x_security_zone(create_time, update_time, added_by_id, upd_by_id, version, name, jsonData, description) VALUES (CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, dbo.getXportalUIdByLoginId('admin'), dbo.getXportalUIdByLoginId('admin'), 1, ' ', '', 'Unzoned zone'); INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('CORE_DB_SCHEMA',CURRENT_TIMESTAMP,'Ranger 1.0.0',CURRENT_TIMESTAMP,'localhost','Y'); INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('016',CURRENT_TIMESTAMP,'Ranger 1.0.0',CURRENT_TIMESTAMP,'localhost','Y'); @@ -4512,6 +4515,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 ('075',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 ('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 ('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); @@ -4543,6 +4547,7 @@ INSERT INTO x_user_module_perm (user_id,module_id,create_time,update_time,added_ INSERT INTO x_ranger_global_state (create_time,update_time,added_by_id,upd_by_id,version,state_name,app_data) VALUES (CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,dbo.getXportalUIdByLoginId('admin'),dbo.getXportalUIdByLoginId('admin'),1,'RangerRole','{"Version":"1"}'); INSERT INTO x_ranger_global_state (create_time,update_time,added_by_id,upd_by_id,version,state_name,app_data) VALUES (CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,dbo.getXportalUIdByLoginId('admin'),dbo.getXportalUIdByLoginId('admin'),1,'RangerUserStore','{"Version":"1"}'); INSERT INTO x_ranger_global_state (create_time,update_time,added_by_id,upd_by_id,version,state_name,app_data) VALUES (CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,dbo.getXportalUIdByLoginId('admin'),dbo.getXportalUIdByLoginId('admin'),1,'RangerSecurityZone','{"Version":"1"}'); +INSERT INTO x_ranger_global_state (create_time,update_time,added_by_id,upd_by_id,version,state_name,app_data) VALUES (CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,dbo.getXportalUIdByLoginId('admin'),dbo.getXportalUIdByLoginId('admin'),1,'RangerAuditPartitionPlan',N'{"version":1,"topic":"ranger_audits","topicPartitionCount":9,"plugins":{},"buffer":{"partitions":[1,2,3,4,5,6,7,8,9]}}'); INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('J10001',CURRENT_TIMESTAMP,'Ranger 1.0.0',CURRENT_TIMESTAMP,'localhost','Y'); INSERT INTO x_db_version_h (version,inst_at,inst_by,updated_at,updated_by,active) VALUES ('J10002',CURRENT_TIMESTAMP,'Ranger 1.0.0',CURRENT_TIMESTAMP,'localhost','Y'); diff --git a/security-admin/db/sqlserver/patches/078-audit-partition-plan-global-state.sql b/security-admin/db/sqlserver/patches/078-audit-partition-plan-global-state.sql new file mode 100644 index 00000000000..119bf55dca8 --- /dev/null +++ b/security-admin/db/sqlserver/patches/078-audit-partition-plan-global-state.sql @@ -0,0 +1,68 @@ +-- 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. +SET ANSI_NULLS ON +GO +SET QUOTED_IDENTIFIER ON +GO +IF EXISTS (SELECT * + FROM sys.objects + WHERE object_id = OBJECT_ID(N'dbo.getXportalUIdByLoginId') + AND type IN ( N'FN', N'IF', N'TF', N'FS', N'FT' )) + DROP FUNCTION dbo.getXportalUIdByLoginId +GO +CREATE FUNCTION dbo.getXportalUIdByLoginId(@inputValue varchar(200)) +RETURNS int +AS +BEGIN + DECLARE @myid int; + SELECT @myid = id FROM x_portal_user WHERE x_portal_user.login_id = @inputValue; + RETURN @myid; +END +GO + +IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'x_ranger_global_state' AND COLUMN_NAME = 'state_name') +BEGIN + IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'x_ranger_global_state' AND COLUMN_NAME = 'app_data' AND DATA_TYPE = 'varchar') + BEGIN + ALTER TABLE [dbo].[x_ranger_global_state] ALTER COLUMN [app_data] NVARCHAR(MAX) NULL; + END; + + IF NOT EXISTS(SELECT * FROM x_portal_user WHERE login_id = 'rangerauditserver') + BEGIN + INSERT INTO x_portal_user (create_time, update_time, added_by_id, upd_by_id, first_name, last_name, pub_scr_name, login_id, password, email, status, user_src, notes) + VALUES (CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, 'rangerauditserver', '', 'rangerauditserver', 'rangerauditserver', '9c8f4e2b1a0d6e3f7b5c4a8291d0e6f3', 'rangerauditserver', 1, 0, NULL); + END; + + IF NOT EXISTS(SELECT * FROM x_portal_user_role WHERE user_id = dbo.getXportalUIdByLoginId('rangerauditserver') AND user_role = 'ROLE_ADMIN_AUDITOR') + BEGIN + INSERT INTO x_portal_user_role (create_time, update_time, added_by_id, upd_by_id, user_id, user_role, status) + VALUES (CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, dbo.getXportalUIdByLoginId('rangerauditserver'), 'ROLE_ADMIN_AUDITOR', 1); + END; + + IF NOT EXISTS(SELECT * FROM x_user WHERE user_name = 'rangerauditserver') + BEGIN + INSERT INTO x_user (create_time, update_time, added_by_id, upd_by_id, user_name, descr, status) + VALUES (CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, 'rangerauditserver', 'Ranger audit server machine user', 0); + END; + + IF NOT EXISTS(SELECT * FROM x_ranger_global_state WHERE state_name = 'RangerAuditPartitionPlan') + BEGIN + INSERT INTO x_ranger_global_state (create_time, update_time, added_by_id, upd_by_id, version, state_name, app_data) + VALUES (CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, dbo.getXportalUIdByLoginId('admin'), dbo.getXportalUIdByLoginId('admin'), 1, 'RangerAuditPartitionPlan', + N'{"version":1,"topic":"ranger_audits","topicPartitionCount":9,"plugins":{},"buffer":{"partitions":[1,2,3,4,5,6,7,8,9]}}'); + END; +END; +GO +EXIT