diff --git a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/HardcodedEntitlements.java b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/HardcodedEntitlements.java index 278b9e773ae1f..01e1092f53d00 100644 --- a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/HardcodedEntitlements.java +++ b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/HardcodedEntitlements.java @@ -92,8 +92,9 @@ private static List createServerEntitlements(Path pidFile) { new CreateClassLoaderEntitlement(), new FilesEntitlement( List.of( - // TODO: what in es.base is accessing shared repo? + // necessary due to lack of delegation ES-12382 FilesEntitlement.FileData.ofBaseDirPath(SHARED_REPO, READ_WRITE), + FilesEntitlement.FileData.ofBaseDirPath(SHARED_DATA, READ_WRITE), FilesEntitlement.FileData.ofBaseDirPath(DATA, READ_WRITE) ) ) @@ -122,6 +123,7 @@ private static List createServerEntitlements(Path pidFile) { new FilesEntitlement( List.of( FilesEntitlement.FileData.ofBaseDirPath(CONFIG, READ), + FilesEntitlement.FileData.ofBaseDirPath(SHARED_DATA, READ_WRITE), FilesEntitlement.FileData.ofBaseDirPath(DATA, READ_WRITE) ) ) @@ -130,7 +132,12 @@ private static List createServerEntitlements(Path pidFile) { new Scope( "org.apache.lucene.misc", List.of( - new FilesEntitlement(List.of(FilesEntitlement.FileData.ofBaseDirPath(DATA, READ_WRITE))), + new FilesEntitlement( + List.of( + FilesEntitlement.FileData.ofBaseDirPath(SHARED_DATA, READ_WRITE), + FilesEntitlement.FileData.ofBaseDirPath(DATA, READ_WRITE) + ) + ), new ReadStoreAttributesEntitlement() ) ), @@ -145,7 +152,12 @@ private static List createServerEntitlements(Path pidFile) { "org.elasticsearch.nativeaccess", List.of( new LoadNativeLibrariesEntitlement(), - new FilesEntitlement(List.of(FilesEntitlement.FileData.ofBaseDirPath(DATA, READ_WRITE))) + new FilesEntitlement( + List.of( + FilesEntitlement.FileData.ofBaseDirPath(SHARED_DATA, READ_WRITE), + FilesEntitlement.FileData.ofBaseDirPath(DATA, READ_WRITE) + ) + ) ) ) ); diff --git a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java index 7f0541911284c..df68da2e251ac 100644 --- a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java +++ b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java @@ -367,9 +367,7 @@ private ModuleEntitlements getModuleScopeEntitlements( * @return true if permission is granted regardless of the entitlement */ boolean isTriviallyAllowed(Class requestingClass) { - if (generalLogger.isTraceEnabled()) { - generalLogger.trace("Stack trace for upcoming trivially-allowed check", new Exception()); - } + // note: do not log exceptions in here, this could interfere with loading of additionally necessary classes such as ThrowableProxy if (requestingClass == null) { generalLogger.debug("Entitlement trivially allowed: no caller frames outside the entitlement library"); return true; diff --git a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/entitlements/FilesEntitlement.java b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/entitlements/FilesEntitlement.java index 872a083a76ba6..cc9ef9d263dd1 100644 --- a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/entitlements/FilesEntitlement.java +++ b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/entitlements/FilesEntitlement.java @@ -182,8 +182,9 @@ private static BaseDir parseBaseDir(String baseDir) { case "config" -> BaseDir.CONFIG; case "data" -> BaseDir.DATA; case "home" -> BaseDir.USER_HOME; + case "shared_data" -> BaseDir.SHARED_DATA; // it would be nice to limit this to just ES modules, but we don't have a way to plumb that through to here - // however, we still don't document in the error case below that shared_repo is valid + // however, we still don't document in the error case below that shared_repo and shared_data is valid case "shared_repo" -> BaseDir.SHARED_REPO; default -> throw new PolicyValidationException( "invalid relative directory: " + baseDir + ", valid values: [config, data, home]" diff --git a/modules/repository-azure/src/internalClusterTest/java/org/elasticsearch/repositories/azure/AzureBlobStoreRepositoryTests.java b/modules/repository-azure/src/internalClusterTest/java/org/elasticsearch/repositories/azure/AzureBlobStoreRepositoryTests.java index c6c1868d5bbc1..493f4d10eea6d 100644 --- a/modules/repository-azure/src/internalClusterTest/java/org/elasticsearch/repositories/azure/AzureBlobStoreRepositoryTests.java +++ b/modules/repository-azure/src/internalClusterTest/java/org/elasticsearch/repositories/azure/AzureBlobStoreRepositoryTests.java @@ -41,6 +41,7 @@ import org.elasticsearch.telemetry.Measurement; import org.elasticsearch.telemetry.TestTelemetryPlugin; import org.elasticsearch.test.BackgroundIndexer; +import org.elasticsearch.test.ESTestCase; import org.elasticsearch.threadpool.ThreadPool; import java.io.ByteArrayInputStream; @@ -73,6 +74,7 @@ import static org.hamcrest.Matchers.is; @SuppressForbidden(reason = "this test uses a HttpServer to emulate an Azure endpoint") +@ESTestCase.WithoutEntitlements // due to dependency issue ES-12435 public class AzureBlobStoreRepositoryTests extends ESMockAPIBasedRepositoryIntegTestCase { protected static final String DEFAULT_ACCOUNT_NAME = "account"; diff --git a/modules/repository-azure/src/internalClusterTest/java/org/elasticsearch/repositories/azure/AzureRepositoryMissingCredentialsIT.java b/modules/repository-azure/src/internalClusterTest/java/org/elasticsearch/repositories/azure/AzureRepositoryMissingCredentialsIT.java index 947f73c2ce580..5e3cad4534b7c 100644 --- a/modules/repository-azure/src/internalClusterTest/java/org/elasticsearch/repositories/azure/AzureRepositoryMissingCredentialsIT.java +++ b/modules/repository-azure/src/internalClusterTest/java/org/elasticsearch/repositories/azure/AzureRepositoryMissingCredentialsIT.java @@ -18,12 +18,14 @@ import org.elasticsearch.plugins.Plugin; import org.elasticsearch.repositories.RepositoryVerificationException; import org.elasticsearch.test.ESIntegTestCase; +import org.elasticsearch.test.ESTestCase; import java.util.Collection; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.containsString; +@ESTestCase.WithoutEntitlements // due to dependency issue ES-12435 public class AzureRepositoryMissingCredentialsIT extends ESIntegTestCase { @Override diff --git a/plugins/store-smb/src/main/plugin-metadata/entitlement-policy.yaml b/plugins/store-smb/src/main/plugin-metadata/entitlement-policy.yaml index 1022253171a11..dbe45c7527967 100644 --- a/plugins/store-smb/src/main/plugin-metadata/entitlement-policy.yaml +++ b/plugins/store-smb/src/main/plugin-metadata/entitlement-policy.yaml @@ -3,3 +3,6 @@ ALL-UNNAMED: - relative_path: "indices/" relative_to: data mode: read_write + - relative_path: "" + relative_to: shared_data + mode: read_write diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/ReloadSecureSettingsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/ReloadSecureSettingsIT.java index 83e79ff7f45a8..82a6c1f6acf85 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/ReloadSecureSettingsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/ReloadSecureSettingsIT.java @@ -27,6 +27,7 @@ import org.elasticsearch.plugins.PluginsService; import org.elasticsearch.plugins.ReloadablePlugin; import org.elasticsearch.test.ESIntegTestCase; +import org.elasticsearch.test.ESTestCase; import org.junit.BeforeClass; import java.io.InputStream; @@ -47,6 +48,7 @@ import static org.hamcrest.Matchers.nullValue; @ESIntegTestCase.ClusterScope(minNumDataNodes = 2) +@ESTestCase.WithoutEntitlements // requires entitlement delegation ES-10920 public class ReloadSecureSettingsIT extends ESIntegTestCase { private static final String VALID_SECURE_SETTING_NAME = "some.setting.that.exists"; diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/tasks/PendingTasksBlocksIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/tasks/PendingTasksBlocksIT.java index 6475e80901ea7..bb048179a437a 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/tasks/PendingTasksBlocksIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/tasks/PendingTasksBlocksIT.java @@ -12,6 +12,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.gateway.GatewayService; import org.elasticsearch.test.ESIntegTestCase; +import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.InternalTestCluster; import java.util.Arrays; @@ -22,6 +23,7 @@ import static org.elasticsearch.cluster.metadata.IndexMetadata.SETTING_READ_ONLY; import static org.elasticsearch.cluster.metadata.IndexMetadata.SETTING_READ_ONLY_ALLOW_DELETE; +@ESTestCase.WithoutEntitlements // requires entitlement delegation ES-10920 public class PendingTasksBlocksIT extends ESIntegTestCase { public void testPendingTasksWithIndexBlocks() { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/cluster/coordination/RemoveCustomsCommandIT.java b/server/src/internalClusterTest/java/org/elasticsearch/cluster/coordination/RemoveCustomsCommandIT.java index 2b2dc114e8ffc..e88a08f983b90 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/cluster/coordination/RemoveCustomsCommandIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/cluster/coordination/RemoveCustomsCommandIT.java @@ -18,12 +18,14 @@ import org.elasticsearch.env.Environment; import org.elasticsearch.env.TestEnvironment; import org.elasticsearch.test.ESIntegTestCase; +import org.elasticsearch.test.ESTestCase; import java.util.Map; import static org.hamcrest.Matchers.containsString; @ESIntegTestCase.ClusterScope(scope = ESIntegTestCase.Scope.TEST, numDataNodes = 0, autoManageMasterNodes = false) +@ESTestCase.WithoutEntitlements // commands don't run with entitlements enforced public class RemoveCustomsCommandIT extends ESIntegTestCase { public void testRemoveCustomsAbortedByUser() throws Exception { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/cluster/coordination/RemoveIndexSettingsCommandIT.java b/server/src/internalClusterTest/java/org/elasticsearch/cluster/coordination/RemoveIndexSettingsCommandIT.java index 65e325a1291d8..62afa9c57cbaa 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/cluster/coordination/RemoveIndexSettingsCommandIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/cluster/coordination/RemoveIndexSettingsCommandIT.java @@ -21,6 +21,7 @@ import org.elasticsearch.env.TestEnvironment; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESIntegTestCase; +import org.elasticsearch.test.ESTestCase; import java.util.Collection; import java.util.List; @@ -31,6 +32,7 @@ import static org.hamcrest.Matchers.not; @ESIntegTestCase.ClusterScope(scope = ESIntegTestCase.Scope.TEST, numDataNodes = 0, autoManageMasterNodes = false) +@ESTestCase.WithoutEntitlements // commands don't run with entitlements enforced public class RemoveIndexSettingsCommandIT extends ESIntegTestCase { static final Setting FOO = Setting.intSetting("index.foo", 1, Setting.Property.IndexScope, Setting.Property.Dynamic); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/cluster/coordination/RemoveSettingsCommandIT.java b/server/src/internalClusterTest/java/org/elasticsearch/cluster/coordination/RemoveSettingsCommandIT.java index d7ed6bb47b98b..1a68211c6ea8e 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/cluster/coordination/RemoveSettingsCommandIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/cluster/coordination/RemoveSettingsCommandIT.java @@ -19,6 +19,7 @@ import org.elasticsearch.env.Environment; import org.elasticsearch.env.TestEnvironment; import org.elasticsearch.test.ESIntegTestCase; +import org.elasticsearch.test.ESTestCase; import java.util.Map; @@ -27,6 +28,7 @@ import static org.hamcrest.Matchers.not; @ESIntegTestCase.ClusterScope(scope = ESIntegTestCase.Scope.TEST, numDataNodes = 0, autoManageMasterNodes = false) +@ESTestCase.WithoutEntitlements // commands don't run with entitlements enforced public class RemoveSettingsCommandIT extends ESIntegTestCase { public void testRemoveSettingsAbortedByUser() throws Exception { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/discovery/single/SingleNodeDiscoveryIT.java b/server/src/internalClusterTest/java/org/elasticsearch/discovery/single/SingleNodeDiscoveryIT.java index 10f13f6ab152f..c7780b7b0d2dd 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/discovery/single/SingleNodeDiscoveryIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/discovery/single/SingleNodeDiscoveryIT.java @@ -87,7 +87,8 @@ public Path nodeConfigPath(int nodeOrdinal) { 0, "other", Arrays.asList(getTestTransportPlugin(), MockHttpTransport.TestPlugin.class), - Function.identity() + Function.identity(), + TEST_ENTITLEMENTS::newNodeGrant ); try { other.beforeTest(random()); @@ -137,7 +138,8 @@ public Path nodeConfigPath(int nodeOrdinal) { 0, "other", Arrays.asList(getTestTransportPlugin(), MockHttpTransport.TestPlugin.class), - Function.identity() + Function.identity(), + TEST_ENTITLEMENTS::newNodeGrant ); try (var mockLog = MockLog.capture(JoinHelper.class)) { mockLog.addExpectation( diff --git a/server/src/internalClusterTest/java/org/elasticsearch/index/shard/RemoveCorruptedShardDataCommandIT.java b/server/src/internalClusterTest/java/org/elasticsearch/index/shard/RemoveCorruptedShardDataCommandIT.java index b9513dfb95187..d1cff7b2a30d1 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/index/shard/RemoveCorruptedShardDataCommandIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/index/shard/RemoveCorruptedShardDataCommandIT.java @@ -58,6 +58,7 @@ import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.CorruptionUtils; import org.elasticsearch.test.ESIntegTestCase; +import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.InternalSettingsPlugin; import org.elasticsearch.test.InternalTestCluster; import org.elasticsearch.test.engine.MockEngineSupport; @@ -93,6 +94,7 @@ import static org.hamcrest.Matchers.startsWith; @ESIntegTestCase.ClusterScope(scope = ESIntegTestCase.Scope.TEST, numDataNodes = 0) +@ESTestCase.WithoutEntitlements // commands don't run with entitlements enforced public class RemoveCorruptedShardDataCommandIT extends ESIntegTestCase { @Override diff --git a/server/src/internalClusterTest/java/org/elasticsearch/snapshots/MultiClusterRepoAccessIT.java b/server/src/internalClusterTest/java/org/elasticsearch/snapshots/MultiClusterRepoAccessIT.java index c1549c1f3d384..1ee449d8af894 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/snapshots/MultiClusterRepoAccessIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/snapshots/MultiClusterRepoAccessIT.java @@ -77,7 +77,8 @@ public Path nodeConfigPath(int nodeOrdinal) { InternalSettingsPlugin.class, getTestTransportPlugin() ), - Function.identity() + Function.identity(), + TEST_ENTITLEMENTS::newNodeGrant ); secondCluster.beforeTest(random()); } diff --git a/server/src/test/java/org/elasticsearch/bootstrap/EntitlementMetaTests.java b/server/src/test/java/org/elasticsearch/bootstrap/EntitlementMetaTests.java index 04e59e5476f3b..230e105feca18 100644 --- a/server/src/test/java/org/elasticsearch/bootstrap/EntitlementMetaTests.java +++ b/server/src/test/java/org/elasticsearch/bootstrap/EntitlementMetaTests.java @@ -10,9 +10,8 @@ package org.elasticsearch.bootstrap; import org.elasticsearch.core.SuppressForbidden; -import org.elasticsearch.entitlement.bootstrap.TestEntitlementBootstrap; +import org.elasticsearch.entitlement.bootstrap.TestEntitlementsRule; import org.elasticsearch.test.ESTestCase; -import org.elasticsearch.test.ESTestCase.WithEntitlementsOnTestCode; import java.io.IOException; import java.nio.file.Path; @@ -42,7 +41,7 @@ */ public class EntitlementMetaTests extends ESTestCase { public void testSelfTestPasses() { - assumeTrue("Not yet working in serverless", TestEntitlementBootstrap.isEnabledForTest()); + assumeTrue("Not yet working in serverless", TestEntitlementsRule.isEnabledForTest()); Elasticsearch.entitlementSelfTest(); } diff --git a/server/src/test/java/org/elasticsearch/bootstrap/WithEntitlementsOnTestCodeMetaTests.java b/server/src/test/java/org/elasticsearch/bootstrap/WithEntitlementsOnTestCodeMetaTests.java index c126490922e48..14e3803fa6bad 100644 --- a/server/src/test/java/org/elasticsearch/bootstrap/WithEntitlementsOnTestCodeMetaTests.java +++ b/server/src/test/java/org/elasticsearch/bootstrap/WithEntitlementsOnTestCodeMetaTests.java @@ -10,7 +10,7 @@ package org.elasticsearch.bootstrap; import org.elasticsearch.core.SuppressForbidden; -import org.elasticsearch.entitlement.bootstrap.TestEntitlementBootstrap; +import org.elasticsearch.entitlement.bootstrap.TestEntitlementsRule; import org.elasticsearch.entitlement.runtime.api.NotEntitledException; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ESTestCase.WithEntitlementsOnTestCode; @@ -30,13 +30,13 @@ public class WithEntitlementsOnTestCodeMetaTests extends ESTestCase { * is called from server code. The self-test should pass as usual. */ public void testSelfTestPasses() { - assumeTrue("Not yet working in serverless", TestEntitlementBootstrap.isEnabledForTest()); + assumeTrue("Not yet working in serverless", TestEntitlementsRule.isEnabledForTest()); Elasticsearch.entitlementSelfTest(); } @SuppressForbidden(reason = "Testing that a forbidden API is disallowed") public void testForbiddenActionDenied() { - assumeTrue("Not yet working in serverless", TestEntitlementBootstrap.isEnabledForTest()); + assumeTrue("Not yet working in serverless", TestEntitlementsRule.isEnabledForTest()); assertThrows(NotEntitledException.class, () -> Path.of(".").toRealPath()); } } diff --git a/server/src/test/java/org/elasticsearch/indices/IndicesServiceCloseTests.java b/server/src/test/java/org/elasticsearch/indices/IndicesServiceCloseTests.java index a48ba63882734..78df6c9e88c65 100644 --- a/server/src/test/java/org/elasticsearch/indices/IndicesServiceCloseTests.java +++ b/server/src/test/java/org/elasticsearch/indices/IndicesServiceCloseTests.java @@ -77,7 +77,8 @@ private Node startNode() throws NodeValidationException { Node node = new MockNode( settings, Arrays.asList(getTestTransportPlugin(), MockHttpTransport.TestPlugin.class, InternalSettingsPlugin.class), - true + true, + () -> {} ); node.start(); return node; diff --git a/test/framework/src/main/java/org/elasticsearch/bootstrap/BootstrapForTesting.java b/test/framework/src/main/java/org/elasticsearch/bootstrap/BootstrapForTesting.java index bf53f14bc9e46..69dbb7061c948 100644 --- a/test/framework/src/main/java/org/elasticsearch/bootstrap/BootstrapForTesting.java +++ b/test/framework/src/main/java/org/elasticsearch/bootstrap/BootstrapForTesting.java @@ -15,7 +15,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.core.Booleans; import org.elasticsearch.core.PathUtils; -import org.elasticsearch.entitlement.bootstrap.TestEntitlementBootstrap; +import org.elasticsearch.entitlement.bootstrap.TestEntitlementsRule; import org.elasticsearch.jdk.JarHell; import java.io.IOException; @@ -75,7 +75,7 @@ public class BootstrapForTesting { // Fire up entitlements try { - TestEntitlementBootstrap.bootstrap(javaTmpDir); + TestEntitlementsRule.initialize(javaTmpDir); } catch (IOException e) { throw new IllegalStateException(e.getClass().getSimpleName() + " while initializing entitlements for tests", e); } diff --git a/test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/TestEntitlementBootstrap.java b/test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/TestEntitlementsRule.java similarity index 51% rename from test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/TestEntitlementBootstrap.java rename to test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/TestEntitlementsRule.java index 1edc0b4763abb..8b56f59b9ac10 100644 --- a/test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/TestEntitlementBootstrap.java +++ b/test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/TestEntitlementsRule.java @@ -9,26 +9,32 @@ package org.elasticsearch.entitlement.bootstrap; +import org.apache.lucene.tests.mockfile.FilterPath; import org.elasticsearch.bootstrap.TestBuildInfo; import org.elasticsearch.bootstrap.TestBuildInfoParser; import org.elasticsearch.bootstrap.TestScopeResolver; import org.elasticsearch.common.Strings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.core.Booleans; -import org.elasticsearch.core.Nullable; import org.elasticsearch.core.PathUtils; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.entitlement.initialization.EntitlementInitialization; import org.elasticsearch.entitlement.runtime.policy.PathLookup; import org.elasticsearch.entitlement.runtime.policy.PathLookup.BaseDir; import org.elasticsearch.entitlement.runtime.policy.Policy; +import org.elasticsearch.entitlement.runtime.policy.PolicyManager; import org.elasticsearch.entitlement.runtime.policy.PolicyParser; import org.elasticsearch.entitlement.runtime.policy.TestPathLookup; import org.elasticsearch.entitlement.runtime.policy.TestPolicyManager; import org.elasticsearch.logging.LogManager; import org.elasticsearch.logging.Logger; import org.elasticsearch.plugins.PluginDescriptor; +import org.elasticsearch.test.ESTestCase; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; +import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.net.URI; @@ -38,13 +44,14 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Consumer; @@ -56,122 +63,184 @@ import static org.elasticsearch.env.Environment.PATH_REPO_SETTING; import static org.elasticsearch.env.Environment.PATH_SHARED_DATA_SETTING; -public class TestEntitlementBootstrap { +public class TestEntitlementsRule implements TestRule { + private static final Logger logger = LogManager.getLogger(TestEntitlementsRule.class); - private static final Logger logger = LogManager.getLogger(TestEntitlementBootstrap.class); + private static final Map> BASE_DIR_PATHS = new ConcurrentHashMap<>(); + private static final TestPolicyManager POLICY_MANAGER; - private static Map> baseDirPaths = new ConcurrentHashMap<>(); - private static TestPolicyManager policyManager; - - /** - * Activates entitlement checking in tests. - */ - public static void bootstrap(@Nullable Path tempDir) throws IOException { - if (isEnabledForTest() == false) { - return; + static { + PathLookup pathLookup = new TestPathLookup(BASE_DIR_PATHS); + try { + if (isEnabledForTest()) { + POLICY_MANAGER = createPolicyManager(pathLookup); + loadAgent(POLICY_MANAGER, pathLookup); + } else { + POLICY_MANAGER = null; + } + } catch (IOException e) { + throw new AssertionError(e); } - var previousTempDir = baseDirPaths.put(TEMP, zeroOrOne(tempDir)); - assert previousTempDir == null : "Test entitlement bootstrap called multiple times"; - TestPathLookup pathLookup = new TestPathLookup(baseDirPaths); - policyManager = createPolicyManager(pathLookup); - EntitlementInitialization.initializeArgs = new EntitlementInitialization.InitializeArgs(pathLookup, Set.of(), policyManager); - logger.debug("Loading entitlement agent"); - EntitlementBootstrap.loadAgent(EntitlementBootstrap.findAgentJar(), EntitlementInitialization.class.getName()); } - public static void registerNodeBaseDirs(Settings settings, Path configPath) { - if (policyManager == null) { - return; + public static void initialize(Path tempDir) throws IOException { + if (POLICY_MANAGER != null) { + var previousTempDir = BASE_DIR_PATHS.put(TEMP, List.of(tempDir)); + assert previousTempDir == null : "Test entitlement bootstrap called multiple times"; } - Path homeDir = absolutePath(PATH_HOME_SETTING.get(settings)); - Path configDir = configPath != null ? configPath : homeDir.resolve("config"); - Collection dataDirs = dataDirs(settings, homeDir); - Collection sharedDataDir = sharedDataDir(settings); - Collection repoDirs = repoDirs(settings); - logger.debug("Registering node dirs: config [{}], dataDirs [{}], repoDirs [{}]", configDir, dataDirs, repoDirs); - baseDirPaths.compute(BaseDir.CONFIG, baseDirModifier(paths -> paths.add(configDir))); - baseDirPaths.compute(BaseDir.DATA, baseDirModifier(paths -> paths.addAll(dataDirs))); - baseDirPaths.compute(BaseDir.SHARED_DATA, baseDirModifier(paths -> paths.addAll(sharedDataDir))); - baseDirPaths.compute(BaseDir.SHARED_REPO, baseDirModifier(paths -> paths.addAll(repoDirs))); - policyManager.reset(); } - public static void unregisterNodeBaseDirs(Settings settings, Path configPath) { - if (policyManager == null) { - return; + @Override + public Statement apply(Statement base, Description description) { + assert description.isSuite() : "must be used as ClassRule"; + + // class / suite level + boolean withoutEntitlements = description.getAnnotation(ESTestCase.WithoutEntitlements.class) != null; + boolean withEntitlementsOnTestCode = description.getAnnotation(ESTestCase.WithEntitlementsOnTestCode.class) != null; + var entitledPackages = description.getAnnotation(ESTestCase.EntitledTestPackages.class); + + if (POLICY_MANAGER != null) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + try { + POLICY_MANAGER.setActive(false == withoutEntitlements); + POLICY_MANAGER.setTriviallyAllowingTestCode(false == withEntitlementsOnTestCode); + if (entitledPackages != null) { + assert entitledPackages.value().length > 0 : "No test packages specified in @EntitledTestPackages"; + POLICY_MANAGER.setEntitledTestPackages(entitledPackages.value()); + } else { + POLICY_MANAGER.setEntitledTestPackages(); + } + BASE_DIR_PATHS.keySet().retainAll(List.of(TEMP)); + POLICY_MANAGER.clearModuleEntitlementsCache(); + // evaluate the suite + base.evaluate(); + } finally { + POLICY_MANAGER.setActive(false); + POLICY_MANAGER.setTriviallyAllowingTestCode(true); + POLICY_MANAGER.setEntitledTestPackages(); + BASE_DIR_PATHS.keySet().retainAll(List.of(TEMP)); + POLICY_MANAGER.clearModuleEntitlementsCache(); + } + } + }; + } else if (withEntitlementsOnTestCode) { + throw new AssertionError( + "Cannot use @WithEntitlementsOnTestCode on tests that are not configured to use entitlements for testing" + ); + } else { + return base; } - Path homeDir = absolutePath(PATH_HOME_SETTING.get(settings)); - Path configDir = configPath != null ? configPath : homeDir.resolve("config"); - Collection dataDirs = dataDirs(settings, homeDir); - Collection sharedDataDir = sharedDataDir(settings); - Collection repoDirs = repoDirs(settings); - logger.debug("Unregistering node dirs: config [{}], dataDirs [{}], repoDirs [{}]", configDir, dataDirs, repoDirs); - baseDirPaths.compute(BaseDir.CONFIG, baseDirModifier(paths -> paths.remove(configDir))); - baseDirPaths.compute(BaseDir.DATA, baseDirModifier(paths -> paths.removeAll(dataDirs))); - baseDirPaths.compute(BaseDir.SHARED_DATA, baseDirModifier(paths -> paths.removeAll(sharedDataDir))); - baseDirPaths.compute(BaseDir.SHARED_REPO, baseDirModifier(paths -> paths.removeAll(repoDirs))); - policyManager.reset(); } - private static Collection dataDirs(Settings settings, Path homeDir) { - List dataDirs = PATH_DATA_SETTING.get(settings); - return dataDirs.isEmpty() - ? List.of(homeDir.resolve("data")) - : dataDirs.stream().map(TestEntitlementBootstrap::absolutePath).toList(); - } + /** + * Creates a entitlement grant for node specific paths. + */ + public Closeable newNodeGrant(Settings settings, Path configPath) { + if (POLICY_MANAGER == null) { + return () -> {}; // noop if not running with entitlements + } - private static Collection sharedDataDir(Settings settings) { - String sharedDataDir = PATH_SHARED_DATA_SETTING.get(settings); - return Strings.hasText(sharedDataDir) ? List.of(absolutePath(sharedDataDir)) : List.of(); + var unwrappedConfigPath = configPath; + while (unwrappedConfigPath instanceof FilterPath fPath) { + unwrappedConfigPath = fPath.getDelegate(); + } + NodeGrant nodeGrant = new NodeGrant(settings, unwrappedConfigPath, this::revokeGrant); + addGrant(nodeGrant); + return nodeGrant; } - private static Collection repoDirs(Settings settings) { - return PATH_REPO_SETTING.get(settings).stream().map(TestEntitlementBootstrap::absolutePath).toList(); + /** + * Revoke all open node grants. + */ + public void revokeNodeGrants() { + BASE_DIR_PATHS.keySet().retainAll(List.of(TEMP)); + POLICY_MANAGER.clearModuleEntitlementsCache(); } - private static BiFunction, Collection> baseDirModifier(Consumer> consumer) { - return (BaseDir baseDir, Collection paths) -> { - if (paths == null) { - paths = new HashSet<>(); - } - consumer.accept(paths); - return paths; - }; - } + private record NodeGrant(Settings settings, Path configPath, Consumer onClose) implements Closeable { + private Path homeDir() { + return absolutePath(PATH_HOME_SETTING.get(settings)); + } - @SuppressForbidden(reason = "must be resolved using the default file system, rather then the mocked test file system") - private static Path absolutePath(String path) { - return Paths.get(path).toAbsolutePath().normalize(); - } + private Path configDir() { + return configPath != null ? configPath : homeDir().resolve("config"); + } - private static List zeroOrOne(T item) { - if (item == null) { - return List.of(); - } else { - return List.of(item); + private Path[] dataDirs() { + List dataDirs = PATH_DATA_SETTING.get(settings); + return dataDirs.isEmpty() + ? new Path[] { homeDir().resolve("data") } + : dataDirs.stream().map(NodeGrant::absolutePath).toArray(Path[]::new); } - } - public static boolean isEnabledForTest() { - return Booleans.parseBoolean(System.getProperty("es.entitlement.enableForTests", "false")); + private Path[] sharedDataDir() { + String sharedDataDir = PATH_SHARED_DATA_SETTING.get(settings); + return Strings.hasText(sharedDataDir) ? new Path[] { absolutePath(sharedDataDir) } : new Path[0]; + } + + private Path[] repoDirs() { + return PATH_REPO_SETTING.get(settings).stream().map(NodeGrant::absolutePath).toArray(Path[]::new); + } + + @SuppressForbidden(reason = "must be resolved using the default file system, rather then the mocked test file system") + private static Path absolutePath(String path) { + return Paths.get(path).toAbsolutePath().normalize(); + } + + @Override + public void close() { + onClose.accept(this); + } + + @Override + public String toString() { + return Strings.format( + "NodeGrant[configDir=%s, dataDirs=%s, sharedDataDir=%s, repoDirs=%s]", + configDir(), + dataDirs(), + sharedDataDir(), + repoDirs() + ); + } } - public static void setActive(boolean newValue) { - policyManager.setActive(newValue); + private void addGrant(NodeGrant nodeGrant) { + logger.debug("Adding node grant: {}", nodeGrant); + BASE_DIR_PATHS.compute(BaseDir.CONFIG, baseDirModifier(Collection::add, nodeGrant.configDir())); + BASE_DIR_PATHS.compute(BaseDir.DATA, baseDirModifier(Collection::add, nodeGrant.dataDirs())); + BASE_DIR_PATHS.compute(BaseDir.SHARED_DATA, baseDirModifier(Collection::add, nodeGrant.sharedDataDir())); + BASE_DIR_PATHS.compute(BaseDir.SHARED_REPO, baseDirModifier(Collection::add, nodeGrant.repoDirs())); + POLICY_MANAGER.clearModuleEntitlementsCache(); } - public static void setTriviallyAllowingTestCode(boolean newValue) { - policyManager.setTriviallyAllowingTestCode(newValue); + private void revokeGrant(NodeGrant nodeGrant) { + logger.debug("Revoking node grant: {}", nodeGrant); + BASE_DIR_PATHS.compute(BaseDir.CONFIG, baseDirModifier(Collection::remove, nodeGrant.configDir())); + BASE_DIR_PATHS.compute(BaseDir.DATA, baseDirModifier(Collection::remove, nodeGrant.dataDirs())); + BASE_DIR_PATHS.compute(BaseDir.SHARED_DATA, baseDirModifier(Collection::remove, nodeGrant.sharedDataDir())); + BASE_DIR_PATHS.compute(BaseDir.SHARED_REPO, baseDirModifier(Collection::remove, nodeGrant.repoDirs())); + POLICY_MANAGER.clearModuleEntitlementsCache(); } - public static void setEntitledTestPackages(String[] entitledTestPackages) { - policyManager.setEntitledTestPackages(entitledTestPackages); + // This must allow for duplicate paths between nodes, the config dir for instance is shared across all nodes. + private static BiFunction, Collection> baseDirModifier( + BiConsumer, Path> operation, + Path... updates + ) { + // always return a new unmodifiable copy + return (BaseDir baseDir, Collection paths) -> { + paths = paths == null ? new ArrayList<>() : new ArrayList<>(paths); + for (Path update : updates) { + operation.accept(paths, update); + } + return Collections.unmodifiableCollection(paths); + }; } - public static void reset() { - if (policyManager != null) { - policyManager.reset(); - } + public static boolean isEnabledForTest() { + return Booleans.parseBoolean(System.getProperty("es.entitlement.enableForTests", "false")); } private static TestPolicyManager createPolicyManager(PathLookup pathLookup) throws IOException { @@ -229,6 +298,12 @@ private static TestPolicyManager createPolicyManager(PathLookup pathLookup) thro ); } + private static void loadAgent(PolicyManager policyManager, PathLookup pathLookup) { + logger.debug("Loading entitlement agent"); + EntitlementInitialization.initializeArgs = new EntitlementInitialization.InitializeArgs(pathLookup, Set.of(), policyManager); + EntitlementBootstrap.loadAgent(EntitlementBootstrap.findAgentJar(), EntitlementInitialization.class.getName()); + } + private static Map parsePluginsPolicies(List pluginsData) { Map policies = new HashMap<>(); for (var pluginData : pluginsData) { diff --git a/test/framework/src/main/java/org/elasticsearch/entitlement/runtime/policy/TestPolicyManager.java b/test/framework/src/main/java/org/elasticsearch/entitlement/runtime/policy/TestPolicyManager.java index b504e51e119f7..5eac4bad38bc1 100644 --- a/test/framework/src/main/java/org/elasticsearch/entitlement/runtime/policy/TestPolicyManager.java +++ b/test/framework/src/main/java/org/elasticsearch/entitlement/runtime/policy/TestPolicyManager.java @@ -29,8 +29,8 @@ public class TestPolicyManager extends PolicyManager { - boolean isActive; - boolean isTriviallyAllowingTestCode; + boolean isActive = true; + boolean isTriviallyAllowingTestCode = true; String[] entitledTestPackages = TEST_FRAMEWORK_PACKAGE_PREFIXES; /** @@ -53,7 +53,6 @@ public TestPolicyManager( super(serverPolicy, apmAgentEntitlements, pluginPolicies, scopeResolver, name -> classpath, pathLookup); this.classpath = classpath; this.testOnlyClasspath = testOnlyClasspath; - reset(); } public void setActive(boolean newValue) { @@ -65,23 +64,27 @@ public void setTriviallyAllowingTestCode(boolean newValue) { } public void setEntitledTestPackages(String... entitledTestPackages) { + if (entitledTestPackages == null || entitledTestPackages.length == 0) { + this.entitledTestPackages = TEST_FRAMEWORK_PACKAGE_PREFIXES; // already validated and sorted + return; + } + assertNoRedundantPrefixes(TEST_FRAMEWORK_PACKAGE_PREFIXES, entitledTestPackages, false); if (entitledTestPackages.length > 1) { assertNoRedundantPrefixes(entitledTestPackages, entitledTestPackages, true); } - String[] packages = ArrayUtils.concat(this.entitledTestPackages, entitledTestPackages); + String[] packages = ArrayUtils.concat(TEST_FRAMEWORK_PACKAGE_PREFIXES, entitledTestPackages); Arrays.sort(packages); this.entitledTestPackages = packages; } /** - * Called between tests so each test is not affected by prior tests + * Clear cached module entitlements. + * This is required after updating entries in {@link TestPathLookup}. */ - public final void reset() { + public final void clearModuleEntitlementsCache() { assert moduleEntitlementsMap.isEmpty() : "We're not supposed to be using moduleEntitlementsMap in tests"; classEntitlementsMap.clear(); - isActive = false; - isTriviallyAllowingTestCode = true; } @Override diff --git a/test/framework/src/main/java/org/elasticsearch/node/MockNode.java b/test/framework/src/main/java/org/elasticsearch/node/MockNode.java index 3d5229435e729..d3d96b5102479 100644 --- a/test/framework/src/main/java/org/elasticsearch/node/MockNode.java +++ b/test/framework/src/main/java/org/elasticsearch/node/MockNode.java @@ -24,7 +24,7 @@ import org.elasticsearch.common.util.MockBigArrays; import org.elasticsearch.common.util.MockPageCacheRecycler; import org.elasticsearch.common.util.PageCacheRecycler; -import org.elasticsearch.entitlement.bootstrap.TestEntitlementBootstrap; +import org.elasticsearch.core.IOUtils; import org.elasticsearch.env.Environment; import org.elasticsearch.http.HttpServerTransport; import org.elasticsearch.indices.ExecutorSelector; @@ -54,11 +54,12 @@ import org.elasticsearch.transport.TransportService; import org.elasticsearch.transport.TransportSettings; -import java.io.IOException; +import java.io.Closeable; import java.nio.file.Path; import java.util.Collection; import java.util.Collections; import java.util.Map; +import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.function.LongSupplier; @@ -237,32 +238,37 @@ protected HttpServerTransport newHttpTransport(PluginsService pluginsService, Ne } private final Collection> classpathPlugins; + // handle for node paths specific entitlement grants + private final Closeable entitlementGrant; public MockNode(final Settings settings, final Collection> classpathPlugins) { - this(settings, classpathPlugins, true); + this(settings, classpathPlugins, true, () -> {}); } public MockNode( final Settings settings, final Collection> classpathPlugins, - final boolean forbidPrivateIndexSettings + final boolean forbidPrivateIndexSettings, + final Closeable entitlementGrant ) { - this(settings, classpathPlugins, null, forbidPrivateIndexSettings); + this(settings, classpathPlugins, null, forbidPrivateIndexSettings, entitlementGrant); } public MockNode( final Settings settings, final Collection> classpathPlugins, final Path configPath, - final boolean forbidPrivateIndexSettings + final boolean forbidPrivateIndexSettings, + final Closeable entitlementGrant ) { - this(prepareEnvironment(settings, configPath), classpathPlugins, forbidPrivateIndexSettings); + this(prepareEnvironment(settings, configPath), classpathPlugins, forbidPrivateIndexSettings, entitlementGrant); } private MockNode( final Environment environment, final Collection> classpathPlugins, - final boolean forbidPrivateIndexSettings + final boolean forbidPrivateIndexSettings, + final Closeable entitlementGrant ) { super(NodeConstruction.prepareConstruction(environment, null, new MockServiceProvider() { @@ -273,10 +279,10 @@ PluginsService newPluginService(Environment environment, PluginsLoader pluginsLo }, forbidPrivateIndexSettings)); this.classpathPlugins = classpathPlugins; + this.entitlementGrant = entitlementGrant; } private static Environment prepareEnvironment(final Settings settings, final Path configPath) { - TestEntitlementBootstrap.registerNodeBaseDirs(settings, configPath); return InternalSettingsPreparer.prepareEnvironment( Settings.builder().put(TransportSettings.PORT.getKey(), ESTestCase.getPortRange()).put(settings).build(), Collections.emptyMap(), @@ -286,11 +292,12 @@ private static Environment prepareEnvironment(final Settings settings, final Pat } @Override - public synchronized void close() throws IOException { + public synchronized boolean awaitClose(long timeout, TimeUnit timeUnit) throws InterruptedException { try { - super.close(); + return super.awaitClose(timeout, timeUnit); } finally { - TestEntitlementBootstrap.unregisterNodeBaseDirs(getEnvironment().settings(), getEnvironment().configDir()); + // wipePendingDataDirectories requires entitlement delegation to work due to this using FileSystemUtils ES-10920 + IOUtils.closeWhileHandlingException(entitlementGrant); } } diff --git a/test/framework/src/main/java/org/elasticsearch/test/AbstractMultiClustersTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/AbstractMultiClustersTestCase.java index b4f91f68b8bb7..2b6abb67b4353 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/AbstractMultiClustersTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/AbstractMultiClustersTestCase.java @@ -127,7 +127,8 @@ public final void startClusters() throws Exception { 0, clusterName + "-", mockPlugins, - Function.identity() + Function.identity(), + TEST_ENTITLEMENTS::newNodeGrant ); try { cluster.beforeTest(random()); diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java index 89099bb7e32ff..52f99ef8fdd1f 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java @@ -549,6 +549,7 @@ private TestCluster buildAndPutCluster(Scope currentClusterScope, long seed) thr // close the previous one and create a new one if (testCluster != null) { IOUtils.closeWhileHandlingException(testCluster::close); + TEST_ENTITLEMENTS.revokeNodeGrants(); } testCluster = buildTestCluster(currentClusterScope, seed); } @@ -2336,7 +2337,8 @@ protected TestCluster buildTestCluster(Scope scope, long seed) throws IOExceptio getClientWrapper(), forbidPrivateIndexSettings(), forceSingleDataPath(), - autoManageVotingExclusions() + autoManageVotingExclusions(), + TEST_ENTITLEMENTS::newNodeGrant ); } diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java index 7ebc5765bda63..66af9e9c56e61 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java @@ -288,7 +288,7 @@ private Node newNode() { plugins.add(ConcurrentSearchTestPlugin.class); } plugins.add(MockScriptService.TestPlugin.class); - Node node = new MockNode(settings, plugins, forbidPrivateIndexSettings()); + Node node = new MockNode(settings, plugins, forbidPrivateIndexSettings(), TEST_ENTITLEMENTS.newNodeGrant(settings, null)); try { node.start(); } catch (NodeValidationException e) { diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java index 50d78657fbfa1..43243e431db48 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java @@ -111,7 +111,7 @@ import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.core.TimeValue; import org.elasticsearch.core.Tuple; -import org.elasticsearch.entitlement.bootstrap.TestEntitlementBootstrap; +import org.elasticsearch.entitlement.bootstrap.TestEntitlementsRule; import org.elasticsearch.env.Environment; import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.env.TestEnvironment; @@ -159,6 +159,7 @@ import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; +import org.junit.ClassRule; import org.junit.Rule; import org.junit.internal.AssumptionViolatedException; import org.junit.rules.RuleChain; @@ -524,30 +525,8 @@ protected void afterIfSuccessful() throws Exception {} String[] value(); } - @BeforeClass - public static void setupEntitlementsForClass() { - boolean withoutEntitlements = getTestClass().isAnnotationPresent(WithoutEntitlements.class); - boolean withEntitlementsOnTestCode = getTestClass().isAnnotationPresent(WithEntitlementsOnTestCode.class); - EntitledTestPackages entitledPackages = getTestClass().getAnnotation(EntitledTestPackages.class); - - if (TestEntitlementBootstrap.isEnabledForTest()) { - TestEntitlementBootstrap.setActive(false == withoutEntitlements); - TestEntitlementBootstrap.setTriviallyAllowingTestCode(false == withEntitlementsOnTestCode); - if (entitledPackages != null) { - assert entitledPackages.value().length > 0 : "No test packages specified in @EntitledTestPackages"; - TestEntitlementBootstrap.setEntitledTestPackages(entitledPackages.value()); - } - } else if (withEntitlementsOnTestCode) { - throw new AssertionError( - "Cannot use @WithEntitlementsOnTestCode on tests that are not configured to use entitlements for testing" - ); - } - } - - @AfterClass - public static void resetEntitlements() { - TestEntitlementBootstrap.reset(); - } + @ClassRule + public static final TestEntitlementsRule TEST_ENTITLEMENTS = new TestEntitlementsRule(); // setup mock filesystems for this test run. we change PathUtils // so that all accesses are plumbed thru any mock wrappers diff --git a/test/framework/src/main/java/org/elasticsearch/test/InternalTestCluster.java b/test/framework/src/main/java/org/elasticsearch/test/InternalTestCluster.java index cb45797f9b822..0ab12b697801c 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/InternalTestCluster.java +++ b/test/framework/src/main/java/org/elasticsearch/test/InternalTestCluster.java @@ -178,6 +178,9 @@ *

*/ public final class InternalTestCluster extends TestCluster { + public interface NodeGrantProvider { + Closeable create(Settings settings, Path configPath); + } private static final Logger logger = LogManager.getLogger(InternalTestCluster.class); @@ -278,6 +281,8 @@ public String toString() { // index of node to bootstrap as master, or BOOTSTRAP_MASTER_NODE_INDEX_AUTO or BOOTSTRAP_MASTER_NODE_INDEX_DONE private int bootstrapMasterNodeIndex = BOOTSTRAP_MASTER_NODE_INDEX_AUTO; + private final NodeGrantProvider nodeGrantProvider; + public InternalTestCluster( final long clusterSeed, final Path baseDir, @@ -290,7 +295,8 @@ public InternalTestCluster( final int numClientNodes, final String nodePrefix, final Collection> mockPlugins, - final Function clientWrapper + final Function clientWrapper, + NodeGrantProvider nodeGrantProvider ) { this( clusterSeed, @@ -307,7 +313,8 @@ public InternalTestCluster( clientWrapper, true, false, - true + true, + nodeGrantProvider ); } @@ -326,7 +333,8 @@ public InternalTestCluster( final Function clientWrapper, final boolean forbidPrivateIndexSettings, final boolean forceSingleDataPath, - final boolean autoManageVotingExclusions + final boolean autoManageVotingExclusions, + final NodeGrantProvider nodeGrantProvider ) { super(clusterSeed); this.autoManageMasterNodes = autoManageMasterNodes; @@ -335,6 +343,7 @@ public InternalTestCluster( this.baseDir = baseDir; this.clusterName = clusterName; this.autoManageVotingExclusions = autoManageVotingExclusions; + this.nodeGrantProvider = nodeGrantProvider; if (minNumDataNodes < 0 || maxNumDataNodes < 0) { throw new IllegalArgumentException("minimum and maximum number of data nodes must be >= 0"); } @@ -783,7 +792,14 @@ private synchronized NodeAndClient buildNode(int nodeId, Settings settings, bool // we clone this here since in the case of a node restart we might need it again secureSettings = ((MockSecureSettings) secureSettings).clone(); } - MockNode node = new MockNode(settings, plugins, nodeConfigurationSource.nodeConfigPath(nodeId), forbidPrivateIndexSettings); + Path configPath = nodeConfigurationSource.nodeConfigPath(nodeId); + MockNode node = new MockNode( + settings, + plugins, + configPath, + forbidPrivateIndexSettings, + nodeGrantProvider.create(settings, configPath) + ); node.injector().getInstance(TransportService.class).addLifecycleListener(new LifecycleListener() { @Override public void afterStart() { @@ -1058,7 +1074,7 @@ private void recreateNode(final Settings newSettings, final Runnable onTransport .put(NodeEnvironment.NODE_ID_SEED_SETTING.getKey(), newIdSeed) .build(); Collection> plugins = node.getClasspathPlugins(); - node = new MockNode(finalSettings, plugins, forbidPrivateIndexSettings); + node = new MockNode(finalSettings, plugins, forbidPrivateIndexSettings, nodeGrantProvider.create(finalSettings, null)); node.injector().getInstance(TransportService.class).addLifecycleListener(new LifecycleListener() { @Override public void afterStart() { diff --git a/test/framework/src/test/java/org/elasticsearch/entitlement/runtime/policy/TestPolicyManagerTests.java b/test/framework/src/test/java/org/elasticsearch/entitlement/runtime/policy/TestPolicyManagerTests.java index 4a62355f398d8..c0f1aaa227cdb 100644 --- a/test/framework/src/test/java/org/elasticsearch/entitlement/runtime/policy/TestPolicyManagerTests.java +++ b/test/framework/src/test/java/org/elasticsearch/entitlement/runtime/policy/TestPolicyManagerTests.java @@ -41,13 +41,13 @@ public void setupPolicyManager() { policyManager.setActive(true); } - public void testReset() { + public void testResetAfterTest() { assertTrue(policyManager.classEntitlementsMap.isEmpty()); assertEquals("example-plugin1", policyManager.getEntitlements(getClass()).componentName()); assertEquals("example-plugin1", policyManager.getEntitlements(getClass()).componentName()); assertFalse(policyManager.classEntitlementsMap.isEmpty()); - policyManager.reset(); + policyManager.resetAfterTest(); assertTrue(policyManager.classEntitlementsMap.isEmpty()); assertEquals("example-plugin2", policyManager.getEntitlements(getClass()).componentName()); diff --git a/test/framework/src/test/java/org/elasticsearch/test/test/InternalTestClusterTests.java b/test/framework/src/test/java/org/elasticsearch/test/test/InternalTestClusterTests.java index f5c358b3b3301..23282572607c0 100644 --- a/test/framework/src/test/java/org/elasticsearch/test/test/InternalTestClusterTests.java +++ b/test/framework/src/test/java/org/elasticsearch/test/test/InternalTestClusterTests.java @@ -85,7 +85,8 @@ public void testInitializiationIsConsistent() { numClientNodes, nodePrefix, Collections.emptyList(), - Function.identity() + Function.identity(), + TEST_ENTITLEMENTS::newNodeGrant ); InternalTestCluster cluster1 = new InternalTestCluster( clusterSeed, @@ -99,7 +100,8 @@ public void testInitializiationIsConsistent() { numClientNodes, nodePrefix, Collections.emptyList(), - Function.identity() + Function.identity(), + TEST_ENTITLEMENTS::newNodeGrant ); assertClusters(cluster0, cluster1, true); } @@ -198,7 +200,8 @@ public Path nodeConfigPath(int nodeOrdinal) { numClientNodes, nodePrefix, mockPlugins(), - Function.identity() + Function.identity(), + TEST_ENTITLEMENTS::newNodeGrant ); cluster0.setBootstrapMasterNodeIndex(bootstrapMasterNodeIndex); @@ -214,7 +217,8 @@ public Path nodeConfigPath(int nodeOrdinal) { numClientNodes, nodePrefix, mockPlugins(), - Function.identity() + Function.identity(), + TEST_ENTITLEMENTS::newNodeGrant ); cluster1.setBootstrapMasterNodeIndex(bootstrapMasterNodeIndex); @@ -280,7 +284,8 @@ public Path nodeConfigPath(int nodeOrdinal) { numClientNodes, nodePrefix, mockPlugins(), - Function.identity() + Function.identity(), + TEST_ENTITLEMENTS::newNodeGrant ); try { cluster.beforeTest(random()); @@ -375,7 +380,8 @@ public Path nodeConfigPath(int nodeOrdinal) { 0, "", mockPlugins(), - Function.identity() + Function.identity(), + TEST_ENTITLEMENTS::newNodeGrant ); cluster.beforeTest(random()); List roles = new ArrayList<>(); @@ -467,7 +473,8 @@ public Path nodeConfigPath(int nodeOrdinal) { 0, nodePrefix, plugins, - Function.identity() + Function.identity(), + TEST_ENTITLEMENTS::newNodeGrant ); try { cluster.beforeTest(random()); diff --git a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/CcrIntegTestCase.java b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/CcrIntegTestCase.java index 644a2a074ac59..bd2f53b238ae7 100644 --- a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/CcrIntegTestCase.java +++ b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/CcrIntegTestCase.java @@ -182,7 +182,8 @@ public final void startClusters() throws Exception { 0, "leader", mockPlugins, - Function.identity() + Function.identity(), + TEST_ENTITLEMENTS::newNodeGrant ); leaderCluster.beforeTest(random()); leaderCluster.ensureAtLeastNumDataNodes(numberOfNodesPerCluster()); @@ -204,7 +205,8 @@ public final void startClusters() throws Exception { 0, "follower", mockPlugins, - Function.identity() + Function.identity(), + TEST_ENTITLEMENTS::newNodeGrant ); clusterGroup = new ClusterGroup(leaderCluster, followerCluster); diff --git a/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/action/filter/ShardBulkInferenceActionFilterBasicLicenseIT.java b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/action/filter/ShardBulkInferenceActionFilterBasicLicenseIT.java index e2d00b8c52781..33b9adb431a0a 100644 --- a/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/action/filter/ShardBulkInferenceActionFilterBasicLicenseIT.java +++ b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/action/filter/ShardBulkInferenceActionFilterBasicLicenseIT.java @@ -23,6 +23,7 @@ import org.elasticsearch.license.LicenseSettings; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESIntegTestCase; +import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.core.XPackField; import org.elasticsearch.xpack.inference.LocalStateInferencePlugin; import org.elasticsearch.xpack.inference.Utils; @@ -42,6 +43,7 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.instanceOf; +@ESTestCase.WithoutEntitlements // due to dependency issue ES-12435 public class ShardBulkInferenceActionFilterBasicLicenseIT extends ESIntegTestCase { public static final String INDEX_NAME = "test-index"; diff --git a/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/action/filter/ShardBulkInferenceActionFilterIT.java b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/action/filter/ShardBulkInferenceActionFilterIT.java index 8405fba22460f..7ddbf4fc55ffd 100644 --- a/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/action/filter/ShardBulkInferenceActionFilterIT.java +++ b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/action/filter/ShardBulkInferenceActionFilterIT.java @@ -32,6 +32,7 @@ import org.elasticsearch.plugins.Plugin; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.test.ESIntegTestCase; +import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.InternalTestCluster; import org.elasticsearch.xpack.inference.InferenceIndex; import org.elasticsearch.xpack.inference.LocalStateInferencePlugin; @@ -56,6 +57,7 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; +@ESTestCase.WithoutEntitlements // due to dependency issue ES-12435 public class ShardBulkInferenceActionFilterIT extends ESIntegTestCase { public static final String INDEX_NAME = "test-index"; diff --git a/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/InferenceRevokeDefaultEndpointsIT.java b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/InferenceRevokeDefaultEndpointsIT.java index 1eb530ac1bb9e..a0055332a36c2 100644 --- a/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/InferenceRevokeDefaultEndpointsIT.java +++ b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/InferenceRevokeDefaultEndpointsIT.java @@ -20,6 +20,7 @@ import org.elasticsearch.plugins.Plugin; import org.elasticsearch.reindex.ReindexPlugin; import org.elasticsearch.test.ESSingleNodeTestCase; +import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.http.MockResponse; import org.elasticsearch.test.http.MockWebServer; import org.elasticsearch.threadpool.ThreadPool; @@ -47,6 +48,7 @@ import static org.hamcrest.Matchers.containsInAnyOrder; import static org.mockito.Mockito.mock; +@ESTestCase.WithoutEntitlements // due to dependency issue ES-12435 public class InferenceRevokeDefaultEndpointsIT extends ESSingleNodeTestCase { private static final TimeValue TIMEOUT = new TimeValue(30, TimeUnit.SECONDS); diff --git a/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/ModelRegistryIT.java b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/ModelRegistryIT.java index 22aebee72df0c..92eea9599ec5d 100644 --- a/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/ModelRegistryIT.java +++ b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/ModelRegistryIT.java @@ -35,6 +35,7 @@ import org.elasticsearch.plugins.Plugin; import org.elasticsearch.reindex.ReindexPlugin; import org.elasticsearch.test.ESSingleNodeTestCase; +import org.elasticsearch.test.ESTestCase; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.xcontent.ToXContentObject; import org.elasticsearch.xcontent.XContentBuilder; @@ -75,6 +76,7 @@ import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; +@ESTestCase.WithoutEntitlements // due to dependency issue ES-12435 public class ModelRegistryIT extends ESSingleNodeTestCase { private static final TimeValue TIMEOUT = new TimeValue(30, TimeUnit.SECONDS); diff --git a/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/SemanticTextIndexOptionsIT.java b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/SemanticTextIndexOptionsIT.java index 035ec4dc9593d..08d3f0a6a9b9f 100644 --- a/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/SemanticTextIndexOptionsIT.java +++ b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/SemanticTextIndexOptionsIT.java @@ -30,6 +30,7 @@ import org.elasticsearch.protocol.xpack.license.GetLicenseRequest; import org.elasticsearch.reindex.ReindexPlugin; import org.elasticsearch.test.ESIntegTestCase; +import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.InternalTestCluster; import org.elasticsearch.xcontent.ToXContent; import org.elasticsearch.xcontent.XContentBuilder; @@ -55,6 +56,7 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.hamcrest.CoreMatchers.equalTo; +@ESTestCase.WithoutEntitlements // due to dependency issue ES-12435 public class SemanticTextIndexOptionsIT extends ESIntegTestCase { private static final String INDEX_NAME = "test-index"; private static final Map BBQ_COMPATIBLE_SERVICE_SETTINGS = Map.of( diff --git a/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/SemanticTextIndexVersionIT.java b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/SemanticTextIndexVersionIT.java index 6f8992b5bd200..8986b0a158e9f 100644 --- a/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/SemanticTextIndexVersionIT.java +++ b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/integration/SemanticTextIndexVersionIT.java @@ -24,6 +24,7 @@ import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder; import org.elasticsearch.test.ESIntegTestCase; +import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.index.IndexVersionUtils; import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xcontent.XContentFactory; @@ -50,6 +51,7 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertResponse; import static org.hamcrest.Matchers.equalTo; +@ESTestCase.WithoutEntitlements // due to dependency issue ES-12435 public class SemanticTextIndexVersionIT extends ESIntegTestCase { private static final int MAXIMUM_NUMBER_OF_VERSIONS_TO_TEST = 25; private static final String SPARSE_SEMANTIC_FIELD = "sparse_field"; diff --git a/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/rest/ServerSentEventsRestActionListenerTests.java b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/rest/ServerSentEventsRestActionListenerTests.java index 87e14e4e89d88..3287995588d86 100644 --- a/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/rest/ServerSentEventsRestActionListenerTests.java +++ b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/rest/ServerSentEventsRestActionListenerTests.java @@ -45,6 +45,7 @@ import org.elasticsearch.rest.RestRequest; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.test.ESIntegTestCase; +import org.elasticsearch.test.ESTestCase; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.xcontent.ToXContent; import org.elasticsearch.xpack.core.inference.action.InferenceAction; @@ -75,6 +76,7 @@ import static org.hamcrest.Matchers.nullValue; @ESIntegTestCase.ClusterScope(numDataNodes = 1) +@ESTestCase.WithoutEntitlements // due to dependency issue ES-12435 public class ServerSentEventsRestActionListenerTests extends ESIntegTestCase { private static final String INFERENCE_ROUTE = "/_inference"; private static final String REQUEST_COUNT = "request_count"; diff --git a/x-pack/plugin/searchable-snapshots/src/main/plugin-metadata/entitlement-policy.yaml b/x-pack/plugin/searchable-snapshots/src/main/plugin-metadata/entitlement-policy.yaml index 69eead6707114..d21ee299b832d 100644 --- a/x-pack/plugin/searchable-snapshots/src/main/plugin-metadata/entitlement-policy.yaml +++ b/x-pack/plugin/searchable-snapshots/src/main/plugin-metadata/entitlement-policy.yaml @@ -6,3 +6,6 @@ org.elasticsearch.searchablesnapshots: - relative_path: indices relative_to: data mode: read_write + - relative_path: "" + relative_to: shared_data + mode: read_write