Merge remote-tracking branch 'elastic/master' into feature/sql_2
Original commit: elastic/x-pack-elasticsearch@d6451b0f6b
This commit is contained in:
commit
2be9cabbe9
|
@ -1,12 +1,26 @@
|
|||
[float]
|
||||
[[ml-buckets]]
|
||||
=== Buckets
|
||||
++++
|
||||
<titleabbrev>Buckets</titleabbrev>
|
||||
++++
|
||||
|
||||
The {xpackml} features use the concept of a bucket to divide the time
|
||||
series into batches for processing. The _bucket span_ is part of the
|
||||
configuration information for a job. It defines the time interval that is used
|
||||
to summarize and model the data. This is typically between 5 minutes to 1 hour
|
||||
and it depends on your data characteristics. When you set the bucket span,
|
||||
take into account the granularity at which you want to analyze, the frequency
|
||||
of the input data, the typical duration of the anomalies, and the frequency at
|
||||
which alerting is required.
|
||||
The {xpackml} features use the concept of a _bucket_ to divide the time series
|
||||
into batches for processing.
|
||||
|
||||
The _bucket span_ is part of the configuration information for a job. It defines
|
||||
the time interval that is used to summarize and model the data. This is
|
||||
typically between 5 minutes to 1 hour and it depends on your data characteristics.
|
||||
When you set the bucket span, take into account the granularity at which you
|
||||
want to analyze, the frequency of the input data, the typical duration of the
|
||||
anomalies, and the frequency at which alerting is required.
|
||||
|
||||
When you view your {ml} results, each bucket has an anomaly score. This score is
|
||||
a statistically aggregated and normalized view of the combined anomalousness of
|
||||
all the record results in the bucket. If you have more than one job, you can
|
||||
also obtain overall bucket results, which combine and correlate anomalies from
|
||||
multiple jobs into an overall score. When you view the results for jobs groups
|
||||
in {kib}, it provides the overall bucket scores.
|
||||
|
||||
For more information, see
|
||||
{ref}/ml-results-resource.html[Results Resources] and
|
||||
{ref}/ml-get-overall-buckets.html[Get Overall Buckets API].
|
||||
|
|
|
@ -9,10 +9,15 @@ The {ml} model is not ill-affected and you do not receive spurious results.
|
|||
You can create calendars and scheduled events in the **Settings** pane on the
|
||||
**Machine Learning** page in {kib} or by using {ref}/ml-apis.html[{ml} APIs].
|
||||
|
||||
A scheduled event must have a start time, end time, and description. You can
|
||||
identify zero or more scheduled events in a calendar. Jobs can then subscribe to
|
||||
calendars and the {ml} analytics handle all subsequent scheduled events
|
||||
appropriately.
|
||||
A scheduled event must have a start time, end time, and description. In general,
|
||||
scheduled events are short in duration (typically lasting from a few hours to a
|
||||
day) and occur infrequently. If you have regularly occurring events, such as
|
||||
weekly maintenance periods, you do not need to create scheduled events for these
|
||||
circumstances; they are already handled by the {ml} analytics.
|
||||
|
||||
You can identify zero or more scheduled events in a calendar. Jobs can then
|
||||
subscribe to calendars and the {ml} analytics handle all subsequent scheduled
|
||||
events appropriately.
|
||||
|
||||
If you want to add multiple scheduled events at once, you can import an
|
||||
iCalendar (`.ics`) file in {kib} or a JSON file in the
|
||||
|
@ -21,8 +26,16 @@ add events to calendar API
|
|||
//]
|
||||
.
|
||||
|
||||
NOTE: Bucket results are generated during scheduled events but they have an
|
||||
[NOTE]
|
||||
--
|
||||
|
||||
* If your iCalendar file contains recurring events, only the first occurrence is
|
||||
imported.
|
||||
* Bucket results are generated during scheduled events but they have an
|
||||
anomaly score of zero. For more information about bucket results, see
|
||||
{ref}/ml-results-resource.html[Results Resources].
|
||||
* If you use long or frequent scheduled events, it might take longer for the
|
||||
{ml} analytics to learn to model your data and some anomalous behavior might be
|
||||
missed.
|
||||
|
||||
//TO-DO: Add screenshot showing special events in Single Metric Viewer?
|
||||
--
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
include::analyzing.asciidoc[]
|
||||
include::forecasting.asciidoc[]
|
||||
include::buckets.asciidoc[]
|
||||
include::calendars.asciidoc[]
|
||||
|
||||
[[ml-concepts]]
|
||||
|
@ -16,5 +17,4 @@ concepts from the outset will tremendously help ease the learning process.
|
|||
|
||||
include::jobs.asciidoc[]
|
||||
include::datafeeds.asciidoc[]
|
||||
include::buckets.asciidoc[]
|
||||
include::architecture.asciidoc[]
|
||||
|
|
|
@ -304,7 +304,7 @@ The format of a log entry is:
|
|||
`<local_node_info>` :: Information about the local node that generated
|
||||
the log entry. You can control what node information
|
||||
is included by configuring the
|
||||
<<audit-log-entry-local-node-info, local node info settings>>.
|
||||
{ref}/auditing-settings.html#node-audit-settings[local node info settings].
|
||||
`<layer>` :: The layer from which this event originated:
|
||||
`rest`, `transport` or `ip_filter`.
|
||||
`<entry_type>` :: The type of event that occurred: `anonymous_access_denied`,
|
||||
|
@ -321,35 +321,13 @@ The format of a log entry is:
|
|||
=== Logfile Output Settings
|
||||
|
||||
The events and some other information about what gets logged can be
|
||||
controlled using settings in the `elasticsearch.yml` file.
|
||||
|
||||
.Audited Event Settings
|
||||
[cols="4,^2,4",options="header"]
|
||||
|======
|
||||
| Name | Default | Description
|
||||
| `xpack.security.audit.logfile.events.include` | `access_denied`, `access_granted`, `anonymous_access_denied`, `authentication_failed`, `connection_denied`, `tampered_request`, `run_as_denied`, `run_as_granted` | Includes the specified events in the output.
|
||||
| `xpack.security.audit.logfile.events.exclude` | | Excludes the specified events from the output.
|
||||
| `xpack.security.audit.logfile.events.emit_request_body`| false | Include or exclude the request body from REST requests
|
||||
on certain event types such as `authentication_failed`.
|
||||
|======
|
||||
|
||||
controlled using settings in the `elasticsearch.yml` file. See
|
||||
{ref}/auditing-settings.html#event-audit-settings[Audited Event Settings] and
|
||||
{ref}/auditing-settings.html#node-audit-settings[Local Node Info Settings].
|
||||
|
||||
IMPORTANT: No filtering is performed when auditing, so sensitive data may be
|
||||
audited in plain text when including the request body in audit events.
|
||||
|
||||
[[audit-log-entry-local-node-info]]
|
||||
.Local Node Info Settings
|
||||
[cols="4,^2,4",options="header"]
|
||||
|======
|
||||
| Name | Default | Description
|
||||
| `xpack.security.audit.logfile.prefix.emit_node_name` | true | Include or exclude the node's name
|
||||
from the local node info.
|
||||
| `xpack.security.audit.logfile.prefix.emit_node_host_address` | false | Include or exclude the node's IP address
|
||||
from the local node info.
|
||||
| `xpack.security.audit.logfile.prefix.emit_node_host_name` | false | Include or exclude the node's host name
|
||||
from the local node info.
|
||||
|======
|
||||
|
||||
[[logging-file]]
|
||||
You can also configure how the logfile is written in the `log4j2.properties`
|
||||
file located in `CONFIG_DIR/x-pack`. By default, audit information is appended to the
|
||||
|
@ -450,19 +428,8 @@ in the `elasticsearch.yml` file:
|
|||
xpack.security.audit.outputs: [ index, logfile ]
|
||||
----------------------------
|
||||
|
||||
.Audit Log Indexing Configuration
|
||||
[options="header"]
|
||||
|======
|
||||
| Attribute | Default Setting | Description
|
||||
| `xpack.security.audit.index.bulk_size` | `1000` | Controls how many audit events are batched into a single write.
|
||||
| `xpack.security.audit.index.flush_interval` | `1s` | Controls how often buffered events are flushed to the index.
|
||||
| `xpack.security.audit.index.rollover` | `daily` | Controls how often to roll over to a new index:
|
||||
`hourly`, `daily`, `weekly`, or `monthly`.
|
||||
| `xpack.security.audit.index.events.include` | `anonymous_access_denied`, `authentication_failed`, `realm_authentication_failed`, `access_granted`, `access_denied`, `tampered_request`, `connection_granted`, `connection_denied`, `run_as_granted`, `run_as_denied` | The audit events to be indexed. See <<audit-event-types, Audit Entry Types>> for the complete list.
|
||||
| `xpack.security.audit.index.events.exclude` | | The audit events to exclude from indexing.
|
||||
| `xpack.security.audit.index.events.emit_request_body`| false | Include or exclude the request body from REST requests
|
||||
on certain event types such as `authentication_failed`.
|
||||
|======
|
||||
For more configuration options, see
|
||||
{ref}/auditing-settings.html#index-audit-settings[Audit Log Indexing Configuration Settings].
|
||||
|
||||
IMPORTANT: No filtering is performed when auditing, so sensitive data may be
|
||||
audited in plain text when including the request body in audit events.
|
||||
|
@ -487,18 +454,14 @@ xpack.security.audit.index.settings:
|
|||
==== Forwarding Audit Logs to a Remote Cluster
|
||||
|
||||
To index audit events to a remote Elasticsearch cluster, you configure
|
||||
the following `xpack.security.audit.index.client` settings.
|
||||
the following `xpack.security.audit.index.client` settings:
|
||||
|
||||
.Remote Audit Log Indexing Configuration
|
||||
[options="header"]
|
||||
|======
|
||||
| Attribute | Description
|
||||
| `xpack.security.audit.index.client.hosts` | Comma-separated list of `host:port` pairs. These hosts
|
||||
should be nodes in the remote cluster.
|
||||
| `xpack.security.audit.index.client.cluster.name` | The name of the remote cluster.
|
||||
| `xpack.security.audit.index.client.xpack.security.user` | The `username:password` pair to use to authenticate with
|
||||
the remote cluster.
|
||||
|======
|
||||
* `xpack.security.audit.index.client.hosts`
|
||||
* `xpack.security.audit.index.client.cluster.name`
|
||||
* `xpack.security.audit.index.client.xpack.security.user`
|
||||
|
||||
For more information about these settings, see
|
||||
{ref}/auditing-settings.html#remote-audit-settings[Remote Audit Log Indexing Configuration Settings].
|
||||
|
||||
You can pass additional settings to the remote client by specifying them in the
|
||||
`xpack.security.audit.index.client` namespace. For example, to allow the remote
|
||||
|
|
|
@ -19,10 +19,6 @@ es_meta_plugin {
|
|||
'ml', 'monitoring', 'security', 'upgrade', 'watcher', 'sql']
|
||||
}
|
||||
|
||||
ext.expansions = [
|
||||
'project.version': version,
|
||||
]
|
||||
|
||||
dependencies {
|
||||
testCompile project(path: ':x-pack-elasticsearch:plugin:core', configuration: 'testArtifacts')
|
||||
}
|
||||
|
@ -41,6 +37,13 @@ artifacts {
|
|||
}
|
||||
|
||||
integTestRunner {
|
||||
/*
|
||||
* We have to disable setting the number of available processors as tests in the same JVM randomize processors and will step on each
|
||||
* other if we allow them to set the number of available processors as it's set-once in Netty.
|
||||
*/
|
||||
systemProperty 'es.set.netty.runtime.available.processors', 'false'
|
||||
|
||||
|
||||
// TODO: fix this rest test to not depend on a hardcoded port!
|
||||
def blacklist = ['getting_started/10_monitor_cluster_health/*']
|
||||
boolean snapshot = "true".equals(System.getProperty("build.snapshot", "true"))
|
||||
|
@ -141,14 +144,6 @@ integTestCluster {
|
|||
}
|
||||
}
|
||||
|
||||
integTestRunner {
|
||||
/*
|
||||
* We have to disable setting the number of available processors as tests in the same JVM randomize processors and will step on each
|
||||
* other if we allow them to set the number of available processors as it's set-once in Netty.
|
||||
*/
|
||||
systemProperty 'es.set.netty.runtime.available.processors', 'false'
|
||||
}
|
||||
|
||||
run {
|
||||
setting 'xpack.ml.enabled', 'true'
|
||||
setting 'xpack.graph.enabled', 'true'
|
||||
|
|
|
@ -5,6 +5,9 @@ import java.nio.file.Path
|
|||
import java.nio.file.StandardCopyOption
|
||||
|
||||
apply plugin: 'elasticsearch.esplugin'
|
||||
|
||||
archivesBaseName = 'x-pack-core'
|
||||
|
||||
esplugin {
|
||||
name 'x-pack-core'
|
||||
description 'Elasticsearch Expanded Pack Plugin - Core'
|
||||
|
@ -18,27 +21,16 @@ esplugin {
|
|||
integTest.enabled = false
|
||||
|
||||
dependencyLicenses {
|
||||
mapping from: /netty-.*/, to: 'netty'
|
||||
mapping from: /bc.*/, to: 'bouncycastle'
|
||||
mapping from: /owasp-java-html-sanitizer.*/, to: 'owasp-java-html-sanitizer'
|
||||
mapping from: /transport-netty.*/, to: 'elasticsearch'
|
||||
mapping from: /transport-nio.*/, to: 'elasticsearch'
|
||||
mapping from: /elasticsearch-nio.*/, to: 'elasticsearch'
|
||||
mapping from: /elasticsearch-rest-client.*/, to: 'elasticsearch'
|
||||
mapping from: /http.*/, to: 'httpclient' // pulled in by rest client
|
||||
mapping from: /commons-.*/, to: 'commons' // pulled in by rest client
|
||||
ignoreSha 'elasticsearch-rest-client'
|
||||
ignoreSha 'transport-netty4'
|
||||
ignoreSha 'transport-nio'
|
||||
ignoreSha 'elasticsearch-nio'
|
||||
ignoreSha 'elasticsearch-rest-client-sniffer'
|
||||
ignoreSha 'x-pack-core'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
provided "org.elasticsearch:elasticsearch:${version}"
|
||||
compile "org.apache.httpcomponents:httpclient:${versions.httpclient}"
|
||||
compile "org.apache.httpcomponents:httpcore:${versions.httpcore}"
|
||||
compile "org.apache.httpcomponents:httpcore-nio:${versions.httpcore}"
|
||||
compile "org.apache.httpcomponents:httpasyncclient:${versions.httpasyncclient}"
|
||||
|
||||
compile "commons-logging:commons-logging:${versions.commonslogging}"
|
||||
|
@ -50,7 +42,6 @@ dependencies {
|
|||
compile 'org.bouncycastle:bcpkix-jdk15on:1.58'
|
||||
compile project(path: ':modules:transport-netty4', configuration: 'runtime')
|
||||
|
||||
//testCompile project(path: ':core:cli', configuration: 'runtime')
|
||||
testCompile 'org.elasticsearch:securemock:1.2'
|
||||
testCompile "org.elasticsearch:mocksocket:${versions.mocksocket}"
|
||||
testCompile "org.apache.logging.log4j:log4j-slf4j-impl:${versions.log4j}"
|
||||
|
@ -60,6 +51,10 @@ dependencies {
|
|||
testCompile project(path: ':modules:analysis-common', configuration: 'runtime')
|
||||
}
|
||||
|
||||
ext.expansions = [
|
||||
'project.version': version
|
||||
]
|
||||
|
||||
processResources {
|
||||
from(sourceSets.main.resources.srcDirs) {
|
||||
exclude '**/public.key'
|
||||
|
@ -81,14 +76,9 @@ forbiddenPatterns {
|
|||
exclude '**/*.zip'
|
||||
}
|
||||
|
||||
archivesBaseName = 'x-pack-core'
|
||||
|
||||
compileJava.options.compilerArgs << "-Xlint:-deprecation,-rawtypes,-serial,-try,-unchecked"
|
||||
compileTestJava.options.compilerArgs << "-Xlint:-deprecation,-rawtypes,-serial,-try,-unchecked"
|
||||
|
||||
// TODO: fix these!
|
||||
thirdPartyAudit.enabled = false
|
||||
|
||||
licenseHeaders {
|
||||
approvedLicenses << 'BCrypt (BSD-like)'
|
||||
additionalLicense 'BCRYP', 'BCrypt (BSD-like)', 'Copyright (c) 2006 Damien Miller <djm@mindrot.org>'
|
||||
|
@ -100,6 +90,7 @@ sourceSets.test.java {
|
|||
srcDir '../../license-tools/src/main/java'
|
||||
}
|
||||
|
||||
// TODO: remove this jar once xpack extensions have been removed
|
||||
// assemble the API JAR for the transport-client and extension authors; this JAR is the core JAR by another name
|
||||
project.afterEvaluate {
|
||||
task apiJar {
|
||||
|
@ -131,108 +122,6 @@ project.afterEvaluate {
|
|||
}
|
||||
}
|
||||
|
||||
//
|
||||
// integTestRunner {
|
||||
// // TODO: fix this rest test to not depend on a hardcoded port!
|
||||
// def blacklist = ['getting_started/10_monitor_cluster_health/*']
|
||||
// boolean snapshot = "true".equals(System.getProperty("build.snapshot", "true"))
|
||||
// if (!snapshot) {
|
||||
// // these tests attempt to install basic/internal licenses signed against the dev/public.key
|
||||
// // Since there is no infrastructure in place (anytime soon) to generate licenses using the production
|
||||
// // private key, these tests are whitelisted in non-snapshot test runs
|
||||
// blacklist.addAll(['xpack/15_basic/*', 'license/20_put_license/*'])
|
||||
// }
|
||||
// systemProperty 'tests.rest.blacklist', blacklist.join(',')
|
||||
// }
|
||||
|
||||
// // location of generated keystores and certificates
|
||||
// File keystoreDir = new File(project.buildDir, 'keystore')
|
||||
|
||||
// // Generate the node's keystore
|
||||
// File nodeKeystore = new File(keystoreDir, 'test-node.jks')
|
||||
// task createNodeKeyStore(type: LoggedExec) {
|
||||
// doFirst {
|
||||
// if (nodeKeystore.parentFile.exists() == false) {
|
||||
// nodeKeystore.parentFile.mkdirs()
|
||||
// }
|
||||
// if (nodeKeystore.exists()) {
|
||||
// delete nodeKeystore
|
||||
// }
|
||||
// }
|
||||
// executable = new File(project.javaHome, 'bin/keytool')
|
||||
// standardInput = new ByteArrayInputStream('FirstName LastName\nUnit\nOrganization\nCity\nState\nNL\nyes\n\n'.getBytes('UTF-8'))
|
||||
// args '-genkey',
|
||||
// '-alias', 'test-node',
|
||||
// '-keystore', nodeKeystore,
|
||||
// '-keyalg', 'RSA',
|
||||
// '-keysize', '2048',
|
||||
// '-validity', '712',
|
||||
// '-dname', 'CN=smoke-test-plugins-ssl',
|
||||
// '-keypass', 'keypass',
|
||||
// '-storepass', 'keypass'
|
||||
// }
|
||||
|
||||
// Add keystores to test classpath: it expects it there
|
||||
//sourceSets.test.resources.srcDir(keystoreDir)
|
||||
//processTestResources.dependsOn(createNodeKeyStore)
|
||||
|
||||
// integTestCluster {
|
||||
// dependsOn createNodeKeyStore
|
||||
// setting 'xpack.ml.enabled', 'true'
|
||||
// setting 'logger.org.elasticsearch.xpack.ml.datafeed', 'TRACE'
|
||||
// // Integration tests are supposed to enable/disable exporters before/after each test
|
||||
// setting 'xpack.monitoring.exporters._local.type', 'local'
|
||||
// setting 'xpack.monitoring.exporters._local.enabled', 'false'
|
||||
// setting 'xpack.monitoring.collection.interval', '-1'
|
||||
// setting 'xpack.security.authc.token.enabled', 'true'
|
||||
// setting 'xpack.security.transport.ssl.enabled', 'true'
|
||||
// setting 'xpack.security.transport.ssl.keystore.path', nodeKeystore.name
|
||||
// setting 'xpack.security.transport.ssl.verification_mode', 'certificate'
|
||||
// setting 'xpack.security.audit.enabled', 'true'
|
||||
// keystoreSetting 'bootstrap.password', 'x-pack-test-password'
|
||||
// keystoreSetting 'xpack.security.transport.ssl.keystore.secure_password', 'keypass'
|
||||
// distribution = 'zip' // this is important since we use the reindex module in ML
|
||||
|
||||
// setupCommand 'setupTestUser', 'bin/x-pack/users', 'useradd', 'x_pack_rest_user', '-p', 'x-pack-test-password', '-r', 'superuser'
|
||||
|
||||
// extraConfigFile nodeKeystore.name, nodeKeystore
|
||||
|
||||
// waitCondition = { NodeInfo node, AntBuilder ant ->
|
||||
// File tmpFile = new File(node.cwd, 'wait.success')
|
||||
|
||||
// for (int i = 0; i < 10; i++) {
|
||||
// // we use custom wait logic here as the elastic user is not available immediately and ant.get will fail when a 401 is returned
|
||||
// HttpURLConnection httpURLConnection = null;
|
||||
// try {
|
||||
// httpURLConnection = (HttpURLConnection) new URL("http://${node.httpUri()}/_cluster/health?wait_for_nodes=${numNodes}&wait_for_status=yellow").openConnection();
|
||||
// httpURLConnection.setRequestProperty("Authorization", "Basic " +
|
||||
// Base64.getEncoder().encodeToString("x_pack_rest_user:x-pack-test-password".getBytes(StandardCharsets.UTF_8)));
|
||||
// httpURLConnection.setRequestMethod("GET");
|
||||
// httpURLConnection.connect();
|
||||
// if (httpURLConnection.getResponseCode() == 200) {
|
||||
// tmpFile.withWriter StandardCharsets.UTF_8.name(), {
|
||||
// it.write(httpURLConnection.getInputStream().getText(StandardCharsets.UTF_8.name()))
|
||||
// }
|
||||
// }
|
||||
// } catch (Exception e) {
|
||||
// if (i == 9) {
|
||||
// logger.error("final attempt of calling cluster health failed", e)
|
||||
// } else {
|
||||
// logger.debug("failed to call cluster health", e)
|
||||
// }
|
||||
// } finally {
|
||||
// if (httpURLConnection != null) {
|
||||
// httpURLConnection.disconnect();
|
||||
// }
|
||||
// }
|
||||
|
||||
// // did not start, so wait a bit before trying again
|
||||
// Thread.sleep(500L);
|
||||
// }
|
||||
// return tmpFile.exists()
|
||||
// }
|
||||
//}
|
||||
|
||||
test {
|
||||
/*
|
||||
* We have to disable setting the number of available processors as tests in the same JVM randomize processors and will step on each
|
||||
|
@ -249,6 +138,7 @@ integTestRunner {
|
|||
systemProperty 'es.set.netty.runtime.available.processors', 'false'
|
||||
}
|
||||
|
||||
|
||||
// TODO: don't publish test artifacts just to run messy tests, fix the tests!
|
||||
// https://github.com/elastic/x-plugins/issues/724
|
||||
configurations {
|
||||
|
@ -264,38 +154,12 @@ artifacts {
|
|||
testArtifacts testJar
|
||||
}
|
||||
|
||||
// pulled in as external dependency to work on java 9
|
||||
if (JavaVersion.current() <= JavaVersion.VERSION_1_8) {
|
||||
thirdPartyAudit.excludes += [
|
||||
'com.sun.activation.registries.MailcapParseException',
|
||||
'javax.activation.ActivationDataFlavor',
|
||||
'javax.activation.CommandInfo',
|
||||
'javax.activation.CommandMap',
|
||||
'javax.activation.CommandObject',
|
||||
'javax.activation.DataContentHandler',
|
||||
'javax.activation.DataContentHandlerFactory',
|
||||
'javax.activation.DataHandler$1',
|
||||
'javax.activation.DataHandler',
|
||||
'javax.activation.DataHandlerDataSource',
|
||||
'javax.activation.DataSource',
|
||||
'javax.activation.DataSourceDataContentHandler',
|
||||
'javax.activation.FileDataSource',
|
||||
'javax.activation.FileTypeMap',
|
||||
'javax.activation.MimeType',
|
||||
'javax.activation.MimeTypeParameterList',
|
||||
'javax.activation.MimeTypeParseException',
|
||||
'javax.activation.ObjectDataContentHandler',
|
||||
'javax.activation.SecuritySupport$1',
|
||||
'javax.activation.SecuritySupport$2',
|
||||
'javax.activation.SecuritySupport$3',
|
||||
'javax.activation.SecuritySupport$4',
|
||||
'javax.activation.SecuritySupport$5',
|
||||
'javax.activation.SecuritySupport',
|
||||
'javax.activation.URLDataSource',
|
||||
'javax.activation.UnsupportedDataTypeException'
|
||||
]
|
||||
}
|
||||
|
||||
run {
|
||||
distribution = 'zip'
|
||||
}
|
||||
thirdPartyAudit.excludes = [
|
||||
//commons-logging optional dependencies
|
||||
'org.apache.avalon.framework.logger.Logger',
|
||||
'org.apache.log.Hierarchy',
|
||||
'org.apache.log.Logger',
|
||||
//commons-logging provided dependencies
|
||||
'javax.servlet.ServletContextEvent',
|
||||
'javax.servlet.ServletContextListener'
|
||||
]
|
||||
|
|
|
@ -8,24 +8,16 @@ package org.elasticsearch.xpack.extensions;
|
|||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.common.settings.Setting;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.watcher.ResourceWatcherService;
|
||||
import org.elasticsearch.xpack.security.authc.AuthenticationFailureHandler;
|
||||
import org.elasticsearch.xpack.security.authc.Realm;
|
||||
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
||||
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
|
||||
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.xpack.security.SecurityExtension;
|
||||
|
||||
/**
|
||||
* An extension point allowing to plug in custom functionality in x-pack authentication module.
|
||||
* @deprecated use {@link SecurityExtension} via SPI instead
|
||||
*/
|
||||
public abstract class XPackExtension {
|
||||
@Deprecated
|
||||
public abstract class XPackExtension implements SecurityExtension {
|
||||
/**
|
||||
* The name of the plugin.
|
||||
*/
|
||||
|
@ -43,72 +35,21 @@ public abstract class XPackExtension {
|
|||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns authentication realm implementations added by this extension.
|
||||
*
|
||||
* The key of the returned {@link Map} is the type name of the realm, and the value
|
||||
* is a {@link org.elasticsearch.xpack.security.authc.Realm.Factory} which will construct
|
||||
* that realm for use in authentication when that realm type is configured.
|
||||
*
|
||||
* @param resourceWatcherService Use to watch configuration files for changes
|
||||
*/
|
||||
public Map<String, Realm.Factory> getRealms(ResourceWatcherService resourceWatcherService) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of {@link Setting settings} that may be configured for the each type of realm.
|
||||
*
|
||||
* Each <em>setting key</em> must be unqualified and is in the same format as will be provided via {@link RealmConfig#settings()}.
|
||||
* If a given realm-type is not present in the returned map, then it will be treated as if it supported <em>all</em> possible settings.
|
||||
*
|
||||
* The life-cycle of an extension dictates that this method will be called before {@link #getRealms(ResourceWatcherService)}
|
||||
*/
|
||||
public Map<String, Set<Setting<?>>> getRealmSettings() { return Collections.emptyMap(); }
|
||||
|
||||
/**
|
||||
* Returns a handler for authentication failures, or null to use the default handler.
|
||||
*
|
||||
* Only one installed extension may have an authentication failure handler. If more than
|
||||
* one extension returns a non-null handler, an error is raised.
|
||||
*/
|
||||
public AuthenticationFailureHandler getAuthenticationFailureHandler() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of settings that should be filtered from API calls. In most cases,
|
||||
* these settings are sensitive such as passwords.
|
||||
*
|
||||
* The value should be the full name of the setting or a wildcard that matches the
|
||||
* desired setting.
|
||||
* @deprecated use {@link Plugin#getSettingsFilter()} ()} via SPI extension instead
|
||||
*/
|
||||
@Deprecated
|
||||
public List<String> getSettingsFilter() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an ordered list of role providers that are used to resolve role names
|
||||
* to {@link RoleDescriptor} objects. Each provider is invoked in order to
|
||||
* resolve any role names not resolved by the reserved or native roles stores.
|
||||
*
|
||||
* Each role provider is represented as a {@link BiConsumer} which takes a set
|
||||
* of roles to resolve as the first parameter to consume and an {@link ActionListener}
|
||||
* as the second parameter to consume. The implementation of the role provider
|
||||
* should be asynchronous if the computation is lengthy or any disk and/or network
|
||||
* I/O is involved. The implementation is responsible for resolving whatever roles
|
||||
* it can into a set of {@link RoleDescriptor} instances. If successful, the
|
||||
* implementation must invoke {@link ActionListener#onResponse(Object)} to pass along
|
||||
* the resolved set of role descriptors. If a failure was encountered, the
|
||||
* implementation must invoke {@link ActionListener#onFailure(Exception)}.
|
||||
*
|
||||
* By default, an empty list is returned.
|
||||
*
|
||||
* @param settings The configured settings for the node
|
||||
* @param resourceWatcherService Use to watch configuration files for changes
|
||||
*/
|
||||
public List<BiConsumer<Set<String>, ActionListener<Set<RoleDescriptor>>>>
|
||||
getRolesProviders(Settings settings, ResourceWatcherService resourceWatcherService) {
|
||||
return Collections.emptyList();
|
||||
@Override
|
||||
public String toString() {
|
||||
return name();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import org.apache.logging.log4j.util.Supplier;
|
|||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.admin.cluster.node.tasks.cancel.CancelTasksRequest;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.tasks.CancellableTask;
|
||||
import org.elasticsearch.tasks.Task;
|
||||
import org.elasticsearch.tasks.TaskCancelledException;
|
||||
|
@ -19,6 +20,7 @@ import org.elasticsearch.tasks.TaskManager;
|
|||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Represents a executor node operation that corresponds to a persistent task
|
||||
|
@ -105,6 +107,15 @@ public class AllocatedPersistentTask extends CancellableTask {
|
|||
COMPLETED // the task is done running and trying to notify caller
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for this persistent task to have the desired state.
|
||||
*/
|
||||
public void waitForPersistentTaskStatus(Predicate<PersistentTasksCustomMetaData.PersistentTask<?>> predicate,
|
||||
@Nullable TimeValue timeout,
|
||||
PersistentTasksService.WaitForPersistentTaskStatusListener<?> listener) {
|
||||
persistentTasksService.waitForPersistentTaskStatus(persistentTaskId, predicate, timeout, listener);
|
||||
}
|
||||
|
||||
public void markAsCompleted() {
|
||||
completeAndNotifyIfNeeded(null);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* 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.lucene.util.SPIClassIterator;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.common.settings.Setting;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.watcher.ResourceWatcherService;
|
||||
import org.elasticsearch.xpack.security.authc.AuthenticationFailureHandler;
|
||||
import org.elasticsearch.xpack.security.authc.Realm;
|
||||
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
||||
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.ServiceConfigurationError;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
/**
|
||||
* An SPI extension point allowing to plug in custom functionality in x-pack authentication module.
|
||||
*/
|
||||
public interface SecurityExtension {
|
||||
|
||||
/**
|
||||
* Returns authentication realm implementations added by this extension.
|
||||
*
|
||||
* The key of the returned {@link Map} is the type name of the realm, and the value
|
||||
* is a {@link Realm.Factory} which will construct
|
||||
* that realm for use in authentication when that realm type is configured.
|
||||
*
|
||||
* @param resourceWatcherService Use to watch configuration files for changes
|
||||
*/
|
||||
default Map<String, Realm.Factory> getRealms(ResourceWatcherService resourceWatcherService) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of {@link Setting settings} that may be configured for the each type of realm.
|
||||
*
|
||||
* Each <em>setting key</em> must be unqualified and is in the same format as will be provided via {@link RealmConfig#settings()}.
|
||||
* If a given realm-type is not present in the returned map, then it will be treated as if it supported <em>all</em> possible settings.
|
||||
*
|
||||
* The life-cycle of an extension dictates that this method will be called before {@link #getRealms(ResourceWatcherService)}
|
||||
*/
|
||||
default Map<String, Set<Setting<?>>> getRealmSettings() { return Collections.emptyMap(); }
|
||||
|
||||
/**
|
||||
* Returns a handler for authentication failures, or null to use the default handler.
|
||||
*
|
||||
* Only one installed extension may have an authentication failure handler. If more than
|
||||
* one extension returns a non-null handler, an error is raised.
|
||||
*/
|
||||
default AuthenticationFailureHandler getAuthenticationFailureHandler() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an ordered list of role providers that are used to resolve role names
|
||||
* to {@link RoleDescriptor} objects. Each provider is invoked in order to
|
||||
* resolve any role names not resolved by the reserved or native roles stores.
|
||||
*
|
||||
* Each role provider is represented as a {@link BiConsumer} which takes a set
|
||||
* of roles to resolve as the first parameter to consume and an {@link ActionListener}
|
||||
* as the second parameter to consume. The implementation of the role provider
|
||||
* should be asynchronous if the computation is lengthy or any disk and/or network
|
||||
* I/O is involved. The implementation is responsible for resolving whatever roles
|
||||
* it can into a set of {@link RoleDescriptor} instances. If successful, the
|
||||
* implementation must invoke {@link ActionListener#onResponse(Object)} to pass along
|
||||
* the resolved set of role descriptors. If a failure was encountered, the
|
||||
* implementation must invoke {@link ActionListener#onFailure(Exception)}.
|
||||
*
|
||||
* By default, an empty list is returned.
|
||||
*
|
||||
* @param settings The configured settings for the node
|
||||
* @param resourceWatcherService Use to watch configuration files for changes
|
||||
*/
|
||||
default List<BiConsumer<Set<String>, ActionListener<Set<RoleDescriptor>>>>
|
||||
getRolesProviders(Settings settings, ResourceWatcherService resourceWatcherService) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the XPackSecurityExtensions from the given class loader
|
||||
*/
|
||||
static List<SecurityExtension> loadExtensions(ClassLoader loader) {
|
||||
SPIClassIterator<SecurityExtension> iterator = SPIClassIterator.get(SecurityExtension.class, loader);
|
||||
List<SecurityExtension> extensions = new ArrayList<>();
|
||||
while (iterator.hasNext()) {
|
||||
final Class<? extends SecurityExtension> c = iterator.next();
|
||||
try {
|
||||
extensions.add(c.getConstructor().newInstance());
|
||||
} catch (Exception e) {
|
||||
throw new ServiceConfigurationError("failed to load security extension [" + c.getName() + "]", e);
|
||||
}
|
||||
}
|
||||
return extensions;
|
||||
}
|
||||
|
||||
}
|
|
@ -360,8 +360,7 @@ public class MachineLearning extends Plugin implements ActionPlugin, AnalysisPlu
|
|||
|
||||
PersistentTasksService persistentTasksService = new PersistentTasksService(settings, clusterService, threadPool, client);
|
||||
|
||||
components.addAll(createComponents(client, clusterService, threadPool, xContentRegistry, environment, resourceWatcherService,
|
||||
persistentTasksService));
|
||||
components.addAll(createComponents(client, clusterService, threadPool, xContentRegistry, environment));
|
||||
|
||||
// This was lifted from the XPackPlugins createComponents when it got split
|
||||
// This is not extensible and anyone copying this code needs to instead make this work
|
||||
|
@ -379,10 +378,11 @@ public class MachineLearning extends Plugin implements ActionPlugin, AnalysisPlu
|
|||
return components;
|
||||
}
|
||||
|
||||
public Collection<Object> createComponents(Client client, ClusterService clusterService, ThreadPool threadPool,
|
||||
NamedXContentRegistry xContentRegistry, Environment environment,
|
||||
ResourceWatcherService resourceWatcherService,
|
||||
PersistentTasksService persistentTasksService) {
|
||||
// TODO: once initialization of the PersistentTasksClusterService, PersistentTasksService
|
||||
// and PersistentTasksExecutorRegistry has been moved somewhere else the entire contents of
|
||||
// this method can replace the entire contents of the overridden createComponents() method
|
||||
private Collection<Object> createComponents(Client client, ClusterService clusterService, ThreadPool threadPool,
|
||||
NamedXContentRegistry xContentRegistry, Environment environment) {
|
||||
if (enabled == false || transportClientMode || tribeNode || tribeNodeClient) {
|
||||
return emptyList();
|
||||
}
|
||||
|
@ -426,12 +426,13 @@ public class MachineLearning extends Plugin implements ActionPlugin, AnalysisPlu
|
|||
this.autodetectProcessManager.set(autodetectProcessManager);
|
||||
DatafeedJobBuilder datafeedJobBuilder = new DatafeedJobBuilder(client, jobProvider, auditor, System::currentTimeMillis);
|
||||
DatafeedManager datafeedManager = new DatafeedManager(threadPool, client, clusterService, datafeedJobBuilder,
|
||||
System::currentTimeMillis, auditor, persistentTasksService);
|
||||
System::currentTimeMillis, auditor);
|
||||
this.datafeedManager.set(datafeedManager);
|
||||
MlLifeCycleService mlLifeCycleService = new MlLifeCycleService(environment, clusterService, datafeedManager,
|
||||
autodetectProcessManager);
|
||||
InvalidLicenseEnforcer invalidLicenseEnforcer =
|
||||
new InvalidLicenseEnforcer(settings, getLicenseState(), threadPool, datafeedManager, autodetectProcessManager);
|
||||
|
||||
// This object's constructor attaches to the license state, so there's no need to retain another reference to it
|
||||
new InvalidLicenseEnforcer(settings, getLicenseState(), threadPool, datafeedManager, autodetectProcessManager);
|
||||
|
||||
return Arrays.asList(
|
||||
mlLifeCycleService,
|
||||
|
@ -442,7 +443,6 @@ public class MachineLearning extends Plugin implements ActionPlugin, AnalysisPlu
|
|||
jobDataCountsPersister,
|
||||
datafeedManager,
|
||||
auditor,
|
||||
invalidLicenseEnforcer,
|
||||
new MlAssignmentNotifier(settings, auditor, clusterService)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import org.elasticsearch.common.settings.Settings;
|
|||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.index.engine.VersionConflictEngineException;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.TransportService;
|
||||
import org.elasticsearch.xpack.ml.MLMetadataField;
|
||||
|
@ -83,8 +84,13 @@ public class TransportPutCalendarAction extends HandledTransportAction<PutCalend
|
|||
|
||||
@Override
|
||||
public void onFailure(Exception e) {
|
||||
listener.onFailure(
|
||||
ExceptionsHelper.serverError("Error putting calendar with id [" + calendar.getId() + "]", e));
|
||||
if (e instanceof VersionConflictEngineException) {
|
||||
listener.onFailure(ExceptionsHelper.badRequestException("Cannot create calendar with id [" +
|
||||
calendar.getId() + "] as it already exists"));
|
||||
} else {
|
||||
listener.onFailure(
|
||||
ExceptionsHelper.serverError("Error putting calendar with id [" + calendar.getId() + "]", e));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -32,7 +32,6 @@ import org.elasticsearch.xpack.ml.job.messages.Messages;
|
|||
import org.elasticsearch.xpack.ml.notifications.Auditor;
|
||||
import org.elasticsearch.xpack.persistent.PersistentTasksCustomMetaData;
|
||||
import org.elasticsearch.xpack.persistent.PersistentTasksCustomMetaData.PersistentTask;
|
||||
import org.elasticsearch.xpack.persistent.PersistentTasksService;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
|
@ -55,7 +54,6 @@ public class DatafeedManager extends AbstractComponent {
|
|||
|
||||
private final Client client;
|
||||
private final ClusterService clusterService;
|
||||
private final PersistentTasksService persistentTasksService;
|
||||
private final ThreadPool threadPool;
|
||||
private final Supplier<Long> currentTimeSupplier;
|
||||
private final Auditor auditor;
|
||||
|
@ -66,14 +64,13 @@ public class DatafeedManager extends AbstractComponent {
|
|||
private volatile boolean isolated;
|
||||
|
||||
public DatafeedManager(ThreadPool threadPool, Client client, ClusterService clusterService, DatafeedJobBuilder datafeedJobBuilder,
|
||||
Supplier<Long> currentTimeSupplier, Auditor auditor, PersistentTasksService persistentTasksService) {
|
||||
Supplier<Long> currentTimeSupplier, Auditor auditor) {
|
||||
super(Settings.EMPTY);
|
||||
this.client = Objects.requireNonNull(client);
|
||||
this.clusterService = Objects.requireNonNull(clusterService);
|
||||
this.threadPool = threadPool;
|
||||
this.currentTimeSupplier = Objects.requireNonNull(currentTimeSupplier);
|
||||
this.auditor = Objects.requireNonNull(auditor);
|
||||
this.persistentTasksService = Objects.requireNonNull(persistentTasksService);
|
||||
this.datafeedJobBuilder = Objects.requireNonNull(datafeedJobBuilder);
|
||||
clusterService.addListener(taskRunner);
|
||||
}
|
||||
|
@ -91,8 +88,7 @@ public class DatafeedManager extends AbstractComponent {
|
|||
|
||||
ActionListener<DatafeedJob> datafeedJobHandler = ActionListener.wrap(
|
||||
datafeedJob -> {
|
||||
Holder holder = new Holder(task.getPersistentTaskId(), task.getAllocationId(), datafeed, datafeedJob,
|
||||
task.isLookbackOnly(), new ProblemTracker(auditor, job.getId()), taskHandler);
|
||||
Holder holder = new Holder(task, datafeed, datafeedJob, new ProblemTracker(auditor, job.getId()), taskHandler);
|
||||
runningDatafeedsOnThisNode.put(task.getAllocationId(), holder);
|
||||
task.updatePersistentStatus(DatafeedState.STARTED, new ActionListener<PersistentTask<?>>() {
|
||||
@Override
|
||||
|
@ -279,7 +275,7 @@ public class DatafeedManager extends AbstractComponent {
|
|||
|
||||
public class Holder {
|
||||
|
||||
private final String taskId;
|
||||
private final TransportStartDatafeedAction.DatafeedTask task;
|
||||
private final long allocationId;
|
||||
private final DatafeedConfig datafeed;
|
||||
// To ensure that we wait until loopback / realtime search has completed before we stop the datafeed
|
||||
|
@ -291,13 +287,13 @@ public class DatafeedManager extends AbstractComponent {
|
|||
volatile Future<?> future;
|
||||
private volatile boolean isRelocating;
|
||||
|
||||
Holder(String taskId, long allocationId, DatafeedConfig datafeed, DatafeedJob datafeedJob, boolean autoCloseJob,
|
||||
Holder(TransportStartDatafeedAction.DatafeedTask task, DatafeedConfig datafeed, DatafeedJob datafeedJob,
|
||||
ProblemTracker problemTracker, Consumer<Exception> handler) {
|
||||
this.taskId = taskId;
|
||||
this.allocationId = allocationId;
|
||||
this.task = task;
|
||||
this.allocationId = task.getAllocationId();
|
||||
this.datafeed = datafeed;
|
||||
this.datafeedJob = datafeedJob;
|
||||
this.autoCloseJob = autoCloseJob;
|
||||
this.autoCloseJob = task.isLookbackOnly();
|
||||
this.problemTracker = problemTracker;
|
||||
this.handler = handler;
|
||||
}
|
||||
|
@ -397,7 +393,7 @@ public class DatafeedManager extends AbstractComponent {
|
|||
return;
|
||||
}
|
||||
|
||||
persistentTasksService.waitForPersistentTaskStatus(taskId, Objects::isNull, TimeValue.timeValueSeconds(20),
|
||||
task.waitForPersistentTaskStatus(Objects::isNull, TimeValue.timeValueSeconds(20),
|
||||
new WaitForPersistentTaskStatusListener<StartDatafeedAction.DatafeedParams>() {
|
||||
@Override
|
||||
public void onResponse(PersistentTask<StartDatafeedAction.DatafeedParams> persistentTask) {
|
||||
|
|
|
@ -99,8 +99,8 @@ class AggregationDataExtractor implements DataExtractor {
|
|||
|
||||
private Aggregations search() throws IOException {
|
||||
LOGGER.debug("[{}] Executing aggregated search", context.jobId);
|
||||
|
||||
SearchResponse searchResponse = executeSearchRequest(buildSearchRequest());
|
||||
LOGGER.debug("[{}] Search response was obtained", context.jobId);
|
||||
ExtractorUtils.checkSearchWasSuccessful(context.jobId, searchResponse);
|
||||
return validateAggs(searchResponse.getAggregations());
|
||||
}
|
||||
|
|
|
@ -117,6 +117,7 @@ public class ChunkedDataExtractor implements DataExtractor {
|
|||
.addAggregation(AggregationBuilders.max(LATEST_TIME).field(context.timeField));
|
||||
|
||||
SearchResponse response = executeSearchRequest(searchRequestBuilder);
|
||||
LOGGER.debug("[{}] Data summary response was obtained", context.jobId);
|
||||
|
||||
ExtractorUtils.checkSearchWasSuccessful(context.jobId, response);
|
||||
|
||||
|
|
|
@ -95,6 +95,7 @@ class ScrollDataExtractor implements DataExtractor {
|
|||
protected InputStream initScroll(long startTimestamp) throws IOException {
|
||||
LOGGER.debug("[{}] Initializing scroll", context.jobId);
|
||||
SearchResponse searchResponse = executeSearchRequest(buildSearchRequest(startTimestamp));
|
||||
LOGGER.debug("[{}] Search response was obtained", context.jobId);
|
||||
return processSearchResponse(searchResponse);
|
||||
}
|
||||
|
||||
|
@ -195,6 +196,7 @@ class ScrollDataExtractor implements DataExtractor {
|
|||
throw searchExecutionException;
|
||||
}
|
||||
}
|
||||
LOGGER.debug("[{}] Search response was obtained", context.jobId);
|
||||
return processSearchResponse(searchResponse);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import org.elasticsearch.common.settings.Settings;
|
|||
import org.elasticsearch.rest.BaseRestHandler;
|
||||
import org.elasticsearch.rest.RestController;
|
||||
import org.elasticsearch.rest.RestRequest;
|
||||
import org.elasticsearch.rest.action.AcknowledgedRestListener;
|
||||
import org.elasticsearch.rest.action.RestToXContentListener;
|
||||
import org.elasticsearch.xpack.ml.MachineLearning;
|
||||
import org.elasticsearch.xpack.ml.action.UpdateCalendarJobAction;
|
||||
import org.elasticsearch.xpack.ml.calendars.Calendar;
|
||||
|
@ -39,6 +39,6 @@ public class RestDeleteCalendarJobAction extends BaseRestHandler {
|
|||
String jobId = restRequest.param(Job.ID.getPreferredName());
|
||||
UpdateCalendarJobAction.Request request =
|
||||
new UpdateCalendarJobAction.Request(calendarId, Collections.emptySet(), Collections.singleton(jobId));
|
||||
return channel -> client.execute(UpdateCalendarJobAction.INSTANCE, request, new AcknowledgedRestListener<>(channel));
|
||||
return channel -> client.execute(UpdateCalendarJobAction.INSTANCE, request, new RestToXContentListener<>(channel));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,6 @@ import org.elasticsearch.xpack.ml.notifications.Auditor;
|
|||
import org.elasticsearch.xpack.ml.notifications.AuditorField;
|
||||
import org.elasticsearch.xpack.persistent.PersistentTasksCustomMetaData;
|
||||
import org.elasticsearch.xpack.persistent.PersistentTasksCustomMetaData.PersistentTask;
|
||||
import org.elasticsearch.xpack.persistent.PersistentTasksService;
|
||||
import org.junit.Before;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
|
@ -122,8 +121,6 @@ public class DatafeedManagerTests extends ESTestCase {
|
|||
when(threadPool.executor(MachineLearning.DATAFEED_THREAD_POOL_NAME)).thenReturn(executorService);
|
||||
when(threadPool.executor(ThreadPool.Names.GENERIC)).thenReturn(executorService);
|
||||
|
||||
PersistentTasksService persistentTasksService = mock(PersistentTasksService.class);
|
||||
|
||||
datafeedJob = mock(DatafeedJob.class);
|
||||
when(datafeedJob.isRunning()).thenReturn(true);
|
||||
when(datafeedJob.stop()).thenReturn(true);
|
||||
|
@ -135,8 +132,7 @@ public class DatafeedManagerTests extends ESTestCase {
|
|||
return null;
|
||||
}).when(datafeedJobBuilder).build(any(), any(), any());
|
||||
|
||||
datafeedManager = new DatafeedManager(threadPool, client, clusterService, datafeedJobBuilder, () -> currentTime, auditor,
|
||||
persistentTasksService);
|
||||
datafeedManager = new DatafeedManager(threadPool, client, clusterService, datafeedJobBuilder, () -> currentTime, auditor);
|
||||
|
||||
verify(clusterService).addListener(capturedClusterStateListener.capture());
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ public abstract class LocalExporterIntegTestCase extends MonitoringIntegTestCase
|
|||
.put("xpack.monitoring.exporters." + exporterName + ".type", LocalExporter.TYPE)
|
||||
.put("xpack.monitoring.exporters." + exporterName + ".enabled", false)
|
||||
.put("xpack.monitoring.exporters." + exporterName + "." + CLUSTER_ALERTS_MANAGEMENT_SETTING, false)
|
||||
// .put(XPackSettings.WATCHER_ENABLED.getKey(), false)
|
||||
.put(XPackSettings.MACHINE_LEARNING_ENABLED.getKey(), false)
|
||||
.put(NetworkModule.HTTP_ENABLED.getKey(), false)
|
||||
.build();
|
||||
}
|
||||
|
|
|
@ -264,6 +264,8 @@ public class Security extends Plugin implements ActionPlugin, IngestPlugin, Netw
|
|||
private final SetOnce<SecurityActionFilter> securityActionFilter = new SetOnce<>();
|
||||
private final List<BootstrapCheck> bootstrapChecks;
|
||||
private final XPackExtensionsService extensionsService;
|
||||
private final List<SecurityExtension> securityExtensions = new ArrayList<>();
|
||||
|
||||
|
||||
public Security(Settings settings, final Path configPath) {
|
||||
this.settings = settings;
|
||||
|
@ -350,15 +352,16 @@ public class Security extends Plugin implements ActionPlugin, IngestPlugin, Netw
|
|||
try {
|
||||
|
||||
return createComponents(client, threadPool, clusterService, resourceWatcherService,
|
||||
extensionsService.getExtensions());
|
||||
extensionsService.getExtensions().stream().collect(Collectors.toList()));
|
||||
} catch (final Exception e) {
|
||||
throw new IllegalStateException("security initialization failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<Object> createComponents(Client client, ThreadPool threadPool, ClusterService clusterService,
|
||||
// pkg private for testing - tests want to pass in their set of extensions hence we are not using the extension service directly
|
||||
Collection<Object> createComponents(Client client, ThreadPool threadPool, ClusterService clusterService,
|
||||
ResourceWatcherService resourceWatcherService,
|
||||
List<XPackExtension> extensions) throws Exception {
|
||||
List<SecurityExtension> extensions) throws Exception {
|
||||
if (enabled == false) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
@ -411,7 +414,10 @@ public class Security extends Plugin implements ActionPlugin, IngestPlugin, Netw
|
|||
anonymousUser, securityLifecycleService, threadPool.getThreadContext());
|
||||
Map<String, Realm.Factory> realmFactories = new HashMap<>(InternalRealms.getFactories(threadPool, resourceWatcherService,
|
||||
getSslService(), nativeUsersStore, nativeRoleMappingStore, securityLifecycleService));
|
||||
for (XPackExtension extension : extensions) {
|
||||
for (SecurityExtension extension : securityExtensions) {
|
||||
extensions.add(extension);
|
||||
}
|
||||
for (SecurityExtension extension : extensions) {
|
||||
Map<String, Realm.Factory> newRealms = extension.getRealms(resourceWatcherService);
|
||||
for (Map.Entry<String, Realm.Factory> entry : newRealms.entrySet()) {
|
||||
if (realmFactories.put(entry.getKey(), entry.getValue()) != null) {
|
||||
|
@ -427,14 +433,14 @@ public class Security extends Plugin implements ActionPlugin, IngestPlugin, Netw
|
|||
|
||||
AuthenticationFailureHandler failureHandler = null;
|
||||
String extensionName = null;
|
||||
for (XPackExtension extension : extensions) {
|
||||
for (SecurityExtension extension : extensions) {
|
||||
AuthenticationFailureHandler extensionFailureHandler = extension.getAuthenticationFailureHandler();
|
||||
if (extensionFailureHandler != null && failureHandler != null) {
|
||||
throw new IllegalStateException("Extensions [" + extensionName + "] and [" + extension.name() + "] " +
|
||||
throw new IllegalStateException("Extensions [" + extensionName + "] and [" + extension.toString() + "] " +
|
||||
"both set an authentication failure handler");
|
||||
}
|
||||
failureHandler = extensionFailureHandler;
|
||||
extensionName = extension.name();
|
||||
extensionName = extension.toString();
|
||||
}
|
||||
if (failureHandler == null) {
|
||||
logger.debug("Using default authentication failure handler");
|
||||
|
@ -451,7 +457,7 @@ public class Security extends Plugin implements ActionPlugin, IngestPlugin, Netw
|
|||
final NativeRolesStore nativeRolesStore = new NativeRolesStore(settings, client, getLicenseState(), securityLifecycleService);
|
||||
final ReservedRolesStore reservedRolesStore = new ReservedRolesStore();
|
||||
List<BiConsumer<Set<String>, ActionListener<Set<RoleDescriptor>>>> rolesProviders = new ArrayList<>();
|
||||
for (XPackExtension extension : extensions) {
|
||||
for (SecurityExtension extension : extensions) {
|
||||
rolesProviders.addAll(extension.getRolesProviders(settings, resourceWatcherService));
|
||||
}
|
||||
final CompositeRolesStore allRolesStore = new CompositeRolesStore(settings, fileRolesStore, nativeRolesStore,
|
||||
|
@ -1043,4 +1049,9 @@ public class Security extends Plugin implements ActionPlugin, IngestPlugin, Netw
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadSPI(ClassLoader loader) {
|
||||
securityExtensions.addAll(SecurityExtension.loadExtensions(loader));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,6 @@ import org.elasticsearch.test.ESTestCase;
|
|||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.watcher.ResourceWatcherService;
|
||||
import org.elasticsearch.xpack.XPackSettings;
|
||||
import org.elasticsearch.xpack.extensions.XPackExtension;
|
||||
import org.elasticsearch.xpack.security.audit.AuditTrailService;
|
||||
import org.elasticsearch.xpack.security.audit.index.IndexAuditTrail;
|
||||
import org.elasticsearch.xpack.security.audit.logfile.LoggingAuditTrail;
|
||||
|
@ -75,26 +74,18 @@ public class SecurityTests extends ESTestCase {
|
|||
private ThreadContext threadContext = null;
|
||||
private TestUtils.UpdatableLicenseState licenseState;
|
||||
|
||||
public static class DummyExtension extends XPackExtension {
|
||||
public static class DummyExtension implements SecurityExtension {
|
||||
private String realmType;
|
||||
DummyExtension(String realmType) {
|
||||
this.realmType = realmType;
|
||||
}
|
||||
@Override
|
||||
public String name() {
|
||||
return "dummy";
|
||||
}
|
||||
@Override
|
||||
public String description() {
|
||||
return "dummy";
|
||||
}
|
||||
@Override
|
||||
public Map<String, Realm.Factory> getRealms(ResourceWatcherService resourceWatcherService) {
|
||||
return Collections.singletonMap(realmType, config -> null);
|
||||
}
|
||||
}
|
||||
|
||||
private Collection<Object> createComponents(Settings testSettings, XPackExtension... extensions) throws Exception {
|
||||
private Collection<Object> createComponents(Settings testSettings, SecurityExtension... extensions) throws Exception {
|
||||
if (security != null) {
|
||||
throw new IllegalStateException("Security object already exists (" + security + ")");
|
||||
}
|
||||
|
|
|
@ -152,7 +152,7 @@
|
|||
calendar_id: "mayan"
|
||||
|
||||
- do:
|
||||
catch: /version_conflict_engine_exception/
|
||||
catch: bad_request
|
||||
xpack.ml.put_calendar:
|
||||
calendar_id: "mayan"
|
||||
|
||||
|
@ -237,6 +237,8 @@
|
|||
xpack.ml.delete_calendar_job:
|
||||
calendar_id: "wildlife"
|
||||
job_id: "tiger"
|
||||
- match: { calendar_id: "wildlife" }
|
||||
- length: { job_ids: 0 }
|
||||
|
||||
- do:
|
||||
xpack.ml.get_calendars:
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
|||
import org.elasticsearch.cluster.routing.Preference;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.util.concurrent.AbstractRunnable;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
|
@ -109,8 +110,14 @@ public class TransportExecuteWatchAction extends WatcherTransportAction<ExecuteW
|
|||
private void executeWatch(ExecuteWatchRequest request, ActionListener<ExecuteWatchResponse> listener,
|
||||
Watch watch, boolean knownWatch) {
|
||||
|
||||
threadPool.executor(XPackField.WATCHER).submit(() -> {
|
||||
try {
|
||||
threadPool.executor(XPackField.WATCHER).submit(new AbstractRunnable() {
|
||||
@Override
|
||||
public void onFailure(Exception e) {
|
||||
listener.onFailure(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doRun() throws Exception {
|
||||
// ensure that the headers from the incoming request are used instead those of the stored watch
|
||||
// otherwise the watch would run as the user who stored the watch, but it needs to be run as the user who
|
||||
// executes this request
|
||||
|
@ -141,8 +148,6 @@ public class TransportExecuteWatchAction extends WatcherTransportAction<ExecuteW
|
|||
|
||||
record.toXContent(builder, WatcherParams.builder().hideSecrets(true).debug(request.isDebug()).build());
|
||||
listener.onResponse(new ExecuteWatchResponse(record.id().value(), builder.bytes(), XContentType.JSON));
|
||||
} catch (IOException e) {
|
||||
listener.onFailure(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
apply plugin: 'elasticsearch.esplugin'
|
||||
|
||||
esplugin {
|
||||
name 'spi-extension'
|
||||
description 'An example spi extension pluing for xpack security'
|
||||
classname 'org.elasticsearch.example.SpiExtensionPlugin'
|
||||
extendedPlugins = ['x-pack-security']
|
||||
}
|
||||
|
||||
dependencies {
|
||||
provided project(path: ':x-pack-elasticsearch:plugin:core', configuration: 'runtime')
|
||||
testCompile project(path: ':x-pack-elasticsearch:transport-client', configuration: 'runtime')
|
||||
}
|
||||
|
||||
|
||||
integTestRunner {
|
||||
systemProperty 'tests.security.manager', 'false'
|
||||
}
|
||||
|
||||
integTestCluster {
|
||||
dependsOn buildZip
|
||||
plugin ':x-pack-elasticsearch:plugin'
|
||||
setting 'xpack.security.authc.realms.custom.order', '0'
|
||||
setting 'xpack.security.authc.realms.custom.type', 'custom'
|
||||
setting 'xpack.security.authc.realms.custom.filtered_setting', 'should be filtered'
|
||||
setting 'xpack.security.authc.realms.esusers.order', '1'
|
||||
setting 'xpack.security.authc.realms.esusers.type', 'file'
|
||||
setting 'xpack.security.authc.realms.native.type', 'native'
|
||||
setting 'xpack.security.authc.realms.native.order', '2'
|
||||
setting 'xpack.ml.enabled', 'false'
|
||||
|
||||
// This is important, so that all the modules are available too.
|
||||
// There are index templates that use token filters that are in analysis-module and
|
||||
// processors are being used that are in ingest-common module.
|
||||
distribution = 'zip'
|
||||
|
||||
setupCommand 'setupDummyUser',
|
||||
'bin/x-pack/users', 'useradd', 'test_user', '-p', 'x-pack-test-password', '-r', 'superuser'
|
||||
waitCondition = { node, ant ->
|
||||
File tmpFile = new File(node.cwd, 'wait.success')
|
||||
ant.get(src: "http://${node.httpUri()}/_cluster/health?wait_for_nodes=>=${numNodes}&wait_for_status=yellow",
|
||||
dest: tmpFile.toString(),
|
||||
username: 'test_user',
|
||||
password: 'x-pack-test-password',
|
||||
ignoreerrors: true,
|
||||
retries: 10)
|
||||
return tmpFile.exists()
|
||||
}
|
||||
}
|
||||
check.dependsOn integTest
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* 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.example;
|
||||
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.example.realm.CustomAuthenticationFailureHandler;
|
||||
import org.elasticsearch.example.realm.CustomRealm;
|
||||
import org.elasticsearch.example.role.CustomInMemoryRolesProvider;
|
||||
import org.elasticsearch.watcher.ResourceWatcherService;
|
||||
import org.elasticsearch.xpack.security.SecurityExtension;
|
||||
import org.elasticsearch.xpack.security.authc.AuthenticationFailureHandler;
|
||||
import org.elasticsearch.xpack.security.authc.Realm;
|
||||
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
|
||||
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import static org.elasticsearch.example.role.CustomInMemoryRolesProvider.ROLE_A;
|
||||
import static org.elasticsearch.example.role.CustomInMemoryRolesProvider.ROLE_B;
|
||||
|
||||
/**
|
||||
* An example x-pack extension for testing custom realms and custom role providers.
|
||||
*/
|
||||
public class ExampleSecurityExtension implements SecurityExtension {
|
||||
|
||||
static {
|
||||
// check that the extension's policy works.
|
||||
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
|
||||
System.getSecurityManager().checkPrintJobAccess();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Realm.Factory> getRealms(ResourceWatcherService resourceWatcherService) {
|
||||
return Collections.singletonMap(CustomRealm.TYPE, CustomRealm::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthenticationFailureHandler getAuthenticationFailureHandler() {
|
||||
return new CustomAuthenticationFailureHandler();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<BiConsumer<Set<String>, ActionListener<Set<RoleDescriptor>>>>
|
||||
getRolesProviders(Settings settings, ResourceWatcherService resourceWatcherService) {
|
||||
CustomInMemoryRolesProvider rp1 = new CustomInMemoryRolesProvider(settings, Collections.singletonMap(ROLE_A, "read"));
|
||||
Map<String, String> roles = new HashMap<>();
|
||||
roles.put(ROLE_A, "all");
|
||||
roles.put(ROLE_B, "all");
|
||||
CustomInMemoryRolesProvider rp2 = new CustomInMemoryRolesProvider(settings, roles);
|
||||
return Arrays.asList(rp1, rp2);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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.example;
|
||||
|
||||
import org.elasticsearch.example.realm.CustomRealm;
|
||||
import org.elasticsearch.plugins.ActionPlugin;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An example x-pack extension for testing custom realms and custom role providers.
|
||||
*/
|
||||
public class SpiExtensionPlugin extends Plugin implements ActionPlugin {
|
||||
|
||||
@Override
|
||||
public Collection<String> getRestHeaders() {
|
||||
return Arrays.asList(CustomRealm.USER_HEADER, CustomRealm.PW_HEADER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getSettingsFilter() {
|
||||
return Collections.singletonList("xpack.security.authc.realms.*.filtered_setting");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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.example.realm;
|
||||
|
||||
import org.elasticsearch.ElasticsearchSecurityException;
|
||||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||
import org.elasticsearch.rest.RestRequest;
|
||||
import org.elasticsearch.xpack.security.authc.AuthenticationToken;
|
||||
import org.elasticsearch.xpack.security.authc.DefaultAuthenticationFailureHandler;
|
||||
import org.elasticsearch.transport.TransportMessage;
|
||||
|
||||
public class CustomAuthenticationFailureHandler extends DefaultAuthenticationFailureHandler {
|
||||
|
||||
@Override
|
||||
public ElasticsearchSecurityException failedAuthentication(RestRequest request, AuthenticationToken token,
|
||||
ThreadContext context) {
|
||||
ElasticsearchSecurityException e = super.failedAuthentication(request, token, context);
|
||||
// set a custom header
|
||||
e.addHeader("WWW-Authenticate", "custom-challenge");
|
||||
return e;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElasticsearchSecurityException failedAuthentication(TransportMessage message, AuthenticationToken token, String action,
|
||||
ThreadContext context) {
|
||||
ElasticsearchSecurityException e = super.failedAuthentication(message, token, action, context);
|
||||
// set a custom header
|
||||
e.addHeader("WWW-Authenticate", "custom-challenge");
|
||||
return e;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElasticsearchSecurityException missingToken(RestRequest request, ThreadContext context) {
|
||||
ElasticsearchSecurityException e = super.missingToken(request, context);
|
||||
// set a custom header
|
||||
e.addHeader("WWW-Authenticate", "custom-challenge");
|
||||
return e;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElasticsearchSecurityException missingToken(TransportMessage message, String action, ThreadContext context) {
|
||||
ElasticsearchSecurityException e = super.missingToken(message, action, context);
|
||||
// set a custom header
|
||||
e.addHeader("WWW-Authenticate", "custom-challenge");
|
||||
return e;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* 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.example.realm;
|
||||
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.common.settings.SecureString;
|
||||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||
import org.elasticsearch.xpack.security.authc.AuthenticationResult;
|
||||
import org.elasticsearch.xpack.security.authc.AuthenticationToken;
|
||||
import org.elasticsearch.xpack.security.authc.Realm;
|
||||
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
||||
import org.elasticsearch.xpack.security.authc.support.CharArrays;
|
||||
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
|
||||
import org.elasticsearch.xpack.security.user.User;
|
||||
|
||||
public class CustomRealm extends Realm {
|
||||
|
||||
public static final String TYPE = "custom";
|
||||
|
||||
public static final String USER_HEADER = "User";
|
||||
public static final String PW_HEADER = "Password";
|
||||
|
||||
public static final String KNOWN_USER = "custom_user";
|
||||
public static final SecureString KNOWN_PW = new SecureString("x-pack-test-password".toCharArray());
|
||||
static final String[] ROLES = new String[] { "superuser" };
|
||||
|
||||
public CustomRealm(RealmConfig config) {
|
||||
super(TYPE, config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(AuthenticationToken token) {
|
||||
return token instanceof UsernamePasswordToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UsernamePasswordToken token(ThreadContext threadContext) {
|
||||
String user = threadContext.getHeader(USER_HEADER);
|
||||
if (user != null) {
|
||||
String password = threadContext.getHeader(PW_HEADER);
|
||||
if (password != null) {
|
||||
return new UsernamePasswordToken(user, new SecureString(password.toCharArray()));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void authenticate(AuthenticationToken authToken, ActionListener<AuthenticationResult> listener) {
|
||||
UsernamePasswordToken token = (UsernamePasswordToken)authToken;
|
||||
final String actualUser = token.principal();
|
||||
if (KNOWN_USER.equals(actualUser)) {
|
||||
if (CharArrays.constantTimeEquals(token.credentials().getChars(), KNOWN_PW.getChars())) {
|
||||
listener.onResponse(AuthenticationResult.success(new User(actualUser, ROLES)));
|
||||
} else {
|
||||
listener.onResponse(AuthenticationResult.unsuccessful("Invalid password for user " + actualUser, null));
|
||||
}
|
||||
} else {
|
||||
listener.onResponse(AuthenticationResult.notHandled());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lookupUser(String username, ActionListener<User> listener) {
|
||||
listener.onResponse(null);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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.example.role;
|
||||
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.common.component.AbstractComponent;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
/**
|
||||
* A custom roles provider implementation for testing that serves
|
||||
* static roles from memory.
|
||||
*/
|
||||
public class CustomInMemoryRolesProvider
|
||||
extends AbstractComponent
|
||||
implements BiConsumer<Set<String>, ActionListener<Set<RoleDescriptor>>> {
|
||||
|
||||
public static final String INDEX = "foo";
|
||||
public static final String ROLE_A = "roleA";
|
||||
public static final String ROLE_B = "roleB";
|
||||
|
||||
private final Map<String, String> rolePermissionSettings;
|
||||
|
||||
public CustomInMemoryRolesProvider(Settings settings, Map<String, String> rolePermissionSettings) {
|
||||
super(settings);
|
||||
this.rolePermissionSettings = rolePermissionSettings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Set<String> roles, ActionListener<Set<RoleDescriptor>> listener) {
|
||||
Set<RoleDescriptor> roleDescriptors = new HashSet<>();
|
||||
for (String role : roles) {
|
||||
if (rolePermissionSettings.containsKey(role)) {
|
||||
roleDescriptors.add(
|
||||
new RoleDescriptor(role, new String[] { "all" },
|
||||
new RoleDescriptor.IndicesPrivileges[] {
|
||||
RoleDescriptor.IndicesPrivileges.builder()
|
||||
.privileges(rolePermissionSettings.get(role))
|
||||
.indices(INDEX)
|
||||
.grantedFields("*")
|
||||
.build()
|
||||
}, null)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
listener.onResponse(roleDescriptors);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
grant {
|
||||
permission java.lang.RuntimePermission "queuePrintJob";
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
org.elasticsearch.example.ExampleSecurityExtension
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* 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.example.realm;
|
||||
|
||||
import org.apache.http.message.BasicHeader;
|
||||
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
|
||||
import org.elasticsearch.action.admin.cluster.node.info.NodeInfo;
|
||||
import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse;
|
||||
import org.elasticsearch.client.Response;
|
||||
import org.elasticsearch.client.ResponseException;
|
||||
import org.elasticsearch.client.transport.NoNodeAvailableException;
|
||||
import org.elasticsearch.client.transport.TransportClient;
|
||||
import org.elasticsearch.common.network.NetworkModule;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.transport.TransportAddress;
|
||||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.test.ESIntegTestCase;
|
||||
import org.elasticsearch.xpack.XPackClientPlugin;
|
||||
import org.elasticsearch.xpack.client.PreBuiltXPackTransportClient;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
/**
|
||||
* Integration test to test authentication with the custom realm
|
||||
*/
|
||||
public class CustomRealmIT extends ESIntegTestCase {
|
||||
|
||||
@Override
|
||||
protected Settings externalClusterClientSettings() {
|
||||
return Settings.builder()
|
||||
.put(ThreadContext.PREFIX + "." + CustomRealm.USER_HEADER, CustomRealm.KNOWN_USER)
|
||||
.put(ThreadContext.PREFIX + "." + CustomRealm.PW_HEADER, CustomRealm.KNOWN_PW.toString())
|
||||
.put(NetworkModule.TRANSPORT_TYPE_KEY, "security4")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<Class<? extends Plugin>> transportClientPlugins() {
|
||||
return Collections.<Class<? extends Plugin>>singleton(XPackClientPlugin.class);
|
||||
}
|
||||
|
||||
public void testHttpConnectionWithNoAuthentication() throws Exception {
|
||||
try {
|
||||
getRestClient().performRequest("GET", "/");
|
||||
fail("request should have failed");
|
||||
} catch(ResponseException e) {
|
||||
Response response = e.getResponse();
|
||||
assertThat(response.getStatusLine().getStatusCode(), is(401));
|
||||
String value = response.getHeader("WWW-Authenticate");
|
||||
assertThat(value, is("custom-challenge"));
|
||||
}
|
||||
}
|
||||
|
||||
public void testHttpAuthentication() throws Exception {
|
||||
Response response = getRestClient().performRequest("GET", "/",
|
||||
new BasicHeader(CustomRealm.USER_HEADER, CustomRealm.KNOWN_USER),
|
||||
new BasicHeader(CustomRealm.PW_HEADER, CustomRealm.KNOWN_PW.toString()));
|
||||
assertThat(response.getStatusLine().getStatusCode(), is(200));
|
||||
}
|
||||
|
||||
public void testTransportClient() throws Exception {
|
||||
NodesInfoResponse nodeInfos = client().admin().cluster().prepareNodesInfo().get();
|
||||
List<NodeInfo> nodes = nodeInfos.getNodes();
|
||||
assertTrue(nodes.isEmpty() == false);
|
||||
TransportAddress publishAddress = randomFrom(nodes).getTransport().address().publishAddress();
|
||||
String clusterName = nodeInfos.getClusterName().value();
|
||||
|
||||
Settings settings = Settings.builder()
|
||||
.put("cluster.name", clusterName)
|
||||
.put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toAbsolutePath().toString())
|
||||
.put(ThreadContext.PREFIX + "." + CustomRealm.USER_HEADER, CustomRealm.KNOWN_USER)
|
||||
.put(ThreadContext.PREFIX + "." + CustomRealm.PW_HEADER, CustomRealm.KNOWN_PW.toString())
|
||||
.build();
|
||||
try (TransportClient client = new PreBuiltXPackTransportClient(settings)) {
|
||||
client.addTransportAddress(publishAddress);
|
||||
ClusterHealthResponse response = client.admin().cluster().prepareHealth().execute().actionGet();
|
||||
assertThat(response.isTimedOut(), is(false));
|
||||
}
|
||||
}
|
||||
|
||||
public void testTransportClientWrongAuthentication() throws Exception {
|
||||
NodesInfoResponse nodeInfos = client().admin().cluster().prepareNodesInfo().get();
|
||||
List<NodeInfo> nodes = nodeInfos.getNodes();
|
||||
assertTrue(nodes.isEmpty() == false);
|
||||
TransportAddress publishAddress = randomFrom(nodes).getTransport().address().publishAddress();
|
||||
String clusterName = nodeInfos.getClusterName().value();
|
||||
|
||||
Settings settings = Settings.builder()
|
||||
.put("cluster.name", clusterName)
|
||||
.put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toAbsolutePath().toString())
|
||||
.put(ThreadContext.PREFIX + "." + CustomRealm.USER_HEADER, CustomRealm.KNOWN_USER + randomAlphaOfLength(1))
|
||||
.put(ThreadContext.PREFIX + "." + CustomRealm.PW_HEADER, CustomRealm.KNOWN_PW.toString())
|
||||
.build();
|
||||
try (TransportClient client = new PreBuiltXPackTransportClient(settings)) {
|
||||
client.addTransportAddress(publishAddress);
|
||||
client.admin().cluster().prepareHealth().execute().actionGet();
|
||||
fail("authentication failure should have resulted in a NoNodesAvailableException");
|
||||
} catch (NoNodeAvailableException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
public void testSettingsFiltering() throws Exception {
|
||||
NodesInfoResponse nodeInfos = client().admin().cluster().prepareNodesInfo().clear().setSettings(true).get();
|
||||
for(NodeInfo info : nodeInfos.getNodes()) {
|
||||
Settings settings = info.getSettings();
|
||||
assertNotNull(settings);
|
||||
assertNull(settings.get("xpack.security.authc.realms.custom.filtered_setting"));
|
||||
assertEquals(CustomRealm.TYPE, settings.get("xpack.security.authc.realms.custom.type"));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.example.realm;
|
||||
|
||||
import org.elasticsearch.action.support.PlainActionFuture;
|
||||
import org.elasticsearch.common.settings.SecureString;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||
import org.elasticsearch.env.TestEnvironment;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.xpack.security.authc.AuthenticationResult;
|
||||
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
||||
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
|
||||
import org.elasticsearch.xpack.security.user.User;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
|
||||
public class CustomRealmTests extends ESTestCase {
|
||||
public void testAuthenticate() {
|
||||
Settings globalSettings = Settings.builder().put("path.home", createTempDir()).build();
|
||||
CustomRealm realm = new CustomRealm(new RealmConfig("test", Settings.EMPTY, globalSettings,
|
||||
TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings)));
|
||||
SecureString password = CustomRealm.KNOWN_PW.clone();
|
||||
UsernamePasswordToken token = new UsernamePasswordToken(CustomRealm.KNOWN_USER, password);
|
||||
PlainActionFuture<AuthenticationResult> plainActionFuture = new PlainActionFuture<>();
|
||||
realm.authenticate(token, plainActionFuture);
|
||||
User user = plainActionFuture.actionGet().getUser();
|
||||
assertThat(user, notNullValue());
|
||||
assertThat(user.roles(), equalTo(CustomRealm.ROLES));
|
||||
assertThat(user.principal(), equalTo(CustomRealm.KNOWN_USER));
|
||||
}
|
||||
|
||||
public void testAuthenticateBadUser() {
|
||||
Settings globalSettings = Settings.builder().put("path.home", createTempDir()).build();
|
||||
CustomRealm realm = new CustomRealm(new RealmConfig("test", Settings.EMPTY, globalSettings,
|
||||
TestEnvironment.newEnvironment(globalSettings), new ThreadContext(globalSettings)));
|
||||
SecureString password = CustomRealm.KNOWN_PW.clone();
|
||||
UsernamePasswordToken token = new UsernamePasswordToken(CustomRealm.KNOWN_USER + "1", password);
|
||||
PlainActionFuture<AuthenticationResult> plainActionFuture = new PlainActionFuture<>();
|
||||
realm.authenticate(token, plainActionFuture);
|
||||
final AuthenticationResult result = plainActionFuture.actionGet();
|
||||
assertThat(result.getStatus(), equalTo(AuthenticationResult.Status.CONTINUE));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* 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.example.role;
|
||||
|
||||
import org.apache.http.message.BasicHeader;
|
||||
import org.elasticsearch.client.Response;
|
||||
import org.elasticsearch.client.ResponseException;
|
||||
import org.elasticsearch.common.network.NetworkModule;
|
||||
import org.elasticsearch.common.settings.SecureString;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||
import org.elasticsearch.example.realm.CustomRealm;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.test.ESIntegTestCase;
|
||||
import org.elasticsearch.xpack.XPackClientPlugin;
|
||||
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
|
||||
import org.elasticsearch.xpack.security.client.SecurityClient;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.elasticsearch.example.role.CustomInMemoryRolesProvider.INDEX;
|
||||
import static org.elasticsearch.example.role.CustomInMemoryRolesProvider.ROLE_A;
|
||||
import static org.elasticsearch.example.role.CustomInMemoryRolesProvider.ROLE_B;
|
||||
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
/**
|
||||
* Integration test for custom roles providers.
|
||||
*/
|
||||
public class CustomRolesProviderIT extends ESIntegTestCase {
|
||||
|
||||
private static final String TEST_USER = "test_user";
|
||||
private static final String TEST_PWD = "change_me";
|
||||
|
||||
@Override
|
||||
protected Settings externalClusterClientSettings() {
|
||||
return Settings.builder()
|
||||
.put(ThreadContext.PREFIX + "." + CustomRealm.USER_HEADER, CustomRealm.KNOWN_USER)
|
||||
.put(ThreadContext.PREFIX + "." + CustomRealm.PW_HEADER, CustomRealm.KNOWN_PW.toString())
|
||||
.put(NetworkModule.TRANSPORT_TYPE_KEY, "security4")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<Class<? extends Plugin>> transportClientPlugins() {
|
||||
return Collections.singleton(XPackClientPlugin.class);
|
||||
}
|
||||
|
||||
public void setupTestUser(String role) {
|
||||
SecurityClient securityClient = new SecurityClient(client());
|
||||
securityClient.preparePutUser(TEST_USER, TEST_PWD.toCharArray(), role).get();
|
||||
}
|
||||
|
||||
public void testAuthorizedCustomRoleSucceeds() throws Exception {
|
||||
setupTestUser(ROLE_B);
|
||||
// roleB has all permissions on index "foo", so creating "foo" should succeed
|
||||
Response response = getRestClient().performRequest("PUT", "/" + INDEX, authHeader());
|
||||
assertThat(response.getStatusLine().getStatusCode(), is(200));
|
||||
}
|
||||
|
||||
public void testFirstResolvedRoleTakesPrecedence() throws Exception {
|
||||
// the first custom roles provider has set ROLE_A to only have read permission on the index,
|
||||
// the second custom roles provider has set ROLE_A to have all permissions, but since
|
||||
// the first custom role provider appears first in order, it should take precedence and deny
|
||||
// permission to create the index
|
||||
setupTestUser(ROLE_A);
|
||||
// roleB has all permissions on index "foo", so creating "foo" should succeed
|
||||
try {
|
||||
getRestClient().performRequest("PUT", "/" + INDEX, authHeader());
|
||||
fail(ROLE_A + " should not be authorized to create index " + INDEX);
|
||||
} catch (ResponseException e) {
|
||||
assertThat(e.getResponse().getStatusLine().getStatusCode(), is(403));
|
||||
}
|
||||
}
|
||||
|
||||
public void testUnresolvedRoleDoesntSucceed() throws Exception {
|
||||
setupTestUser("unknown");
|
||||
// roleB has all permissions on index "foo", so creating "foo" should succeed
|
||||
try {
|
||||
getRestClient().performRequest("PUT", "/" + INDEX, authHeader());
|
||||
fail(ROLE_A + " should not be authorized to create index " + INDEX);
|
||||
} catch (ResponseException e) {
|
||||
assertThat(e.getResponse().getStatusLine().getStatusCode(), is(403));
|
||||
}
|
||||
}
|
||||
|
||||
private BasicHeader authHeader() {
|
||||
return new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
|
||||
basicAuthHeaderValue(TEST_USER, new SecureString(TEST_PWD.toCharArray())));
|
||||
}
|
||||
}
|
|
@ -34,3 +34,77 @@
|
|||
|
||||
- is_true: error.script_stack
|
||||
- match: { status: 500 }
|
||||
|
||||
---
|
||||
"Test painless exceptions are returned when logging a broken response":
|
||||
- do:
|
||||
cluster.health:
|
||||
wait_for_status: green
|
||||
|
||||
- do:
|
||||
xpack.watcher.execute_watch:
|
||||
body: >
|
||||
{
|
||||
"watch" : {
|
||||
"trigger": {
|
||||
"schedule": {
|
||||
"interval": "1d"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"simple": {
|
||||
"foo": "bar"
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
"my-logging": {
|
||||
"transform": {
|
||||
"script": {
|
||||
"source": "def x = [:] ; def y = [:] ; x.a = y ; y.a = x ; return x"
|
||||
}
|
||||
},
|
||||
"logging": {
|
||||
"text": "{{ctx}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- match: { watch_record.watch_id: "_inlined_" }
|
||||
- match: { watch_record.trigger_event.type: "manual" }
|
||||
- match: { watch_record.state: "executed" }
|
||||
- match: { watch_record.result.actions.0.status: "failure" }
|
||||
- match: { watch_record.result.actions.0.error.caused_by.caused_by.type: "illegal_argument_exception" }
|
||||
- match: { watch_record.result.actions.0.error.caused_by.caused_by.reason: "Iterable object is self-referencing itself" }
|
||||
|
||||
- do:
|
||||
catch: bad_request
|
||||
xpack.watcher.execute_watch:
|
||||
body: >
|
||||
{
|
||||
"watch": {
|
||||
"trigger": {
|
||||
"schedule": {
|
||||
"interval": "10s"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"simple": {
|
||||
"foo": "bar"
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
"my-logging": {
|
||||
"transform": {
|
||||
"script": {
|
||||
"source": "def x = [:] ; def y = [:] ; x.a = y ; y.a = x ; return x"
|
||||
}
|
||||
},
|
||||
"logging": {
|
||||
"text": "{{#join}}ctx.payload{{/join}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue