Avoid NPE in set_security_user without security (#53543)
If security was disabled (explicitly), then the SecurityContext would be null, but the set_security_user processor was still registered. Attempting to define a pipeline that used that processor would fail with an (intentional) NPE. This behaviour, introduced in #52032, is a regression from previous releases where the pipeline was allowed, but was no usable. This change restores the previous behaviour (with a new warning). Backport of: #52691
This commit is contained in:
parent
e7f38674ed
commit
74dbdb991c
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* This QA project tests the security plugin when security is explicitlt disabled.
|
||||||
|
* It is intended to cover security functionality which is supposed to
|
||||||
|
* function in a specific way even if security is disabled on the cluster
|
||||||
|
* For example: If a cluster has a pipeline with the set_security_user processor
|
||||||
|
* defined, it should be not fail
|
||||||
|
*/
|
||||||
|
|
||||||
|
apply plugin: 'elasticsearch.testclusters'
|
||||||
|
apply plugin: 'elasticsearch.standalone-rest-test'
|
||||||
|
apply plugin: 'elasticsearch.rest-test'
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
testCompile project(path: xpackModule('core'), configuration: 'default')
|
||||||
|
testCompile project(path: xpackModule('security'), configuration: 'testArtifacts')
|
||||||
|
testCompile project(path: xpackModule('core'), configuration: 'testArtifacts')
|
||||||
|
}
|
||||||
|
|
||||||
|
testClusters.integTest {
|
||||||
|
testDistribution = 'DEFAULT'
|
||||||
|
numberOfNodes = 2
|
||||||
|
|
||||||
|
setting 'xpack.ilm.enabled', 'false'
|
||||||
|
setting 'xpack.ml.enabled', 'false'
|
||||||
|
// We run with a trial license, but explicitly disable security.
|
||||||
|
// This means the security plugin is loaded and all feature are permitted, but they are not enabled
|
||||||
|
setting 'xpack.license.self_generated.type', 'trial'
|
||||||
|
setting 'xpack.security.enabled', 'false'
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
package org.elasticsearch.xpack.security;
|
||||||
|
|
||||||
|
import org.apache.http.util.EntityUtils;
|
||||||
|
import org.elasticsearch.client.Request;
|
||||||
|
import org.elasticsearch.client.Response;
|
||||||
|
import org.elasticsearch.client.ResponseException;
|
||||||
|
import org.elasticsearch.test.rest.ESRestTestCase;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that it is possible to <em>define</em> a pipeline with the
|
||||||
|
* {@link org.elasticsearch.xpack.security.ingest.SetSecurityUserProcessor} on a cluster with security disabled, but it is not possible
|
||||||
|
* to use that pipeline for ingestion.
|
||||||
|
*/
|
||||||
|
public class SetSecurityUserProcessorWithSecurityDisabledIT extends ESRestTestCase {
|
||||||
|
|
||||||
|
public void testDefineAndUseProcessor() throws Exception {
|
||||||
|
final String pipeline = "pipeline-" + getTestName();
|
||||||
|
final String index = "index-" + getTestName();
|
||||||
|
{
|
||||||
|
final Request putPipeline = new Request("PUT", "/_ingest/pipeline/" + pipeline);
|
||||||
|
putPipeline.setJsonEntity("{" +
|
||||||
|
" \"description\": \"Test pipeline (" + getTestName() + ")\"," +
|
||||||
|
" \"processors\":[{" +
|
||||||
|
" \"set_security_user\":{ \"field\": \"user\" }" +
|
||||||
|
" }]" +
|
||||||
|
"}");
|
||||||
|
final Response response = client().performRequest(putPipeline);
|
||||||
|
assertOK(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
final Request ingest = new Request("PUT", "/" + index + "/_doc/1?pipeline=" + pipeline);
|
||||||
|
ingest.setJsonEntity("{\"field\":\"value\"}");
|
||||||
|
final ResponseException ex = expectThrows(ResponseException.class, () -> client().performRequest(ingest));
|
||||||
|
final Response response = ex.getResponse();
|
||||||
|
assertThat(EntityUtils.toString(response.getEntity()),
|
||||||
|
containsString("Security (authentication) is not enabled on this cluster"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* This QA project tests the security plugin when security is not enabled.
|
||||||
|
* It is intended to cover security functionality which is supposed to
|
||||||
|
* function in a specific way even if security is not enabled on the cluster
|
||||||
|
* For example: If a cluster has a pipeline with the set_security_user processor
|
||||||
|
* defined, it should be not fail
|
||||||
|
*/
|
||||||
|
|
||||||
|
apply plugin: 'elasticsearch.testclusters'
|
||||||
|
apply plugin: 'elasticsearch.standalone-rest-test'
|
||||||
|
apply plugin: 'elasticsearch.rest-test'
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
testCompile project(path: xpackModule('core'), configuration: 'default')
|
||||||
|
testCompile project(path: xpackModule('security'), configuration: 'testArtifacts')
|
||||||
|
testCompile project(path: xpackModule('core'), configuration: 'testArtifacts')
|
||||||
|
}
|
||||||
|
|
||||||
|
testClusters.integTest {
|
||||||
|
testDistribution = 'DEFAULT'
|
||||||
|
numberOfNodes = 2
|
||||||
|
|
||||||
|
setting 'xpack.ilm.enabled', 'false'
|
||||||
|
setting 'xpack.ml.enabled', 'false'
|
||||||
|
// We run with a trial license, but do not enable security.
|
||||||
|
// This means the security plugin is loaded and all feature are permitted, but they are not enabled
|
||||||
|
setting 'xpack.license.self_generated.type', 'trial'
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
package org.elasticsearch.xpack.security;
|
||||||
|
|
||||||
|
import org.apache.http.util.EntityUtils;
|
||||||
|
import org.elasticsearch.client.Request;
|
||||||
|
import org.elasticsearch.client.Response;
|
||||||
|
import org.elasticsearch.client.ResponseException;
|
||||||
|
import org.elasticsearch.test.rest.ESRestTestCase;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that it is possible to <em>define</em> a pipeline with the
|
||||||
|
* {@link org.elasticsearch.xpack.security.ingest.SetSecurityUserProcessor} on a cluster where security is not enabled,
|
||||||
|
* but it is not possible to use that pipeline for ingestion.
|
||||||
|
*/
|
||||||
|
public class SetSecurityUserProcessorWithSecurityNotEnabledIT extends ESRestTestCase {
|
||||||
|
|
||||||
|
public void testDefineAndUseProcessor() throws Exception {
|
||||||
|
final String pipeline = "pipeline-" + getTestName();
|
||||||
|
final String index = "index-" + getTestName();
|
||||||
|
{
|
||||||
|
final Request putPipeline = new Request("PUT", "/_ingest/pipeline/" + pipeline);
|
||||||
|
putPipeline.setJsonEntity("{" +
|
||||||
|
" \"description\": \"Test pipeline (" + getTestName() + ")\"," +
|
||||||
|
" \"processors\":[{" +
|
||||||
|
" \"set_security_user\":{ \"field\": \"user\" }" +
|
||||||
|
" }]" +
|
||||||
|
"}");
|
||||||
|
final Response response = client().performRequest(putPipeline);
|
||||||
|
assertOK(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
final Request ingest = new Request("PUT", "/" + index + "/_doc/1?pipeline=" + pipeline);
|
||||||
|
ingest.setJsonEntity("{\"field\":\"value\"}");
|
||||||
|
final ResponseException ex = expectThrows(ResponseException.class, () -> client().performRequest(ingest));
|
||||||
|
final Response response = ex.getResponse();
|
||||||
|
assertThat(EntityUtils.toString(response.getEntity()),
|
||||||
|
containsString("Security (authentication) is not enabled on this cluster"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -864,7 +864,8 @@ public class Security extends Plugin implements SystemIndexPlugin, IngestPlugin,
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Processor.Factory> getProcessors(Processor.Parameters parameters) {
|
public Map<String, Processor.Factory> getProcessors(Processor.Parameters parameters) {
|
||||||
return Collections.singletonMap(SetSecurityUserProcessor.TYPE, new SetSecurityUserProcessor.Factory(securityContext::get));
|
return Collections.singletonMap(SetSecurityUserProcessor.TYPE,
|
||||||
|
new SetSecurityUserProcessor.Factory(securityContext::get, this::getLicenseState));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -5,9 +5,12 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.xpack.security.ingest;
|
package org.elasticsearch.xpack.security.ingest;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.elasticsearch.ingest.AbstractProcessor;
|
import org.elasticsearch.ingest.AbstractProcessor;
|
||||||
import org.elasticsearch.ingest.IngestDocument;
|
import org.elasticsearch.ingest.IngestDocument;
|
||||||
import org.elasticsearch.ingest.Processor;
|
import org.elasticsearch.ingest.Processor;
|
||||||
|
import org.elasticsearch.license.XPackLicenseState;
|
||||||
import org.elasticsearch.xpack.core.security.SecurityContext;
|
import org.elasticsearch.xpack.core.security.SecurityContext;
|
||||||
import org.elasticsearch.xpack.core.security.authc.Authentication;
|
import org.elasticsearch.xpack.core.security.authc.Authentication;
|
||||||
import org.elasticsearch.xpack.core.security.user.User;
|
import org.elasticsearch.xpack.core.security.user.User;
|
||||||
|
@ -34,27 +37,51 @@ public final class SetSecurityUserProcessor extends AbstractProcessor {
|
||||||
|
|
||||||
public static final String TYPE = "set_security_user";
|
public static final String TYPE = "set_security_user";
|
||||||
|
|
||||||
|
private final Logger logger = LogManager.getLogger();
|
||||||
|
|
||||||
private final SecurityContext securityContext;
|
private final SecurityContext securityContext;
|
||||||
|
private final XPackLicenseState licenseState;
|
||||||
private final String field;
|
private final String field;
|
||||||
private final Set<Property> properties;
|
private final Set<Property> properties;
|
||||||
|
|
||||||
public
|
public SetSecurityUserProcessor(String tag, SecurityContext securityContext, XPackLicenseState licenseState, String field,
|
||||||
SetSecurityUserProcessor(String tag, SecurityContext securityContext, String field, Set<Property> properties) {
|
Set<Property> properties) {
|
||||||
super(tag);
|
super(tag);
|
||||||
this.securityContext = Objects.requireNonNull(securityContext, "security context must be provided");
|
this.securityContext = securityContext;
|
||||||
|
this.licenseState = Objects.requireNonNull(licenseState, "license state cannot be null");
|
||||||
|
if (licenseState.isAuthAllowed() == false) {
|
||||||
|
logger.warn("Creating processor [{}] (tag [{}]) on field [{}] but authentication is not currently enabled on this cluster " +
|
||||||
|
" - this processor is likely to fail at runtime if it is used", TYPE, tag, field);
|
||||||
|
} else if (this.securityContext == null) {
|
||||||
|
throw new IllegalArgumentException("Authentication is allowed on this cluster state, but there is no security context");
|
||||||
|
}
|
||||||
this.field = field;
|
this.field = field;
|
||||||
this.properties = properties;
|
this.properties = properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IngestDocument execute(IngestDocument ingestDocument) throws Exception {
|
public IngestDocument execute(IngestDocument ingestDocument) throws Exception {
|
||||||
Authentication authentication = securityContext.getAuthentication();
|
Authentication authentication = null;
|
||||||
if (authentication == null) {
|
User user = null;
|
||||||
throw new IllegalStateException("No user authenticated, only use this processor via authenticated user");
|
if (this.securityContext != null) {
|
||||||
|
authentication = securityContext.getAuthentication();
|
||||||
|
if (authentication != null) {
|
||||||
|
user = authentication.getUser();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
User user = authentication.getUser();
|
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw new IllegalStateException("No user for authentication");
|
logger.debug(
|
||||||
|
"Failed to find active user. SecurityContext=[{}] Authentication=[{}] User=[{}]", securityContext, authentication, user);
|
||||||
|
if (licenseState.isAuthAllowed()) {
|
||||||
|
// This shouldn't happen. If authentication is allowed (and active), then there _should_ always be an authenticated user.
|
||||||
|
// If we ever see this error message, then one of our assumptions are wrong.
|
||||||
|
throw new IllegalStateException("There is no authenticated user - the [" + TYPE
|
||||||
|
+ "] processor requires an authenticated user");
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException("Security (authentication) is not enabled on this cluster, so there is no active user - " +
|
||||||
|
"the [" + TYPE + "] processor cannot be used without security");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Object fieldValue = ingestDocument.getFieldValue(field, Object.class, true);
|
Object fieldValue = ingestDocument.getFieldValue(field, Object.class, true);
|
||||||
|
@ -155,9 +182,11 @@ public final class SetSecurityUserProcessor extends AbstractProcessor {
|
||||||
public static final class Factory implements Processor.Factory {
|
public static final class Factory implements Processor.Factory {
|
||||||
|
|
||||||
private final Supplier<SecurityContext> securityContext;
|
private final Supplier<SecurityContext> securityContext;
|
||||||
|
private final Supplier<XPackLicenseState> licenseState;
|
||||||
|
|
||||||
public Factory(Supplier<SecurityContext> securityContext) {
|
public Factory(Supplier<SecurityContext> securityContext, Supplier<XPackLicenseState> licenseState) {
|
||||||
this.securityContext = securityContext;
|
this.securityContext = securityContext;
|
||||||
|
this.licenseState = licenseState;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -174,7 +203,7 @@ public final class SetSecurityUserProcessor extends AbstractProcessor {
|
||||||
} else {
|
} else {
|
||||||
properties = EnumSet.allOf(Property.class);
|
properties = EnumSet.allOf(Property.class);
|
||||||
}
|
}
|
||||||
return new SetSecurityUserProcessor(tag, securityContext.get(), field, properties);
|
return new SetSecurityUserProcessor(tag, securityContext.get(), licenseState.get(), field, properties);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,6 +60,7 @@ public final class RestGetApiKeyAction extends ApiKeyBaseRestHandler {
|
||||||
}
|
}
|
||||||
return new BytesRestResponse(RestStatus.OK, builder);
|
return new BytesRestResponse(RestStatus.OK, builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,31 +8,36 @@ package org.elasticsearch.xpack.security.ingest;
|
||||||
import org.elasticsearch.ElasticsearchParseException;
|
import org.elasticsearch.ElasticsearchParseException;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||||
|
import org.elasticsearch.license.XPackLicenseState;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.elasticsearch.xpack.core.security.SecurityContext;
|
import org.elasticsearch.xpack.core.security.SecurityContext;
|
||||||
import org.elasticsearch.xpack.security.ingest.SetSecurityUserProcessor.Property;
|
import org.elasticsearch.xpack.security.ingest.SetSecurityUserProcessor.Property;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.elasticsearch.test.TestMatchers.throwableWithMessage;
|
|
||||||
import static org.hamcrest.Matchers.containsString;
|
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
import static org.hamcrest.Matchers.notNullValue;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
public class SetSecurityUserProcessorFactoryTests extends ESTestCase {
|
public class SetSecurityUserProcessorFactoryTests extends ESTestCase {
|
||||||
|
|
||||||
private SecurityContext securityContext;
|
private SecurityContext securityContext;
|
||||||
|
private XPackLicenseState licenseState;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setupContext() {
|
public void setupContext() {
|
||||||
securityContext = new SecurityContext(Settings.EMPTY, new ThreadContext(Settings.EMPTY));
|
securityContext = new SecurityContext(Settings.EMPTY, new ThreadContext(Settings.EMPTY));
|
||||||
|
licenseState = Mockito.mock(XPackLicenseState.class);
|
||||||
|
when(licenseState.isAuthAllowed()).thenReturn(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testProcessor() throws Exception {
|
public void testProcessor() throws Exception {
|
||||||
SetSecurityUserProcessor.Factory factory = new SetSecurityUserProcessor.Factory(() -> securityContext);
|
SetSecurityUserProcessor.Factory factory = new SetSecurityUserProcessor.Factory(() -> securityContext, () -> licenseState);
|
||||||
Map<String, Object> config = new HashMap<>();
|
Map<String, Object> config = new HashMap<>();
|
||||||
config.put("field", "_field");
|
config.put("field", "_field");
|
||||||
SetSecurityUserProcessor processor = factory.create(null, "_tag", config);
|
SetSecurityUserProcessor processor = factory.create(null, "_tag", config);
|
||||||
|
@ -41,7 +46,7 @@ public class SetSecurityUserProcessorFactoryTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testProcessor_noField() throws Exception {
|
public void testProcessor_noField() throws Exception {
|
||||||
SetSecurityUserProcessor.Factory factory = new SetSecurityUserProcessor.Factory(() -> securityContext);
|
SetSecurityUserProcessor.Factory factory = new SetSecurityUserProcessor.Factory(() -> securityContext, () -> licenseState);
|
||||||
Map<String, Object> config = new HashMap<>();
|
Map<String, Object> config = new HashMap<>();
|
||||||
ElasticsearchParseException e = expectThrows(ElasticsearchParseException.class, () -> factory.create(null, "_tag", config));
|
ElasticsearchParseException e = expectThrows(ElasticsearchParseException.class, () -> factory.create(null, "_tag", config));
|
||||||
assertThat(e.getMetadata("es.property_name").get(0), equalTo("field"));
|
assertThat(e.getMetadata("es.property_name").get(0), equalTo("field"));
|
||||||
|
@ -50,7 +55,7 @@ public class SetSecurityUserProcessorFactoryTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testProcessor_validProperties() throws Exception {
|
public void testProcessor_validProperties() throws Exception {
|
||||||
SetSecurityUserProcessor.Factory factory = new SetSecurityUserProcessor.Factory(() -> securityContext);
|
SetSecurityUserProcessor.Factory factory = new SetSecurityUserProcessor.Factory(() -> securityContext, () -> licenseState);
|
||||||
Map<String, Object> config = new HashMap<>();
|
Map<String, Object> config = new HashMap<>();
|
||||||
config.put("field", "_field");
|
config.put("field", "_field");
|
||||||
config.put("properties", Arrays.asList(Property.USERNAME.name(), Property.ROLES.name()));
|
config.put("properties", Arrays.asList(Property.USERNAME.name(), Property.ROLES.name()));
|
||||||
|
@ -60,7 +65,7 @@ public class SetSecurityUserProcessorFactoryTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testProcessor_invalidProperties() throws Exception {
|
public void testProcessor_invalidProperties() throws Exception {
|
||||||
SetSecurityUserProcessor.Factory factory = new SetSecurityUserProcessor.Factory(() -> securityContext);
|
SetSecurityUserProcessor.Factory factory = new SetSecurityUserProcessor.Factory(() -> securityContext, () -> licenseState);
|
||||||
Map<String, Object> config = new HashMap<>();
|
Map<String, Object> config = new HashMap<>();
|
||||||
config.put("field", "_field");
|
config.put("field", "_field");
|
||||||
config.put("properties", Arrays.asList("invalid"));
|
config.put("properties", Arrays.asList("invalid"));
|
||||||
|
@ -70,12 +75,13 @@ public class SetSecurityUserProcessorFactoryTests extends ESTestCase {
|
||||||
assertThat(e.getMetadata("es.processor_tag").get(0), equalTo("_tag"));
|
assertThat(e.getMetadata("es.processor_tag").get(0), equalTo("_tag"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testNullSecurityContextThrowsException() throws Exception {
|
public void testCanConstructorProcessorWithoutSecurityEnabled() throws Exception {
|
||||||
SetSecurityUserProcessor.Factory factory = new SetSecurityUserProcessor.Factory(() -> null);
|
when(licenseState.isAuthAllowed()).thenReturn(false);
|
||||||
|
SetSecurityUserProcessor.Factory factory = new SetSecurityUserProcessor.Factory(() -> null, () -> licenseState);
|
||||||
Map<String, Object> config = new HashMap<>();
|
Map<String, Object> config = new HashMap<>();
|
||||||
config.put("field", "_field");
|
config.put("field", "_field");
|
||||||
NullPointerException e = expectThrows(NullPointerException.class, () -> factory.create(null, "_tag", config));
|
final SetSecurityUserProcessor processor = factory.create(null, "_tag", config);
|
||||||
assertThat(e, throwableWithMessage(containsString("security context")));
|
assertThat(processor, notNullValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import org.elasticsearch.common.collect.MapBuilder;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||||
import org.elasticsearch.ingest.IngestDocument;
|
import org.elasticsearch.ingest.IngestDocument;
|
||||||
|
import org.elasticsearch.license.XPackLicenseState;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.elasticsearch.xpack.core.security.SecurityContext;
|
import org.elasticsearch.xpack.core.security.SecurityContext;
|
||||||
import org.elasticsearch.xpack.core.security.authc.Authentication;
|
import org.elasticsearch.xpack.core.security.authc.Authentication;
|
||||||
|
@ -28,16 +29,20 @@ import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
public class SetSecurityUserProcessorTests extends ESTestCase {
|
public class SetSecurityUserProcessorTests extends ESTestCase {
|
||||||
|
|
||||||
private ThreadContext threadContext;
|
private ThreadContext threadContext;
|
||||||
private SecurityContext securityContext;
|
private SecurityContext securityContext;
|
||||||
|
private XPackLicenseState licenseState;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setupObjects() {
|
public void setupObjects() {
|
||||||
threadContext = new ThreadContext(Settings.EMPTY);
|
threadContext = new ThreadContext(Settings.EMPTY);
|
||||||
securityContext = new SecurityContext(Settings.EMPTY, threadContext);
|
securityContext = new SecurityContext(Settings.EMPTY, threadContext);
|
||||||
|
licenseState = Mockito.mock(XPackLicenseState.class);
|
||||||
|
when(licenseState.isAuthAllowed()).thenReturn(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testProcessorWithData() throws Exception {
|
public void testProcessorWithData() throws Exception {
|
||||||
|
@ -47,7 +52,8 @@ public class SetSecurityUserProcessorTests extends ESTestCase {
|
||||||
new Authentication(user, realmRef, null).writeToContext(threadContext);
|
new Authentication(user, realmRef, null).writeToContext(threadContext);
|
||||||
|
|
||||||
IngestDocument ingestDocument = new IngestDocument(new HashMap<>(), new HashMap<>());
|
IngestDocument ingestDocument = new IngestDocument(new HashMap<>(), new HashMap<>());
|
||||||
SetSecurityUserProcessor processor = new SetSecurityUserProcessor("_tag", securityContext, "_field", EnumSet.allOf(Property.class));
|
SetSecurityUserProcessor processor = new SetSecurityUserProcessor(
|
||||||
|
"_tag", securityContext, licenseState, "_field", EnumSet.allOf(Property.class));
|
||||||
processor.execute(ingestDocument);
|
processor.execute(ingestDocument);
|
||||||
|
|
||||||
Map<String, Object> result = ingestDocument.getFieldValue("_field", Map.class);
|
Map<String, Object> result = ingestDocument.getFieldValue("_field", Map.class);
|
||||||
|
@ -67,16 +73,17 @@ public class SetSecurityUserProcessorTests extends ESTestCase {
|
||||||
// test when user returns null for all values (need a mock, because a real user cannot have a null username)
|
// test when user returns null for all values (need a mock, because a real user cannot have a null username)
|
||||||
User user = Mockito.mock(User.class);
|
User user = Mockito.mock(User.class);
|
||||||
Authentication authentication = Mockito.mock(Authentication.class);
|
Authentication authentication = Mockito.mock(Authentication.class);
|
||||||
Mockito.when(authentication.getUser()).thenReturn(user);
|
when(authentication.getUser()).thenReturn(user);
|
||||||
final Authentication.RealmRef authByRealm = new Authentication.RealmRef("_name", "_type", "_node_name");
|
final Authentication.RealmRef authByRealm = new Authentication.RealmRef("_name", "_type", "_node_name");
|
||||||
Mockito.when(authentication.getSourceRealm()).thenReturn(authByRealm);
|
when(authentication.getSourceRealm()).thenReturn(authByRealm);
|
||||||
Mockito.when(authentication.getAuthenticatedBy()).thenReturn(authByRealm);
|
when(authentication.getAuthenticatedBy()).thenReturn(authByRealm);
|
||||||
Mockito.when(authentication.getAuthenticationType()).thenReturn(AuthenticationType.REALM);
|
when(authentication.getAuthenticationType()).thenReturn(AuthenticationType.REALM);
|
||||||
Mockito.when(authentication.encode()).thenReturn(randomAlphaOfLength(24)); // don't care as long as it's not null
|
when(authentication.encode()).thenReturn(randomAlphaOfLength(24)); // don't care as long as it's not null
|
||||||
new AuthenticationContextSerializer().writeToContext(authentication, threadContext);
|
new AuthenticationContextSerializer().writeToContext(authentication, threadContext);
|
||||||
|
|
||||||
IngestDocument ingestDocument = new IngestDocument(new HashMap<>(), new HashMap<>());
|
IngestDocument ingestDocument = new IngestDocument(new HashMap<>(), new HashMap<>());
|
||||||
SetSecurityUserProcessor processor = new SetSecurityUserProcessor("_tag", securityContext, "_field", EnumSet.allOf(Property.class));
|
SetSecurityUserProcessor processor = new SetSecurityUserProcessor(
|
||||||
|
"_tag", securityContext, licenseState, "_field", EnumSet.allOf(Property.class));
|
||||||
processor.execute(ingestDocument);
|
processor.execute(ingestDocument);
|
||||||
Map<String, Object> result = ingestDocument.getFieldValue("_field", Map.class);
|
Map<String, Object> result = ingestDocument.getFieldValue("_field", Map.class);
|
||||||
// Still holds data for realm and authentication type
|
// Still holds data for realm and authentication type
|
||||||
|
@ -88,9 +95,21 @@ public class SetSecurityUserProcessorTests extends ESTestCase {
|
||||||
|
|
||||||
public void testNoCurrentUser() throws Exception {
|
public void testNoCurrentUser() throws Exception {
|
||||||
IngestDocument ingestDocument = new IngestDocument(new HashMap<>(), new HashMap<>());
|
IngestDocument ingestDocument = new IngestDocument(new HashMap<>(), new HashMap<>());
|
||||||
SetSecurityUserProcessor processor = new SetSecurityUserProcessor("_tag", securityContext, "_field", EnumSet.allOf(Property.class));
|
SetSecurityUserProcessor processor = new SetSecurityUserProcessor(
|
||||||
IllegalStateException e = expectThrows(IllegalStateException.class, () -> processor.execute(ingestDocument));
|
"_tag", securityContext, licenseState, "_field", EnumSet.allOf(Property.class));
|
||||||
assertThat(e.getMessage(), equalTo("No user authenticated, only use this processor via authenticated user"));
|
IllegalStateException e = expectThrows(IllegalStateException.class, () -> processor.execute(ingestDocument));
|
||||||
|
assertThat(e.getMessage(),
|
||||||
|
equalTo("There is no authenticated user - the [set_security_user] processor requires an authenticated user"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSecurityDisabled() throws Exception {
|
||||||
|
when(licenseState.isAuthAllowed()).thenReturn(false);
|
||||||
|
IngestDocument ingestDocument = new IngestDocument(new HashMap<>(), new HashMap<>());
|
||||||
|
SetSecurityUserProcessor processor = new SetSecurityUserProcessor(
|
||||||
|
"_tag", securityContext, licenseState, "_field", EnumSet.allOf(Property.class));
|
||||||
|
IllegalStateException e = expectThrows(IllegalStateException.class, () -> processor.execute(ingestDocument));
|
||||||
|
assertThat(e.getMessage(), equalTo("Security (authentication) is not enabled on this cluster, so there is no active user" +
|
||||||
|
" - the [set_security_user] processor cannot be used without security"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testUsernameProperties() throws Exception {
|
public void testUsernameProperties() throws Exception {
|
||||||
|
@ -99,7 +118,8 @@ public class SetSecurityUserProcessorTests extends ESTestCase {
|
||||||
new Authentication(user, realmRef, null).writeToContext(threadContext);
|
new Authentication(user, realmRef, null).writeToContext(threadContext);
|
||||||
|
|
||||||
IngestDocument ingestDocument = new IngestDocument(new HashMap<>(), new HashMap<>());
|
IngestDocument ingestDocument = new IngestDocument(new HashMap<>(), new HashMap<>());
|
||||||
SetSecurityUserProcessor processor = new SetSecurityUserProcessor("_tag", securityContext, "_field", EnumSet.of(Property.USERNAME));
|
SetSecurityUserProcessor processor = new SetSecurityUserProcessor(
|
||||||
|
"_tag", securityContext, licenseState, "_field", EnumSet.of(Property.USERNAME));
|
||||||
processor.execute(ingestDocument);
|
processor.execute(ingestDocument);
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@ -114,7 +134,8 @@ public class SetSecurityUserProcessorTests extends ESTestCase {
|
||||||
new Authentication(user, realmRef, null).writeToContext(threadContext);
|
new Authentication(user, realmRef, null).writeToContext(threadContext);
|
||||||
|
|
||||||
IngestDocument ingestDocument = new IngestDocument(new HashMap<>(), new HashMap<>());
|
IngestDocument ingestDocument = new IngestDocument(new HashMap<>(), new HashMap<>());
|
||||||
SetSecurityUserProcessor processor = new SetSecurityUserProcessor("_tag", securityContext, "_field", EnumSet.of(Property.ROLES));
|
SetSecurityUserProcessor processor = new SetSecurityUserProcessor(
|
||||||
|
"_tag", securityContext, licenseState, "_field", EnumSet.of(Property.ROLES));
|
||||||
processor.execute(ingestDocument);
|
processor.execute(ingestDocument);
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@ -130,7 +151,7 @@ public class SetSecurityUserProcessorTests extends ESTestCase {
|
||||||
|
|
||||||
IngestDocument ingestDocument = new IngestDocument(new HashMap<>(), new HashMap<>());
|
IngestDocument ingestDocument = new IngestDocument(new HashMap<>(), new HashMap<>());
|
||||||
SetSecurityUserProcessor processor
|
SetSecurityUserProcessor processor
|
||||||
= new SetSecurityUserProcessor("_tag", securityContext, "_field", EnumSet.of(Property.FULL_NAME));
|
= new SetSecurityUserProcessor("_tag", securityContext, licenseState, "_field", EnumSet.of(Property.FULL_NAME));
|
||||||
processor.execute(ingestDocument);
|
processor.execute(ingestDocument);
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@ -145,7 +166,8 @@ public class SetSecurityUserProcessorTests extends ESTestCase {
|
||||||
new Authentication(user, realmRef, null).writeToContext(threadContext);
|
new Authentication(user, realmRef, null).writeToContext(threadContext);
|
||||||
|
|
||||||
IngestDocument ingestDocument = new IngestDocument(new HashMap<>(), new HashMap<>());
|
IngestDocument ingestDocument = new IngestDocument(new HashMap<>(), new HashMap<>());
|
||||||
SetSecurityUserProcessor processor = new SetSecurityUserProcessor("_tag", securityContext, "_field", EnumSet.of(Property.EMAIL));
|
SetSecurityUserProcessor processor = new SetSecurityUserProcessor(
|
||||||
|
"_tag", securityContext, licenseState, "_field", EnumSet.of(Property.EMAIL));
|
||||||
processor.execute(ingestDocument);
|
processor.execute(ingestDocument);
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@ -160,7 +182,8 @@ public class SetSecurityUserProcessorTests extends ESTestCase {
|
||||||
new Authentication(user, realmRef, null).writeToContext(threadContext);
|
new Authentication(user, realmRef, null).writeToContext(threadContext);
|
||||||
|
|
||||||
IngestDocument ingestDocument = new IngestDocument(new HashMap<>(), new HashMap<>());
|
IngestDocument ingestDocument = new IngestDocument(new HashMap<>(), new HashMap<>());
|
||||||
SetSecurityUserProcessor processor = new SetSecurityUserProcessor("_tag", securityContext, "_field", EnumSet.of(Property.METADATA));
|
SetSecurityUserProcessor processor = new SetSecurityUserProcessor(
|
||||||
|
"_tag", securityContext, licenseState, "_field", EnumSet.of(Property.METADATA));
|
||||||
processor.execute(ingestDocument);
|
processor.execute(ingestDocument);
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@ -175,7 +198,8 @@ public class SetSecurityUserProcessorTests extends ESTestCase {
|
||||||
Authentication.RealmRef realmRef = new Authentication.RealmRef("_name", "_type", "_node_name");
|
Authentication.RealmRef realmRef = new Authentication.RealmRef("_name", "_type", "_node_name");
|
||||||
new Authentication(user, realmRef, null).writeToContext(threadContext);
|
new Authentication(user, realmRef, null).writeToContext(threadContext);
|
||||||
|
|
||||||
SetSecurityUserProcessor processor = new SetSecurityUserProcessor("_tag", securityContext, "_field", EnumSet.of(Property.USERNAME));
|
SetSecurityUserProcessor processor = new SetSecurityUserProcessor(
|
||||||
|
"_tag", securityContext, licenseState, "_field", EnumSet.of(Property.USERNAME));
|
||||||
|
|
||||||
IngestDocument ingestDocument = new IngestDocument(new HashMap<>(), new HashMap<>());
|
IngestDocument ingestDocument = new IngestDocument(new HashMap<>(), new HashMap<>());
|
||||||
ingestDocument.setFieldValue("_field", "test");
|
ingestDocument.setFieldValue("_field", "test");
|
||||||
|
@ -209,11 +233,13 @@ public class SetSecurityUserProcessorTests extends ESTestCase {
|
||||||
.put(ApiKeyService.API_KEY_CREATOR_REALM_NAME, "creator_realm_name")
|
.put(ApiKeyService.API_KEY_CREATOR_REALM_NAME, "creator_realm_name")
|
||||||
.put(ApiKeyService.API_KEY_CREATOR_REALM_TYPE, "creator_realm_type")
|
.put(ApiKeyService.API_KEY_CREATOR_REALM_TYPE, "creator_realm_type")
|
||||||
.immutableMap();
|
.immutableMap();
|
||||||
new Authentication(user, realmRef, null, Version.CURRENT, AuthenticationType.API_KEY,metadata)
|
Authentication auth = new Authentication(user, realmRef, null, Version.CURRENT,
|
||||||
.writeToContext(threadContext);
|
AuthenticationType.API_KEY, metadata);
|
||||||
|
auth.writeToContext(threadContext);
|
||||||
|
|
||||||
IngestDocument ingestDocument = new IngestDocument(new HashMap<>(), new HashMap<>());
|
IngestDocument ingestDocument = new IngestDocument(new HashMap<>(), new HashMap<>());
|
||||||
SetSecurityUserProcessor processor = new SetSecurityUserProcessor("_tag", securityContext, "_field", EnumSet.allOf(Property.class));
|
SetSecurityUserProcessor processor = new SetSecurityUserProcessor(
|
||||||
|
"_tag", securityContext, licenseState, "_field", EnumSet.allOf(Property.class));
|
||||||
processor.execute(ingestDocument);
|
processor.execute(ingestDocument);
|
||||||
|
|
||||||
Map<String, Object> result = ingestDocument.getFieldValue("_field", Map.class);
|
Map<String, Object> result = ingestDocument.getFieldValue("_field", Map.class);
|
||||||
|
@ -245,7 +271,9 @@ public class SetSecurityUserProcessorTests extends ESTestCase {
|
||||||
.put("api_key", new MapBuilder<>().put("version", 42).immutableMap())
|
.put("api_key", new MapBuilder<>().put("version", 42).immutableMap())
|
||||||
.put("realm", new MapBuilder<>().put("id", 7).immutableMap()).immutableMap()
|
.put("realm", new MapBuilder<>().put("id", 7).immutableMap()).immutableMap()
|
||||||
).immutableMap()), new HashMap<>());
|
).immutableMap()), new HashMap<>());
|
||||||
SetSecurityUserProcessor processor = new SetSecurityUserProcessor("_tag", securityContext, "_field", EnumSet.allOf(Property.class));
|
|
||||||
|
SetSecurityUserProcessor processor = new SetSecurityUserProcessor(
|
||||||
|
"_tag", securityContext, licenseState, "_field", EnumSet.allOf(Property.class));
|
||||||
processor.execute(ingestDocument);
|
processor.execute(ingestDocument);
|
||||||
|
|
||||||
Map<String, Object> result = ingestDocument.getFieldValue("_field", Map.class);
|
Map<String, Object> result = ingestDocument.getFieldValue("_field", Map.class);
|
||||||
|
@ -266,7 +294,8 @@ public class SetSecurityUserProcessorTests extends ESTestCase {
|
||||||
Collections.emptyMap()).writeToContext(threadContext);
|
Collections.emptyMap()).writeToContext(threadContext);
|
||||||
|
|
||||||
IngestDocument ingestDocument = new IngestDocument(new HashMap<>(), new HashMap<>());
|
IngestDocument ingestDocument = new IngestDocument(new HashMap<>(), new HashMap<>());
|
||||||
SetSecurityUserProcessor processor = new SetSecurityUserProcessor("_tag", securityContext, "_field", EnumSet.allOf(Property.class));
|
SetSecurityUserProcessor processor = new SetSecurityUserProcessor(
|
||||||
|
"_tag", securityContext, licenseState, "_field", EnumSet.allOf(Property.class));
|
||||||
processor.execute(ingestDocument);
|
processor.execute(ingestDocument);
|
||||||
|
|
||||||
Map<String, Object> result = ingestDocument.getFieldValue("_field", Map.class);
|
Map<String, Object> result = ingestDocument.getFieldValue("_field", Map.class);
|
||||||
|
|
Loading…
Reference in New Issue