From ab6ec7744fff981d79edcf298bb80b09d8942035 Mon Sep 17 00:00:00 2001 From: Vishnutheep B Date: Tue, 3 Mar 2026 23:50:34 +0530 Subject: [PATCH 1/6] Add preferred_tenants support to multitenancy config and dashboards info API Signed-off-by: Vishnutheep B --- .../rest/api/MultiTenancyConfigApiAction.java | 22 ++++++++++ .../DashboardsMultiTenancyConfiguration.java | 8 ++++ .../security/rest/DashboardsInfoAction.java | 1 + .../securityconf/impl/v7/ConfigV7.java | 3 ++ .../rest/api/MultiTenancyConfigApiTest.java | 44 +++++++++++++++++++ .../securityconf/impl/v7/ConfigV7Test.java | 15 ++++++- 6 files changed, 92 insertions(+), 1 deletion(-) 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 4782f4686a..c47371f54f 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 @@ -55,6 +55,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 PREFERRED_TENANTS = "preferred_tenants"; public static final String SIGN_IN_OPTIONS = "sign_in_options"; private static final List ROUTES = addRoutesPrefix( @@ -140,6 +141,8 @@ public Map allowedKeys() { MULTITENANCY_ENABLED_JSON_PROPERTY, DataType.BOOLEAN, SIGN_IN_OPTIONS, + DataType.ARRAY, + PREFERRED_TENANTS, DataType.ARRAY ); } @@ -154,6 +157,7 @@ private ToXContent multitenancyContent(final ConfigV7 config) { .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) + .field(PREFERRED_TENANTS, config.dynamic.kibana.preferred_tenants) .endObject(); } @@ -204,6 +208,10 @@ private void updateAndValidatesValues(final ConfigV7 config, final JsonNode json List options = getNewSignInOptions(newOptions, config.dynamic.authc); config.dynamic.kibana.sign_in_options = options; } + if (jsonContent.hasNonNull(PREFERRED_TENANTS)) { + List preferredTenants = getPreferredTenants(jsonContent.findValue(PREFERRED_TENANTS)); + config.dynamic.kibana.preferred_tenants = preferredTenants; + } final String defaultTenant = Optional.ofNullable(config.dynamic.kibana.default_tenant).map(String::toLowerCase).orElse(""); @@ -222,6 +230,7 @@ private void updateAndValidatesValues(final ConfigV7 config, final JsonNode json .stream() .map(String::toLowerCase) .collect(Collectors.toSet()); + if (!availableTenants.contains(defaultTenant)) { throw new IllegalArgumentException( config.dynamic.kibana.default_tenant @@ -246,4 +255,17 @@ private List getNewSignInOptions(JsonNode newOptions, Aut } }).map(DashboardSignInOption::valueOf).collect(Collectors.toList()); } + + private List getPreferredTenants(JsonNode preferredTenants) { + if (!preferredTenants.isArray()) { + throw new IllegalArgumentException(PREFERRED_TENANTS + " should be an array of strings."); + } + + return IntStream.range(0, preferredTenants.size()).mapToObj(preferredTenants::get).map(tenant -> { + if (!tenant.isTextual()) { + throw new IllegalArgumentException(PREFERRED_TENANTS + " should only contain string values."); + } + return tenant.asText(); + }).collect(Collectors.toList()); + } } diff --git a/src/main/java/org/opensearch/security/privileges/DashboardsMultiTenancyConfiguration.java b/src/main/java/org/opensearch/security/privileges/DashboardsMultiTenancyConfiguration.java index 12bbb739f4..0ab9d651d8 100644 --- a/src/main/java/org/opensearch/security/privileges/DashboardsMultiTenancyConfiguration.java +++ b/src/main/java/org/opensearch/security/privileges/DashboardsMultiTenancyConfiguration.java @@ -13,6 +13,8 @@ import org.opensearch.security.securityconf.impl.v7.ConfigV7; +import java.util.List; + /** * Provides access to the current configuration related to Dashboards multi-tenancy. *

@@ -24,6 +26,7 @@ public class DashboardsMultiTenancyConfiguration { private final boolean multitenancyEnabled; private final boolean privateTenantEnabled; private final String defaultTenant; + private final List preferredTenants; private final String index; private final String serverUsername; private final String role; @@ -32,6 +35,7 @@ public DashboardsMultiTenancyConfiguration(ConfigV7.Kibana dashboardsConfig) { this.multitenancyEnabled = dashboardsConfig.multitenancy_enabled; this.privateTenantEnabled = dashboardsConfig.private_tenant_enabled; this.defaultTenant = dashboardsConfig.default_tenant; + this.preferredTenants = dashboardsConfig.preferred_tenants; this.index = dashboardsConfig.index; this.serverUsername = dashboardsConfig.server_username; this.role = dashboardsConfig.opendistro_role; @@ -65,6 +69,10 @@ public String dashboardsOpenSearchRole() { return role; } + public List preferredTenants() { + return preferredTenants; + } + private static ConfigV7.Kibana dashboardsConfig(ConfigV7 generalConfig) { if (generalConfig != null && generalConfig.dynamic != null && generalConfig.dynamic.kibana != null) { return generalConfig.dynamic.kibana; diff --git a/src/main/java/org/opensearch/security/rest/DashboardsInfoAction.java b/src/main/java/org/opensearch/security/rest/DashboardsInfoAction.java index 61dcd353da..c1ace4b690 100644 --- a/src/main/java/org/opensearch/security/rest/DashboardsInfoAction.java +++ b/src/main/java/org/opensearch/security/rest/DashboardsInfoAction.java @@ -143,6 +143,7 @@ public void accept(RestChannel channel) throws Exception { builder.field("opensearch_dashboards_index", multiTenancyConfiguration.dashboardsIndex()); builder.field("opensearch_dashboards_server_user", multiTenancyConfiguration.dashboardsServerUsername()); builder.field("multitenancy_enabled", multiTenancyConfiguration.multitenancyEnabled()); + builder.field("preferred_tenants", multiTenancyConfiguration.preferredTenants()); builder.field("private_tenant_enabled", multiTenancyConfiguration.privateTenantEnabled()); builder.field("default_tenant", multiTenancyConfiguration.dashboardsDefaultTenant()); builder.field("sign_in_options", getSignInOptions()); 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 def5247590..9313d2a722 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 @@ -113,6 +113,7 @@ public static class Kibana { public boolean private_tenant_enabled = true; @JsonInclude(JsonInclude.Include.NON_NULL) public String default_tenant = ""; + public List preferred_tenants = Collections.emptyList(); public String server_username = "kibanaserver"; public String opendistro_role = null; public String index = ".kibana"; @@ -127,6 +128,8 @@ public String toString() { + private_tenant_enabled + ", default_tenant=" + default_tenant + + ", preferred_tenants=" + + preferred_tenants + ", server_username=" + server_username + ", opendistro_role=" 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 752335b802..75d8c2d2f7 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 @@ -46,6 +46,7 @@ private void verifyTenantUpdate(final Header... header) throws Exception { getDashboardsinfoResponse.findValueInJson("default_tenant"), equalTo(ConfigConstants.TENANCY_GLOBAL_TENANT_DEFAULT_NAME) ); + assertThat(getDashboardsinfoResponse.findArrayInJson("preferred_tenants").size(), equalTo(0)); final HttpResponse setPrivateTenantAsDefaultResponse = rh.executePutRequest( "/_plugins/_security/api/tenancy/config", @@ -68,12 +69,31 @@ private void verifyTenantUpdate(final Header... header) throws Exception { ); assertThat(updateDashboardSignInOptions.getBody(), updateDashboardSignInOptions.getStatusCode(), equalTo(HttpStatus.SC_OK)); + final HttpResponse updatePreferredTenants = rh.executePutRequest( + "/_plugins/_security/api/tenancy/config", + "{\"preferred_tenants\": [\"Private\", \"Global\"]}", + header + ); + assertThat(updatePreferredTenants.getBody(), updatePreferredTenants.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()))); + assertThat(getDashboardsinfoResponse.findArrayInJson("preferred_tenants"), hasItem("Private")); + assertThat(getDashboardsinfoResponse.findArrayInJson("preferred_tenants"), hasItem("Global")); + + final HttpResponse clearPreferredTenants = rh.executePutRequest( + "/_plugins/_security/api/tenancy/config", + "{\"preferred_tenants\": []}", + header + ); + assertThat(clearPreferredTenants.getBody(), clearPreferredTenants.getStatusCode(), equalTo(HttpStatus.SC_OK)); + + getDashboardsinfoResponse = rh.executeGetRequest("/_plugins/_security/dashboardsinfo", ADMIN_FULL_ACCESS_USER); + assertThat(getDashboardsinfoResponse.findArrayInJson("preferred_tenants").size(), equalTo(0)); final HttpResponse updateUnavailableSignInOption = rh.executePutRequest( "/_plugins/_security/api/tenancy/config", @@ -209,6 +229,30 @@ private void verifyTenantUpdateFailed(final Header... header) throws Exception { invalidSignInOption.findValueInJson("error.reason"), containsString("authentication provider is not available for this cluster") ); + + final HttpResponse preferredTenantsNonArrayValue = rh.executePutRequest( + "/_plugins/_security/api/tenancy/config", + "{\"preferred_tenants\": \"Private\"}", + header + ); + assertThat(preferredTenantsNonArrayValue.getStatusCode(), equalTo(HttpStatus.SC_BAD_REQUEST)); + assertThat( + preferredTenantsNonArrayValue.getBody(), + preferredTenantsNonArrayValue.findValueInJson("reason"), + containsString("Wrong datatype") + ); + + final HttpResponse preferredTenantsContainInvalidType = rh.executePutRequest( + "/_plugins/_security/api/tenancy/config", + "{\"preferred_tenants\": [\"Private\", 1]}", + header + ); + assertThat(preferredTenantsContainInvalidType.getStatusCode(), equalTo(HttpStatus.SC_BAD_REQUEST)); + assertThat( + preferredTenantsContainInvalidType.getBody(), + preferredTenantsContainInvalidType.findValueInJson("error.reason"), + containsString("preferred_tenants should only contain string values") + ); } @Test 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 f05448bcd0..3ac2732172 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 @@ -11,8 +11,11 @@ package org.opensearch.security.securityconf.impl.v7; -import com.google.common.collect.ImmutableList; import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.collect.ImmutableList; + +import java.util.Arrays; +import java.util.Collections; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -37,6 +40,13 @@ public void assertEquals(ConfigV7.Kibana expected, JsonNode node) { assertThat(node.get("multitenancy_enabled").asBoolean(), is(expected.multitenancy_enabled)); assertThat(node.get("sign_in_options").isArray(), is(true)); assertThat(node.get("sign_in_options").toString(), containsString(expected.sign_in_options.get(0).toString())); + JsonNode preferredTenantsNode = node.get("preferred_tenants"); + if (omitDefaults && expected.preferred_tenants.isEmpty()) { + Assert.assertNull(preferredTenantsNode); + } else { + assertThat(preferredTenantsNode.isArray(), is(true)); + assertThat(preferredTenantsNode.size(), is(expected.preferred_tenants.size())); + } if (expected.server_username == null) { Assert.assertNull(node.get("server_username")); @@ -59,6 +69,7 @@ public void assertEquals(ConfigV7.Kibana expected, JsonNode node) { private void assertEquals(ConfigV7.Kibana expected, ConfigV7.Kibana actual) { assertThat(actual.multitenancy_enabled, is(expected.multitenancy_enabled)); assertThat(expected.sign_in_options, is(actual.sign_in_options)); + assertThat(expected.preferred_tenants, is(actual.preferred_tenants)); if (expected.server_username == null) { // null is restored to default instead of null assertThat(actual.server_username, is(new ConfigV7.Kibana().server_username)); @@ -85,6 +96,7 @@ public void testDashboards() throws Exception { String json; kibana = new ConfigV7.Kibana(); + assertThat(kibana.preferred_tenants, is(Collections.emptyList())); json = DefaultObjectMapper.writeValueAsString(kibana, omitDefaults); assertEquals(kibana, DefaultObjectMapper.readTree(json)); assertEquals(kibana, DefaultObjectMapper.readValue(json, ConfigV7.Kibana.class)); @@ -101,6 +113,7 @@ public void testDashboards() throws Exception { kibana.server_username = "user"; kibana.opendistro_role = "role"; kibana.index = "index"; + kibana.preferred_tenants = Arrays.asList("Private", "Global"); json = DefaultObjectMapper.writeValueAsString(kibana, omitDefaults); assertEquals(kibana, DefaultObjectMapper.readTree(json)); assertEquals(kibana, DefaultObjectMapper.readValue(json, ConfigV7.Kibana.class)); From 0a97a7c5d71fd100df3a7c86d696146897bb9093 Mon Sep 17 00:00:00 2001 From: Vishnutheep B Date: Wed, 4 Mar 2026 00:11:48 +0530 Subject: [PATCH 2/6] Add preferred_tenants to config files for tests Signed-off-by: Vishnutheep B --- src/test/resources/auditlog/config.yml | 1 + src/test/resources/cache/config.yml | 1 + src/test/resources/composite_config.yml | 1 + src/test/resources/config.yml | 1 + src/test/resources/config_anon.yml | 1 + src/test/resources/config_clientcert.yml | 1 + src/test/resources/config_disable_all.yml | 1 + src/test/resources/config_dnfof.yml | 1 + src/test/resources/config_invalidlic.yml | 1 + src/test/resources/config_protected_indices.yml | 1 + src/test/resources/config_proxy.yml | 1 + src/test/resources/config_proxy_custom.yml | 1 + src/test/resources/config_respect_indices_options.yml | 1 + src/test/resources/config_rest_impersonation.yml | 1 + src/test/resources/config_transport_username.yml | 1 + src/test/resources/config_xff.yml | 1 + src/test/resources/dlsfls/config.yml | 1 + src/test/resources/ldap/config.yml | 1 + src/test/resources/ldap/config_ldap2.yml | 1 + src/test/resources/multitenancy/config.yml | 1 + src/test/resources/multitenancy/config_anonymous.yml | 1 + src/test/resources/multitenancy/config_basic_auth.yml | 1 + src/test/resources/multitenancy/config_nodnfof.yml | 1 + src/test/resources/restapi/config.yml | 1 + src/test/resources/restapi/invalid_config.json | 1 + src/test/resources/restapi/security_config.json | 1 + src/test/resources/restapi/securityconfig.json | 1 + src/test/resources/restapi/securityconfig_nondefault.json | 1 + src/test/resources/security_passive/config.yml | 1 + src/test/resources/system_indices/config.yml | 1 + 30 files changed, 30 insertions(+) diff --git a/src/test/resources/auditlog/config.yml b/src/test/resources/auditlog/config.yml index 7aa50d7f9d..4216ce7028 100644 --- a/src/test/resources/auditlog/config.yml +++ b/src/test/resources/auditlog/config.yml @@ -13,6 +13,7 @@ config: multitenancy_enabled: true private_tenant_enabled: true default_tenant: "" + preferred_tenants: [] server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/cache/config.yml b/src/test/resources/cache/config.yml index a2dffb300b..db350c17a6 100644 --- a/src/test/resources/cache/config.yml +++ b/src/test/resources/cache/config.yml @@ -13,6 +13,7 @@ config: multitenancy_enabled: true private_tenant_enabled: true default_tenant: "" + preferred_tenants: [] server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/composite_config.yml b/src/test/resources/composite_config.yml index a69d36178f..8bece9488c 100644 --- a/src/test/resources/composite_config.yml +++ b/src/test/resources/composite_config.yml @@ -13,6 +13,7 @@ config: multitenancy_enabled: true private_tenant_enabled: true default_tenant: "" + preferred_tenants: [] server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/config.yml b/src/test/resources/config.yml index 388cba2903..b3e0c39c63 100644 --- a/src/test/resources/config.yml +++ b/src/test/resources/config.yml @@ -12,6 +12,7 @@ config: multitenancy_enabled: true private_tenant_enabled: true default_tenant: "" + preferred_tenants: [] server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/config_anon.yml b/src/test/resources/config_anon.yml index e9e50b2f34..555d4b0103 100644 --- a/src/test/resources/config_anon.yml +++ b/src/test/resources/config_anon.yml @@ -13,6 +13,7 @@ config: multitenancy_enabled: true private_tenant_enabled: true default_tenant: "" + preferred_tenants: [] server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/config_clientcert.yml b/src/test/resources/config_clientcert.yml index 256aa9cb32..c044a4004c 100644 --- a/src/test/resources/config_clientcert.yml +++ b/src/test/resources/config_clientcert.yml @@ -13,6 +13,7 @@ config: multitenancy_enabled: true private_tenant_enabled: true default_tenant: "" + preferred_tenants: [] server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/config_disable_all.yml b/src/test/resources/config_disable_all.yml index ceaedb43c1..42af95bf9d 100644 --- a/src/test/resources/config_disable_all.yml +++ b/src/test/resources/config_disable_all.yml @@ -13,6 +13,7 @@ config: multitenancy_enabled: true private_tenant_enabled: true default_tenant: "" + preferred_tenants: [] server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/config_dnfof.yml b/src/test/resources/config_dnfof.yml index fd58eedd6f..8fbff4f169 100644 --- a/src/test/resources/config_dnfof.yml +++ b/src/test/resources/config_dnfof.yml @@ -12,6 +12,7 @@ config: multitenancy_enabled: true private_tenant_enabled: true default_tenant: "" + preferred_tenants: [] server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/config_invalidlic.yml b/src/test/resources/config_invalidlic.yml index 388cba2903..b3e0c39c63 100644 --- a/src/test/resources/config_invalidlic.yml +++ b/src/test/resources/config_invalidlic.yml @@ -12,6 +12,7 @@ config: multitenancy_enabled: true private_tenant_enabled: true default_tenant: "" + preferred_tenants: [] server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/config_protected_indices.yml b/src/test/resources/config_protected_indices.yml index 5d9e2bfb2b..636f3a1536 100644 --- a/src/test/resources/config_protected_indices.yml +++ b/src/test/resources/config_protected_indices.yml @@ -12,6 +12,7 @@ config: multitenancy_enabled: true private_tenant_enabled: true default_tenant: "" + preferred_tenants: [] server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/config_proxy.yml b/src/test/resources/config_proxy.yml index 5785c7784c..0243cc9f0a 100644 --- a/src/test/resources/config_proxy.yml +++ b/src/test/resources/config_proxy.yml @@ -13,6 +13,7 @@ config: multitenancy_enabled: true private_tenant_enabled: true default_tenant: "" + preferred_tenants: [] server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/config_proxy_custom.yml b/src/test/resources/config_proxy_custom.yml index 0bf81a8f32..cbb4fa640b 100644 --- a/src/test/resources/config_proxy_custom.yml +++ b/src/test/resources/config_proxy_custom.yml @@ -13,6 +13,7 @@ config: multitenancy_enabled: true private_tenant_enabled: true default_tenant: "" + preferred_tenants: [] server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/config_respect_indices_options.yml b/src/test/resources/config_respect_indices_options.yml index e13768c1ac..9fbd6cd556 100644 --- a/src/test/resources/config_respect_indices_options.yml +++ b/src/test/resources/config_respect_indices_options.yml @@ -13,6 +13,7 @@ config: multitenancy_enabled: true private_tenant_enabled: true default_tenant: "" + preferred_tenants: [] server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/config_rest_impersonation.yml b/src/test/resources/config_rest_impersonation.yml index 422d120c50..49abd7f3d9 100644 --- a/src/test/resources/config_rest_impersonation.yml +++ b/src/test/resources/config_rest_impersonation.yml @@ -13,6 +13,7 @@ config: multitenancy_enabled: true private_tenant_enabled: true default_tenant: "" + preferred_tenants: [] server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/config_transport_username.yml b/src/test/resources/config_transport_username.yml index 0f3b59686e..a286752b74 100644 --- a/src/test/resources/config_transport_username.yml +++ b/src/test/resources/config_transport_username.yml @@ -12,6 +12,7 @@ config: multitenancy_enabled: true private_tenant_enabled: true default_tenant: "" + preferred_tenants: [] server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/config_xff.yml b/src/test/resources/config_xff.yml index 771de152d4..63055a9993 100644 --- a/src/test/resources/config_xff.yml +++ b/src/test/resources/config_xff.yml @@ -12,6 +12,7 @@ config: multitenancy_enabled: true private_tenant_enabled: true default_tenant: "" + preferred_tenants: [] server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/dlsfls/config.yml b/src/test/resources/dlsfls/config.yml index 574e2db354..02947e64b0 100644 --- a/src/test/resources/dlsfls/config.yml +++ b/src/test/resources/dlsfls/config.yml @@ -13,6 +13,7 @@ config: multitenancy_enabled: true private_tenant_enabled: true default_tenant: "" + preferred_tenants: [] server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/ldap/config.yml b/src/test/resources/ldap/config.yml index 6e76282843..b7a099a36c 100644 --- a/src/test/resources/ldap/config.yml +++ b/src/test/resources/ldap/config.yml @@ -13,6 +13,7 @@ config: multitenancy_enabled: true private_tenant_enabled: true default_tenant: "" + preferred_tenants: [] server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/ldap/config_ldap2.yml b/src/test/resources/ldap/config_ldap2.yml index c385c3f7f2..cea994dd02 100644 --- a/src/test/resources/ldap/config_ldap2.yml +++ b/src/test/resources/ldap/config_ldap2.yml @@ -13,6 +13,7 @@ config: multitenancy_enabled: true private_tenant_enabled: true default_tenant: "" + preferred_tenants: [] server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/multitenancy/config.yml b/src/test/resources/multitenancy/config.yml index 61100dbf60..e3ba91ff52 100644 --- a/src/test/resources/multitenancy/config.yml +++ b/src/test/resources/multitenancy/config.yml @@ -13,6 +13,7 @@ config: multitenancy_enabled: true private_tenant_enabled: true default_tenant: "" + preferred_tenants: [] server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/multitenancy/config_anonymous.yml b/src/test/resources/multitenancy/config_anonymous.yml index 44a113cd7f..de5e75269e 100644 --- a/src/test/resources/multitenancy/config_anonymous.yml +++ b/src/test/resources/multitenancy/config_anonymous.yml @@ -13,6 +13,7 @@ config: multitenancy_enabled: true private_tenant_enabled: true default_tenant: "" + preferred_tenants: [] server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/multitenancy/config_basic_auth.yml b/src/test/resources/multitenancy/config_basic_auth.yml index 1b0de2c671..9f0b13bb8c 100644 --- a/src/test/resources/multitenancy/config_basic_auth.yml +++ b/src/test/resources/multitenancy/config_basic_auth.yml @@ -13,6 +13,7 @@ config: multitenancy_enabled: true private_tenant_enabled: true default_tenant: "" + preferred_tenants: [] server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/multitenancy/config_nodnfof.yml b/src/test/resources/multitenancy/config_nodnfof.yml index 278639fdaf..71223104bd 100644 --- a/src/test/resources/multitenancy/config_nodnfof.yml +++ b/src/test/resources/multitenancy/config_nodnfof.yml @@ -13,6 +13,7 @@ config: multitenancy_enabled: true private_tenant_enabled: true default_tenant: "" + preferred_tenants: [] server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/restapi/config.yml b/src/test/resources/restapi/config.yml index a536616e5a..77726b953c 100644 --- a/src/test/resources/restapi/config.yml +++ b/src/test/resources/restapi/config.yml @@ -12,6 +12,7 @@ config: multitenancy_enabled: true private_tenant_enabled: true default_tenant: "" + preferred_tenants: [] server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/restapi/invalid_config.json b/src/test/resources/restapi/invalid_config.json index 8ffa638a11..a5df104b3a 100644 --- a/src/test/resources/restapi/invalid_config.json +++ b/src/test/resources/restapi/invalid_config.json @@ -9,6 +9,7 @@ "multitenancy_enabled":true, "private_tenant_enabled" : true, "default_tenant" : "", + "preferred_tenants" : [], "server_username":"kibanaserver", "index":".kibana" }, diff --git a/src/test/resources/restapi/security_config.json b/src/test/resources/restapi/security_config.json index 28acf19fb2..0478bc5217 100644 --- a/src/test/resources/restapi/security_config.json +++ b/src/test/resources/restapi/security_config.json @@ -9,6 +9,7 @@ "multitenancy_enabled":true, "private_tenant_enabled" : true, "default_tenant" : "", + "preferred_tenants" : [], "server_username":"kibanaserver", "index":".kibana" }, diff --git a/src/test/resources/restapi/securityconfig.json b/src/test/resources/restapi/securityconfig.json index fa2c3f04e0..844703a9da 100644 --- a/src/test/resources/restapi/securityconfig.json +++ b/src/test/resources/restapi/securityconfig.json @@ -9,6 +9,7 @@ "multitenancy_enabled":true, "private_tenant_enabled" : true, "default_tenant" : "", + "preferred_tenants" : [], "server_username":"kibanaserver", "index":".kibana" }, diff --git a/src/test/resources/restapi/securityconfig_nondefault.json b/src/test/resources/restapi/securityconfig_nondefault.json index 8dfb838fa0..6865d2c138 100644 --- a/src/test/resources/restapi/securityconfig_nondefault.json +++ b/src/test/resources/restapi/securityconfig_nondefault.json @@ -8,6 +8,7 @@ "multitenancy_enabled" : true, "private_tenant_enabled" : true, "default_tenant" : "", + "preferred_tenants" : [], "server_username" : "kibanaserver", "index" : ".kibana", "sign_in_options":["BASIC"] diff --git a/src/test/resources/security_passive/config.yml b/src/test/resources/security_passive/config.yml index 388cba2903..b3e0c39c63 100644 --- a/src/test/resources/security_passive/config.yml +++ b/src/test/resources/security_passive/config.yml @@ -12,6 +12,7 @@ config: multitenancy_enabled: true private_tenant_enabled: true default_tenant: "" + preferred_tenants: [] server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/system_indices/config.yml b/src/test/resources/system_indices/config.yml index 5d9e2bfb2b..636f3a1536 100644 --- a/src/test/resources/system_indices/config.yml +++ b/src/test/resources/system_indices/config.yml @@ -12,6 +12,7 @@ config: multitenancy_enabled: true private_tenant_enabled: true default_tenant: "" + preferred_tenants: [] server_username: "kibanaserver" index: ".kibana" http: From 4d42126f6922537123decab96fdf7f402d784554 Mon Sep 17 00:00:00 2001 From: Vishnutheep B Date: Sun, 8 Mar 2026 18:42:43 +0530 Subject: [PATCH 3/6] Add input validation for preferred_tenants Signed-off-by: Vishnutheep B --- CHANGELOG.md | 1 + .../rest/api/MultiTenancyConfigApiAction.java | 51 ++++++++++++------- .../validation/RequestContentValidator.java | 46 ++++++++++++++--- .../rest/api/MultiTenancyConfigApiTest.java | 2 +- .../RequestContentValidatorTest.java | 22 ++++++++ 5 files changed, 98 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e310304f5..659e036394 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Enh - Make security plugin aware of FIPS build param (-Pcrypto.standard=FIPS-140-3) ([#5952](https://github.com/opensearch-project/security/pull/5952)) - Hardens input validation for resource sharing APIs ([#5831](https://github.com/opensearch-project/security/pull/5831) +- Add `preferred_tenants` to config and dashboard info APIs ([#5969](https://github.com/opensearch-project/security/issues/5969)) ### Bug Fixes - Fix the issue of unprocessed X-Request-Id ([#5954](https://github.com/opensearch-project/security/pull/5954)) 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 c47371f54f..9b7d710f0d 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 @@ -32,6 +32,7 @@ import org.opensearch.rest.RestRequest; import org.opensearch.security.dlic.rest.validation.EndpointValidator; import org.opensearch.security.dlic.rest.validation.RequestContentValidator; +import org.opensearch.security.dlic.rest.validation.RequestContentValidator.FieldConfiguration; import org.opensearch.security.dlic.rest.validation.RequestContentValidator.DataType; import org.opensearch.security.securityconf.impl.CType; import org.opensearch.security.securityconf.impl.DashboardSignInOption; @@ -49,6 +50,7 @@ import static org.opensearch.security.dlic.rest.support.Utils.OPENDISTRO_API_DEPRECATION_MESSAGE; import static org.opensearch.security.dlic.rest.support.Utils.addLegacyRoutesPrefix; import static org.opensearch.security.dlic.rest.support.Utils.addRoutesPrefix; +import static org.opensearch.security.dlic.rest.validation.RequestContentValidator.*; public class MultiTenancyConfigApiAction extends AbstractApiAction { @@ -75,6 +77,8 @@ public class MultiTenancyConfigApiAction extends AbstractApiAction { ConfigConstants.TENANCY_PRIVATE_TENANT_NAME ); + private static final int PREFERRED_TENANTS_MAX_SIZE = 100; + @Override public String getName() { return "Multi Tenancy actions to Retrieve / Update configs."; @@ -133,19 +137,39 @@ public Settings settings() { @Override public Map allowedKeys() { + // Provide basic type information for backward compatibility return ImmutableMap.of( - DEFAULT_TENANT_JSON_PROPERTY, - DataType.STRING, - PRIVATE_TENANT_ENABLED_JSON_PROPERTY, - DataType.BOOLEAN, - MULTITENANCY_ENABLED_JSON_PROPERTY, - DataType.BOOLEAN, - SIGN_IN_OPTIONS, - DataType.ARRAY, - PREFERRED_TENANTS, - DataType.ARRAY + DEFAULT_TENANT_JSON_PROPERTY, DataType.STRING, + PRIVATE_TENANT_ENABLED_JSON_PROPERTY, DataType.BOOLEAN, + MULTITENANCY_ENABLED_JSON_PROPERTY, DataType.BOOLEAN, + SIGN_IN_OPTIONS, DataType.ARRAY, + PREFERRED_TENANTS, DataType.ARRAY ); } + + @Override + public Map allowedKeysWithConfig() { + return ImmutableMap.builder() + .put( + DEFAULT_TENANT_JSON_PROPERTY, + FieldConfiguration.of( + DataType.STRING, + RequestContentValidator.MAX_STRING_LENGTH, + RequestContentValidator.principalValidator(false) + ) + ) + .put(PRIVATE_TENANT_ENABLED_JSON_PROPERTY, FieldConfiguration.of(DataType.BOOLEAN)) + .put(MULTITENANCY_ENABLED_JSON_PROPERTY, FieldConfiguration.of(DataType.BOOLEAN)) + .put(SIGN_IN_OPTIONS, FieldConfiguration.of(DataType.ARRAY)) + .put( + PREFERRED_TENANTS, + FieldConfiguration.of(DataType.ARRAY, (fieldName, value) -> { + arraySizeValidator(PREFERRED_TENANTS_MAX_SIZE).validate(fieldName, value); + ARRAY_OF_STRINGS_VALIDATOR.validate(fieldName, value); + }) + ) + .build(); + } }); } }; @@ -257,14 +281,7 @@ private List getNewSignInOptions(JsonNode newOptions, Aut } private List getPreferredTenants(JsonNode preferredTenants) { - if (!preferredTenants.isArray()) { - throw new IllegalArgumentException(PREFERRED_TENANTS + " should be an array of strings."); - } - return IntStream.range(0, preferredTenants.size()).mapToObj(preferredTenants::get).map(tenant -> { - if (!tenant.isTextual()) { - throw new IllegalArgumentException(PREFERRED_TENANTS + " should only contain string values."); - } return tenant.asText(); }).collect(Collectors.toList()); } diff --git a/src/main/java/org/opensearch/security/dlic/rest/validation/RequestContentValidator.java b/src/main/java/org/opensearch/security/dlic/rest/validation/RequestContentValidator.java index fcb053e1f1..4b179440b7 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/validation/RequestContentValidator.java +++ b/src/main/java/org/opensearch/security/dlic/rest/validation/RequestContentValidator.java @@ -20,6 +20,7 @@ import java.util.Optional; import java.util.Set; import java.util.regex.Pattern; +import java.util.stream.IntStream; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; @@ -379,7 +380,17 @@ private ValidationResult validateDataType(final JsonNode jsonContent) } private ValidationResult nullValuesInArrayValidator(final JsonNode jsonContent) { - for (final Map.Entry allowedKey : validationContext.allowedKeys().entrySet()) { + // Use allowedKeysWithConfig if provided, otherwise fall back to allowedKeys + final Map fieldConfigs = validationContext.allowedKeysWithConfig(); + final Map allowedKeys = new HashMap<>(); + if (fieldConfigs != null) { + fieldConfigs.forEach((fieldName, fieldConfig) -> { + allowedKeys.put(fieldName, fieldConfig.dataType); + }); + } else { + allowedKeys.putAll(validationContext.allowedKeys()); + } + for (final Map.Entry allowedKey : allowedKeys.entrySet()) { JsonNode value = jsonContent.get(allowedKey.getKey()); if (value != null) { if (hasNullOrBlankArrayElement(value)) { @@ -702,19 +713,42 @@ public static FieldValidator principalValidator(boolean allowWildcard) { }; /** - * Validator for array entry counts (works with JsonNode arrays) + * Validator for array entry counts with default MAX_ARRAY_SIZE (works with JsonNode arrays) */ public static final FieldValidator ARRAY_SIZE_VALIDATOR = (fieldName, value) -> { + validateArraySize(fieldName, value, MAX_ARRAY_SIZE); + }; + + /** + * Validator for array entry counts with custom maxSize (works with JsonNode arrays) + */ + public static FieldValidator arraySizeValidator(int maxSize) { + return (fieldName, value) -> { + validateArraySize(fieldName, value, maxSize); + }; + } + + private static void validateArraySize(String fieldName, Object value, int maxSize) { if (value instanceof JsonNode node) { - if (node.isArray() && node.size() > MAX_ARRAY_SIZE) { - throw new IllegalArgumentException("Array field [" + fieldName + "] exceeds maximum size of " + MAX_ARRAY_SIZE); + if (node.isArray() && node.size() > maxSize) { + throw new IllegalArgumentException("Array field [" + fieldName + "] exceeds maximum size of " + maxSize); } } else if (value instanceof Integer) { int count = (Integer) value; - if (count > MAX_ARRAY_SIZE) { - throw new IllegalArgumentException("Array field [" + fieldName + "] exceeds maximum size of " + MAX_ARRAY_SIZE); + if (count > maxSize) { + throw new IllegalArgumentException("Array field [" + fieldName + "] exceeds maximum size of " + maxSize); } } + } + + public static final FieldValidator ARRAY_OF_STRINGS_VALIDATOR = (fieldName, value) -> { + if (value instanceof JsonNode node) { + IntStream.range(0, node.size()).mapToObj(node::get).forEach(element -> { + if (!element.isTextual()) { + throw new IllegalArgumentException(fieldName + " should only contain string values."); + } + }); + } }; /** 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 75d8c2d2f7..7764e3c581 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 @@ -250,7 +250,7 @@ private void verifyTenantUpdateFailed(final Header... header) throws Exception { assertThat(preferredTenantsContainInvalidType.getStatusCode(), equalTo(HttpStatus.SC_BAD_REQUEST)); assertThat( preferredTenantsContainInvalidType.getBody(), - preferredTenantsContainInvalidType.findValueInJson("error.reason"), + preferredTenantsContainInvalidType.findValueInJson("preferred_tenants"), containsString("preferred_tenants should only contain string values") ); } diff --git a/src/test/java/org/opensearch/security/dlic/rest/validation/RequestContentValidatorTest.java b/src/test/java/org/opensearch/security/dlic/rest/validation/RequestContentValidatorTest.java index 628e297621..7723d79233 100644 --- a/src/test/java/org/opensearch/security/dlic/rest/validation/RequestContentValidatorTest.java +++ b/src/test/java/org/opensearch/security/dlic/rest/validation/RequestContentValidatorTest.java @@ -646,6 +646,28 @@ public void testArraySizeValidatorRejectsLargeCount() { ); } + @Test + public void testArraySizeValidatorFactoryUsesCustomLimit() { + final RequestContentValidator.FieldValidator validator = RequestContentValidator.arraySizeValidator(2); + final ObjectNode node = DefaultObjectMapper.objectMapper.createObjectNode(); + node.putArray("arr").add("1").add("2").add("3"); + expectThrows(IllegalArgumentException.class, () -> validator.validate("arr", node.get("arr"))); + } + + @Test + public void testArrayOfStringsValidatorAcceptsStringArray() { + final ObjectNode node = DefaultObjectMapper.objectMapper.createObjectNode(); + node.putArray("arr").add("one").add("two"); + RequestContentValidator.ARRAY_OF_STRINGS_VALIDATOR.validate("arr", node.get("arr")); + } + + @Test + public void testArrayOfStringsValidatorRejectsNonStringElement() { + final ObjectNode node = DefaultObjectMapper.objectMapper.createObjectNode(); + node.putArray("arr").add("one").add(2); + expectThrows(IllegalArgumentException.class, () -> RequestContentValidator.ARRAY_OF_STRINGS_VALIDATOR.validate("arr", node.get("arr"))); + } + @Test public void testAllowedValuesValidator() { final Set allowed = Set.of("read", "write"); From 5f3f93e56fec4c983d5f6b0fa8fb4d27afe792b4 Mon Sep 17 00:00:00 2001 From: Vishnutheep B Date: Sun, 8 Mar 2026 18:55:57 +0530 Subject: [PATCH 4/6] Fix checkstyle Signed-off-by: Vishnutheep B --- .../security/dlic/rest/api/MultiTenancyConfigApiAction.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 9b7d710f0d..32ec8259f0 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 @@ -50,7 +50,8 @@ import static org.opensearch.security.dlic.rest.support.Utils.OPENDISTRO_API_DEPRECATION_MESSAGE; import static org.opensearch.security.dlic.rest.support.Utils.addLegacyRoutesPrefix; import static org.opensearch.security.dlic.rest.support.Utils.addRoutesPrefix; -import static org.opensearch.security.dlic.rest.validation.RequestContentValidator.*; +import static org.opensearch.security.dlic.rest.validation.RequestContentValidator.arraySizeValidator; +import static org.opensearch.security.dlic.rest.validation.RequestContentValidator.ARRAY_OF_STRINGS_VALIDATOR; public class MultiTenancyConfigApiAction extends AbstractApiAction { From 8f12076beb91add56d0e760c02e8c936d6912d80 Mon Sep 17 00:00:00 2001 From: Vishnutheep B Date: Sun, 22 Mar 2026 20:22:39 +0530 Subject: [PATCH 5/6] Fix spotless checks Signed-off-by: Vishnutheep B --- .../rest/api/MultiTenancyConfigApiAction.java | 37 ++++++++++--------- .../validation/RequestContentValidator.java | 8 +--- .../DashboardsMultiTenancyConfiguration.java | 4 +- .../RequestContentValidatorTest.java | 5 ++- .../securityconf/impl/v7/ConfigV7Test.java | 6 +-- 5 files changed, 31 insertions(+), 29 deletions(-) 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 32ec8259f0..132ba0fa40 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 @@ -32,8 +32,8 @@ import org.opensearch.rest.RestRequest; import org.opensearch.security.dlic.rest.validation.EndpointValidator; import org.opensearch.security.dlic.rest.validation.RequestContentValidator; -import org.opensearch.security.dlic.rest.validation.RequestContentValidator.FieldConfiguration; import org.opensearch.security.dlic.rest.validation.RequestContentValidator.DataType; +import org.opensearch.security.dlic.rest.validation.RequestContentValidator.FieldConfiguration; import org.opensearch.security.securityconf.impl.CType; import org.opensearch.security.securityconf.impl.DashboardSignInOption; import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; @@ -50,8 +50,8 @@ import static org.opensearch.security.dlic.rest.support.Utils.OPENDISTRO_API_DEPRECATION_MESSAGE; import static org.opensearch.security.dlic.rest.support.Utils.addLegacyRoutesPrefix; import static org.opensearch.security.dlic.rest.support.Utils.addRoutesPrefix; -import static org.opensearch.security.dlic.rest.validation.RequestContentValidator.arraySizeValidator; import static org.opensearch.security.dlic.rest.validation.RequestContentValidator.ARRAY_OF_STRINGS_VALIDATOR; +import static org.opensearch.security.dlic.rest.validation.RequestContentValidator.arraySizeValidator; public class MultiTenancyConfigApiAction extends AbstractApiAction { @@ -140,11 +140,16 @@ public Settings settings() { public Map allowedKeys() { // Provide basic type information for backward compatibility return ImmutableMap.of( - DEFAULT_TENANT_JSON_PROPERTY, DataType.STRING, - PRIVATE_TENANT_ENABLED_JSON_PROPERTY, DataType.BOOLEAN, - MULTITENANCY_ENABLED_JSON_PROPERTY, DataType.BOOLEAN, - SIGN_IN_OPTIONS, DataType.ARRAY, - PREFERRED_TENANTS, DataType.ARRAY + DEFAULT_TENANT_JSON_PROPERTY, + DataType.STRING, + PRIVATE_TENANT_ENABLED_JSON_PROPERTY, + DataType.BOOLEAN, + MULTITENANCY_ENABLED_JSON_PROPERTY, + DataType.BOOLEAN, + SIGN_IN_OPTIONS, + DataType.ARRAY, + PREFERRED_TENANTS, + DataType.ARRAY ); } @@ -162,13 +167,10 @@ public Map allowedKeysWithConfig() { .put(PRIVATE_TENANT_ENABLED_JSON_PROPERTY, FieldConfiguration.of(DataType.BOOLEAN)) .put(MULTITENANCY_ENABLED_JSON_PROPERTY, FieldConfiguration.of(DataType.BOOLEAN)) .put(SIGN_IN_OPTIONS, FieldConfiguration.of(DataType.ARRAY)) - .put( - PREFERRED_TENANTS, - FieldConfiguration.of(DataType.ARRAY, (fieldName, value) -> { - arraySizeValidator(PREFERRED_TENANTS_MAX_SIZE).validate(fieldName, value); - ARRAY_OF_STRINGS_VALIDATOR.validate(fieldName, value); - }) - ) + .put(PREFERRED_TENANTS, FieldConfiguration.of(DataType.ARRAY, (fieldName, value) -> { + arraySizeValidator(PREFERRED_TENANTS_MAX_SIZE).validate(fieldName, value); + ARRAY_OF_STRINGS_VALIDATOR.validate(fieldName, value); + })) .build(); } }); @@ -282,8 +284,9 @@ private List getNewSignInOptions(JsonNode newOptions, Aut } private List getPreferredTenants(JsonNode preferredTenants) { - return IntStream.range(0, preferredTenants.size()).mapToObj(preferredTenants::get).map(tenant -> { - return tenant.asText(); - }).collect(Collectors.toList()); + return IntStream.range(0, preferredTenants.size()) + .mapToObj(preferredTenants::get) + .map(tenant -> { return tenant.asText(); }) + .collect(Collectors.toList()); } } diff --git a/src/main/java/org/opensearch/security/dlic/rest/validation/RequestContentValidator.java b/src/main/java/org/opensearch/security/dlic/rest/validation/RequestContentValidator.java index 4b179440b7..6cce8efda6 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/validation/RequestContentValidator.java +++ b/src/main/java/org/opensearch/security/dlic/rest/validation/RequestContentValidator.java @@ -384,9 +384,7 @@ private ValidationResult nullValuesInArrayValidator(final JsonNode jso final Map fieldConfigs = validationContext.allowedKeysWithConfig(); final Map allowedKeys = new HashMap<>(); if (fieldConfigs != null) { - fieldConfigs.forEach((fieldName, fieldConfig) -> { - allowedKeys.put(fieldName, fieldConfig.dataType); - }); + fieldConfigs.forEach((fieldName, fieldConfig) -> { allowedKeys.put(fieldName, fieldConfig.dataType); }); } else { allowedKeys.putAll(validationContext.allowedKeys()); } @@ -723,9 +721,7 @@ public static FieldValidator principalValidator(boolean allowWildcard) { * Validator for array entry counts with custom maxSize (works with JsonNode arrays) */ public static FieldValidator arraySizeValidator(int maxSize) { - return (fieldName, value) -> { - validateArraySize(fieldName, value, maxSize); - }; + return (fieldName, value) -> { validateArraySize(fieldName, value, maxSize); }; } private static void validateArraySize(String fieldName, Object value, int maxSize) { diff --git a/src/main/java/org/opensearch/security/privileges/DashboardsMultiTenancyConfiguration.java b/src/main/java/org/opensearch/security/privileges/DashboardsMultiTenancyConfiguration.java index 0ab9d651d8..7a7501bb9c 100644 --- a/src/main/java/org/opensearch/security/privileges/DashboardsMultiTenancyConfiguration.java +++ b/src/main/java/org/opensearch/security/privileges/DashboardsMultiTenancyConfiguration.java @@ -11,10 +11,10 @@ package org.opensearch.security.privileges; -import org.opensearch.security.securityconf.impl.v7.ConfigV7; - import java.util.List; +import org.opensearch.security.securityconf.impl.v7.ConfigV7; + /** * Provides access to the current configuration related to Dashboards multi-tenancy. *

diff --git a/src/test/java/org/opensearch/security/dlic/rest/validation/RequestContentValidatorTest.java b/src/test/java/org/opensearch/security/dlic/rest/validation/RequestContentValidatorTest.java index 7723d79233..a5000de215 100644 --- a/src/test/java/org/opensearch/security/dlic/rest/validation/RequestContentValidatorTest.java +++ b/src/test/java/org/opensearch/security/dlic/rest/validation/RequestContentValidatorTest.java @@ -665,7 +665,10 @@ public void testArrayOfStringsValidatorAcceptsStringArray() { public void testArrayOfStringsValidatorRejectsNonStringElement() { final ObjectNode node = DefaultObjectMapper.objectMapper.createObjectNode(); node.putArray("arr").add("one").add(2); - expectThrows(IllegalArgumentException.class, () -> RequestContentValidator.ARRAY_OF_STRINGS_VALIDATOR.validate("arr", node.get("arr"))); + expectThrows( + IllegalArgumentException.class, + () -> RequestContentValidator.ARRAY_OF_STRINGS_VALIDATOR.validate("arr", node.get("arr")) + ); } @Test 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 3ac2732172..d94661074f 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 @@ -11,11 +11,11 @@ package org.opensearch.security.securityconf.impl.v7; -import com.fasterxml.jackson.databind.JsonNode; -import com.google.common.collect.ImmutableList; - import java.util.Arrays; import java.util.Collections; + +import com.google.common.collect.ImmutableList; +import com.fasterxml.jackson.databind.JsonNode; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; From e63c7e748859eb862f9a087b4022458fb3fce175 Mon Sep 17 00:00:00 2001 From: Vishnutheep B Date: Fri, 27 Mar 2026 02:25:03 +0530 Subject: [PATCH 6/6] Removed DataType as just the key name is sufficient Signed-off-by: Vishnutheep B --- .../rest/validation/RequestContentValidator.java | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/opensearch/security/dlic/rest/validation/RequestContentValidator.java b/src/main/java/org/opensearch/security/dlic/rest/validation/RequestContentValidator.java index 6cce8efda6..db7fc4fcd3 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/validation/RequestContentValidator.java +++ b/src/main/java/org/opensearch/security/dlic/rest/validation/RequestContentValidator.java @@ -382,14 +382,10 @@ private ValidationResult validateDataType(final JsonNode jsonContent) private ValidationResult nullValuesInArrayValidator(final JsonNode jsonContent) { // Use allowedKeysWithConfig if provided, otherwise fall back to allowedKeys final Map fieldConfigs = validationContext.allowedKeysWithConfig(); - final Map allowedKeys = new HashMap<>(); - if (fieldConfigs != null) { - fieldConfigs.forEach((fieldName, fieldConfig) -> { allowedKeys.put(fieldName, fieldConfig.dataType); }); - } else { - allowedKeys.putAll(validationContext.allowedKeys()); - } - for (final Map.Entry allowedKey : allowedKeys.entrySet()) { - JsonNode value = jsonContent.get(allowedKey.getKey()); + final Set allowedKeys = (fieldConfigs != null) ? fieldConfigs.keySet() : validationContext.allowedKeys().keySet(); + + for (final String key : allowedKeys) { + JsonNode value = jsonContent.get(key); if (value != null) { if (hasNullOrBlankArrayElement(value)) { this.validationError = ValidationError.NULL_ARRAY_ELEMENT;