From 80cee28870872a4d1a5974a2157a243a0b48b785 Mon Sep 17 00:00:00 2001 From: "opensearch-trigger-bot[bot]" <98922864+opensearch-trigger-bot[bot]@users.noreply.github.com> Date: Tue, 26 Mar 2024 06:11:20 -0500 Subject: [PATCH] [Backport 2.x] Dynamic sign in options (#4137) Backport 25e2e51edecdd1a436223a2bae5fab8e975d2069 from #3869. --------- Signed-off-by: David Osorno Signed-off-by: github-actions[bot] Signed-off-by: Craig Perkins Co-authored-by: github-actions[bot] Co-authored-by: Craig Perkins --- .../security/api/DashboardsInfoTest.java | 12 ++++ .../rest/api/MultiTenancyConfigApiAction.java | 31 +++++++++- .../privileges/PrivilegesEvaluator.java | 5 ++ .../security/rest/DashboardsInfoAction.java | 1 + .../securityconf/DynamicConfigModel.java | 3 + .../securityconf/DynamicConfigModelV6.java | 6 ++ .../securityconf/DynamicConfigModelV7.java | 6 ++ .../impl/DashboardSignInOption.java | 29 +++++++++ .../securityconf/impl/v6/ConfigV6.java | 7 +++ .../securityconf/impl/v7/ConfigV7.java | 8 +++ .../rest/api/MultiTenancyConfigApiTest.java | 61 +++++++++++++++++++ .../securityconf/impl/v6/ConfigV6Test.java | 8 +++ .../securityconf/impl/v7/ConfigV7Test.java | 8 +++ .../security/test/helper/rest/RestHelper.java | 41 ++++++++++--- src/test/resources/restapi/config.yml | 12 ++++ .../restapi/securityconfig_nondefault.json | 3 +- 16 files changed, 232 insertions(+), 9 deletions(-) create mode 100644 src/main/java/org/opensearch/security/securityconf/impl/DashboardSignInOption.java diff --git a/src/integrationTest/java/org/opensearch/security/api/DashboardsInfoTest.java b/src/integrationTest/java/org/opensearch/security/api/DashboardsInfoTest.java index 8bfcd3b8a8..15634e8890 100644 --- a/src/integrationTest/java/org/opensearch/security/api/DashboardsInfoTest.java +++ b/src/integrationTest/java/org/opensearch/security/api/DashboardsInfoTest.java @@ -17,6 +17,7 @@ import org.junit.Test; import org.junit.runner.RunWith; +import org.opensearch.security.securityconf.impl.DashboardSignInOption; import org.opensearch.test.framework.TestSecurityConfig; import org.opensearch.test.framework.TestSecurityConfig.Role; import org.opensearch.test.framework.cluster.ClusterManager; @@ -25,6 +26,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; import static org.opensearch.security.rest.DashboardsInfoAction.DEFAULT_PASSWORD_MESSAGE; import static org.opensearch.security.rest.DashboardsInfoAction.DEFAULT_PASSWORD_REGEX; import static org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.AUTHC_HTTPBASIC_INTERNAL; @@ -53,4 +55,14 @@ public void testDashboardsInfoValidationMessage() throws Exception { assertThat(response.getTextFromJsonBody("/password_validation_regex"), equalTo(DEFAULT_PASSWORD_REGEX)); } } + + @Test + public void testDashboardsInfoContainsSignInOptions() throws Exception { + + try (TestRestClient client = cluster.getRestClient(DASHBOARDS_USER)) { + TestRestClient.HttpResponse response = client.get("_plugins/_security/dashboardsinfo"); + assertThat(response.getStatusCode(), equalTo(HttpStatus.SC_OK)); + assertThat(response.getTextArrayFromJsonBody("/sign_in_options").contains(DashboardSignInOption.BASIC.toString()), is(true)); + } + } } diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/MultiTenancyConfigApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/MultiTenancyConfigApiAction.java index d56025aec1..2be5778956 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/MultiTenancyConfigApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/MultiTenancyConfigApiAction.java @@ -18,6 +18,7 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.IntStream; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -33,8 +34,10 @@ import org.opensearch.security.dlic.rest.validation.RequestContentValidator; import org.opensearch.security.dlic.rest.validation.RequestContentValidator.DataType; import org.opensearch.security.securityconf.impl.CType; +import org.opensearch.security.securityconf.impl.DashboardSignInOption; import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; import org.opensearch.security.securityconf.impl.v7.ConfigV7; +import org.opensearch.security.securityconf.impl.v7.ConfigV7.Authc; import org.opensearch.security.support.ConfigConstants; import org.opensearch.threadpool.ThreadPool; @@ -49,6 +52,7 @@ public class MultiTenancyConfigApiAction extends AbstractApiAction { public static final String DEFAULT_TENANT_JSON_PROPERTY = "default_tenant"; public static final String PRIVATE_TENANT_ENABLED_JSON_PROPERTY = "private_tenant_enabled"; public static final String MULTITENANCY_ENABLED_JSON_PROPERTY = "multitenancy_enabled"; + public static final String SIGN_IN_OPTIONS = "sign_in_options"; private static final List ROUTES = addRoutesPrefix( ImmutableList.of(new Route(GET, "/tenancy/config"), new Route(PUT, "/tenancy/config")) @@ -119,7 +123,9 @@ public Map allowedKeys() { PRIVATE_TENANT_ENABLED_JSON_PROPERTY, DataType.BOOLEAN, MULTITENANCY_ENABLED_JSON_PROPERTY, - DataType.BOOLEAN + DataType.BOOLEAN, + SIGN_IN_OPTIONS, + DataType.ARRAY ); } }); @@ -132,6 +138,7 @@ private ToXContent multitenancyContent(final ConfigV7 config) { .field(DEFAULT_TENANT_JSON_PROPERTY, config.dynamic.kibana.default_tenant) .field(PRIVATE_TENANT_ENABLED_JSON_PROPERTY, config.dynamic.kibana.private_tenant_enabled) .field(MULTITENANCY_ENABLED_JSON_PROPERTY, config.dynamic.kibana.multitenancy_enabled) + .field(SIGN_IN_OPTIONS, config.dynamic.kibana.sign_in_options) .endObject(); } @@ -177,6 +184,12 @@ private void updateAndValidatesValues(final ConfigV7 config, final JsonNode json if (Objects.nonNull(jsonContent.findValue(MULTITENANCY_ENABLED_JSON_PROPERTY))) { config.dynamic.kibana.multitenancy_enabled = jsonContent.findValue(MULTITENANCY_ENABLED_JSON_PROPERTY).asBoolean(); } + if (jsonContent.hasNonNull(SIGN_IN_OPTIONS) && jsonContent.findValue(SIGN_IN_OPTIONS).isEmpty() == false) { + JsonNode newOptions = jsonContent.findValue(SIGN_IN_OPTIONS); + List options = getNewSignInOptions(newOptions, config.dynamic.authc); + config.dynamic.kibana.sign_in_options = options; + } + final String defaultTenant = Optional.ofNullable(config.dynamic.kibana.default_tenant).map(String::toLowerCase).orElse(""); if (!config.dynamic.kibana.private_tenant_enabled && ConfigConstants.TENANCY_PRIVATE_TENANT_NAME.equals(defaultTenant)) { @@ -202,4 +215,20 @@ private void updateAndValidatesValues(final ConfigV7 config, final JsonNode json } } + private List getNewSignInOptions(JsonNode newOptions, Authc authc) { + + Set domains = authc.getDomains().keySet(); + + return IntStream.range(0, newOptions.size()).mapToObj(newOptions::get).map(JsonNode::asText).filter(option -> { + // Checking if the new sign-in options are set in backend. + if (option.equals(DashboardSignInOption.ANONYMOUS.toString()) + || domains.stream().anyMatch(domain -> domain.contains(option.toLowerCase()))) { + return true; + } else { + throw new IllegalArgumentException( + "Validation failure: " + option.toUpperCase() + " authentication provider is not available for this cluster." + ); + } + }).map(DashboardSignInOption::valueOf).collect(Collectors.toList()); + } } diff --git a/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java b/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java index 178bd97735..b669137d8e 100644 --- a/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java @@ -90,6 +90,7 @@ import org.opensearch.security.securityconf.ConfigModel; import org.opensearch.security.securityconf.DynamicConfigModel; import org.opensearch.security.securityconf.SecurityRoles; +import org.opensearch.security.securityconf.impl.DashboardSignInOption; import org.opensearch.security.support.ConfigConstants; import org.opensearch.security.support.WildcardMatcher; import org.opensearch.security.user.User; @@ -621,6 +622,10 @@ public String dashboardsOpenSearchRole() { return dcm.getDashboardsOpenSearchRole(); } + public List getSignInOptions() { + return dcm.getSignInOptions(); + } + private Set evaluateAdditionalIndexPermissions(final ActionRequest request, final String originalAction) { // --- check inner bulk requests final Set additionalPermissionsRequired = new HashSet<>(); diff --git a/src/main/java/org/opensearch/security/rest/DashboardsInfoAction.java b/src/main/java/org/opensearch/security/rest/DashboardsInfoAction.java index 2b286d0c3d..070648ed92 100644 --- a/src/main/java/org/opensearch/security/rest/DashboardsInfoAction.java +++ b/src/main/java/org/opensearch/security/rest/DashboardsInfoAction.java @@ -108,6 +108,7 @@ public void accept(RestChannel channel) throws Exception { builder.field("multitenancy_enabled", evaluator.multitenancyEnabled()); builder.field("private_tenant_enabled", evaluator.privateTenantEnabled()); builder.field("default_tenant", evaluator.dashboardsDefaultTenant()); + builder.field("sign_in_options", evaluator.getSignInOptions()); builder.field( "password_validation_error_message", client.settings().get(ConfigConstants.SECURITY_RESTAPI_PASSWORD_VALIDATION_ERROR_MESSAGE, DEFAULT_PASSWORD_MESSAGE) diff --git a/src/main/java/org/opensearch/security/securityconf/DynamicConfigModel.java b/src/main/java/org/opensearch/security/securityconf/DynamicConfigModel.java index e3d10878da..064f555a75 100644 --- a/src/main/java/org/opensearch/security/securityconf/DynamicConfigModel.java +++ b/src/main/java/org/opensearch/security/securityconf/DynamicConfigModel.java @@ -52,6 +52,7 @@ import org.opensearch.security.http.HTTPClientCertAuthenticator; import org.opensearch.security.http.HTTPProxyAuthenticator; import org.opensearch.security.http.proxy.HTTPExtendedProxyAuthenticator; +import org.opensearch.security.securityconf.impl.DashboardSignInOption; public abstract class DynamicConfigModel { @@ -105,6 +106,8 @@ public abstract class DynamicConfigModel { public abstract Multimap> getAuthBackendClientBlockRegistries(); + public abstract List getSignInOptions(); + public abstract Settings getDynamicOnBehalfOfSettings(); protected final Map authImplMap = new HashMap<>(); diff --git a/src/main/java/org/opensearch/security/securityconf/DynamicConfigModelV6.java b/src/main/java/org/opensearch/security/securityconf/DynamicConfigModelV6.java index b652893bdd..c7edaf938c 100644 --- a/src/main/java/org/opensearch/security/securityconf/DynamicConfigModelV6.java +++ b/src/main/java/org/opensearch/security/securityconf/DynamicConfigModelV6.java @@ -54,6 +54,7 @@ import org.opensearch.security.auth.HTTPAuthenticator; import org.opensearch.security.auth.blocking.ClientBlockRegistry; import org.opensearch.security.auth.internal.InternalAuthenticationBackend; +import org.opensearch.security.securityconf.impl.DashboardSignInOption; import org.opensearch.security.securityconf.impl.v6.ConfigV6; import org.opensearch.security.securityconf.impl.v6.ConfigV6.Authc; import org.opensearch.security.securityconf.impl.v6.ConfigV6.AuthcDomain; @@ -205,6 +206,11 @@ public Multimap> getAuthBackendClientBlockRe return Multimaps.unmodifiableMultimap(authBackendClientBlockRegistries); } + @Override + public List getSignInOptions() { + return config.dynamic.kibana.sign_in_options; + } + @Override public Settings getDynamicOnBehalfOfSettings() { return Settings.EMPTY; diff --git a/src/main/java/org/opensearch/security/securityconf/DynamicConfigModelV7.java b/src/main/java/org/opensearch/security/securityconf/DynamicConfigModelV7.java index 91bb59db64..ca237bc054 100644 --- a/src/main/java/org/opensearch/security/securityconf/DynamicConfigModelV7.java +++ b/src/main/java/org/opensearch/security/securityconf/DynamicConfigModelV7.java @@ -60,6 +60,7 @@ import org.opensearch.security.auth.internal.NoOpAuthenticationBackend; import org.opensearch.security.configuration.ClusterInfoHolder; import org.opensearch.security.http.OnBehalfOfAuthenticator; +import org.opensearch.security.securityconf.impl.DashboardSignInOption; import org.opensearch.security.securityconf.impl.v7.ConfigV7; import org.opensearch.security.securityconf.impl.v7.ConfigV7.Authc; import org.opensearch.security.securityconf.impl.v7.ConfigV7.AuthcDomain; @@ -221,6 +222,11 @@ public Multimap> getAuthBackendClientBlockRe return Multimaps.unmodifiableMultimap(authBackendClientBlockRegistries); } + @Override + public List getSignInOptions() { + return config.dynamic.kibana.sign_in_options; + } + @Override public Settings getDynamicOnBehalfOfSettings() { return Settings.builder() diff --git a/src/main/java/org/opensearch/security/securityconf/impl/DashboardSignInOption.java b/src/main/java/org/opensearch/security/securityconf/impl/DashboardSignInOption.java new file mode 100644 index 0000000000..3d9fa20e97 --- /dev/null +++ b/src/main/java/org/opensearch/security/securityconf/impl/DashboardSignInOption.java @@ -0,0 +1,29 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.security.securityconf.impl; + +public enum DashboardSignInOption { + BASIC("basic"), + SAML("saml"), + OPENID("openid"), + ANONYMOUS("anonymous"); + + private String option; + + DashboardSignInOption(String option) { + this.option = option; + } + + public String getOption() { + return option; + } +} diff --git a/src/main/java/org/opensearch/security/securityconf/impl/v6/ConfigV6.java b/src/main/java/org/opensearch/security/securityconf/impl/v6/ConfigV6.java index c5b954675b..78758e0603 100644 --- a/src/main/java/org/opensearch/security/securityconf/impl/v6/ConfigV6.java +++ b/src/main/java/org/opensearch/security/securityconf/impl/v6/ConfigV6.java @@ -27,8 +27,10 @@ package org.opensearch.security.securityconf.impl.v6; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.regex.Pattern; @@ -43,6 +45,7 @@ import org.opensearch.security.DefaultObjectMapper; import org.opensearch.security.auth.internal.InternalAuthenticationBackend; +import org.opensearch.security.securityconf.impl.DashboardSignInOption; import org.opensearch.security.setting.DeprecatedSettings; public class ConfigV6 { @@ -100,6 +103,8 @@ public static class Kibana { public String opendistro_role = null; public String index = ".kibana"; public boolean do_not_fail_on_forbidden; + @JsonInclude(JsonInclude.Include.NON_NULL) + public List sign_in_options = Arrays.asList(DashboardSignInOption.BASIC); @Override public String toString() { @@ -113,6 +118,8 @@ public String toString() { + index + ", do_not_fail_on_forbidden=" + do_not_fail_on_forbidden + + ", sign_in_options=" + + sign_in_options + "]"; } diff --git a/src/main/java/org/opensearch/security/securityconf/impl/v7/ConfigV7.java b/src/main/java/org/opensearch/security/securityconf/impl/v7/ConfigV7.java index 4028719379..dc9be395b1 100644 --- a/src/main/java/org/opensearch/security/securityconf/impl/v7/ConfigV7.java +++ b/src/main/java/org/opensearch/security/securityconf/impl/v7/ConfigV7.java @@ -27,8 +27,10 @@ package org.opensearch.security.securityconf.impl.v7; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -44,6 +46,7 @@ import org.opensearch.security.DefaultObjectMapper; import org.opensearch.security.auth.internal.InternalAuthenticationBackend; +import org.opensearch.security.securityconf.impl.DashboardSignInOption; import org.opensearch.security.securityconf.impl.v6.ConfigV6; import org.opensearch.security.setting.DeprecatedSettings; @@ -76,6 +79,7 @@ public ConfigV7(ConfigV6 c6) { dynamic.kibana.private_tenant_enabled = true; dynamic.kibana.default_tenant = ""; dynamic.kibana.server_username = c6.dynamic.kibana.server_username; + dynamic.kibana.sign_in_options = c6.dynamic.kibana.sign_in_options; dynamic.http = new Http(); @@ -168,6 +172,8 @@ public static class Kibana { public String server_username = "kibanaserver"; public String opendistro_role = null; public String index = ".kibana"; + @JsonInclude(JsonInclude.Include.NON_NULL) + public List sign_in_options = Arrays.asList(DashboardSignInOption.BASIC); @Override public String toString() { @@ -183,6 +189,8 @@ public String toString() { + opendistro_role + ", index=" + index + + ", sign_in_options=" + + sign_in_options + "]"; } diff --git a/src/test/java/org/opensearch/security/dlic/rest/api/MultiTenancyConfigApiTest.java b/src/test/java/org/opensearch/security/dlic/rest/api/MultiTenancyConfigApiTest.java index 8d355b4551..f9a597df0c 100644 --- a/src/test/java/org/opensearch/security/dlic/rest/api/MultiTenancyConfigApiTest.java +++ b/src/test/java/org/opensearch/security/dlic/rest/api/MultiTenancyConfigApiTest.java @@ -15,10 +15,13 @@ import org.apache.http.HttpStatus; import org.junit.Test; +import org.opensearch.security.securityconf.impl.DashboardSignInOption; import org.opensearch.security.support.ConfigConstants; import org.opensearch.security.test.helper.rest.RestHelper.HttpResponse; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.not; import static org.hamcrest.core.IsEqual.equalTo; import static org.hamcrest.core.StringContains.containsString; @@ -54,9 +57,43 @@ private void verifyTenantUpdate(final Header... header) throws Exception { setPrivateTenantAsDefaultResponse.getStatusCode(), equalTo(HttpStatus.SC_OK) ); + assertThat(getDashboardsinfoResponse.findArrayInJson("sign_in_options"), hasItem(DashboardSignInOption.BASIC.toString())); + assertThat(getDashboardsinfoResponse.findArrayInJson("sign_in_options"), not(hasItem(DashboardSignInOption.SAML.toString()))); + assertThat(getDashboardsinfoResponse.findArrayInJson("sign_in_options"), not(hasItem(DashboardSignInOption.OPENID.toString()))); + + final HttpResponse updateDashboardSignInOptions = rh.executePutRequest( + "/_plugins/_security/api/tenancy/config", + "{\"sign_in_options\": [\"BASIC\", \"OPENID\"]}", + header + ); + assertThat(updateDashboardSignInOptions.getBody(), updateDashboardSignInOptions.getStatusCode(), equalTo(HttpStatus.SC_OK)); + getDashboardsinfoResponse = rh.executeGetRequest("/_plugins/_security/dashboardsinfo", ADMIN_FULL_ACCESS_USER); assertThat(getDashboardsinfoResponse.getStatusCode(), equalTo(HttpStatus.SC_OK)); assertThat(getDashboardsinfoResponse.findValueInJson("default_tenant"), equalTo("Private")); + + assertThat(getDashboardsinfoResponse.findArrayInJson("sign_in_options"), hasItem((DashboardSignInOption.BASIC.toString()))); + assertThat(getDashboardsinfoResponse.findArrayInJson("sign_in_options"), hasItem((DashboardSignInOption.OPENID.toString()))); + + final HttpResponse updateUnavailableSignInOption = rh.executePutRequest( + "/_plugins/_security/api/tenancy/config", + "{\"sign_in_options\": [\"BASIC\", \"SAML\"]}", + header + ); + assertThat(updateUnavailableSignInOption.getStatusCode(), equalTo(HttpStatus.SC_BAD_REQUEST)); + assertThat( + updateUnavailableSignInOption.findValueInJson("error.reason"), + containsString("Validation failure: SAML authentication provider is not available for this cluster.") + ); + + // Ensuring the sign in options array has not been modified due to the Bad Request response. + getDashboardsinfoResponse = rh.executeGetRequest("/_plugins/_security/dashboardsinfo", ADMIN_FULL_ACCESS_USER); + assertThat(getDashboardsinfoResponse.getStatusCode(), equalTo(HttpStatus.SC_OK)); + + assertThat(getDashboardsinfoResponse.findArrayInJson("sign_in_options").size(), equalTo(2)); + assertThat(getDashboardsinfoResponse.findArrayInJson("sign_in_options"), hasItem(DashboardSignInOption.BASIC.toString())); + assertThat(getDashboardsinfoResponse.findArrayInJson("sign_in_options"), hasItem(DashboardSignInOption.OPENID.toString())); + assertThat(getDashboardsinfoResponse.findArrayInJson("sign_in_options"), not(hasItem(DashboardSignInOption.SAML.toString()))); } @Test @@ -148,6 +185,30 @@ private void verifyTenantUpdateFailed(final Header... header) throws Exception { setRandomStringAsDefaultTenant.findValueInJson("error.reason"), containsString("Default tenant should be selected from one of the available tenants.") ); + + final HttpResponse signInOptionsNonArrayValue = rh.executePutRequest( + "/_plugins/_security/api/tenancy/config", + "{\"sign_in_options\": \"BASIC\"}", + header + ); + assertThat(signInOptionsNonArrayValue.getStatusCode(), equalTo(HttpStatus.SC_BAD_REQUEST)); + assertThat( + signInOptionsNonArrayValue.getBody(), + signInOptionsNonArrayValue.findValueInJson("reason"), + containsString("Wrong datatype") + ); + + final HttpResponse invalidSignInOption = rh.executePutRequest( + "/_plugins/_security/api/tenancy/config", + "{\"sign_in_options\": [\"INVALID_OPTION\"]}", + header + ); + assertThat(invalidSignInOption.getStatusCode(), equalTo(HttpStatus.SC_BAD_REQUEST)); + assertThat( + invalidSignInOption.getBody(), + invalidSignInOption.findValueInJson("error.reason"), + containsString("authentication provider is not available for this cluster") + ); } @Test diff --git a/src/test/java/org/opensearch/security/securityconf/impl/v6/ConfigV6Test.java b/src/test/java/org/opensearch/security/securityconf/impl/v6/ConfigV6Test.java index 2983fc6064..a780b0066f 100644 --- a/src/test/java/org/opensearch/security/securityconf/impl/v6/ConfigV6Test.java +++ b/src/test/java/org/opensearch/security/securityconf/impl/v6/ConfigV6Test.java @@ -20,6 +20,10 @@ import org.opensearch.security.DefaultObjectMapper; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + @RunWith(Parameterized.class) public class ConfigV6Test { private final boolean omitDefaults; @@ -31,6 +35,9 @@ public static Iterable omitDefaults() { public void assertEquals(ConfigV6.Kibana expected, JsonNode node) { Assert.assertEquals(expected.multitenancy_enabled, node.get("multitenancy_enabled").asBoolean()); + assertThat(node.get("sign_in_options").isArray(), is(true)); + assertThat(node.get("sign_in_options").toString(), containsString(expected.sign_in_options.get(0).toString())); + if (expected.server_username == null) { Assert.assertNull(node.get("server_username")); } else { @@ -57,6 +64,7 @@ public void assertEquals(ConfigV6.Kibana expected, JsonNode node) { private void assertEquals(ConfigV6.Kibana expected, ConfigV6.Kibana actual) { Assert.assertEquals(expected.multitenancy_enabled, actual.multitenancy_enabled); + assertThat(expected.sign_in_options, is(actual.sign_in_options)); if (expected.server_username == null) { // null is restored to default instead of null Assert.assertEquals(new ConfigV6.Kibana().server_username, actual.server_username); diff --git a/src/test/java/org/opensearch/security/securityconf/impl/v7/ConfigV7Test.java b/src/test/java/org/opensearch/security/securityconf/impl/v7/ConfigV7Test.java index 542ce878bd..246247c6d9 100644 --- a/src/test/java/org/opensearch/security/securityconf/impl/v7/ConfigV7Test.java +++ b/src/test/java/org/opensearch/security/securityconf/impl/v7/ConfigV7Test.java @@ -20,6 +20,10 @@ import org.opensearch.security.DefaultObjectMapper; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + @RunWith(Parameterized.class) public class ConfigV7Test { private final boolean omitDefaults; @@ -31,6 +35,9 @@ public static Iterable omitDefaults() { public void assertEquals(ConfigV7.Kibana expected, JsonNode node) { Assert.assertEquals(expected.multitenancy_enabled, node.get("multitenancy_enabled").asBoolean()); + assertThat(node.get("sign_in_options").isArray(), is(true)); + assertThat(node.get("sign_in_options").toString(), containsString(expected.sign_in_options.get(0).toString())); + if (expected.server_username == null) { Assert.assertNull(node.get("server_username")); } else { @@ -51,6 +58,7 @@ public void assertEquals(ConfigV7.Kibana expected, JsonNode node) { private void assertEquals(ConfigV7.Kibana expected, ConfigV7.Kibana actual) { Assert.assertEquals(expected.multitenancy_enabled, actual.multitenancy_enabled); + assertThat(expected.sign_in_options, is(actual.sign_in_options)); if (expected.server_username == null) { // null is restored to default instead of null Assert.assertEquals(new ConfigV7.Kibana().server_username, actual.server_username); diff --git a/src/test/java/org/opensearch/security/test/helper/rest/RestHelper.java b/src/test/java/org/opensearch/security/test/helper/rest/RestHelper.java index 3c9bfd8842..89c53e7f0d 100644 --- a/src/test/java/org/opensearch/security/test/helper/rest/RestHelper.java +++ b/src/test/java/org/opensearch/security/test/helper/rest/RestHelper.java @@ -31,6 +31,7 @@ import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.security.KeyStore; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -414,9 +415,40 @@ public String toString() { } /** - * Given a json path with dots delimiated returns the object at the leaf + * Given a json path with dots delimiated returns the object at the leaf as a string */ public String findValueInJson(final String jsonDotPath) { + final JsonNode node = this.findObjectInJson(jsonDotPath); + throwIfNotValueNode(node); + return node.asText(); + } + + /** + * Given a json path with dots delimited, returns the array at the leaf + */ + public List findArrayInJson(final String jsonDotPath) { + final JsonNode node = this.findObjectInJson(jsonDotPath); + if (!node.isArray()) { + throw new RuntimeException("Found object was not an array, object\n" + node.toPrettyString()); + } + final List elements = new ArrayList<>(); + for (int i = 0; i < node.size(); i++) { + final JsonNode currentNode = node.get(i); + throwIfNotValueNode(currentNode); + elements.add(currentNode.asText()); + } + return elements; + } + + private void throwIfNotValueNode(final JsonNode node) { + if (!node.isValueNode()) { + throw new RuntimeException( + "Unexpected value note, index directly to the object to reference, object\n" + node.toPrettyString() + ); + } + } + + private JsonNode findObjectInJson(final String jsonDotPath) { // Make sure its json / then parse it if (!isJsonContentType()) { throw new RuntimeException("Response was expected to be JSON, body was: \n" + body); @@ -489,12 +521,7 @@ public String findValueInJson(final String jsonDotPath) { } } while (jsonPathScanner.hasNext()); - if (!currentNode.isValueNode()) { - throw new RuntimeException( - "Unexpected value note, index directly to the object to reference, object\n" + currentNode.toPrettyString() - ); - } - return currentNode.asText(); + return currentNode; } } } diff --git a/src/test/resources/restapi/config.yml b/src/test/resources/restapi/config.yml index 2ed865657a..7a7d3d0e98 100644 --- a/src/test/resources/restapi/config.yml +++ b/src/test/resources/restapi/config.yml @@ -21,6 +21,18 @@ config: internalProxies: "192\\.168\\.0\\.10|192\\.168\\.0\\.11" remoteIpHeader: "x-forwarded-for" authc: + openid_auth_domain: + http_enabled: true + transport_enabled: true + order: 4 + http_authenticator: + type: openid + challenge: false + config: {} + authentication_backend: + type: "noop" + config: {} + description: "Migrated from v6" authentication_domain_kerb: http_enabled: false transport_enabled: false diff --git a/src/test/resources/restapi/securityconfig_nondefault.json b/src/test/resources/restapi/securityconfig_nondefault.json index 2482e99674..aae6948b01 100644 --- a/src/test/resources/restapi/securityconfig_nondefault.json +++ b/src/test/resources/restapi/securityconfig_nondefault.json @@ -9,7 +9,8 @@ "private_tenant_enabled" : true, "default_tenant" : "", "server_username" : "kibanaserver", - "index" : ".kibana" + "index" : ".kibana", + "sign_in_options":["BASIC"] }, "http" : { "anonymous_auth_enabled" : false,