add the option to disable DLS and FLS completely

This commit reverts a previous change where searcher were not wrapped when the RequestContext
could not be found. If DLS/FLS is enabled, which is the default, any bulk request that contains an
update request will not be permitted. This change also exposes the ability to completely disable DLS
and FLS so that users who are not using these features can still use bulk updates.

See elastic/elasticsearch#938

Original commit: elastic/x-pack-elasticsearch@513782db1c
This commit is contained in:
jaymode 2015-11-17 07:08:35 -05:00
parent 722bc3713e
commit cc2096b4f9
8 changed files with 60 additions and 22 deletions

View File

@ -29,6 +29,21 @@ indices, while Elasticsearch with Shield returns exceptions. Note that this beha
multiple items will fail the entire set of operations if any one operation throws an exception due to wildcard multiple items will fail the entire set of operations if any one operation throws an exception due to wildcard
expansion resulting in an empty set of authorized indices. expansion resulting in an empty set of authorized indices.
[float]
=== Field and Document Level Security Limitations
Bulk updates do not work when document and field level security is enabled. If you are not using document and field level
security, bulk updates can be enabled by setting `shield.dls_fls.enabled` to `false`.
When document level security is enabled for an index:
* The get, multi get, termsvector and multi termsvector APIs aren't executed in real time. The realtime option for these APIs is forcefully set to false.
* Document level security isn't applied for APIs that aren't document based. An example is the field stats API.
* Document level security doesn't affect global index statistics that relevancy scoring uses. So this means that scores are computed without taking the role query into account.
Note that documents not matching with the role query are never returned.
* The `has_child` and `has_parent` queries aren't supported as role query in the `roles.yml` file.
The `has_child` and `has_parent` queries can be used in the search API with document level security enabled.
[float] [float]
=== Document Expiration (_ttl) === Document Expiration (_ttl)

View File

@ -184,6 +184,13 @@ The parameters listed in this section are configured in the `config/elasticsearc
| `shield.authc.anonymous.authz_exception` | `true` | When `true`, a HTTP 403 response will be returned when the anonymous user does not have the appropriate permissions for the requested action. The user will not be prompted to provide credentials to access the requested resource. When set to `false`, a HTTP 401 will be returned allowing for credentials to be provided for a user with the appropriate permissions. | `shield.authc.anonymous.authz_exception` | `true` | When `true`, a HTTP 403 response will be returned when the anonymous user does not have the appropriate permissions for the requested action. The user will not be prompted to provide credentials to access the requested resource. When set to `false`, a HTTP 401 will be returned allowing for credentials to be provided for a user with the appropriate permissions.
|====== |======
.Shield Document and Field Level Security Settings
[options="header"]
|======
| Name | Default | Description
| `shield.dls_fls.enabled` | `true` | This setting can be used to completely disable document and field level security regardless of how roles are configured.
|======
[[ref-realm-settings]] [[ref-realm-settings]]

View File

@ -158,14 +158,3 @@ customer_care:
privileges: read privileges: read
query: '{"term" : {"department_id" : "12"}}'' query: '{"term" : {"department_id" : "12"}}''
-------------------------------------------------- --------------------------------------------------
===== Limitations
When document level security is enabled for an index:
* The get, multi get, termsvector and multi termsvector APIs aren't executed in real time. The realtime option for these APIs is forcefully set to false.
* Document level security isn't applied for APIs that aren't document based oriented. For example this is the case for the field stats API.
* Document level security doesn't affect global index statistics that relevancy scoring uses. So this means that scores are computed without taking the role query into account.
Note that documents not matching with the role query are never returned.
* The `has_child` and `has_parent` queries aren't supported as role query in the `roles.yml` file.
The `has_child` and `has_parent` queries can be used in the search API with document level security enabled.

View File

@ -62,6 +62,7 @@ public class ShieldPlugin extends Plugin {
public static final String NAME = "shield"; public static final String NAME = "shield";
public static final String ENABLED_SETTING_NAME = NAME + ".enabled"; public static final String ENABLED_SETTING_NAME = NAME + ".enabled";
public static final String OPT_OUT_QUERY_CACHE = "opt_out_cache"; public static final String OPT_OUT_QUERY_CACHE = "opt_out_cache";
public static final String DLS_FLS_ENABLED_SETTING = "shield.dls_fls.enabled";
private static final boolean DEFAULT_ENABLED_SETTING = true; private static final boolean DEFAULT_ENABLED_SETTING = true;
@ -159,9 +160,11 @@ public class ShieldPlugin extends Plugin {
return; return;
} }
assert shieldLicenseState != null; assert shieldLicenseState != null;
module.setSearcherWrapper((indexService) -> new ShieldIndexSearcherWrapper(indexService.getIndexSettings(), if (flsDlsEnabled(settings)) {
indexService.getQueryShardContext(), indexService.mapperService(), module.setSearcherWrapper((indexService) -> new ShieldIndexSearcherWrapper(indexService.getIndexSettings(),
indexService.cache().bitsetFilterCache(), shieldLicenseState)); indexService.getQueryShardContext(), indexService.mapperService(),
indexService.cache().bitsetFilterCache(), shieldLicenseState));
}
if (clientMode == false) { if (clientMode == false) {
module.registerQueryCache(ShieldPlugin.OPT_OUT_QUERY_CACHE, OptOutQueryCache::new); module.registerQueryCache(ShieldPlugin.OPT_OUT_QUERY_CACHE, OptOutQueryCache::new);
} }
@ -305,6 +308,10 @@ public class ShieldPlugin extends Plugin {
return settings.getAsBoolean(ENABLED_SETTING_NAME, DEFAULT_ENABLED_SETTING); return settings.getAsBoolean(ENABLED_SETTING_NAME, DEFAULT_ENABLED_SETTING);
} }
public static boolean flsDlsEnabled(Settings settings) {
return settings.getAsBoolean(DLS_FLS_ENABLED_SETTING, true);
}
private void failIfShieldQueryCacheIsNotActive(Settings settings, boolean nodeSettings) { private void failIfShieldQueryCacheIsNotActive(Settings settings, boolean nodeSettings) {
String queryCacheImplementation; String queryCacheImplementation;
if (nodeSettings) { if (nodeSettings) {

View File

@ -12,6 +12,8 @@ import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.rest.RestStatus; import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.shield.ShieldPlugin;
import org.elasticsearch.shield.User;
import org.elasticsearch.transport.TransportRequest; import org.elasticsearch.transport.TransportRequest;
/** /**
@ -24,11 +26,21 @@ public class BulkRequestInterceptor extends FieldSecurityRequestInterceptor<Bulk
super(settings); super(settings);
} }
@Override
public void intercept(BulkRequest request, User user) {
// FIXME remove this method override once we support bulk updates with DLS and FLS enabled overall. We'll still
// need this interceptor because individual users may still have FLS/DLS enabled and we'll want to reject only
// their requests. Also update the message to remove "document"
if (ShieldPlugin.flsDlsEnabled(this.settings)) {
disableFeatures(request);
}
}
@Override @Override
protected void disableFeatures(BulkRequest bulkRequest) { protected void disableFeatures(BulkRequest bulkRequest) {
for (ActionRequest actionRequest : bulkRequest.requests()) { for (ActionRequest actionRequest : bulkRequest.requests()) {
if (actionRequest instanceof UpdateRequest) { if (actionRequest instanceof UpdateRequest) {
throw new ElasticsearchSecurityException("Can't execute an bulk request with update requests embedded if field level security is enabled", RestStatus.BAD_REQUEST); throw new ElasticsearchSecurityException("Can't execute an bulk request with update requests embedded if document and field level security is enabled", RestStatus.BAD_REQUEST);
} }
} }
} }

View File

@ -83,8 +83,8 @@ public class ShieldIndexSearcherWrapper extends IndexSearcherWrapper {
try { try {
RequestContext context = RequestContext.current(); RequestContext context = RequestContext.current();
if (context == null) { if (context == null) {
logger.debug("couldn't locate the current request, field level security will not be applied"); logger.debug("couldn't locate the current request, field level security will only allow meta fields");
return reader; return FieldSubsetReader.wrap(reader, allowedMetaFields);
} }
IndicesAccessControl indicesAccessControl = context.getRequest().getFromContext(InternalAuthorizationService.INDICES_PERMISSIONS_KEY); IndicesAccessControl indicesAccessControl = context.getRequest().getFromContext(InternalAuthorizationService.INDICES_PERMISSIONS_KEY);

View File

@ -10,6 +10,7 @@ import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.update.UpdateResponse; import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.node.Node; import org.elasticsearch.node.Node;
import org.elasticsearch.shield.ShieldPlugin;
import org.elasticsearch.shield.authc.support.SecuredString; import org.elasticsearch.shield.authc.support.SecuredString;
import org.elasticsearch.shield.authc.support.UsernamePasswordToken; import org.elasticsearch.shield.authc.support.UsernamePasswordToken;
import org.elasticsearch.test.ShieldIntegTestCase; import org.elasticsearch.test.ShieldIntegTestCase;
@ -29,6 +30,7 @@ public class BulkUpdateTests extends ShieldIntegTestCase {
return Settings.builder() return Settings.builder()
.put(super.nodeSettings(nodeOrdinal)) .put(super.nodeSettings(nodeOrdinal))
.put(Node.HTTP_ENABLED, true) .put(Node.HTTP_ENABLED, true)
.put(ShieldPlugin.DLS_FLS_ENABLED_SETTING, false) //FIXME randomize once DLS/FLS works with Bulk updates...
.build(); .build();
} }

View File

@ -775,15 +775,21 @@ public class FieldLevelSecurityTests extends ShieldIntegTestCase {
fail("failed, because bulk request with updates shouldn't be allowed if field level security is enabled"); fail("failed, because bulk request with updates shouldn't be allowed if field level security is enabled");
} catch (ElasticsearchSecurityException e) { } catch (ElasticsearchSecurityException e) {
assertThat(e.status(), equalTo(RestStatus.BAD_REQUEST)); assertThat(e.status(), equalTo(RestStatus.BAD_REQUEST));
assertThat(e.getMessage(), equalTo("Can't execute an bulk request with update requests embedded if field level security is enabled")); assertThat(e.getMessage(), equalTo("Can't execute an bulk request with update requests embedded if document and field level security is enabled"));
} }
assertThat(client().prepareGet("test", "type", "1").get().getSource().get("field2").toString(), equalTo("value2")); assertThat(client().prepareGet("test", "type", "1").get().getSource().get("field2").toString(), equalTo("value2"));
// FIXME this should work once we can support update requests within BulkRequests...
// With no field level security enabled the update in bulk is allowed: // With no field level security enabled the update in bulk is allowed:
client().prepareBulk() try {
.add(new UpdateRequest("test", "type", "1").doc("field2", "value3")) client().prepareBulk()
.get(); .add(new UpdateRequest("test", "type", "1").doc("field2", "value3"))
assertThat(client().prepareGet("test", "type", "1").get().getSource().get("field2").toString(), equalTo("value3")); .get();
assertThat(client().prepareGet("test", "type", "1").get().getSource().get("field2").toString(), equalTo("value3"));
} catch (ElasticsearchSecurityException e) {
assertThat(e.status(), equalTo(RestStatus.BAD_REQUEST));
assertThat(e.getMessage(), equalTo("Can't execute an bulk request with update requests embedded if document and field level security is enabled"));
}
} }
public void testQuery_withRoleWithFieldWildcards() throws Exception { public void testQuery_withRoleWithFieldWildcards() throws Exception {