This is an automated email from the ASF dual-hosted git repository.
mmuzaf pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/master by this push:
new 636227e IGNITE-13535 Add snapshot security permissions (#8335)
636227e is described below
commit 636227e5705f1051647a081e1705589ec9a5e707
Author: Maxim Muzafarov <[email protected]>
AuthorDate: Wed Oct 14 11:37:10 2020 +0300
IGNITE-13535 Add snapshot security permissions (#8335)
---
.../snapshot/IgniteSnapshotManager.java | 63 ++++++---
.../ignite/plugin/security/SecurityPermission.java | 5 +-
.../security/impl/TestSecurityContext.java | 1 +
.../snapshot/SnapshotPermissionCheckTest.java | 154 +++++++++++++++++++++
.../ignite/testsuites/SecurityTestSuite.java | 2 +
5 files changed, 204 insertions(+), 21 deletions(-)
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotManager.java
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotManager.java
index 7f54d4e..03e7e1c 100644
---
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotManager.java
+++
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotManager.java
@@ -99,13 +99,12 @@ import
org.apache.ignite.internal.util.future.IgniteFutureImpl;
import org.apache.ignite.internal.util.lang.GridClosureException;
import org.apache.ignite.internal.util.lang.GridPlainRunnable;
import org.apache.ignite.internal.util.tostring.GridToStringInclude;
-import org.apache.ignite.internal.util.typedef.CX1;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.internal.A;
import org.apache.ignite.internal.util.typedef.internal.CU;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;
-import org.apache.ignite.lang.IgniteClosure;
+import org.apache.ignite.lang.IgniteCallable;
import org.apache.ignite.lang.IgniteFuture;
import org.apache.ignite.resources.IgniteInstanceResource;
import org.apache.ignite.thread.IgniteThreadPoolExecutor;
@@ -120,6 +119,8 @@ import static
org.apache.ignite.events.EventType.EVT_CLUSTER_SNAPSHOT_FINISHED;
import static org.apache.ignite.events.EventType.EVT_CLUSTER_SNAPSHOT_STARTED;
import static org.apache.ignite.events.EventType.EVT_NODE_FAILED;
import static org.apache.ignite.events.EventType.EVT_NODE_LEFT;
+import static org.apache.ignite.internal.GridClosureCallMode.BALANCE;
+import static org.apache.ignite.internal.GridClosureCallMode.BROADCAST;
import static
org.apache.ignite.internal.IgniteFeatures.PERSISTENCE_CACHE_SNAPSHOT;
import static
org.apache.ignite.internal.MarshallerContextImpl.mappingFileStoreWorkDir;
import static org.apache.ignite.internal.MarshallerContextImpl.saveMappings;
@@ -136,6 +137,7 @@ import static
org.apache.ignite.internal.processors.cache.persistence.partstate.
import static
org.apache.ignite.internal.util.IgniteUtils.isLocalNodeCoordinator;
import static
org.apache.ignite.internal.util.distributed.DistributedProcess.DistributedProcessType.END_SNAPSHOT;
import static
org.apache.ignite.internal.util.distributed.DistributedProcess.DistributedProcessType.START_SNAPSHOT;
+import static
org.apache.ignite.plugin.security.SecurityPermission.ADMIN_SNAPSHOT;
/**
* Internal implementation of snapshot operations over persistence caches.
@@ -684,18 +686,15 @@ public class IgniteSnapshotManager extends
GridCacheSharedManagerAdapter
@Override public IgniteFuture<Void> cancelSnapshot(String name) {
A.notNullOrEmpty(name, "Snapshot name must be not empty or null");
+ cctx.kernalContext().security().authorize(ADMIN_SNAPSHOT);
+
IgniteInternalFuture<Void> fut0 = cctx.kernalContext().closure()
- .broadcast(new CancelSnapshotClosure(),
- name,
+ .callAsyncNoFailover(BROADCAST,
+ new CancelSnapshotCallable(name),
cctx.discovery().aliveServerNodes(),
- null)
- .chain(new CX1<IgniteInternalFuture<Collection<Void>>, Void>() {
- @Override public Void
applyx(IgniteInternalFuture<Collection<Void>> f) throws IgniteCheckedException {
- f.get();
-
- return null;
- }
- });
+ false,
+ 0,
+ true);
return new IgniteFutureImpl<>(fut0);
}
@@ -746,6 +745,8 @@ public class IgniteSnapshotManager extends
GridCacheSharedManagerAdapter
A.ensure(U.alphanumericUnderscore(name), "Snapshot name must satisfy
the following name pattern: a-zA-Z0-9_");
try {
+ cctx.kernalContext().security().authorize(ADMIN_SNAPSHOT);
+
if
(!IgniteFeatures.allNodesSupports(cctx.discovery().aliveServerNodes(),
PERSISTENCE_CACHE_SNAPSHOT))
throw new IgniteException("Not all nodes in the cluster
support a snapshot operation.");
@@ -769,10 +770,12 @@ public class IgniteSnapshotManager extends
GridCacheSharedManagerAdapter
throw new IgniteException("There is no alive server nodes
in the cluster");
return new
IgniteSnapshotFutureImpl(cctx.kernalContext().closure()
- .callAsync(new CreateSnapshotClosure(),
- name,
+ .callAsyncNoFailover(BALANCE,
+ new CreateSnapshotCallable(name),
Collections.singletonList(crd),
- null));
+ false,
+ 0,
+ true));
}
ClusterSnapshotFuture snpFut0;
@@ -1399,17 +1402,27 @@ public class IgniteSnapshotManager extends
GridCacheSharedManagerAdapter
/** Start creation of cluster snapshot closure. */
@GridInternal
- private static class CreateSnapshotClosure implements
IgniteClosure<String, Void> {
+ private static class CreateSnapshotCallable implements
IgniteCallable<Void> {
/** Serial version UID. */
private static final long serialVersionUID = 0L;
+ /** Snapshot name. */
+ private final String snpName;
+
/** Auto-injected grid instance. */
@IgniteInstanceResource
private transient IgniteEx ignite;
+ /**
+ * @param snpName Snapshot name.
+ */
+ public CreateSnapshotCallable(String snpName) {
+ this.snpName = snpName;
+ }
+
/** {@inheritDoc} */
- @Override public Void apply(String name) {
- ignite.snapshot().createSnapshot(name).get();
+ @Override public Void call() throws Exception {
+ ignite.snapshot().createSnapshot(snpName).get();
return null;
}
@@ -1417,16 +1430,26 @@ public class IgniteSnapshotManager extends
GridCacheSharedManagerAdapter
/** Cancel snapshot operation closure. */
@GridInternal
- private static class CancelSnapshotClosure implements
IgniteClosure<String, Void> {
+ private static class CancelSnapshotCallable implements
IgniteCallable<Void> {
/** Serial version uid. */
private static final long serialVersionUID = 0L;
+ /** Snapshot name. */
+ private final String snpName;
+
/** Auto-injected grid instance. */
@IgniteInstanceResource
private transient IgniteEx ignite;
+ /**
+ * @param snpName Snapshot name.
+ */
+ public CancelSnapshotCallable(String snpName) {
+ this.snpName = snpName;
+ }
+
/** {@inheritDoc} */
- @Override public Void apply(String snpName) {
+ @Override public Void call() throws Exception {
ignite.context().cache().context().snapshotMgr().cancelLocalSnapshotTask(snpName);
return null;
diff --git
a/modules/core/src/main/java/org/apache/ignite/plugin/security/SecurityPermission.java
b/modules/core/src/main/java/org/apache/ignite/plugin/security/SecurityPermission.java
index d6fe810..2d2f15a 100644
---
a/modules/core/src/main/java/org/apache/ignite/plugin/security/SecurityPermission.java
+++
b/modules/core/src/main/java/org/apache/ignite/plugin/security/SecurityPermission.java
@@ -82,7 +82,10 @@ public enum SecurityPermission {
ADMIN_READ_DISTRIBUTED_PROPERTY,
/** Administration operation: write distributed properties values. */
- ADMIN_WRITE_DISTRIBUTED_PROPERTY;
+ ADMIN_WRITE_DISTRIBUTED_PROPERTY,
+
+ /** Administration operation with cluster snapshots (CREATE, CANCEL). */
+ ADMIN_SNAPSHOT;
/** Enumerated values. */
private static final SecurityPermission[] VALS = values();
diff --git
a/modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestSecurityContext.java
b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestSecurityContext.java
index 6e8dfba..6040c38 100644
---
a/modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestSecurityContext.java
+++
b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestSecurityContext.java
@@ -68,6 +68,7 @@ public class TestSecurityContext implements SecurityContext,
Serializable {
case ADMIN_CACHE:
case ADMIN_QUERY:
case ADMIN_OPS:
+ case ADMIN_SNAPSHOT:
case JOIN_AS_SERVER:
return systemOperationAllowed(perm);
diff --git
a/modules/core/src/test/java/org/apache/ignite/internal/processors/security/snapshot/SnapshotPermissionCheckTest.java
b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/snapshot/SnapshotPermissionCheckTest.java
new file mode 100644
index 0000000..8c1e3fe
--- /dev/null
+++
b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/snapshot/SnapshotPermissionCheckTest.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.ignite.internal.processors.security.snapshot;
+
+import java.util.Arrays;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.cluster.ClusterState;
+import org.apache.ignite.configuration.DataRegionConfiguration;
+import org.apache.ignite.configuration.DataStorageConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.processors.security.AbstractSecurityTest;
+import
org.apache.ignite.internal.processors.security.AbstractTestSecurityPluginProvider;
+import org.apache.ignite.plugin.security.SecurityException;
+import org.apache.ignite.plugin.security.SecurityPermission;
+import org.apache.ignite.plugin.security.SecurityPermissionSet;
+import org.apache.ignite.plugin.security.SecurityPermissionSetBuilder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import static java.lang.String.format;
+import static
org.apache.ignite.plugin.security.SecurityPermission.ADMIN_SNAPSHOT;
+import static
org.apache.ignite.plugin.security.SecurityPermission.CACHE_CREATE;
+import static org.apache.ignite.plugin.security.SecurityPermission.CACHE_PUT;
+import static
org.apache.ignite.testframework.GridTestUtils.assertThrowsWithCause;
+
+/**
+ * Snapshot execute permission tests.
+ */
+@RunWith(JUnit4.class)
+public class SnapshotPermissionCheckTest extends AbstractSecurityTest {
+ /** Test snapshot name. */
+ private static final String SNAPSHOT_NAME = "security_snapshot_%s";
+
+ /** Reentrant lock. */
+ private static final ReentrantLock RNT_LOCK = new ReentrantLock();
+
+ /** Snapshot atomic sequence. */
+ private final AtomicInteger snpCntr = new AtomicInteger();
+
+ /** {@inheritDoc} */
+ @Override protected void afterTest() throws Exception {
+ super.afterTest();
+
+ stopAllGrids();
+ cleanPersistenceDir();
+ }
+
+ /** @throws Exception If fails. */
+ @Test
+ public void testSnapshotAllowed() throws Exception {
+ doTest(this::run, ADMIN_SNAPSHOT);
+ }
+
+ /** @throws Exception If fails. */
+ @Test
+ public void testSnapshotForbidden() throws Exception {
+ doTest(op -> assertThrowsWithCause(() -> run(op),
SecurityException.class), EMPTY_PERMS);
+ }
+
+ /**
+ * @param check Check condition.
+ * @param perms Permissions to check.
+ * @throws Exception If fails.
+ */
+ private void doTest(Consumer<? super Supplier<Future<Void>>> check,
SecurityPermission... perms) throws Exception {
+ Ignite srv = startGrid("srv", permissions(perms), false);
+
+ Ignite clnt = startGrid("clnt", permissions(perms), true);
+
+ srv.cluster().state(ClusterState.ACTIVE);
+
+ IgniteCache<Integer, Integer> cache =
srv.getOrCreateCache(DEFAULT_CACHE_NAME);
+
+ for (int idx = 0; idx < 1024; idx++)
+ cache.put(idx, -1);
+
+ forceCheckpoint();
+
+ asyncOperations(srv, clnt).forEach(check);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected IgniteConfiguration getConfiguration(String
instanceName,
+ AbstractTestSecurityPluginProvider pluginProv) throws Exception {
+ return super.getConfiguration(instanceName, pluginProv)
+ .setDataStorageConfiguration(new DataStorageConfiguration()
+ .setDefaultDataRegionConfiguration(new
DataRegionConfiguration()
+ .setPersistenceEnabled(true)));
+ }
+
+ /** */
+ private Stream<Supplier<Future<Void>>> asyncOperations(Ignite... nodes) {
+ Function<Ignite, Stream<Supplier<Future<Void>>>> nodeOps = (node) ->
Stream.of(
+ () -> new
TestFutureAdapter<>(node.snapshot().createSnapshot(format(SNAPSHOT_NAME,
snpCntr.getAndIncrement()))),
+ () -> new
TestFutureAdapter<>(node.snapshot().cancelSnapshot(format(SNAPSHOT_NAME,
snpCntr.getAndIncrement())))
+ );
+
+ return Arrays.stream(nodes).flatMap(nodeOps);
+ }
+
+ /**
+ * @param perms Permissions.
+ */
+ private static SecurityPermissionSet permissions(SecurityPermission...
perms) {
+ return SecurityPermissionSetBuilder.create()
+ .defaultAllowAll(false)
+ .appendCachePermissions(DEFAULT_CACHE_NAME, CACHE_CREATE,
CACHE_PUT)
+ .appendSystemPermissions(perms)
+ .build();
+ }
+
+ /**
+ * @param sup Supplier.
+ */
+ private void run(Supplier<Future<Void>> sup) {
+ RNT_LOCK.lock();
+
+ try {
+ sup.get().get();
+ }
+ catch (InterruptedException | ExecutionException e) {
+ throw new IgniteException(e);
+ }
+ finally {
+ RNT_LOCK.unlock();
+ }
+ }
+}
diff --git
a/modules/core/src/test/java/org/apache/ignite/testsuites/SecurityTestSuite.java
b/modules/core/src/test/java/org/apache/ignite/testsuites/SecurityTestSuite.java
index ed2901a..46bcf92 100644
---
a/modules/core/src/test/java/org/apache/ignite/testsuites/SecurityTestSuite.java
+++
b/modules/core/src/test/java/org/apache/ignite/testsuites/SecurityTestSuite.java
@@ -61,6 +61,7 @@ import
org.apache.ignite.internal.processors.security.sandbox.PrivilegedProxyTes
import
org.apache.ignite.internal.processors.security.sandbox.SchedulerSandboxTest;
import
org.apache.ignite.internal.processors.security.sandbox.SecuritySubjectPermissionsTest;
import
org.apache.ignite.internal.processors.security.scheduler.SchedulerRemoteSecurityContextCheckTest;
+import
org.apache.ignite.internal.processors.security.snapshot.SnapshotPermissionCheckTest;
import org.apache.ignite.ssl.MultipleSSLContextsTest;
import org.apache.ignite.tools.junit.JUnitTeamcityReporter;
import org.junit.BeforeClass;
@@ -82,6 +83,7 @@ import org.junit.runners.Suite;
ThinClientPermissionCheckSecurityTest.class,
ContinuousQueryPermissionCheckTest.class,
IgniteClientContainSubjectAddressTest.class,
+ SnapshotPermissionCheckTest.class,
DistributedClosureRemoteSecurityContextCheckTest.class,
ComputeTaskRemoteSecurityContextCheckTest.class,