diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/SecurityFeatureSetUsage.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/SecurityFeatureSetUsage.java index a06eacefcf81f..618414426f085 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/SecurityFeatureSetUsage.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/SecurityFeatureSetUsage.java @@ -107,6 +107,10 @@ protected void innerXContent(XContentBuilder builder, Params params) throws IOEx builder.field(AUDIT_XFIELD, auditUsage); builder.field(IP_FILTER_XFIELD, ipFilterUsage); builder.field(ANONYMOUS_XFIELD, anonymousUsage); + } else if (sslUsage.isEmpty() == false) { + // A trial (or basic) license can have SSL without security. + // This is because security defaults to disabled on that license, but that dynamic-default does not disable SSL. + builder.field(SSL_XFIELD, sslUsage); } } diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/SecurityFeatureSet.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/SecurityFeatureSet.java index 2e5832d0834e7..ce7e54a3cb29d 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/SecurityFeatureSet.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/SecurityFeatureSet.java @@ -150,10 +150,18 @@ public void usage(ActionListener listener) { } static Map sslUsage(Settings settings) { - Map map = new HashMap<>(2); - map.put("http", singletonMap("enabled", HTTP_SSL_ENABLED.get(settings))); - map.put("transport", singletonMap("enabled", TRANSPORT_SSL_ENABLED.get(settings))); - return map; + // If security has been explicitly disabled in the settings, then SSL is also explicitly disabled, and we don't want to report + // these http/transport settings as they would be misleading (they could report `true` even though they were ignored) + // But, if security has not been explicitly configured, but has defaulted to off due to the current license type, + // then these SSL settings are still respected (that is SSL might be enabled, while the rest of security is disabled). + if (XPackSettings.SECURITY_ENABLED.get(settings)) { + Map map = new HashMap<>(2); + map.put("http", singletonMap("enabled", HTTP_SSL_ENABLED.get(settings))); + map.put("transport", singletonMap("enabled", TRANSPORT_SSL_ENABLED.get(settings))); + return map; + } else { + return Collections.emptyMap(); + } } static Map tokenServiceUsage(Settings settings) { diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/SecurityFeatureSetTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/SecurityFeatureSetTests.java index 146dc78698eca..2fc2ea8865d91 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/SecurityFeatureSetTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/SecurityFeatureSetTests.java @@ -28,6 +28,7 @@ import org.elasticsearch.xpack.security.transport.filter.IPFilter; import org.junit.Before; +import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -126,29 +127,10 @@ public void testUsage() throws Exception { final boolean rolesStoreEnabled = randomBoolean(); - doAnswer(invocationOnMock -> { - ActionListener> listener = (ActionListener>) invocationOnMock.getArguments()[0]; - if (rolesStoreEnabled) { - listener.onResponse(Collections.singletonMap("count", 1)); - } else { - listener.onResponse(Collections.emptyMap()); - } - return Void.TYPE; - }).when(rolesStore).usageStats(any(ActionListener.class)); + configureRoleStoreUsage(rolesStoreEnabled); final boolean roleMappingStoreEnabled = randomBoolean(); - doAnswer(invocationOnMock -> { - ActionListener> listener = (ActionListener) invocationOnMock.getArguments()[0]; - if (roleMappingStoreEnabled) { - final Map map = new HashMap<>(); - map.put("size", 12L); - map.put("enabled", 10L); - listener.onResponse(map); - } else { - listener.onResponse(Collections.emptyMap()); - } - return Void.TYPE; - }).when(roleMappingStore).usageStats(any(ActionListener.class)); + configureRoleMappingStoreUsage(roleMappingStoreEnabled); Map realmsUsageStats = new HashMap<>(); for (int i = 0; i < 5; i++) { @@ -158,11 +140,7 @@ public void testUsage() throws Exception { realmUsage.put("key2", Arrays.asList(i)); realmUsage.put("key3", Arrays.asList(i % 2 == 0)); } - doAnswer(invocationOnMock -> { - ActionListener> listener = (ActionListener) invocationOnMock.getArguments()[0]; - listener.onResponse(realmsUsageStats); - return Void.TYPE; - }).when(realms).usageStats(any(ActionListener.class)); + configureRealmsUsage(realmsUsageStats); final boolean anonymousEnabled = randomBoolean(); if (anonymousEnabled) { @@ -182,11 +160,7 @@ public void testUsage() throws Exception { assertThat(usage.name(), is(XPackField.SECURITY)); assertThat(usage.enabled(), is(enabled)); assertThat(usage.available(), is(authcAuthzAvailable)); - XContentSource source; - try (XContentBuilder builder = XContentFactory.jsonBuilder()) { - usage.toXContent(builder, ToXContent.EMPTY_PARAMS); - source = new XContentSource(builder); - } + XContentSource source = getXContentSource(usage); if (enabled) { if (authcAuthzAvailable) { @@ -251,4 +225,101 @@ public void testUsage() throws Exception { } } } + + public void testUsageOnTrialLicenseWithSecurityDisabledByDefault() throws Exception { + when(licenseState.isSecurityAvailable()).thenReturn(true); + when(licenseState.isSecurityDisabledByTrialLicense()).thenReturn(true); + + Settings.Builder settings = Settings.builder().put(this.settings); + + final boolean httpSSLEnabled = randomBoolean(); + settings.put("xpack.security.http.ssl.enabled", httpSSLEnabled); + final boolean transportSSLEnabled = randomBoolean(); + settings.put("xpack.security.transport.ssl.enabled", transportSSLEnabled); + + final boolean auditingEnabled = randomBoolean(); + settings.put(XPackSettings.AUDIT_ENABLED.getKey(), auditingEnabled); + + final boolean rolesStoreEnabled = randomBoolean(); + configureRoleStoreUsage(rolesStoreEnabled); + + final boolean roleMappingStoreEnabled = randomBoolean(); + configureRoleMappingStoreUsage(roleMappingStoreEnabled); + + configureRealmsUsage(Collections.emptyMap()); + + SecurityFeatureSet featureSet = new SecurityFeatureSet(settings.build(), licenseState, + realms, rolesStore, roleMappingStore, ipFilter); + PlainActionFuture future = new PlainActionFuture<>(); + featureSet.usage(future); + XPackFeatureSet.Usage securityUsage = future.get(); + BytesStreamOutput out = new BytesStreamOutput(); + securityUsage.writeTo(out); + XPackFeatureSet.Usage serializedUsage = new SecurityFeatureSetUsage(out.bytes().streamInput()); + for (XPackFeatureSet.Usage usage : Arrays.asList(securityUsage, serializedUsage)) { + assertThat(usage, is(notNullValue())); + assertThat(usage.name(), is(XPackField.SECURITY)); + assertThat(usage.enabled(), is(false)); + assertThat(usage.available(), is(true)); + XContentSource source = getXContentSource(usage); + + // check SSL : This is permitted even though security has been dynamically disabled by the trial license. + assertThat(source.getValue("ssl"), is(notNullValue())); + assertThat(source.getValue("ssl.http.enabled"), is(httpSSLEnabled)); + assertThat(source.getValue("ssl.transport.enabled"), is(transportSSLEnabled)); + + // everything else is missing because security is disabled + assertThat(source.getValue("realms"), is(nullValue())); + assertThat(source.getValue("token_service"), is(nullValue())); + assertThat(source.getValue("api_key_service"), is(nullValue())); + assertThat(source.getValue("audit"), is(nullValue())); + assertThat(source.getValue("anonymous"), is(nullValue())); + assertThat(source.getValue("ipfilter"), is(nullValue())); + assertThat(source.getValue("roles"), is(nullValue())); + } + } + + private XContentSource getXContentSource(XPackFeatureSet.Usage usage) throws IOException { + XContentSource source; + try (XContentBuilder builder = XContentFactory.jsonBuilder()) { + usage.toXContent(builder, ToXContent.EMPTY_PARAMS); + source = new XContentSource(builder); + } + return source; + } + + private void configureRealmsUsage(Map realmsUsageStats) { + doAnswer(invocationOnMock -> { + ActionListener> listener = (ActionListener) invocationOnMock.getArguments()[0]; + listener.onResponse(realmsUsageStats); + return Void.TYPE; + }).when(realms).usageStats(any(ActionListener.class)); + } + + private void configureRoleStoreUsage(boolean rolesStoreEnabled) { + doAnswer(invocationOnMock -> { + ActionListener> listener = (ActionListener>) invocationOnMock.getArguments()[0]; + if (rolesStoreEnabled) { + listener.onResponse(Collections.singletonMap("count", 1)); + } else { + listener.onResponse(Collections.emptyMap()); + } + return Void.TYPE; + }).when(rolesStore).usageStats(any(ActionListener.class)); + } + + private void configureRoleMappingStoreUsage(boolean roleMappingStoreEnabled) { + doAnswer(invocationOnMock -> { + ActionListener> listener = (ActionListener) invocationOnMock.getArguments()[0]; + if (roleMappingStoreEnabled) { + final Map map = new HashMap<>(); + map.put("size", 12L); + map.put("enabled", 10L); + listener.onResponse(map); + } else { + listener.onResponse(Collections.emptyMap()); + } + return Void.TYPE; + }).when(roleMappingStore).usageStats(any(ActionListener.class)); + } }