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,

Reply via email to