Fix doc-update interceptor for indices with DLS and FLS (#61516)
This fixes the protection against updates (and bulk updates) for indices with DLS and/or FLS, when the request uses date math expressions.
This commit is contained in:
parent
663b85b98f
commit
b4ec821067
|
@ -5,13 +5,16 @@
|
|||
*/
|
||||
package org.elasticsearch.integration;
|
||||
|
||||
import org.elasticsearch.ElasticsearchSecurityException;
|
||||
import org.elasticsearch.action.admin.indices.get.GetIndexResponse;
|
||||
import org.elasticsearch.action.admin.indices.mapping.get.GetFieldMappingsResponse;
|
||||
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse;
|
||||
import org.elasticsearch.action.bulk.BulkResponse;
|
||||
import org.elasticsearch.action.fieldcaps.FieldCapabilities;
|
||||
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesRequest;
|
||||
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesResponse;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.client.Requests;
|
||||
import org.elasticsearch.cluster.metadata.MappingMetadata;
|
||||
import org.elasticsearch.common.collect.ImmutableOpenMap;
|
||||
import org.elasticsearch.common.settings.SecureString;
|
||||
|
@ -23,6 +26,7 @@ import org.elasticsearch.search.sort.SortOrder;
|
|||
import org.elasticsearch.test.SecurityIntegTestCase;
|
||||
import org.elasticsearch.xpack.core.XPackSettings;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
@ -34,7 +38,9 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitC
|
|||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHits;
|
||||
import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.BASIC_AUTH_HEADER;
|
||||
import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
public class DocumentAndFieldLevelSecurityTests extends SecurityIntegTestCase {
|
||||
|
||||
|
@ -144,6 +150,41 @@ public class DocumentAndFieldLevelSecurityTests extends SecurityIntegTestCase {
|
|||
assertThat(response.getHits().getAt(1).getSourceAsMap().get("field2").toString(), equalTo("value2"));
|
||||
}
|
||||
|
||||
public void testUpdatesAreRejected() {
|
||||
for (String indexName : Arrays.asList("<test-{2015.05.05||+1d}>", "test")) {
|
||||
assertAcked(client().admin().indices().prepareCreate(indexName)
|
||||
.addMapping("type1", "id", "type=keyword", "field1", "type=text", "field2", "type=text")
|
||||
.setSettings(Settings.builder()
|
||||
.put("index.number_of_replicas", 0)
|
||||
.put("index.number_of_shards", 1))
|
||||
);
|
||||
client().prepareIndex(indexName, "type1", "1").setSource("id", "1", "field1", "value1")
|
||||
.setRefreshPolicy(IMMEDIATE)
|
||||
.get();
|
||||
|
||||
ElasticsearchSecurityException exception = expectThrows(ElasticsearchSecurityException.class, () -> {
|
||||
client().filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER,
|
||||
basicAuthHeaderValue("user1", USERS_PASSWD)))
|
||||
.prepareUpdate(indexName, "type1", "1")
|
||||
.setDoc(Requests.INDEX_CONTENT_TYPE, "field2", "value2")
|
||||
.get();
|
||||
});
|
||||
assertThat(exception.getDetailedMessage(), containsString("Can't execute an update request if field or document level " +
|
||||
"security"));
|
||||
|
||||
BulkResponse bulkResponse = client().filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user1",
|
||||
USERS_PASSWD)))
|
||||
.prepareBulk()
|
||||
.add(client().prepareUpdate(indexName, "type1", "1")
|
||||
.setDoc(Requests.INDEX_CONTENT_TYPE, "field2", "value2"))
|
||||
.get();
|
||||
assertThat(bulkResponse.getItems().length, is(1));
|
||||
assertThat(bulkResponse.getItems()[0].getFailureMessage(), containsString("Can't execute a bulk item request with update " +
|
||||
"requests" +
|
||||
" embedded if field or document level security is enabled"));
|
||||
}
|
||||
}
|
||||
|
||||
public void testDLSIsAppliedBeforeFLS() {
|
||||
assertAcked(client().admin().indices().prepareCreate("test")
|
||||
.addMapping("type1", "field1", "type=text", "field2", "type=text")
|
||||
|
|
|
@ -46,29 +46,30 @@ public class BulkShardRequestInterceptor implements RequestInterceptor {
|
|||
MemoizedSupplier<Boolean> licenseChecker = new MemoizedSupplier<>(() -> licenseState.checkFeature(Feature.SECURITY_DLS_FLS));
|
||||
if (requestInfo.getRequest() instanceof BulkShardRequest && shouldIntercept) {
|
||||
IndicesAccessControl indicesAccessControl = threadContext.getTransient(AuthorizationServiceField.INDICES_PERMISSIONS_KEY);
|
||||
|
||||
final BulkShardRequest bulkShardRequest = (BulkShardRequest) requestInfo.getRequest();
|
||||
for (BulkItemRequest bulkItemRequest : bulkShardRequest.items()) {
|
||||
IndicesAccessControl.IndexAccessControl indexAccessControl =
|
||||
indicesAccessControl.getIndexPermissions(bulkItemRequest.index());
|
||||
boolean found = false;
|
||||
if (indexAccessControl != null) {
|
||||
boolean fls = indexAccessControl.getFieldPermissions().hasFieldLevelSecurity();
|
||||
boolean dls = indexAccessControl.getDocumentPermissions().hasDocumentLevelPermissions();
|
||||
if (fls || dls) {
|
||||
if (licenseChecker.get() && bulkItemRequest.request() instanceof UpdateRequest) {
|
||||
BulkShardRequest bulkShardRequest = (BulkShardRequest) requestInfo.getRequest();
|
||||
// this uses the {@code BulkShardRequest#index()} because the {@code bulkItemRequest#index()}
|
||||
// can still be an unresolved date math expression
|
||||
IndicesAccessControl.IndexAccessControl indexAccessControl = indicesAccessControl.getIndexPermissions(bulkShardRequest.index());
|
||||
// TODO replace if condition with assertion
|
||||
if (indexAccessControl != null) {
|
||||
for (BulkItemRequest bulkItemRequest : bulkShardRequest.items()) {
|
||||
boolean found = false;
|
||||
if (bulkItemRequest.request() instanceof UpdateRequest) {
|
||||
boolean fls = indexAccessControl.getFieldPermissions().hasFieldLevelSecurity();
|
||||
boolean dls = indexAccessControl.getDocumentPermissions().hasDocumentLevelPermissions();
|
||||
// the feature usage checker is a "last-ditch" verification, it doesn't have practical importance
|
||||
if ((fls || dls) && licenseChecker.get()) {
|
||||
found = true;
|
||||
logger.trace("aborting bulk item update request for index [{}]", bulkItemRequest.index());
|
||||
logger.trace("aborting bulk item update request for index [{}]", bulkShardRequest.index());
|
||||
bulkItemRequest.abort(bulkItemRequest.index(), new ElasticsearchSecurityException("Can't execute a bulk " +
|
||||
"item request with update requests embedded if field or document level security is enabled",
|
||||
RestStatus.BAD_REQUEST));
|
||||
"item request with update requests embedded if field or document level security is enabled",
|
||||
RestStatus.BAD_REQUEST));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found == false) {
|
||||
logger.trace("intercepted bulk request for index [{}] without any update requests, continuing execution",
|
||||
bulkItemRequest.index());
|
||||
if (found == false) {
|
||||
logger.trace("intercepted bulk request for index [{}] without any update requests, continuing execution",
|
||||
bulkShardRequest.index());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ abstract class FieldAndDocumentLevelSecurityRequestInterceptor implements Reques
|
|||
if (supports(indicesRequest) && shouldIntercept) {
|
||||
final IndicesAccessControl indicesAccessControl =
|
||||
threadContext.getTransient(AuthorizationServiceField.INDICES_PERMISSIONS_KEY);
|
||||
for (String index : indicesRequest.indices()) {
|
||||
for (String index : requestIndices(indicesRequest)) {
|
||||
IndicesAccessControl.IndexAccessControl indexAccessControl = indicesAccessControl.getIndexPermissions(index);
|
||||
if (indexAccessControl != null) {
|
||||
boolean fieldLevelSecurityEnabled = indexAccessControl.getFieldPermissions().hasFieldLevelSecurity();
|
||||
|
@ -65,6 +65,10 @@ abstract class FieldAndDocumentLevelSecurityRequestInterceptor implements Reques
|
|||
listener.onResponse(null);
|
||||
}
|
||||
|
||||
String[] requestIndices(IndicesRequest indicesRequest) {
|
||||
return indicesRequest.indices();
|
||||
}
|
||||
|
||||
abstract void disableFeatures(IndicesRequest request, boolean fieldLevelSecurityEnabled, boolean documentLevelSecurityEnabled,
|
||||
ActionListener<Void> listener);
|
||||
|
||||
|
|
|
@ -33,6 +33,17 @@ public class UpdateRequestInterceptor extends FieldAndDocumentLevelSecurityReque
|
|||
"is enabled", RestStatus.BAD_REQUEST));
|
||||
}
|
||||
|
||||
@Override
|
||||
String[] requestIndices(IndicesRequest indicesRequest) {
|
||||
if (indicesRequest instanceof UpdateRequest) {
|
||||
UpdateRequest updateRequest = (UpdateRequest) indicesRequest;
|
||||
if (updateRequest.getShardId() != null) {
|
||||
return new String[]{updateRequest.getShardId().getIndexName()};
|
||||
}
|
||||
}
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(IndicesRequest request) {
|
||||
return request instanceof UpdateRequest;
|
||||
|
|
Loading…
Reference in New Issue