|
@@ -46,7 +46,10 @@ import org.elasticsearch.license.LicenseService;
|
|
|
import org.elasticsearch.license.TestUtils;
|
|
|
import org.elasticsearch.license.XPackLicenseState;
|
|
|
import org.elasticsearch.license.internal.XPackLicenseStatus;
|
|
|
+import org.elasticsearch.plugins.ExtensiblePlugin;
|
|
|
import org.elasticsearch.plugins.MapperPlugin;
|
|
|
+import org.elasticsearch.rest.RestChannel;
|
|
|
+import org.elasticsearch.rest.RestHandler;
|
|
|
import org.elasticsearch.rest.RestRequest;
|
|
|
import org.elasticsearch.script.ScriptService;
|
|
|
import org.elasticsearch.test.ESTestCase;
|
|
@@ -57,6 +60,7 @@ import org.elasticsearch.test.rest.FakeRestRequest;
|
|
|
import org.elasticsearch.threadpool.TestThreadPool;
|
|
|
import org.elasticsearch.threadpool.ThreadPool;
|
|
|
import org.elasticsearch.tracing.Tracer;
|
|
|
+import org.elasticsearch.transport.TransportRequest;
|
|
|
import org.elasticsearch.usage.UsageService;
|
|
|
import org.elasticsearch.watcher.ResourceWatcherService;
|
|
|
import org.elasticsearch.xpack.core.XPackField;
|
|
@@ -85,11 +89,16 @@ import org.elasticsearch.xpack.security.authc.Realms;
|
|
|
import org.elasticsearch.xpack.security.authc.esnative.NativeUsersStore;
|
|
|
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
|
|
|
import org.elasticsearch.xpack.security.authc.service.CachingServiceAccountTokenStore;
|
|
|
+import org.elasticsearch.xpack.security.operator.DefaultOperatorOnlyRegistry;
|
|
|
+import org.elasticsearch.xpack.security.operator.OperatorOnlyRegistry;
|
|
|
+import org.elasticsearch.xpack.security.operator.OperatorPrivileges;
|
|
|
+import org.elasticsearch.xpack.security.operator.OperatorPrivilegesViolation;
|
|
|
import org.hamcrest.Matchers;
|
|
|
import org.junit.After;
|
|
|
|
|
|
import java.nio.file.Files;
|
|
|
import java.nio.file.Path;
|
|
|
+import java.util.ArrayList;
|
|
|
import java.util.Arrays;
|
|
|
import java.util.Collection;
|
|
|
import java.util.Collections;
|
|
@@ -107,6 +116,8 @@ import java.util.stream.Collectors;
|
|
|
import static java.util.Collections.emptyMap;
|
|
|
import static org.elasticsearch.cluster.metadata.IndexMetadata.INDEX_FORMAT_SETTING;
|
|
|
import static org.elasticsearch.xpack.core.security.authc.RealmSettings.getFullSettingKey;
|
|
|
+import static org.elasticsearch.xpack.security.operator.OperatorPrivileges.NOOP_OPERATOR_PRIVILEGES_SERVICE;
|
|
|
+import static org.elasticsearch.xpack.security.operator.OperatorPrivileges.OPERATOR_PRIVILEGES_ENABLED;
|
|
|
import static org.elasticsearch.xpack.security.support.SecuritySystemIndices.INTERNAL_MAIN_INDEX_FORMAT;
|
|
|
import static org.elasticsearch.xpack.security.support.SecuritySystemIndices.SECURITY_MAIN_ALIAS;
|
|
|
import static org.hamcrest.Matchers.containsInAnyOrder;
|
|
@@ -116,6 +127,7 @@ import static org.hamcrest.Matchers.equalTo;
|
|
|
import static org.hamcrest.Matchers.hasItem;
|
|
|
import static org.hamcrest.Matchers.hasSize;
|
|
|
import static org.hamcrest.Matchers.instanceOf;
|
|
|
+import static org.hamcrest.Matchers.is;
|
|
|
import static org.hamcrest.Matchers.not;
|
|
|
import static org.hamcrest.Matchers.notNullValue;
|
|
|
import static org.hamcrest.Matchers.nullValue;
|
|
@@ -143,22 +155,40 @@ public class SecurityTests extends ESTestCase {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private Collection<Object> createComponentsUtil(Settings settings, SecurityExtension... extensions) throws Exception {
|
|
|
+ public static class DummyOperatorOnlyRegistry implements OperatorOnlyRegistry {
|
|
|
+ @Override
|
|
|
+ public OperatorPrivilegesViolation check(String action, TransportRequest request) {
|
|
|
+ throw new RuntimeException("boom");
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public OperatorPrivilegesViolation checkRest(RestHandler restHandler, RestRequest restRequest, RestChannel restChannel) {
|
|
|
+ throw new RuntimeException("boom");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void constructNewSecurityObject(Settings settings, SecurityExtension... extensions) {
|
|
|
Environment env = TestEnvironment.newEnvironment(settings);
|
|
|
- NodeMetadata nodeMetadata = new NodeMetadata(randomAlphaOfLength(8), Version.CURRENT, Version.CURRENT);
|
|
|
licenseState = new TestUtils.UpdatableLicenseState(settings);
|
|
|
SSLService sslService = new SSLService(env);
|
|
|
- security = new Security(settings, Arrays.asList(extensions)) {
|
|
|
- @Override
|
|
|
- protected XPackLicenseState getLicenseState() {
|
|
|
- return licenseState;
|
|
|
- }
|
|
|
+ if (security == null) {
|
|
|
+ security = new Security(settings, Arrays.asList(extensions)) {
|
|
|
+ @Override
|
|
|
+ protected XPackLicenseState getLicenseState() {
|
|
|
+ return licenseState;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ protected SSLService getSslService() {
|
|
|
+ return sslService;
|
|
|
+ }
|
|
|
+ };
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- @Override
|
|
|
- protected SSLService getSslService() {
|
|
|
- return sslService;
|
|
|
- }
|
|
|
- };
|
|
|
+ private Collection<Object> createComponentsUtil(Settings settings) throws Exception {
|
|
|
+ Environment env = TestEnvironment.newEnvironment(settings);
|
|
|
+ NodeMetadata nodeMetadata = new NodeMetadata(randomAlphaOfLength(8), Version.CURRENT, Version.CURRENT);
|
|
|
ThreadPool threadPool = mock(ThreadPool.class);
|
|
|
ClusterService clusterService = mock(ClusterService.class);
|
|
|
settings = Security.additionalSettings(settings, true);
|
|
@@ -195,7 +225,8 @@ public class SecurityTests extends ESTestCase {
|
|
|
.put(testSettings)
|
|
|
.put("path.home", createTempDir())
|
|
|
.build();
|
|
|
- return createComponentsUtil(settings, extensions);
|
|
|
+ constructNewSecurityObject(settings, extensions);
|
|
|
+ return createComponentsUtil(settings);
|
|
|
}
|
|
|
|
|
|
private static <T> T findComponent(Class<T> type, Collection<Object> components) {
|
|
@@ -897,6 +928,112 @@ public class SecurityTests extends ESTestCase {
|
|
|
);
|
|
|
}
|
|
|
|
|
|
+ public void testLoadExtensions() throws Exception {
|
|
|
+ Settings settings = Settings.builder()
|
|
|
+ .put("xpack.security.enabled", true)
|
|
|
+ .put("path.home", createTempDir())
|
|
|
+ .put(OPERATOR_PRIVILEGES_ENABLED.getKey(), true)
|
|
|
+ .build();
|
|
|
+ constructNewSecurityObject(settings);
|
|
|
+ security.loadExtensions(new ExtensiblePlugin.ExtensionLoader() {
|
|
|
+ @Override
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ public <T> List<T> loadExtensions(Class<T> extensionPointType) {
|
|
|
+ List<Object> extensions = new ArrayList<>();
|
|
|
+ if (extensionPointType == OperatorOnlyRegistry.class) {
|
|
|
+ extensions.add(new DummyOperatorOnlyRegistry());
|
|
|
+ }
|
|
|
+ return (List<T>) extensions;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ createComponentsUtil(settings);
|
|
|
+ OperatorPrivileges.OperatorPrivilegesService operatorPrivilegesService = security.getOperatorPrivilegesService();
|
|
|
+ assertThat(operatorPrivilegesService, instanceOf(OperatorPrivileges.DefaultOperatorPrivilegesService.class));
|
|
|
+ OperatorOnlyRegistry registry = ((OperatorPrivileges.DefaultOperatorPrivilegesService) operatorPrivilegesService)
|
|
|
+ .getOperatorOnlyRegistry();
|
|
|
+ assertThat(registry, instanceOf(DummyOperatorOnlyRegistry.class));
|
|
|
+ }
|
|
|
+
|
|
|
+ public void testLoadNoExtensions() throws Exception {
|
|
|
+ Settings settings = Settings.builder()
|
|
|
+ .put("xpack.security.enabled", true)
|
|
|
+ .put("path.home", createTempDir())
|
|
|
+ .put(OPERATOR_PRIVILEGES_ENABLED.getKey(), true)
|
|
|
+ .build();
|
|
|
+ constructNewSecurityObject(settings);
|
|
|
+ security.loadExtensions(new ExtensiblePlugin.ExtensionLoader() {
|
|
|
+ @Override
|
|
|
+ public <T> List<T> loadExtensions(Class<T> extensionPointType) {
|
|
|
+ return new ArrayList<>();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ createComponentsUtil(settings);
|
|
|
+ OperatorPrivileges.OperatorPrivilegesService operatorPrivilegesService = security.getOperatorPrivilegesService();
|
|
|
+ assertThat(operatorPrivilegesService, instanceOf(OperatorPrivileges.DefaultOperatorPrivilegesService.class));
|
|
|
+ OperatorOnlyRegistry registry = ((OperatorPrivileges.DefaultOperatorPrivilegesService) operatorPrivilegesService)
|
|
|
+ .getOperatorOnlyRegistry();
|
|
|
+ assertThat(registry, instanceOf(DefaultOperatorOnlyRegistry.class));
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public void testLoadExtensionsWhenOperatorPrivsAreDisabled() throws Exception {
|
|
|
+ assumeFalse("feature flag for serverless is expected to be false", DiscoveryNode.isServerless());
|
|
|
+ Settings.Builder settingsBuilder = Settings.builder().put("xpack.security.enabled", true).put("path.home", createTempDir());
|
|
|
+
|
|
|
+ if (randomBoolean()) {
|
|
|
+ settingsBuilder.put(OPERATOR_PRIVILEGES_ENABLED.getKey(), false); // doesn't matter if explicit or implicitly disabled
|
|
|
+ }
|
|
|
+
|
|
|
+ Settings settings = settingsBuilder.build();
|
|
|
+ constructNewSecurityObject(settings);
|
|
|
+ security.loadExtensions(new ExtensiblePlugin.ExtensionLoader() {
|
|
|
+ @Override
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ public <T> List<T> loadExtensions(Class<T> extensionPointType) {
|
|
|
+ List<Object> extensions = new ArrayList<>();
|
|
|
+ if (extensionPointType == OperatorOnlyRegistry.class) {
|
|
|
+ if (randomBoolean()) {
|
|
|
+ extensions.add(new DummyOperatorOnlyRegistry()); // won't ever be used
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return (List<T>) extensions;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ createComponentsUtil(settings);
|
|
|
+ OperatorPrivileges.OperatorPrivilegesService operatorPrivilegesService = security.getOperatorPrivilegesService();
|
|
|
+ assertThat(operatorPrivilegesService, is(NOOP_OPERATOR_PRIVILEGES_SERVICE));
|
|
|
+ }
|
|
|
+
|
|
|
+ public void testLoadExtensionsWhenOperatorPrivsAreDisabledAndServerless() throws Exception {
|
|
|
+ assumeTrue("feature flag for serverless is expected to be true", DiscoveryNode.isServerless());
|
|
|
+ Settings.Builder settingsBuilder = Settings.builder().put("xpack.security.enabled", true).put("path.home", createTempDir());
|
|
|
+
|
|
|
+ if (randomBoolean()) {
|
|
|
+ settingsBuilder.put(OPERATOR_PRIVILEGES_ENABLED.getKey(), false); // doesn't matter if explicit or implicitly disabled
|
|
|
+ }
|
|
|
+
|
|
|
+ Settings settings = settingsBuilder.build();
|
|
|
+ constructNewSecurityObject(settings);
|
|
|
+ security.loadExtensions(new ExtensiblePlugin.ExtensionLoader() {
|
|
|
+ @Override
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ public <T> List<T> loadExtensions(Class<T> extensionPointType) {
|
|
|
+ List<Object> extensions = new ArrayList<>();
|
|
|
+ if (extensionPointType == OperatorOnlyRegistry.class) {
|
|
|
+ if (randomBoolean()) {
|
|
|
+ extensions.add(new DummyOperatorOnlyRegistry()); // will be used
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return (List<T>) extensions;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ createComponentsUtil(settings);
|
|
|
+ OperatorPrivileges.OperatorPrivilegesService operatorPrivilegesService = security.getOperatorPrivilegesService();
|
|
|
+ OperatorOnlyRegistry registry = ((OperatorPrivileges.DefaultOperatorPrivilegesService) operatorPrivilegesService)
|
|
|
+ .getOperatorOnlyRegistry();
|
|
|
+ assertThat(registry, instanceOf(DummyOperatorOnlyRegistry.class));
|
|
|
+ }
|
|
|
+
|
|
|
private void verifyHasAuthenticationHeaderValue(Exception e, String... expectedValues) {
|
|
|
assertThat(e, instanceOf(ElasticsearchSecurityException.class));
|
|
|
assertThat(((ElasticsearchSecurityException) e).getHeader("WWW-Authenticate"), notNullValue());
|