From f79faec442d061ccf28abe7af163a73b9231ac8e Mon Sep 17 00:00:00 2001 From: Ryan Liang <109499885+RyanL1997@users.noreply.github.com> Date: Tue, 18 Apr 2023 09:24:16 -0700 Subject: [PATCH] [Backport 2.7] Manual Backport of Dynamic tenancy configurations (#2693) (#2696) * Manual Backport of Dynamic tenancy configurations Signed-off-by: Stephen Crawford Signed-off-by: Abhi Kalra Co-authored-by: Stephen Crawford <65832608+scrawfor99@users.noreply.github.com> --- config/config.yml | 2 + legacy/securityconfig_v6/config.yml | 1 + .../security/OpenSearchSecurityPlugin.java | 96 ++++++----- .../security/action/tenancy/EmptyRequest.java | 35 ++++ .../tenancy/TenancyConfigRestHandler.java | 65 ++++++++ .../tenancy/TenancyConfigRetrieveActions.java | 24 +++ .../TenancyConfigRetrieveResponse.java | 70 ++++++++ .../TenancyConfigRetrieveTransportAction.java | 63 +++++++ .../tenancy/TenancyConfigUpdateAction.java | 26 +++ .../tenancy/TenancyConfigUpdateRequest.java | 69 ++++++++ .../TenancyConfigUpdateTransportAction.java | 155 ++++++++++++++++++ .../action/tenancy/TenancyConfigs.java | 18 ++ .../PrivilegesInterceptorImpl.java | 7 + .../dlic/rest/api/AbstractApiAction.java | 18 +- .../dlic/rest/api/AccountApiAction.java | 2 +- .../dlic/rest/api/AllowlistApiAction.java | 2 +- .../dlic/rest/api/InternalUsersApiAction.java | 2 +- .../dlic/rest/api/MigrateApiAction.java | 10 +- .../rest/api/PatchableResourceApiAction.java | 4 +- .../dlic/rest/api/RolesMappingApiAction.java | 2 +- .../privileges/PrivilegesEvaluator.java | 9 + .../security/rest/DashboardsInfoAction.java | 3 + .../securityconf/DynamicConfigModel.java | 2 + .../securityconf/DynamicConfigModelV6.java | 6 + .../securityconf/DynamicConfigModelV7.java | 6 + .../securityconf/impl/v6/ConfigV6.java | 4 + .../securityconf/impl/v7/ConfigV7.java | 10 +- .../security/support/ConfigConstants.java | 4 + .../test/TenancyDefaultTenantTests.java | 96 +++++++++++ .../test/TenancyMultitenancyEnabledTests.java | 89 ++++++++++ .../TenancyPrivateTenantEnabledTests.java | 101 ++++++++++++ .../security/test/helper/rest/RestHelper.java | 14 +- src/test/resources/auditlog/config.yml | 2 + src/test/resources/cache/config.yml | 2 + src/test/resources/composite_config.yml | 2 + src/test/resources/config.yml | 2 + src/test/resources/config_anon.yml | 2 + src/test/resources/config_clientcert.yml | 2 + src/test/resources/config_disable_all.yml | 2 + src/test/resources/config_dnfof.yml | 2 + src/test/resources/config_invalidlic.yml | 2 + src/test/resources/config_lic.yml | 2 + src/test/resources/config_lic_rk.yml | 2 + src/test/resources/config_multirolespan.yml | 2 + .../resources/config_protected_indices.yml | 2 + src/test/resources/config_proxy.yml | 2 + src/test/resources/config_proxy_custom.yml | 2 + .../config_respect_indices_options.yml | 2 + .../resources/config_rest_impersonation.yml | 2 + src/test/resources/config_system_indices.yml | 2 + .../resources/config_transport_username.yml | 2 + src/test/resources/config_xff.yml | 2 + src/test/resources/dlsfls/config.yml | 2 + src/test/resources/ldap/config.yml | 2 + src/test/resources/ldap/config_ldap2.yml | 2 + src/test/resources/multitenancy/config.yml | 2 + .../multitenancy/config_anonymous.yml | 2 + .../multitenancy/config_basic_auth.yml | 2 + .../resources/multitenancy/config_nodnfof.yml | 2 + src/test/resources/restapi/config.yml | 2 + .../resources/restapi/invalid_config.json | 2 + .../resources/restapi/security_config.json | 2 + .../resources/restapi/securityconfig.json | 2 + .../restapi/securityconfig_nondefault.json | 2 + .../resources/security_passive/config.yml | 2 + 65 files changed, 1007 insertions(+), 74 deletions(-) create mode 100644 src/main/java/org/opensearch/security/action/tenancy/EmptyRequest.java create mode 100644 src/main/java/org/opensearch/security/action/tenancy/TenancyConfigRestHandler.java create mode 100644 src/main/java/org/opensearch/security/action/tenancy/TenancyConfigRetrieveActions.java create mode 100644 src/main/java/org/opensearch/security/action/tenancy/TenancyConfigRetrieveResponse.java create mode 100644 src/main/java/org/opensearch/security/action/tenancy/TenancyConfigRetrieveTransportAction.java create mode 100644 src/main/java/org/opensearch/security/action/tenancy/TenancyConfigUpdateAction.java create mode 100644 src/main/java/org/opensearch/security/action/tenancy/TenancyConfigUpdateRequest.java create mode 100644 src/main/java/org/opensearch/security/action/tenancy/TenancyConfigUpdateTransportAction.java create mode 100644 src/main/java/org/opensearch/security/action/tenancy/TenancyConfigs.java create mode 100644 src/test/java/org/opensearch/security/multitenancy/test/TenancyDefaultTenantTests.java create mode 100644 src/test/java/org/opensearch/security/multitenancy/test/TenancyMultitenancyEnabledTests.java create mode 100644 src/test/java/org/opensearch/security/multitenancy/test/TenancyPrivateTenantEnabledTests.java diff --git a/config/config.yml b/config/config.yml index 0537ff8406..1493a0d7f1 100644 --- a/config/config.yml +++ b/config/config.yml @@ -68,6 +68,8 @@ config: #kibana: # Kibana multitenancy #multitenancy_enabled: true + #private_tenant_enabled: true + #default_tenant: "" #server_username: kibanaserver #index: '.kibana' http: diff --git a/legacy/securityconfig_v6/config.yml b/legacy/securityconfig_v6/config.yml index da2be1e38d..15d5ee9973 100644 --- a/legacy/securityconfig_v6/config.yml +++ b/legacy/securityconfig_v6/config.yml @@ -60,6 +60,7 @@ opendistro_security: #filtered_alias_mode: warn #kibana: #multitenancy_enabled: true + #server_username: kibanaserver #index: '.kibana' #do_not_fail_on_forbidden: false diff --git a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java index ca5d4e6ce4..c5b77b2a0b 100644 --- a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java +++ b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java @@ -115,6 +115,11 @@ import org.opensearch.search.query.QuerySearchResult; import org.opensearch.security.action.configupdate.ConfigUpdateAction; import org.opensearch.security.action.configupdate.TransportConfigUpdateAction; +import org.opensearch.security.action.tenancy.TenancyConfigRestHandler; +import org.opensearch.security.action.tenancy.TenancyConfigRetrieveActions; +import org.opensearch.security.action.tenancy.TenancyConfigRetrieveTransportAction; +import org.opensearch.security.action.tenancy.TenancyConfigUpdateAction; +import org.opensearch.security.action.tenancy.TenancyConfigUpdateTransportAction; import org.opensearch.security.action.whoami.TransportWhoAmIAction; import org.opensearch.security.action.whoami.WhoAmIAction; import org.opensearch.security.auditlog.AuditLog; @@ -328,7 +333,7 @@ public Object run() { final List filesWithWrongPermissions = AccessController.doPrivileged(new PrivilegedAction>() { @Override public List run() { - final Path confPath = new Environment(settings, configPath).configFile().toAbsolutePath(); + final Path confPath = new Environment(settings, configPath).configFile().toAbsolutePath(); if(Files.isDirectory(confPath, LinkOption.NOFOLLOW_LINKS)) { try (Stream s = Files.walk(confPath)) { return s.distinct().filter(p -> checkFilePermissions(p)).collect(Collectors.toList()); @@ -358,7 +363,7 @@ public List run() { final List files = AccessController.doPrivileged(new PrivilegedAction>() { @Override public List run() { - final Path confPath = new Environment(settings, configPath).configFile().toAbsolutePath(); + final Path confPath = new Environment(settings, configPath).configFile().toAbsolutePath(); if(Files.isDirectory(confPath, LinkOption.NOFOLLOW_LINKS)) { try (Stream s = Files.walk(confPath)) { return s.distinct().map(p -> sha256(p)).collect(Collectors.toList()); @@ -455,8 +460,8 @@ private boolean checkFilePermissions(final Path p) { @Override public List getRestHandlers(Settings settings, RestController restController, ClusterSettings clusterSettings, - IndexScopedSettings indexScopedSettings, SettingsFilter settingsFilter, - IndexNameExpressionResolver indexNameExpressionResolver, Supplier nodesInCluster) { + IndexScopedSettings indexScopedSettings, SettingsFilter settingsFilter, + IndexNameExpressionResolver indexNameExpressionResolver, Supplier nodesInCluster) { final List handlers = new ArrayList(1); @@ -470,7 +475,8 @@ public List getRestHandlers(Settings settings, RestController restC handlers.add(new SecuritySSLCertsInfoAction(settings, restController, sks, Objects.requireNonNull(threadPool), Objects.requireNonNull(adminDns))); handlers.add(new DashboardsInfoAction(settings, restController, Objects.requireNonNull(evaluator), Objects.requireNonNull(threadPool))); handlers.add(new TenantInfoAction(settings, restController, Objects.requireNonNull(evaluator), Objects.requireNonNull(threadPool), - Objects.requireNonNull(cs), Objects.requireNonNull(adminDns), Objects.requireNonNull(cr))); + Objects.requireNonNull(cs), Objects.requireNonNull(adminDns), Objects.requireNonNull(cr))); + handlers.add(new TenancyConfigRestHandler()); handlers.add(new SecurityConfigUpdateAction(settings, restController,Objects.requireNonNull(threadPool), adminDns, configPath, principalExtractor)); handlers.add(new SecurityWhoAmIAction(settings ,restController,Objects.requireNonNull(threadPool), adminDns, configPath, principalExtractor)); if (sslCertReloadEnabled) { @@ -501,6 +507,8 @@ public UnaryOperator getRestHandlerWrapper(final ThreadContext thre if(!disabled && !SSLConfig.isSslOnlyMode()) { actions.add(new ActionHandler<>(ConfigUpdateAction.INSTANCE, TransportConfigUpdateAction.class)); actions.add(new ActionHandler<>(WhoAmIAction.INSTANCE, TransportWhoAmIAction.class)); + actions.add(new ActionHandler<>(TenancyConfigRetrieveActions.INSTANCE, TenancyConfigRetrieveTransportAction.class)); + actions.add(new ActionHandler<>(TenancyConfigUpdateAction.INSTANCE, TenancyConfigUpdateTransportAction.class)); } return actions; } @@ -651,7 +659,7 @@ public List getTransportInterceptors(NamedWriteableRegistr @Override public TransportRequestHandler interceptHandler(String action, String executor, - boolean forceExecution, TransportRequestHandler actualHandler) { + boolean forceExecution, TransportRequestHandler actualHandler) { return new TransportRequestHandler() { @@ -670,7 +678,7 @@ public AsyncSender interceptSender(AsyncSender sender) { @Override public void sendRequest(Connection connection, String action, - TransportRequest request, TransportRequestOptions options, TransportResponseHandler handler) { + TransportRequest request, TransportRequestOptions options, TransportResponseHandler handler) { si.sendRequestDecorate(sender, connection, action, request, options, handler); } }; @@ -683,7 +691,7 @@ public void sendRequest(Connection connection, Str @Override public Map> getTransports(Settings settings, ThreadPool threadPool, PageCacheRecycler pageCacheRecycler, - CircuitBreakerService circuitBreakerService, NamedWriteableRegistry namedWriteableRegistry, NetworkService networkService) { + CircuitBreakerService circuitBreakerService, NamedWriteableRegistry namedWriteableRegistry, NetworkService networkService) { Map> transports = new HashMap>(); if(SSLConfig.isSslOnlyMode()) { @@ -700,12 +708,12 @@ public Map> getTransports(Settings settings, ThreadP @Override public Map> getHttpTransports(Settings settings, ThreadPool threadPool, BigArrays bigArrays, - PageCacheRecycler pageCacheRecycler, CircuitBreakerService circuitBreakerService, NamedXContentRegistry xContentRegistry, - NetworkService networkService, Dispatcher dispatcher, ClusterSettings clusterSettings) { + PageCacheRecycler pageCacheRecycler, CircuitBreakerService circuitBreakerService, NamedXContentRegistry xContentRegistry, + NetworkService networkService, Dispatcher dispatcher, ClusterSettings clusterSettings) { if(SSLConfig.isSslOnlyMode()) { return super.getHttpTransports(settings, threadPool, bigArrays, pageCacheRecycler, circuitBreakerService, xContentRegistry, - networkService, dispatcher, clusterSettings); + networkService, dispatcher, clusterSettings); } if(!disabled) { @@ -731,9 +739,9 @@ public Map> getHttpTransports(Settings set @Override public Collection createComponents(Client localClient, ClusterService clusterService, ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, ScriptService scriptService, NamedXContentRegistry xContentRegistry, - Environment environment, NodeEnvironment nodeEnvironment, NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver indexNameExpressionResolver, Supplier repositoriesServiceSupplier) { + ResourceWatcherService resourceWatcherService, ScriptService scriptService, NamedXContentRegistry xContentRegistry, + Environment environment, NodeEnvironment nodeEnvironment, NamedWriteableRegistry namedWriteableRegistry, + IndexNameExpressionResolver indexNameExpressionResolver, Supplier repositoriesServiceSupplier) { SSLConfig.registerClusterSettingsChangeListener(clusterService.getClusterSettings()); if(SSLConfig.isSslOnlyMode()) { @@ -798,7 +806,7 @@ public Collection createComponents(Client localClient, ClusterService cl sslExceptionHandler = new AuditLogSslExceptionHandler(auditLog); adminDns = new AdminDNs(settings); - + cr = ConfigurationRepository.create(settings, this.configPath, threadPool, localClient, clusterService, auditLog); final XFFResolver xffResolver = new XFFResolver(threadPool); @@ -812,7 +820,7 @@ public Collection createComponents(Client localClient, ClusterService cl settings, privilegesInterceptor, cih, irr, dlsFlsEnabled, namedXContentRegistry.get()); sf = new SecurityFilter(settings, evaluator, adminDns, dlsFlsValve, auditLog, threadPool, cs, compatConfig, irr, xffResolver); - + final String principalExtractorClass = settings.get(SSLConfigConstants.SECURITY_SSL_TRANSPORT_PRINCIPAL_EXTRACTOR_CLASS, null); if(principalExtractorClass == null) { @@ -875,8 +883,8 @@ public Settings additionalSettings() { builder.put(super.additionalSettings()); if(!SSLConfig.isSslOnlyMode()){ - builder.put(NetworkModule.TRANSPORT_TYPE_KEY, "org.opensearch.security.ssl.http.netty.SecuritySSLNettyTransport"); - builder.put(NetworkModule.HTTP_TYPE_KEY, "org.opensearch.security.http.SecurityHttpServerTransport"); + builder.put(NetworkModule.TRANSPORT_TYPE_KEY, "org.opensearch.security.ssl.http.netty.SecuritySSLNettyTransport"); + builder.put(NetworkModule.HTTP_TYPE_KEY, "org.opensearch.security.http.SecurityHttpServerTransport"); } return builder.build(); } @@ -884,7 +892,7 @@ public Settings additionalSettings() { public List> getSettings() { List> settings = new ArrayList>(); settings.addAll(super.getSettings()); - + settings.add(Setting.boolSetting(ConfigConstants.SECURITY_SSL_ONLY, false, Property.NodeScope, Property.Filtered)); // currently dual mode is supported only when ssl_only is enabled, but this stance would change in future @@ -902,26 +910,26 @@ public List> getSettings() { if(!SSLConfig.isSslOnlyMode()) { settings.add(Setting.listSetting(ConfigConstants.SECURITY_AUTHCZ_ADMIN_DN, Collections.emptyList(), Function.identity(), Property.NodeScope)); //not filtered here - + settings.add(Setting.simpleString(ConfigConstants.SECURITY_CONFIG_INDEX_NAME, Property.NodeScope, Property.Filtered)); settings.add(Setting.groupSetting(ConfigConstants.SECURITY_AUTHCZ_IMPERSONATION_DN+".", Property.NodeScope)); //not filtered here - + settings.add(Setting.simpleString(ConfigConstants.SECURITY_CERT_OID, Property.NodeScope, Property.Filtered)); - + settings.add(Setting.simpleString(ConfigConstants.SECURITY_CERT_INTERCLUSTER_REQUEST_EVALUATOR_CLASS, Property.NodeScope, Property.Filtered)); settings.add(Setting.listSetting(ConfigConstants.SECURITY_NODES_DN, Collections.emptyList(), Function.identity(), Property.NodeScope));//not filtered here settings.add(Setting.boolSetting(ConfigConstants.SECURITY_NODES_DN_DYNAMIC_CONFIG_ENABLED, false, Property.NodeScope));//not filtered here - + settings.add(Setting.boolSetting(ConfigConstants.SECURITY_ENABLE_SNAPSHOT_RESTORE_PRIVILEGE, ConfigConstants.SECURITY_DEFAULT_ENABLE_SNAPSHOT_RESTORE_PRIVILEGE, Property.NodeScope, Property.Filtered)); settings.add(Setting.boolSetting(ConfigConstants.SECURITY_CHECK_SNAPSHOT_RESTORE_WRITE_PRIVILEGES, ConfigConstants.SECURITY_DEFAULT_CHECK_SNAPSHOT_RESTORE_WRITE_PRIVILEGES, Property.NodeScope, Property.Filtered)); - + settings.add(Setting.boolSetting(ConfigConstants.SECURITY_DISABLED, false, Property.NodeScope, Property.Filtered)); - + settings.add(Setting.intSetting(ConfigConstants.SECURITY_CACHE_TTL_MINUTES, 60, 0, Property.NodeScope, Property.Filtered)); - + //Security settings.add(Setting.boolSetting(ConfigConstants.SECURITY_ADVANCED_MODULES_ENABLED, true, Property.NodeScope, Property.Filtered)); settings.add(Setting.boolSetting(ConfigConstants.SECURITY_ALLOW_UNSAFE_DEMOCERTIFICATES, false, Property.NodeScope, Property.Filtered)); @@ -929,10 +937,10 @@ public List> getSettings() { settings.add(Setting.boolSetting(ConfigConstants.SECURITY_BACKGROUND_INIT_IF_SECURITYINDEX_NOT_EXIST, true, Property.NodeScope, Property.Filtered)); settings.add(Setting.boolSetting(ConfigConstants.SECURITY_DFM_EMPTY_OVERRIDES_ALL, false, Property.NodeScope, Property.Filtered)); settings.add(Setting.groupSetting(ConfigConstants.SECURITY_AUTHCZ_REST_IMPERSONATION_USERS+".", Property.NodeScope)); //not filtered here - + settings.add(Setting.simpleString(ConfigConstants.SECURITY_ROLES_MAPPING_RESOLUTION, Property.NodeScope, Property.Filtered)); settings.add(Setting.boolSetting(ConfigConstants.SECURITY_DISABLE_ENVVAR_REPLACEMENT, false, Property.NodeScope, Property.Filtered)); - + // Security - Audit settings.add(Setting.simpleString(ConfigConstants.SECURITY_AUDIT_TYPE_DEFAULT, Property.NodeScope, Property.Filtered)); settings.add(Setting.groupSetting(ConfigConstants.SECURITY_AUDIT_CONFIG_ROUTES + ".", Property.NodeScope)); @@ -954,7 +962,7 @@ public List> getSettings() { settings.add(Setting.listSetting(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_IGNORE_REQUESTS, Collections.emptyList(), Function.identity(), Property.NodeScope)); //not filtered here settings.add(Setting.boolSetting(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_RESOLVE_BULK_REQUESTS, false, Property.NodeScope, Property.Filtered)); settings.add(Setting.boolSetting(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_EXCLUDE_SENSITIVE_HEADERS, true, Property.NodeScope, Property.Filtered)); - + final BiFunction> boolSettingNodeScopeFiltered = (String keyWithNamespace, Boolean value) -> Setting.boolSetting(keyWithNamespace, value, Property.NodeScope, Property.Filtered); Arrays.stream(FilterEntries.values()).map(filterEntry -> { @@ -979,12 +987,12 @@ public List> getSettings() { throw new RuntimeException("Please add support for new FilterEntries value '" + filterEntry.name() + "'"); } }).forEach(settings::add); - - + + // Security - Audit - Sink settings.add(Setting.simpleString(ConfigConstants.SECURITY_AUDIT_CONFIG_DEFAULT_PREFIX + ConfigConstants.SECURITY_AUDIT_OPENSEARCH_INDEX, Property.NodeScope, Property.Filtered)); settings.add(Setting.simpleString(ConfigConstants.SECURITY_AUDIT_CONFIG_DEFAULT_PREFIX + ConfigConstants.SECURITY_AUDIT_OPENSEARCH_TYPE, Property.NodeScope, Property.Filtered)); - + // External OpenSearch settings.add(Setting.listSetting(ConfigConstants.SECURITY_AUDIT_CONFIG_DEFAULT_PREFIX + ConfigConstants.SECURITY_AUDIT_EXTERNAL_OPENSEARCH_HTTP_ENDPOINTS, Lists.newArrayList("localhost:9200"), Function.identity(), Property.NodeScope)); //not filtered here settings.add(Setting.simpleString(ConfigConstants.SECURITY_AUDIT_CONFIG_DEFAULT_PREFIX + ConfigConstants.SECURITY_AUDIT_EXTERNAL_OPENSEARCH_USERNAME, Property.NodeScope, Property.Filtered)); @@ -1002,33 +1010,33 @@ public List> getSettings() { settings.add(Setting.simpleString(ConfigConstants.SECURITY_AUDIT_CONFIG_DEFAULT_PREFIX + ConfigConstants.SECURITY_AUDIT_EXTERNAL_OPENSEARCH_JKS_CERT_ALIAS, Property.NodeScope, Property.Filtered)); settings.add(Setting.listSetting(ConfigConstants.SECURITY_AUDIT_CONFIG_DEFAULT_PREFIX + ConfigConstants.SECURITY_AUDIT_EXTERNAL_OPENSEARCH_ENABLED_SSL_CIPHERS, Collections.emptyList(), Function.identity(), Property.NodeScope));//not filtered here settings.add(Setting.listSetting(ConfigConstants.SECURITY_AUDIT_CONFIG_DEFAULT_PREFIX + ConfigConstants.SECURITY_AUDIT_EXTERNAL_OPENSEARCH_ENABLED_SSL_PROTOCOLS, Collections.emptyList(), Function.identity(), Property.NodeScope));//not filtered here - + // Webhooks settings.add(Setting.simpleString(ConfigConstants.SECURITY_AUDIT_CONFIG_DEFAULT_PREFIX + ConfigConstants.SECURITY_AUDIT_WEBHOOK_URL, Property.NodeScope, Property.Filtered)); settings.add(Setting.simpleString(ConfigConstants.SECURITY_AUDIT_CONFIG_DEFAULT_PREFIX + ConfigConstants.SECURITY_AUDIT_WEBHOOK_FORMAT, Property.NodeScope, Property.Filtered)); settings.add(Setting.boolSetting(ConfigConstants.SECURITY_AUDIT_CONFIG_DEFAULT_PREFIX + ConfigConstants.SECURITY_AUDIT_WEBHOOK_SSL_VERIFY, true, Property.NodeScope, Property.Filtered)); settings.add(Setting.simpleString(ConfigConstants.SECURITY_AUDIT_CONFIG_DEFAULT_PREFIX + ConfigConstants.SECURITY_AUDIT_WEBHOOK_PEMTRUSTEDCAS_FILEPATH, Property.NodeScope, Property.Filtered)); settings.add(Setting.simpleString(ConfigConstants.SECURITY_AUDIT_CONFIG_DEFAULT_PREFIX + ConfigConstants.SECURITY_AUDIT_WEBHOOK_PEMTRUSTEDCAS_CONTENT, Property.NodeScope, Property.Filtered)); - + // Log4j settings.add(Setting.simpleString(ConfigConstants.SECURITY_AUDIT_CONFIG_DEFAULT_PREFIX + ConfigConstants.SECURITY_AUDIT_LOG4J_LOGGER_NAME, Property.NodeScope, Property.Filtered)); settings.add(Setting.simpleString(ConfigConstants.SECURITY_AUDIT_CONFIG_DEFAULT_PREFIX + ConfigConstants.SECURITY_AUDIT_LOG4J_LEVEL, Property.NodeScope, Property.Filtered)); - - + + // Kerberos settings.add(Setting.simpleString(ConfigConstants.SECURITY_KERBEROS_KRB5_FILEPATH, Property.NodeScope, Property.Filtered)); settings.add(Setting.simpleString(ConfigConstants.SECURITY_KERBEROS_ACCEPTOR_KEYTAB_FILEPATH, Property.NodeScope, Property.Filtered)); settings.add(Setting.simpleString(ConfigConstants.SECURITY_KERBEROS_ACCEPTOR_PRINCIPAL, Property.NodeScope, Property.Filtered)); - - + + // OpenSearch Security - REST API settings.add(Setting.listSetting(ConfigConstants.SECURITY_RESTAPI_ROLES_ENABLED, Collections.emptyList(), Function.identity(), Property.NodeScope)); //not filtered here settings.add(Setting.groupSetting(ConfigConstants.SECURITY_RESTAPI_ENDPOINTS_DISABLED + ".", Property.NodeScope)); - + settings.add(Setting.simpleString(ConfigConstants.SECURITY_RESTAPI_PASSWORD_VALIDATION_REGEX, Property.NodeScope, Property.Filtered)); settings.add(Setting.simpleString(ConfigConstants.SECURITY_RESTAPI_PASSWORD_VALIDATION_ERROR_MESSAGE, Property.NodeScope, Property.Filtered)); - + // Compliance settings.add(Setting.listSetting(ConfigConstants.OPENDISTRO_SECURITY_COMPLIANCE_HISTORY_WRITE_WATCHED_INDICES, Collections.emptyList(), Function.identity(), Property.NodeScope)); //not filtered here settings.add(Setting.listSetting(ConfigConstants.OPENDISTRO_SECURITY_COMPLIANCE_HISTORY_READ_WATCHED_FIELDS, Collections.emptyList(), Function.identity(), Property.NodeScope)); //not filtered here @@ -1061,7 +1069,7 @@ public List> getSettings() { settings.add(Setting.boolSetting(ConfigConstants.SECURITY_SSL_CERT_RELOAD_ENABLED, false, Property.NodeScope, Property.Filtered)); settings.add(Setting.boolSetting(ConfigConstants.SECURITY_UNSUPPORTED_ACCEPT_INVALID_CONFIG, false, Property.NodeScope, Property.Filtered)); } - + return settings; } @@ -1076,7 +1084,7 @@ public List getSettingsFilter() { settingsFilter.add("plugins.security.*"); return settingsFilter; } - + @Override public void onNodeStarted() { log.info("Node started"); @@ -1168,7 +1176,7 @@ public static class GuiceHolder implements LifecycleComponent { @Inject public GuiceHolder(final RepositoriesService repositoriesService, - final TransportService remoteClusterService, IndicesService indicesService, PitService pitService) { + final TransportService remoteClusterService, IndicesService indicesService, PitService pitService) { GuiceHolder.repositoriesService = repositoriesService; GuiceHolder.remoteClusterService = remoteClusterService.getRemoteClusterService(); GuiceHolder.indicesService = indicesService; @@ -1188,7 +1196,7 @@ public static IndicesService getIndicesService() { } public static PitService getPitService() { return pitService; } - + @Override public void close() { } diff --git a/src/main/java/org/opensearch/security/action/tenancy/EmptyRequest.java b/src/main/java/org/opensearch/security/action/tenancy/EmptyRequest.java new file mode 100644 index 0000000000..607216a8c3 --- /dev/null +++ b/src/main/java/org/opensearch/security/action/tenancy/EmptyRequest.java @@ -0,0 +1,35 @@ +/* + * 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.action.tenancy; + +import java.io.IOException; + +import org.opensearch.action.ActionRequest; +import org.opensearch.action.ActionRequestValidationException; +import org.opensearch.common.io.stream.StreamInput; + +public class EmptyRequest extends ActionRequest { + + public EmptyRequest(final StreamInput in) throws IOException { + super(in); + } + + public EmptyRequest() throws IOException { + super(); + } + + @Override + public ActionRequestValidationException validate() + { + return null; + } +} diff --git a/src/main/java/org/opensearch/security/action/tenancy/TenancyConfigRestHandler.java b/src/main/java/org/opensearch/security/action/tenancy/TenancyConfigRestHandler.java new file mode 100644 index 0000000000..0a00d16694 --- /dev/null +++ b/src/main/java/org/opensearch/security/action/tenancy/TenancyConfigRestHandler.java @@ -0,0 +1,65 @@ +/* + * 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.action.tenancy; + +import java.io.IOException; +import java.util.List; + +import com.google.common.collect.ImmutableList; + +import org.opensearch.client.node.NodeClient; +import org.opensearch.rest.BaseRestHandler; +import org.opensearch.rest.RestRequest; +import org.opensearch.rest.action.RestToXContentListener; + +import static org.opensearch.rest.RestRequest.Method.GET; +import static org.opensearch.rest.RestRequest.Method.PUT; + +public class TenancyConfigRestHandler extends BaseRestHandler { + + public TenancyConfigRestHandler() { + super(); + } + + @Override + public String getName() { + return "Multi Tenancy actions to Retrieve / Update configs."; + } + + @Override + public List routes() { + return ImmutableList.of( + new Route(GET, "/_plugins/_security/api/tenancy/config"), + new Route(PUT, "/_plugins/_security/api/tenancy/config") + ); + } + + @Override + protected RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient nodeClient) throws IOException { + + switch (request.method()) { + case GET: + return channel -> nodeClient.execute( + TenancyConfigRetrieveActions.INSTANCE, + new EmptyRequest(), + new RestToXContentListener<>(channel)); + case PUT: + return channel -> nodeClient.execute( + TenancyConfigUpdateAction.INSTANCE, + TenancyConfigUpdateRequest.fromXContent(request.contentParser()), + new RestToXContentListener<>(channel)); + default: + throw new RuntimeException("Not implemented"); + } + } + +} diff --git a/src/main/java/org/opensearch/security/action/tenancy/TenancyConfigRetrieveActions.java b/src/main/java/org/opensearch/security/action/tenancy/TenancyConfigRetrieveActions.java new file mode 100644 index 0000000000..796f233f13 --- /dev/null +++ b/src/main/java/org/opensearch/security/action/tenancy/TenancyConfigRetrieveActions.java @@ -0,0 +1,24 @@ +/* + * 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.action.tenancy; + +import org.opensearch.action.ActionType; + +public class TenancyConfigRetrieveActions extends ActionType { + + public static final TenancyConfigRetrieveActions INSTANCE = new TenancyConfigRetrieveActions(); + public static final String NAME = "cluster:feature/tenancy/config/read"; + + protected TenancyConfigRetrieveActions() { + super(NAME, TenancyConfigRetrieveResponse::new); + } +} diff --git a/src/main/java/org/opensearch/security/action/tenancy/TenancyConfigRetrieveResponse.java b/src/main/java/org/opensearch/security/action/tenancy/TenancyConfigRetrieveResponse.java new file mode 100644 index 0000000000..463cc7d831 --- /dev/null +++ b/src/main/java/org/opensearch/security/action/tenancy/TenancyConfigRetrieveResponse.java @@ -0,0 +1,70 @@ +/* + * 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.action.tenancy; + +import java.io.IOException; + +import org.opensearch.action.ActionResponse; +import org.opensearch.common.Strings; +import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; + +public class TenancyConfigRetrieveResponse extends ActionResponse implements ToXContentObject { + + public TenancyConfigs tenancyConfigs = new TenancyConfigs(); + + public TenancyConfigRetrieveResponse(final StreamInput in) throws IOException { + super(in); + this.tenancyConfigs.multitenancy_enabled = in.readOptionalBoolean(); + this.tenancyConfigs.private_tenant_enabled = in.readOptionalBoolean(); + this.tenancyConfigs.default_tenant = in.readOptionalString(); + } + + public TenancyConfigRetrieveResponse(final TenancyConfigs tenancyConfigs) { + this.tenancyConfigs = tenancyConfigs; + } + + public TenancyConfigs getMultitenancyConfig() { + return tenancyConfigs; + } + + public Boolean getMultitenancyEnabled() { return tenancyConfigs.multitenancy_enabled; } + + public Boolean getPrivateTenantEnabled() { return tenancyConfigs.private_tenant_enabled; } + + public String getDefaultTenant() { return tenancyConfigs.default_tenant; } + + @Override + public void writeTo(final StreamOutput out) throws IOException { + out.writeBoolean(getMultitenancyEnabled()); + out.writeBoolean(getPrivateTenantEnabled()); + out.writeString(getDefaultTenant()); + } + + @Override + public String toString() { + return Strings.toString(XContentType.JSON, this, true, true); + } + + @Override + public XContentBuilder toXContent(final XContentBuilder builder, final Params params) throws IOException { + builder.startObject(); + builder.field("multitenancy_enabled", getMultitenancyEnabled()); + builder.field("private_tenant_enabled", getPrivateTenantEnabled()); + builder.field("default_tenant", getDefaultTenant()); + builder.endObject(); + return builder; + } +} diff --git a/src/main/java/org/opensearch/security/action/tenancy/TenancyConfigRetrieveTransportAction.java b/src/main/java/org/opensearch/security/action/tenancy/TenancyConfigRetrieveTransportAction.java new file mode 100644 index 0000000000..a68bbae85e --- /dev/null +++ b/src/main/java/org/opensearch/security/action/tenancy/TenancyConfigRetrieveTransportAction.java @@ -0,0 +1,63 @@ +/* + * 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.action.tenancy; + +import java.util.Collections; + +import org.opensearch.action.ActionListener; +import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.HandledTransportAction; +import org.opensearch.common.inject.Inject; +import org.opensearch.common.settings.Settings; +import org.opensearch.security.configuration.ConfigurationRepository; +import org.opensearch.security.securityconf.impl.CType; +import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; +import org.opensearch.security.securityconf.impl.v7.ConfigV7; +import org.opensearch.tasks.Task; +import org.opensearch.transport.TransportService; + +public class TenancyConfigRetrieveTransportAction + extends HandledTransportAction { + + private final ConfigurationRepository config; + + @Inject + public TenancyConfigRetrieveTransportAction(final Settings settings, + final TransportService transportService, + final ActionFilters actionFilters, + final ConfigurationRepository config) { + super(TenancyConfigRetrieveActions.NAME, transportService, actionFilters, EmptyRequest::new); + + this.config = config; + } + + /** Load the configuration from the security index and return a copy */ + protected final SecurityDynamicConfiguration load() { + return config.getConfigurationsFromIndex(Collections.singleton(CType.CONFIG), false).get(CType.CONFIG).deepClone(); + } + + @Override + protected void doExecute(final Task task, final EmptyRequest request, final ActionListener listener) { + + // Get the security configuration and lookup the config setting state + final SecurityDynamicConfiguration dynamicConfig = load(); + ConfigV7 config = (ConfigV7)dynamicConfig.getCEntry("config"); + + final TenancyConfigs tenancyConfigs= new TenancyConfigs(); + + tenancyConfigs.multitenancy_enabled = config.dynamic.kibana.multitenancy_enabled; + tenancyConfigs.private_tenant_enabled = config.dynamic.kibana.private_tenant_enabled; + tenancyConfigs.default_tenant = config.dynamic.kibana.default_tenant; + + listener.onResponse(new TenancyConfigRetrieveResponse(tenancyConfigs)); + } +} diff --git a/src/main/java/org/opensearch/security/action/tenancy/TenancyConfigUpdateAction.java b/src/main/java/org/opensearch/security/action/tenancy/TenancyConfigUpdateAction.java new file mode 100644 index 0000000000..73d515b7d8 --- /dev/null +++ b/src/main/java/org/opensearch/security/action/tenancy/TenancyConfigUpdateAction.java @@ -0,0 +1,26 @@ +/* + * 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.action.tenancy; + +import org.opensearch.action.ActionType; + +public class TenancyConfigUpdateAction extends ActionType { + + public static final TenancyConfigUpdateAction INSTANCE = new TenancyConfigUpdateAction(); + public static final String NAME = "cluster:feature/tenancy/config/update"; + + + protected TenancyConfigUpdateAction() + { + super(NAME, TenancyConfigRetrieveResponse::new); + } +} diff --git a/src/main/java/org/opensearch/security/action/tenancy/TenancyConfigUpdateRequest.java b/src/main/java/org/opensearch/security/action/tenancy/TenancyConfigUpdateRequest.java new file mode 100644 index 0000000000..2d03f698a6 --- /dev/null +++ b/src/main/java/org/opensearch/security/action/tenancy/TenancyConfigUpdateRequest.java @@ -0,0 +1,69 @@ +/* + * 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.action.tenancy; +import java.io.IOException; + +import org.opensearch.action.ActionRequest; +import org.opensearch.action.ActionRequestValidationException; +import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentParser; + +public class TenancyConfigUpdateRequest extends ActionRequest { + + private TenancyConfigs tenancyConfigs = new TenancyConfigs(); + + public TenancyConfigUpdateRequest(final StreamInput in) throws IOException { + super(in); + in.readOptionalBoolean(); + in.readOptionalBoolean(); + in.readOptionalString(); + } + + public TenancyConfigUpdateRequest(final Boolean multitenancy_enabled, final Boolean private_tenant_enabled, final String default_tenant) { + super(); + this.tenancyConfigs.multitenancy_enabled = multitenancy_enabled; + this.tenancyConfigs.private_tenant_enabled = private_tenant_enabled; + this.tenancyConfigs.default_tenant = default_tenant; + } + + public TenancyConfigs getTenancyConfigs() { + return tenancyConfigs; + } + + @Override + public ActionRequestValidationException validate() { + if (getTenancyConfigs() == null) { + final ActionRequestValidationException validationException = new ActionRequestValidationException(); + validationException.addValidationError("Missing tenancy configs"); + return validationException; + } + return null; + } + + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + TenancyConfigUpdateRequest.class.getName(), + args -> new TenancyConfigUpdateRequest((Boolean)args[0], (Boolean) args[1], (String) args[2]) + ); + + static { + PARSER.declareBoolean(ConstructingObjectParser.optionalConstructorArg(), new ParseField("multitenancy_enabled")); + PARSER.declareBoolean(ConstructingObjectParser.optionalConstructorArg(), new ParseField("private_tenant_enabled")); + PARSER.declareString(ConstructingObjectParser.optionalConstructorArg(), new ParseField("default_tenant")); + + } + + public static TenancyConfigUpdateRequest fromXContent(final XContentParser parser) { + return PARSER.apply(parser, null); + } +} diff --git a/src/main/java/org/opensearch/security/action/tenancy/TenancyConfigUpdateTransportAction.java b/src/main/java/org/opensearch/security/action/tenancy/TenancyConfigUpdateTransportAction.java new file mode 100644 index 0000000000..1d4b563ca4 --- /dev/null +++ b/src/main/java/org/opensearch/security/action/tenancy/TenancyConfigUpdateTransportAction.java @@ -0,0 +1,155 @@ +/* + * 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.action.tenancy; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import org.opensearch.action.ActionListener; +import org.opensearch.action.index.IndexResponse; +import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.HandledTransportAction; +import org.opensearch.client.Client; +import org.opensearch.common.inject.Inject; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.concurrent.ThreadContext; +import org.opensearch.security.configuration.ConfigurationRepository; +import org.opensearch.security.dlic.rest.api.AbstractApiAction; +import org.opensearch.security.securityconf.impl.CType; +import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; +import org.opensearch.security.securityconf.impl.v7.ConfigV7; +import org.opensearch.security.support.ConfigConstants; +import org.opensearch.tasks.Task; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; + +public class TenancyConfigUpdateTransportAction extends HandledTransportAction { + + private static final Logger log = LogManager.getLogger(TenancyConfigUpdateTransportAction.class); + + private final String securityIndex; + private final ConfigurationRepository config; + private final Client client; + private final ThreadPool pool; + + @Inject + public TenancyConfigUpdateTransportAction(final Settings settings, + final TransportService transportService, + final ActionFilters actionFilters, + final ConfigurationRepository config, + final ThreadPool pool, + final Client client) { + super(TenancyConfigUpdateAction.NAME, transportService, actionFilters, TenancyConfigUpdateRequest::new); + + this.securityIndex = settings.get(ConfigConstants.SECURITY_CONFIG_INDEX_NAME, ConfigConstants.OPENDISTRO_SECURITY_DEFAULT_CONFIG_INDEX); + + this.config = config; + this.client = client; + this.pool = pool; + } + + /** Load the configuration from the security index and return a copy */ + protected final SecurityDynamicConfiguration load() { + return config.getConfigurationsFromIndex(Collections.singleton(CType.CONFIG), false).get(CType.CONFIG).deepClone(); + } + + private Set getAcceptableDefaultTenants() { + Set acceptableDefaultTenants = new HashSet(); + acceptableDefaultTenants.add(ConfigConstants.TENANCY_GLOBAL_TENANT_DEFAULT_NAME); + acceptableDefaultTenants.add(ConfigConstants.TENANCY_GLOBAL_TENANT_NAME); + acceptableDefaultTenants.add(ConfigConstants.TENANCY_PRIVATE_TENANT_NAME); + return acceptableDefaultTenants; + } + + private Set getAllConfiguredTenantNames() { + + return this.config.getConfiguration(CType.TENANTS).getCEntries().keySet(); + } + + protected void validate(ConfigV7 updatedConfig) { + if(!updatedConfig.dynamic.kibana.private_tenant_enabled && (updatedConfig.dynamic.kibana.default_tenant).equals(ConfigConstants.TENANCY_PRIVATE_TENANT_NAME)) { + throw new IllegalArgumentException("Private tenant can not be disabled if it is the default tenant."); + } + + Set acceptableDefaultTenants = getAcceptableDefaultTenants(); + + if(acceptableDefaultTenants.contains(updatedConfig.dynamic.kibana.default_tenant)) { + return; + } + + Set availableTenants = getAllConfiguredTenantNames(); + + if(!availableTenants.contains(updatedConfig.dynamic.kibana.default_tenant)){ + throw new IllegalArgumentException(updatedConfig.dynamic.kibana.default_tenant + " can not be set to default tenant. Default tenant should be selected from one of the available tenants."); + } + + } + + @Override + protected void doExecute(final Task task, final TenancyConfigUpdateRequest request, final ActionListener listener) { + + // Get the current security config and prepare the config with the updated value + final SecurityDynamicConfiguration dynamicConfig = load(); + final ConfigV7 config = (ConfigV7)dynamicConfig.getCEntry("config"); + + final TenancyConfigs tenancyConfigs = request.getTenancyConfigs(); + if(tenancyConfigs.multitenancy_enabled != null) + { + config.dynamic.kibana.multitenancy_enabled = tenancyConfigs.multitenancy_enabled; + } + + if(tenancyConfigs.private_tenant_enabled != null) + { + config.dynamic.kibana.private_tenant_enabled = tenancyConfigs.private_tenant_enabled; + } + + if(tenancyConfigs.default_tenant != null) + { + config.dynamic.kibana.default_tenant = tenancyConfigs.default_tenant; + } + + validate(config); + + dynamicConfig.putCEntry("config", config); + + // When performing an update to the configuration run as admin + try (final ThreadContext.StoredContext stashedContext = pool.getThreadContext().stashContext()) { + // Update the security configuration and make sure the cluster has fully refreshed + AbstractApiAction.saveAndUpdateConfigs(this.securityIndex, this.client, CType.CONFIG, dynamicConfig, new ActionListener(){ + + @Override + public void onResponse(final IndexResponse response) { + // After processing the request, restore the user context + stashedContext.close(); + try { + // Lookup the current value and notify the listener + client.execute(TenancyConfigRetrieveActions.INSTANCE, new EmptyRequest(), listener); + } catch (IOException ioe) { + log.error(ioe); + listener.onFailure(ioe); + } + } + + @Override + public void onFailure(Exception e) { + log.error(e); + listener.onFailure(e); + } + }); + } + } +} diff --git a/src/main/java/org/opensearch/security/action/tenancy/TenancyConfigs.java b/src/main/java/org/opensearch/security/action/tenancy/TenancyConfigs.java new file mode 100644 index 0000000000..4e8fc41ef4 --- /dev/null +++ b/src/main/java/org/opensearch/security/action/tenancy/TenancyConfigs.java @@ -0,0 +1,18 @@ +/* + * 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.action.tenancy; + +public class TenancyConfigs { + public Boolean multitenancy_enabled; + public Boolean private_tenant_enabled; + public String default_tenant; +} diff --git a/src/main/java/org/opensearch/security/configuration/PrivilegesInterceptorImpl.java b/src/main/java/org/opensearch/security/configuration/PrivilegesInterceptorImpl.java index 262aadf424..e2f10dfcae 100644 --- a/src/main/java/org/opensearch/security/configuration/PrivilegesInterceptorImpl.java +++ b/src/main/java/org/opensearch/security/configuration/PrivilegesInterceptorImpl.java @@ -108,6 +108,13 @@ public ReplaceResult replaceDashboardsIndex(final ActionRequest request, final S final String dashboardsIndexName = config.getDashboardsIndexname();//config.dynamic.kibana.index; String requestedTenant = user.getRequestedTenant(); + if(USER_TENANT.equals(requestedTenant)) { + final boolean private_tenant_enabled = config.isDashboardsPrivateTenantEnabled(); + if(!private_tenant_enabled) { + return ACCESS_DENIED_REPLACE_RESULT; + } + } + final boolean isDebugEnabled = log.isDebugEnabled(); if (isDebugEnabled) { log.debug("raw requestedTenant: '" + requestedTenant + "'"); diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/AbstractApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/AbstractApiAction.java index f79aa3aaa8..3c7f969e6c 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/AbstractApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/AbstractApiAction.java @@ -73,7 +73,7 @@ public abstract class AbstractApiAction extends BaseRestHandler { protected final ConfigurationRepository cl; protected final ClusterService cs; final ThreadPool threadPool; - protected String opendistroIndex; + protected String securityIndexName; private final RestApiPrivilegesEvaluator restApiPrivilegesEvaluator; protected final AuditLog auditLog; protected final Settings settings; @@ -85,7 +85,7 @@ protected AbstractApiAction(final Settings settings, final Path configPath, fina ThreadPool threadPool, AuditLog auditLog) { super(); this.settings = settings; - this.opendistroIndex = settings.get(ConfigConstants.SECURITY_CONFIG_INDEX_NAME, + this.securityIndexName = settings.get(ConfigConstants.SECURITY_CONFIG_INDEX_NAME, ConfigConstants.OPENDISTRO_SECURITY_DEFAULT_CONFIG_INDEX); this.adminDNs = adminDNs; @@ -152,7 +152,7 @@ protected void handleDelete(final RestChannel channel, final RestRequest request existingConfiguration.remove(name); if (existed) { - saveAnUpdateConfigs(client, request, getConfigName(), existingConfiguration, new OnSucessActionListener(channel) { + AbstractApiAction.saveAndUpdateConfigs(this.securityIndexName, client, getConfigName(), existingConfiguration, new OnSucessActionListener(channel) { @Override public void onResponse(IndexResponse response) { @@ -197,7 +197,7 @@ protected void handlePut(final RestChannel channel, final RestRequest request, f boolean existed = existingConfiguration.exists(name); existingConfiguration.putCObject(name, DefaultObjectMapper.readTree(content, existingConfiguration.getImplementingClass())); - saveAnUpdateConfigs(client, request, getConfigName(), existingConfiguration, new OnSucessActionListener(channel) { + AbstractApiAction.saveAndUpdateConfigs(this.securityIndexName, client, getConfigName(), existingConfiguration, new OnSucessActionListener(channel) { @Override public void onResponse(IndexResponse response) { @@ -254,7 +254,7 @@ protected final SecurityDynamicConfiguration load(final CType config, boolean } protected boolean ensureIndexExists() { - if (!cs.state().metadata().hasConcreteIndex(this.opendistroIndex)) { + if (!cs.state().metadata().hasConcreteIndex(this.securityIndexName)) { return false; } return true; @@ -297,11 +297,8 @@ public final void onFailure(Exception e) { } - protected void saveAnUpdateConfigs(final Client client, final RestRequest request, final CType cType, - final SecurityDynamicConfiguration configuration, OnSucessActionListener actionListener) { - final IndexRequest ir = new IndexRequest(this.opendistroIndex); - - //final String type = "_doc"; + public static void saveAndUpdateConfigs(final String indexName, final Client client, final CType cType, final SecurityDynamicConfiguration configuration, final ActionListener actionListener) { + final IndexRequest ir = new IndexRequest(indexName); final String id = cType.toLCString(); configuration.removeStatic(); @@ -470,6 +467,7 @@ protected void successResponse(RestChannel channel) { try { final XContentBuilder builder = channel.newBuilder(); builder.startObject(); + builder.endObject(); channel.sendResponse( new BytesRestResponse(RestStatus.OK, builder)); } catch (IOException e) { diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/AccountApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/AccountApiAction.java index 831e390949..39f6e0c40b 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/AccountApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/AccountApiAction.java @@ -217,7 +217,7 @@ protected void handlePut(RestChannel channel, final RestRequest request, final C internalUserEntry.setHash(hash); - saveAnUpdateConfigs(client, request, CType.INTERNALUSERS, internalUser, new OnSucessActionListener(channel) { + AccountApiAction.saveAndUpdateConfigs(this.securityIndexName, client, CType.INTERNALUSERS, internalUser, new OnSucessActionListener(channel) { @Override public void onResponse(IndexResponse response) { successResponse(channel, "'" + username + "' updated."); diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/AllowlistApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/AllowlistApiAction.java index afe08bc486..54c79b6675 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/AllowlistApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/AllowlistApiAction.java @@ -134,7 +134,7 @@ protected void handlePut(final RestChannel channel, final RestRequest request, f boolean existed = existingConfiguration.exists(name); existingConfiguration.putCObject(name, DefaultObjectMapper.readTree(content, existingConfiguration.getImplementingClass())); - saveAnUpdateConfigs(client, request, getConfigName(), existingConfiguration, new OnSucessActionListener(channel) { + saveAndUpdateConfigs(this.securityIndexName,client, getConfigName(), existingConfiguration, new OnSucessActionListener(channel) { @Override public void onResponse(IndexResponse response) { diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/InternalUsersApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/InternalUsersApiAction.java index e902178b7f..2d6512dd34 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/InternalUsersApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/InternalUsersApiAction.java @@ -165,7 +165,7 @@ protected void handlePut(RestChannel channel, final RestRequest request, final C // checks complete, create or update the user internalUsersConfiguration.putCObject(username, DefaultObjectMapper.readTree(contentAsNode, internalUsersConfiguration.getImplementingClass())); - saveAnUpdateConfigs(client, request, CType.INTERNALUSERS, internalUsersConfiguration, new OnSucessActionListener(channel) { + saveAndUpdateConfigs(this.securityIndexName,client, CType.INTERNALUSERS, internalUsersConfiguration, new OnSucessActionListener(channel) { @Override public void onResponse(IndexResponse response) { diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/MigrateApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/MigrateApiAction.java index 3403dc1ee8..970408e3d1 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/MigrateApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/MigrateApiAction.java @@ -142,8 +142,8 @@ protected void handlePost(RestChannel channel, RestRequest request, Client clien final SecurityDynamicConfiguration auditConfigV7 = Migration.migrateAudit(auditConfigV6); builder.add(auditConfigV7); - final int replicas = cs.state().metadata().index(opendistroIndex).getNumberOfReplicas(); - final String autoExpandReplicas = cs.state().metadata().index(opendistroIndex).getSettings().get(IndexMetadata.SETTING_AUTO_EXPAND_REPLICAS); + final int replicas = cs.state().metadata().index(securityIndexName).getNumberOfReplicas(); + final String autoExpandReplicas = cs.state().metadata().index(securityIndexName).getSettings().get(IndexMetadata.SETTING_AUTO_EXPAND_REPLICAS); final Builder securityIndexSettings = Settings.builder(); @@ -155,7 +155,7 @@ protected void handlePost(RestChannel channel, RestRequest request, Client clien securityIndexSettings.put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1); - client.admin().indices().prepareDelete(this.opendistroIndex).execute(new ActionListener() { + client.admin().indices().prepareDelete(this.securityIndexName).execute(new ActionListener() { @Override public void onResponse(AcknowledgedResponse response) { @@ -163,14 +163,14 @@ public void onResponse(AcknowledgedResponse response) { if (response.isAcknowledged()) { log.debug("opendistro_security index deleted successfully"); - client.admin().indices().prepareCreate(opendistroIndex).setSettings(securityIndexSettings) + client.admin().indices().prepareCreate(securityIndexName).setSettings(securityIndexSettings) .execute(new ActionListener() { @Override public void onResponse(CreateIndexResponse response) { final List> dynamicConfigurations = builder.build(); final ImmutableList.Builder cTypes = ImmutableList.builderWithExpectedSize(dynamicConfigurations.size()); - final BulkRequestBuilder br = client.prepareBulk(opendistroIndex); + final BulkRequestBuilder br = client.prepareBulk(securityIndexName); br.setRefreshPolicy(RefreshPolicy.IMMEDIATE); try { for (SecurityDynamicConfiguration dynamicConfiguration : dynamicConfigurations) { diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/PatchableResourceApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/PatchableResourceApiAction.java index 74abb1d10a..2d141a4318 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/PatchableResourceApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/PatchableResourceApiAction.java @@ -162,7 +162,7 @@ private void handleSinglePatch(RestChannel channel, RestRequest request, Client } } - saveAnUpdateConfigs(client, request, getConfigName(), mdc, new OnSucessActionListener(channel){ + saveAndUpdateConfigs(this.securityIndexName,client, getConfigName(), mdc, new OnSucessActionListener(channel){ @Override public void onResponse(IndexResponse response) { @@ -239,7 +239,7 @@ private void handleBulkPatch(RestChannel channel, RestRequest request, Client cl } } - saveAnUpdateConfigs(client, request, getConfigName(), mdc, new OnSucessActionListener(channel) { + saveAndUpdateConfigs(this.securityIndexName,client, getConfigName(), mdc, new OnSucessActionListener(channel) { @Override public void onResponse(IndexResponse response) { diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/RolesMappingApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/RolesMappingApiAction.java index c7e7f3d7ec..4ed4ce48ba 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/RolesMappingApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/RolesMappingApiAction.java @@ -75,7 +75,7 @@ protected void handlePut(RestChannel channel, final RestRequest request, final C rolesMappingConfiguration.putCObject(name, DefaultObjectMapper.readTree(content, rolesMappingConfiguration.getImplementingClass())); - saveAnUpdateConfigs(client, request, getConfigName(), rolesMappingConfiguration, new OnSucessActionListener(channel) { + saveAndUpdateConfigs(this.securityIndexName,client, getConfigName(), rolesMappingConfiguration, new OnSucessActionListener(channel) { @Override public void onResponse(IndexResponse response) { diff --git a/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java b/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java index 43722dad46..195b8b758e 100644 --- a/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java @@ -513,6 +513,15 @@ public boolean multitenancyEnabled() { && dcm.isDashboardsMultitenancyEnabled(); } + public boolean privateTenantEnabled() { + return privilegesInterceptor.getClass() != PrivilegesInterceptor.class + && dcm.isDashboardsPrivateTenantEnabled(); + } + + public String dashboardsDefaultTenant() { + return dcm.getDashboardsDefaultTenant(); + } + public boolean notFailOnForbiddenEnabled() { return privilegesInterceptor.getClass() != PrivilegesInterceptor.class && dcm.isDnfofEnabled(); diff --git a/src/main/java/org/opensearch/security/rest/DashboardsInfoAction.java b/src/main/java/org/opensearch/security/rest/DashboardsInfoAction.java index aa714ebcbb..0fd88e7565 100644 --- a/src/main/java/org/opensearch/security/rest/DashboardsInfoAction.java +++ b/src/main/java/org/opensearch/security/rest/DashboardsInfoAction.java @@ -103,6 +103,9 @@ public void accept(RestChannel channel) throws Exception { builder.field("opensearch_dashboards_mt_enabled", evaluator.multitenancyEnabled()); builder.field("opensearch_dashboards_index", evaluator.dashboardsIndex()); builder.field("opensearch_dashboards_server_user", evaluator.dashboardsServerUsername()); + builder.field("multitenancy_enabled", evaluator.multitenancyEnabled()); + builder.field("private_tenant_enabled", evaluator.privateTenantEnabled()); + builder.field("default_tenant", evaluator.dashboardsDefaultTenant()); builder.endObject(); response = new BytesRestResponse(RestStatus.OK, builder); diff --git a/src/main/java/org/opensearch/security/securityconf/DynamicConfigModel.java b/src/main/java/org/opensearch/security/securityconf/DynamicConfigModel.java index 87bcf5241d..f91e768283 100644 --- a/src/main/java/org/opensearch/security/securityconf/DynamicConfigModel.java +++ b/src/main/java/org/opensearch/security/securityconf/DynamicConfigModel.java @@ -68,6 +68,8 @@ public abstract class DynamicConfigModel { public abstract String getDashboardsOpenSearchRole(); public abstract String getDashboardsIndexname(); public abstract boolean isDashboardsMultitenancyEnabled(); + public abstract boolean isDashboardsPrivateTenantEnabled(); + public abstract String getDashboardsDefaultTenant(); public abstract boolean isDnfofEnabled(); public abstract boolean isMultiRolespanEnabled(); public abstract String getFilteredAliasMode(); diff --git a/src/main/java/org/opensearch/security/securityconf/DynamicConfigModelV6.java b/src/main/java/org/opensearch/security/securityconf/DynamicConfigModelV6.java index c9738c7e70..40b3e3319a 100644 --- a/src/main/java/org/opensearch/security/securityconf/DynamicConfigModelV6.java +++ b/src/main/java/org/opensearch/security/securityconf/DynamicConfigModelV6.java @@ -141,6 +141,12 @@ public boolean isDashboardsMultitenancyEnabled() { return config.dynamic.kibana.multitenancy_enabled; } @Override + public boolean isDashboardsPrivateTenantEnabled() { + return config.dynamic.kibana.private_tenant_enabled; + } + @Override + public String getDashboardsDefaultTenant() { return config.dynamic.kibana.default_tenant; } + @Override public boolean isDnfofEnabled() { return config.dynamic.do_not_fail_on_forbidden || config.dynamic.kibana.do_not_fail_on_forbidden; } diff --git a/src/main/java/org/opensearch/security/securityconf/DynamicConfigModelV7.java b/src/main/java/org/opensearch/security/securityconf/DynamicConfigModelV7.java index f2ec110cbd..6db5fba0a7 100644 --- a/src/main/java/org/opensearch/security/securityconf/DynamicConfigModelV7.java +++ b/src/main/java/org/opensearch/security/securityconf/DynamicConfigModelV7.java @@ -141,6 +141,12 @@ public boolean isDashboardsMultitenancyEnabled() { return config.dynamic.kibana.multitenancy_enabled; } @Override + public boolean isDashboardsPrivateTenantEnabled() { + return config.dynamic.kibana.private_tenant_enabled; + } + @Override + public String getDashboardsDefaultTenant() { return config.dynamic.kibana.default_tenant; } + @Override public boolean isDnfofEnabled() { return config.dynamic.do_not_fail_on_forbidden; } 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 f9da5ea314..6599942d34 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 @@ -83,6 +83,10 @@ public static class Kibana { @JsonInclude(JsonInclude.Include.NON_NULL) public boolean multitenancy_enabled = true; + @JsonInclude(JsonInclude.Include.NON_NULL) + public boolean private_tenant_enabled = true; + @JsonInclude(JsonInclude.Include.NON_NULL) + public String default_tenant = ""; public String server_username = "kibanaserver"; public String opendistro_role = null; public String index = ".kibana"; 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 0e83590d3e..3029b42abf 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 @@ -69,6 +69,8 @@ public ConfigV7(ConfigV6 c6) { dynamic.kibana.index = c6.dynamic.kibana.index; dynamic.kibana.multitenancy_enabled = c6.dynamic.kibana.multitenancy_enabled; + dynamic.kibana.private_tenant_enabled = true; + dynamic.kibana.default_tenant = ""; dynamic.kibana.server_username = c6.dynamic.kibana.server_username; dynamic.http = new Http(); @@ -135,12 +137,18 @@ public static class Kibana { @JsonInclude(JsonInclude.Include.NON_NULL) public boolean multitenancy_enabled = true; + @JsonInclude(JsonInclude.Include.NON_NULL) + public boolean private_tenant_enabled = true; + @JsonInclude(JsonInclude.Include.NON_NULL) + public String default_tenant = ""; public String server_username = "kibanaserver"; public String opendistro_role = null; public String index = ".kibana"; @Override public String toString() { - return "Kibana [multitenancy_enabled=" + multitenancy_enabled + ", server_username=" + server_username + ", opendistro_role=" + opendistro_role + return "Kibana [multitenancy_enabled=" + multitenancy_enabled + ", private_tenant_enabled=" + + private_tenant_enabled + ", default_tenant=" + default_tenant + ", server_username=" + + server_username + ", opendistro_role=" + opendistro_role + ", index=" + index + "]"; } diff --git a/src/main/java/org/opensearch/security/support/ConfigConstants.java b/src/main/java/org/opensearch/security/support/ConfigConstants.java index ee83284ca4..a8cc892ab8 100644 --- a/src/main/java/org/opensearch/security/support/ConfigConstants.java +++ b/src/main/java/org/opensearch/security/support/ConfigConstants.java @@ -287,6 +287,10 @@ public enum RolesMappingResolution { public static final String SECURITY_SYSTEM_INDICES_KEY = "plugins.security.system_indices.indices"; public static final List SECURITY_SYSTEM_INDICES_DEFAULT = Collections.emptyList(); + public static final String TENANCY_PRIVATE_TENANT_NAME = "Private"; + public static final String TENANCY_GLOBAL_TENANT_NAME = "Global"; + public static final String TENANCY_GLOBAL_TENANT_DEFAULT_NAME = ""; + public static Set getSettingAsSet(final Settings settings, final String key, final List defaultList, final boolean ignoreCaseForNone) { final List list = settings.getAsList(key, defaultList); if (list.size() == 1 && "NONE".equals(ignoreCaseForNone? list.get(0).toUpperCase() : list.get(0))) { diff --git a/src/test/java/org/opensearch/security/multitenancy/test/TenancyDefaultTenantTests.java b/src/test/java/org/opensearch/security/multitenancy/test/TenancyDefaultTenantTests.java new file mode 100644 index 0000000000..27f02a90b7 --- /dev/null +++ b/src/test/java/org/opensearch/security/multitenancy/test/TenancyDefaultTenantTests.java @@ -0,0 +1,96 @@ +/* + * 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.multitenancy.test; + +import org.apache.http.Header; +import org.apache.http.HttpStatus; +import org.junit.Test; + +import org.opensearch.security.support.ConfigConstants; +import org.opensearch.security.test.SingleClusterTest; +import org.opensearch.security.test.helper.rest.RestHelper.HttpResponse; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.hamcrest.core.StringContains.containsString; + +public class TenancyDefaultTenantTests extends SingleClusterTest { + private final Header asAdminUser = encodeBasicHeader("admin", "admin"); + private final Header asUser = encodeBasicHeader("kirk", "kirk"); + + @Override + protected String getResourceFolder() { + return "multitenancy"; + } + + @Test + public void testDefaultTenantUpdate() throws Exception { + setup(); + + final HttpResponse getSettingResponse = nonSslRestHelper().executeGetRequest("/_plugins/_security/api/tenancy/config", asAdminUser); + assertThat(getSettingResponse.getStatusCode(), equalTo(HttpStatus.SC_OK)); + assertThat(getSettingResponse.findValueInJson("default_tenant"), equalTo(ConfigConstants.TENANCY_GLOBAL_TENANT_DEFAULT_NAME)); + + HttpResponse getDashboardsinfoResponse = nonSslRestHelper().executeGetRequest("/_plugins/_security/dashboardsinfo", asAdminUser); + assertThat(getDashboardsinfoResponse.getStatusCode(), equalTo(HttpStatus.SC_OK)); + assertThat(getDashboardsinfoResponse.findValueInJson("default_tenant"), equalTo(ConfigConstants.TENANCY_GLOBAL_TENANT_DEFAULT_NAME)); + + final HttpResponse setPrivateTenantAsDefaultResponse = nonSslRestHelper().executePutRequest("/_plugins/_security/api/tenancy/config", "{\"default_tenant\": \"Private\"}", asAdminUser); + assertThat(setPrivateTenantAsDefaultResponse.getStatusCode(), equalTo(HttpStatus.SC_OK)); + getDashboardsinfoResponse = nonSslRestHelper().executeGetRequest("/_plugins/_security/dashboardsinfo", asAdminUser); + assertThat(getDashboardsinfoResponse.getStatusCode(), equalTo(HttpStatus.SC_OK)); + assertThat(getDashboardsinfoResponse.findValueInJson("default_tenant"), equalTo(ConfigConstants.TENANCY_PRIVATE_TENANT_NAME)); + } + + @Test + public void testDefaultTenant_UpdateFailed() throws Exception { + setup(); + + final HttpResponse disablePrivateTenantResponse = nonSslRestHelper().executePutRequest("/_plugins/_security/api/tenancy/config", "{\"private_tenant_enabled\":false}", asAdminUser); + assertThat(disablePrivateTenantResponse.getStatusCode(), equalTo(HttpStatus.SC_OK)); + + + final HttpResponse setPrivateTenantAsDefaultFailResponse = nonSslRestHelper().executePutRequest("/_plugins/_security/api/tenancy/config", "{\"default_tenant\": \"Private\"}", asAdminUser); + assertThat(setPrivateTenantAsDefaultFailResponse.getStatusCode(), equalTo(HttpStatus.SC_BAD_REQUEST)); + assertThat(setPrivateTenantAsDefaultFailResponse.findValueInJson("error.reason"), containsString("Private tenant can not be disabled if it is the default tenant.")); + + final HttpResponse enablePrivateTenantResponse = nonSslRestHelper().executePutRequest("/_plugins/_security/api/tenancy/config", "{\"private_tenant_enabled\":true}", asAdminUser); + assertThat(enablePrivateTenantResponse.getStatusCode(), equalTo(HttpStatus.SC_OK)); + + final HttpResponse setPrivateTenantAsDefaultResponse = nonSslRestHelper().executePutRequest("/_plugins/_security/api/tenancy/config", "{\"default_tenant\": \"Private\"}", asAdminUser); + assertThat(setPrivateTenantAsDefaultResponse.getStatusCode(), equalTo(HttpStatus.SC_OK)); + + final HttpResponse getSettingResponseAfterUpdate = nonSslRestHelper().executeGetRequest("/_plugins/_security/api/tenancy/config", asAdminUser); + assertThat(getSettingResponseAfterUpdate.getStatusCode(), equalTo(HttpStatus.SC_OK)); + assertThat(getSettingResponseAfterUpdate.findValueInJson("default_tenant"), equalTo(ConfigConstants.TENANCY_PRIVATE_TENANT_NAME)); + + HttpResponse getDashboardsinfoResponse = nonSslRestHelper().executeGetRequest("/_plugins/_security/dashboardsinfo", asAdminUser); + assertThat(getDashboardsinfoResponse.findValueInJson("default_tenant"),equalTo(ConfigConstants.TENANCY_PRIVATE_TENANT_NAME)); + + final HttpResponse setRandomStringAsDefaultTenant = nonSslRestHelper().executePutRequest("/_plugins/_security/api/tenancy/config", "{\"default_tenant\": \"NonExistentTenant\"}", asAdminUser); + assertThat(setRandomStringAsDefaultTenant.getStatusCode(), equalTo(HttpStatus.SC_BAD_REQUEST)); + assertThat(setRandomStringAsDefaultTenant.findValueInJson("error.reason"), containsString("Default tenant should be selected from one of the available tenants.")); + + } + @Test + public void testForbiddenAccess() throws Exception { + setup(); + + final HttpResponse getSettingResponse = nonSslRestHelper().executeGetRequest("/_plugins/_security/api/tenancy/config", asUser); + assertThat(getSettingResponse.getStatusCode(), equalTo(HttpStatus.SC_FORBIDDEN)); + assertThat(getSettingResponse.findValueInJson("error.reason"), containsString("no permissions for [cluster:feature/tenancy/config/read]")); + + final HttpResponse updateSettingResponse = nonSslRestHelper().executePutRequest("/_plugins/_security/api/tenancy/config", "{\"default_tenant\": \"Private\"}", asUser); + assertThat(updateSettingResponse.getStatusCode(), equalTo(HttpStatus.SC_FORBIDDEN)); + assertThat(updateSettingResponse.findValueInJson("error.reason"), containsString("no permissions for [cluster:feature/tenancy/config/update]")); + } +} diff --git a/src/test/java/org/opensearch/security/multitenancy/test/TenancyMultitenancyEnabledTests.java b/src/test/java/org/opensearch/security/multitenancy/test/TenancyMultitenancyEnabledTests.java new file mode 100644 index 0000000000..e33842d433 --- /dev/null +++ b/src/test/java/org/opensearch/security/multitenancy/test/TenancyMultitenancyEnabledTests.java @@ -0,0 +1,89 @@ +/* + * 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.multitenancy.test; + +import org.apache.http.Header; +import org.apache.http.HttpStatus; +import org.apache.http.message.BasicHeader; +import org.junit.Test; + +import org.opensearch.security.test.SingleClusterTest; +import org.opensearch.security.test.helper.rest.RestHelper.HttpResponse; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.hamcrest.core.StringContains.containsString; + +public class TenancyMultitenancyEnabledTests extends SingleClusterTest { + private final Header asAdminUser = encodeBasicHeader("admin", "admin"); + private final Header asUser = encodeBasicHeader("kirk", "kirk"); + private final Header onUserTenant = new BasicHeader("securitytenant", "__user__"); + + private static String createIndexPatternDoc(final String title) { + return "{"+ + "\"type\" : \"index-pattern\","+ + "\"updated_at\" : \"2018-09-29T08:56:59.066Z\","+ + "\"index-pattern\" : {"+ + "\"title\" : \"" + title + "\""+ + "}}"; + } + + @Override + protected String getResourceFolder() { + return "multitenancy"; + } + + @Test + public void testMultitenancyDisabled_endToEndTest() throws Exception { + setup(); + + final HttpResponse getSettingResponse = nonSslRestHelper().executeGetRequest("/_plugins/_security/api/tenancy/config", asAdminUser); + assertThat(getSettingResponse.getStatusCode(), equalTo(HttpStatus.SC_OK)); + assertThat(getSettingResponse.findValueInJson("multitenancy_enabled"), equalTo("true")); + + HttpResponse getDashboardsinfoResponse = nonSslRestHelper().executeGetRequest("/_plugins/_security/dashboardsinfo", asAdminUser); + assertThat(getDashboardsinfoResponse.findValueInJson("multitenancy_enabled"),equalTo("true")); + + final HttpResponse createDocInGlobalTenantResponse = nonSslRestHelper().executePostRequest(".kibana/_doc?refresh=true", createIndexPatternDoc("globalIndex"), asAdminUser); + assertThat(createDocInGlobalTenantResponse.getStatusCode(), equalTo(HttpStatus.SC_CREATED)); + final HttpResponse createDocInUserTenantResponse = nonSslRestHelper().executePostRequest(".kibana/_doc?refresh=true", createIndexPatternDoc("userIndex"), onUserTenant, asAdminUser); + assertThat(createDocInUserTenantResponse.getStatusCode(), equalTo(HttpStatus.SC_CREATED)); + + final HttpResponse searchInUserTenantWithMutlitenancyEnabled = nonSslRestHelper().executeGetRequest(".kibana/_search", onUserTenant, asAdminUser); + assertThat(searchInUserTenantWithMutlitenancyEnabled.getStatusCode(), equalTo(HttpStatus.SC_OK)); + assertThat(searchInUserTenantWithMutlitenancyEnabled.findValueInJson("hits.hits[0]._source.index-pattern.title"), equalTo("userIndex")); + + final HttpResponse updateMutlitenancyToDisabled = nonSslRestHelper().executePutRequest("/_plugins/_security/api/tenancy/config", "{\"multitenancy_enabled\": \"false\"}", asAdminUser); + assertThat(updateMutlitenancyToDisabled.getStatusCode(), equalTo(HttpStatus.SC_OK)); + assertThat(updateMutlitenancyToDisabled.findValueInJson("multitenancy_enabled"), equalTo("false")); + + getDashboardsinfoResponse = nonSslRestHelper().executeGetRequest("/_plugins/_security/dashboardsinfo", asAdminUser); + assertThat(getDashboardsinfoResponse.findValueInJson("multitenancy_enabled"),equalTo("false")); + + final HttpResponse searchInUserTenantWithMutlitenancyDisabled = nonSslRestHelper().executeGetRequest(".kibana/_search", onUserTenant, asAdminUser); + assertThat(searchInUserTenantWithMutlitenancyDisabled.getStatusCode(), equalTo(HttpStatus.SC_OK)); + assertThat(searchInUserTenantWithMutlitenancyDisabled.findValueInJson("hits.hits[0]._source.index-pattern.title"), equalTo("globalIndex")); + } + + @Test + public void testForbiddenAccess() throws Exception { + setup(); + + final HttpResponse getSettingResponse = nonSslRestHelper().executeGetRequest("/_plugins/_security/api/tenancy/config", asUser); + assertThat(getSettingResponse.getStatusCode(), equalTo(HttpStatus.SC_FORBIDDEN)); + assertThat(getSettingResponse.findValueInJson("error.reason"), containsString("no permissions for [cluster:feature/tenancy/config/read]")); + + final HttpResponse updateSettingResponse = nonSslRestHelper().executePutRequest("/_plugins/_security/api/tenancy/config", "{\"multitenancy_enabled\": \"false\"}", asUser); + assertThat(updateSettingResponse.getStatusCode(), equalTo(HttpStatus.SC_FORBIDDEN)); + assertThat(updateSettingResponse.findValueInJson("error.reason"), containsString("no permissions for [cluster:feature/tenancy/config/update]")); + } +} diff --git a/src/test/java/org/opensearch/security/multitenancy/test/TenancyPrivateTenantEnabledTests.java b/src/test/java/org/opensearch/security/multitenancy/test/TenancyPrivateTenantEnabledTests.java new file mode 100644 index 0000000000..d1db4117a1 --- /dev/null +++ b/src/test/java/org/opensearch/security/multitenancy/test/TenancyPrivateTenantEnabledTests.java @@ -0,0 +1,101 @@ +/* + * 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.multitenancy.test; + +import org.apache.http.Header; +import org.apache.http.HttpStatus; +import org.apache.http.message.BasicHeader; +import org.junit.Test; + +import org.opensearch.security.test.SingleClusterTest; +import org.opensearch.security.test.helper.rest.RestHelper.HttpResponse; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.hamcrest.core.StringContains.containsString; + +public class TenancyPrivateTenantEnabledTests extends SingleClusterTest { + private final Header asAdminUser = encodeBasicHeader("admin", "admin"); + private final Header asUser = encodeBasicHeader("kirk", "kirk"); + private final Header onUserTenant = new BasicHeader("securitytenant", "__user__"); + + private static String createIndexPatternDoc(final String title) { + return "{"+ + "\"type\" : \"index-pattern\","+ + "\"updated_at\" : \"2018-09-29T08:56:59.066Z\","+ + "\"index-pattern\" : {"+ + "\"title\" : \"" + title + "\""+ + "}}"; + } + + @Override + protected String getResourceFolder() { + return "multitenancy"; + } + + @Test + public void testPrivateTenantDisabled_Update() throws Exception { + setup(); + + final HttpResponse getSettingResponse = nonSslRestHelper().executeGetRequest("/_plugins/_security/api/tenancy/config", asAdminUser); + assertThat(getSettingResponse.getStatusCode(), equalTo(HttpStatus.SC_OK)); + assertThat(getSettingResponse.findValueInJson("private_tenant_enabled"), equalTo("true")); + + HttpResponse getDashboardsinfoResponse = nonSslRestHelper().executeGetRequest("/_plugins/_security/dashboardsinfo", asAdminUser); + assertThat(getDashboardsinfoResponse.findValueInJson("private_tenant_enabled"), equalTo("true")); + + final HttpResponse createDocInGlobalTenantResponse = nonSslRestHelper().executePostRequest(".kibana/_doc?refresh=true", createIndexPatternDoc("globalIndex"), asAdminUser); + assertThat(createDocInGlobalTenantResponse.getStatusCode(), equalTo(HttpStatus.SC_CREATED)); + final HttpResponse createDocInUserTenantResponse = nonSslRestHelper().executePostRequest(".kibana/_doc?refresh=true", createIndexPatternDoc("userIndex"), onUserTenant, asUser); + assertThat(createDocInUserTenantResponse.getStatusCode(), equalTo(HttpStatus.SC_CREATED)); + + final HttpResponse searchInUserTenantWithPrivateTenantEnabled = nonSslRestHelper().executeGetRequest(".kibana/_search", onUserTenant, asUser); + assertThat(searchInUserTenantWithPrivateTenantEnabled.getStatusCode(), equalTo(HttpStatus.SC_OK)); + assertThat(searchInUserTenantWithPrivateTenantEnabled.findValueInJson("hits.hits[0]._source.index-pattern.title"), equalTo("userIndex")); + + final HttpResponse disablePrivateTenantResponse = nonSslRestHelper().executePutRequest("/_plugins/_security/api/tenancy/config", "{\"private_tenant_enabled\": \"false\"}", asAdminUser); + assertThat(disablePrivateTenantResponse.getStatusCode(), equalTo(HttpStatus.SC_OK)); + assertThat(disablePrivateTenantResponse.findValueInJson("private_tenant_enabled"), equalTo("false")); + + getDashboardsinfoResponse = nonSslRestHelper().executeGetRequest("/_plugins/_security/dashboardsinfo", asAdminUser); + assertThat(getDashboardsinfoResponse.findValueInJson("private_tenant_enabled"),equalTo("false")); + + final HttpResponse searchInUserTenantWithPrivateTenantDisabled = nonSslRestHelper().executeGetRequest(".kibana/_search", onUserTenant, asUser); + assertThat(searchInUserTenantWithPrivateTenantDisabled.getStatusCode(), equalTo(HttpStatus.SC_FORBIDDEN)); + assertThat(searchInUserTenantWithPrivateTenantDisabled.findValueInJson("error.reason"), containsString("no permissions for [indices:data/read/search] and User")); + + } + + @Test + public void testPrivateTenantDisabled_UpdateFailed() throws Exception { + setup(); + + final HttpResponse setPrivateTenantAsDefaultResponse = nonSslRestHelper().executePutRequest("/_plugins/_security/api/tenancy/config", "{\"default_tenant\": \"Private\"}", asAdminUser); + assertThat(setPrivateTenantAsDefaultResponse.getStatusCode(), equalTo(HttpStatus.SC_OK)); + final HttpResponse updateSettingResponse = nonSslRestHelper().executePutRequest("/_plugins/_security/api/tenancy/config", "{\"private_tenant_enabled\":false}", asAdminUser); + assertThat(updateSettingResponse.getStatusCode(), equalTo(HttpStatus.SC_BAD_REQUEST)); + assertThat(updateSettingResponse.findValueInJson("error.reason"), containsString("Private tenant can not be disabled if it is the default tenant.")); + } + + @Test + public void testForbiddenAccess() throws Exception { + setup(); + + final HttpResponse getSettingResponse = nonSslRestHelper().executeGetRequest("/_plugins/_security/api/tenancy/config", asUser); + assertThat(getSettingResponse.getStatusCode(), equalTo(HttpStatus.SC_FORBIDDEN)); + assertThat(getSettingResponse.findValueInJson("error.reason"), containsString("no permissions for [cluster:feature/tenancy/config/read]")); + + final HttpResponse updateSettingResponse = nonSslRestHelper().executePutRequest("/_plugins/_security/api/tenancy/config", "{\"private_tenant_enabled\": false}", asUser); + assertThat(updateSettingResponse.getStatusCode(), equalTo(HttpStatus.SC_FORBIDDEN)); + assertThat(updateSettingResponse.findValueInJson("error.reason"), containsString("no permissions for [cluster:feature/tenancy/config/update]")); + } +} 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 49d498833e..04c0b3a99d 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 @@ -78,8 +78,6 @@ import org.opensearch.security.test.helper.cluster.ClusterInfo; import org.opensearch.security.test.helper.file.FileHelper; -import static org.junit.jupiter.api.Assertions.fail; - public class RestHelper { protected final Logger log = LogManager.getLogger(RestHelper.class); @@ -395,7 +393,7 @@ private static void findArrayAccessor(String input) { public String findValueInJson(final String jsonDotPath) { // Make sure its json / then parse it if (!isJsonContentType()) { - fail("Response was expected to be JSON, body was: \n" + body); + throw new RuntimeException("Response was expected to be JSON, body was: \n" + body); } JsonNode currentNode = null; try { @@ -407,7 +405,7 @@ public String findValueInJson(final String jsonDotPath) { // Break the path into parts, and scan into the json object try (final Scanner jsonPathScanner = new Scanner(jsonDotPath).useDelimiter("\\.")) { if (!jsonPathScanner.hasNext()) { - fail("Invalid json dot path '" + jsonDotPath + "', rewrite with '.' characters between path elements."); + throw new RuntimeException("Invalid json dot path '" + jsonDotPath + "', rewrite with '.' characters between path elements."); } do { String pathEntry = jsonPathScanner.next(); @@ -425,23 +423,23 @@ public String findValueInJson(final String jsonDotPath) { } if (!currentNode.has(pathEntry)) { - fail("Unable to resolve '" + jsonDotPath + "', on path entry '" + pathEntry + "' from available fields " + currentNode.toPrettyString()); + throw new RuntimeException("Unable to resolve '" + jsonDotPath + "', on path entry '" + pathEntry + "' from available fields " + currentNode.toPrettyString()); } currentNode = currentNode.get(pathEntry); // if it's an Array lookup we get the requested index item if (arrayEntryIdx > -1) { if(!currentNode.isArray()) { - fail("Unable to resolve '" + jsonDotPath + "', the '" + pathEntry + "' field is not an array " + currentNode.toPrettyString()); + throw new RuntimeException("Unable to resolve '" + jsonDotPath + "', the '" + pathEntry + "' field is not an array " + currentNode.toPrettyString()); } else if (!currentNode.has(arrayEntryIdx)) { - fail("Unable to resolve '" + jsonDotPath + "', index '" + arrayEntryIdx + "' is out of bounds for array '" + pathEntry + "' \n" + currentNode.toPrettyString()); + throw new RuntimeException("Unable to resolve '" + jsonDotPath + "', index '" + arrayEntryIdx + "' is out of bounds for array '" + pathEntry + "' \n" + currentNode.toPrettyString()); } currentNode = currentNode.get(arrayEntryIdx); } } while (jsonPathScanner.hasNext()); if (!currentNode.isValueNode()) { - fail("Unexpected value note, index directly to the object to reference, object\n" + currentNode.toPrettyString()); + throw new RuntimeException("Unexpected value note, index directly to the object to reference, object\n" + currentNode.toPrettyString()); } return currentNode.asText(); } diff --git a/src/test/resources/auditlog/config.yml b/src/test/resources/auditlog/config.yml index 938a38cd38..c1d0c63174 100644 --- a/src/test/resources/auditlog/config.yml +++ b/src/test/resources/auditlog/config.yml @@ -11,6 +11,8 @@ config: license: null kibana: multitenancy_enabled: true + private_tenant_enabled: true + default_tenant: "" server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/cache/config.yml b/src/test/resources/cache/config.yml index 2a48434203..1a5e86fc2f 100644 --- a/src/test/resources/cache/config.yml +++ b/src/test/resources/cache/config.yml @@ -11,6 +11,8 @@ config: license: null kibana: multitenancy_enabled: true + private_tenant_enabled: true + default_tenant: "" server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/composite_config.yml b/src/test/resources/composite_config.yml index f636d823e0..3f6eb78627 100644 --- a/src/test/resources/composite_config.yml +++ b/src/test/resources/composite_config.yml @@ -11,6 +11,8 @@ config: license: null kibana: multitenancy_enabled: true + private_tenant_enabled: true + default_tenant: "" server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/config.yml b/src/test/resources/config.yml index 34f4aff093..3663b3c706 100644 --- a/src/test/resources/config.yml +++ b/src/test/resources/config.yml @@ -10,6 +10,8 @@ config: respect_request_indices_options: false kibana: multitenancy_enabled: true + private_tenant_enabled: true + default_tenant: "" server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/config_anon.yml b/src/test/resources/config_anon.yml index 7c8a037f25..0c2c5ccbcc 100644 --- a/src/test/resources/config_anon.yml +++ b/src/test/resources/config_anon.yml @@ -11,6 +11,8 @@ config: license: null kibana: multitenancy_enabled: true + private_tenant_enabled: true + default_tenant: "" server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/config_clientcert.yml b/src/test/resources/config_clientcert.yml index 441f6c9e5f..c50d770c26 100644 --- a/src/test/resources/config_clientcert.yml +++ b/src/test/resources/config_clientcert.yml @@ -11,6 +11,8 @@ config: license: null kibana: multitenancy_enabled: true + private_tenant_enabled: true + default_tenant: "" 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 8f8e414672..fef8297b7f 100644 --- a/src/test/resources/config_disable_all.yml +++ b/src/test/resources/config_disable_all.yml @@ -11,6 +11,8 @@ config: license: null kibana: multitenancy_enabled: true + private_tenant_enabled: true + default_tenant: "" server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/config_dnfof.yml b/src/test/resources/config_dnfof.yml index 84c71d231a..6e0a2dcd93 100644 --- a/src/test/resources/config_dnfof.yml +++ b/src/test/resources/config_dnfof.yml @@ -10,6 +10,8 @@ config: respect_request_indices_options: false kibana: multitenancy_enabled: true + private_tenant_enabled: true + default_tenant: "" server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/config_invalidlic.yml b/src/test/resources/config_invalidlic.yml index 34f4aff093..3663b3c706 100644 --- a/src/test/resources/config_invalidlic.yml +++ b/src/test/resources/config_invalidlic.yml @@ -10,6 +10,8 @@ config: respect_request_indices_options: false kibana: multitenancy_enabled: true + private_tenant_enabled: true + default_tenant: "" server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/config_lic.yml b/src/test/resources/config_lic.yml index 34f4aff093..3663b3c706 100644 --- a/src/test/resources/config_lic.yml +++ b/src/test/resources/config_lic.yml @@ -10,6 +10,8 @@ config: respect_request_indices_options: false kibana: multitenancy_enabled: true + private_tenant_enabled: true + default_tenant: "" server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/config_lic_rk.yml b/src/test/resources/config_lic_rk.yml index 34f4aff093..3663b3c706 100644 --- a/src/test/resources/config_lic_rk.yml +++ b/src/test/resources/config_lic_rk.yml @@ -10,6 +10,8 @@ config: respect_request_indices_options: false kibana: multitenancy_enabled: true + private_tenant_enabled: true + default_tenant: "" server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/config_multirolespan.yml b/src/test/resources/config_multirolespan.yml index dee3d042b1..388cba2903 100644 --- a/src/test/resources/config_multirolespan.yml +++ b/src/test/resources/config_multirolespan.yml @@ -10,6 +10,8 @@ config: respect_request_indices_options: false kibana: multitenancy_enabled: true + private_tenant_enabled: true + default_tenant: "" 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 791656523d..86b01d197c 100644 --- a/src/test/resources/config_protected_indices.yml +++ b/src/test/resources/config_protected_indices.yml @@ -10,6 +10,8 @@ config: respect_request_indices_options: false kibana: multitenancy_enabled: true + private_tenant_enabled: true + default_tenant: "" server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/config_proxy.yml b/src/test/resources/config_proxy.yml index 52f5880806..b3151d9748 100644 --- a/src/test/resources/config_proxy.yml +++ b/src/test/resources/config_proxy.yml @@ -11,6 +11,8 @@ config: license: null kibana: multitenancy_enabled: true + private_tenant_enabled: true + default_tenant: "" 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 78e0aa70f3..cf03610e4b 100644 --- a/src/test/resources/config_proxy_custom.yml +++ b/src/test/resources/config_proxy_custom.yml @@ -11,6 +11,8 @@ config: license: null kibana: multitenancy_enabled: true + private_tenant_enabled: true + default_tenant: "" 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 9508c12b34..86e9e6487b 100644 --- a/src/test/resources/config_respect_indices_options.yml +++ b/src/test/resources/config_respect_indices_options.yml @@ -11,6 +11,8 @@ config: license: null kibana: multitenancy_enabled: true + private_tenant_enabled: true + default_tenant: "" 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 36c1b09cf1..a34232ff77 100644 --- a/src/test/resources/config_rest_impersonation.yml +++ b/src/test/resources/config_rest_impersonation.yml @@ -11,6 +11,8 @@ config: license: null kibana: multitenancy_enabled: true + private_tenant_enabled: true + default_tenant: "" server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/config_system_indices.yml b/src/test/resources/config_system_indices.yml index 791656523d..86b01d197c 100644 --- a/src/test/resources/config_system_indices.yml +++ b/src/test/resources/config_system_indices.yml @@ -10,6 +10,8 @@ config: respect_request_indices_options: false kibana: multitenancy_enabled: true + private_tenant_enabled: true + default_tenant: "" 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 0cd5204005..f473a47320 100644 --- a/src/test/resources/config_transport_username.yml +++ b/src/test/resources/config_transport_username.yml @@ -10,6 +10,8 @@ config: respect_request_indices_options: false kibana: multitenancy_enabled: true + private_tenant_enabled: true + default_tenant: "" server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/config_xff.yml b/src/test/resources/config_xff.yml index 85ceca5e21..ed93efc2b1 100644 --- a/src/test/resources/config_xff.yml +++ b/src/test/resources/config_xff.yml @@ -10,6 +10,8 @@ config: respect_request_indices_options: false kibana: multitenancy_enabled: true + private_tenant_enabled: true + default_tenant: "" server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/dlsfls/config.yml b/src/test/resources/dlsfls/config.yml index 1288ebeea2..cf110efda5 100644 --- a/src/test/resources/dlsfls/config.yml +++ b/src/test/resources/dlsfls/config.yml @@ -11,6 +11,8 @@ config: license: null kibana: multitenancy_enabled: true + private_tenant_enabled: true + default_tenant: "" server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/ldap/config.yml b/src/test/resources/ldap/config.yml index ea8271c6df..9257fa5b50 100644 --- a/src/test/resources/ldap/config.yml +++ b/src/test/resources/ldap/config.yml @@ -11,6 +11,8 @@ config: license: null kibana: multitenancy_enabled: true + private_tenant_enabled: true + default_tenant: "" 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 51956d4cfc..a9b477fcec 100644 --- a/src/test/resources/ldap/config_ldap2.yml +++ b/src/test/resources/ldap/config_ldap2.yml @@ -11,6 +11,8 @@ config: license: null kibana: multitenancy_enabled: true + private_tenant_enabled: true + default_tenant: "" server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/multitenancy/config.yml b/src/test/resources/multitenancy/config.yml index 704937b9ee..d1514e9cb2 100644 --- a/src/test/resources/multitenancy/config.yml +++ b/src/test/resources/multitenancy/config.yml @@ -11,6 +11,8 @@ config: license: null kibana: multitenancy_enabled: true + private_tenant_enabled: true + default_tenant: "" 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 97a43b20a8..1ae5e145fe 100644 --- a/src/test/resources/multitenancy/config_anonymous.yml +++ b/src/test/resources/multitenancy/config_anonymous.yml @@ -11,6 +11,8 @@ config: license: null kibana: multitenancy_enabled: true + private_tenant_enabled: true + default_tenant: "" 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 289eb19cd2..f60caab0b7 100644 --- a/src/test/resources/multitenancy/config_basic_auth.yml +++ b/src/test/resources/multitenancy/config_basic_auth.yml @@ -11,6 +11,8 @@ config: license: null kibana: multitenancy_enabled: true + private_tenant_enabled: true + default_tenant: "" 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 462e3e9e64..f4afce87d4 100644 --- a/src/test/resources/multitenancy/config_nodnfof.yml +++ b/src/test/resources/multitenancy/config_nodnfof.yml @@ -11,6 +11,8 @@ config: license: null kibana: multitenancy_enabled: true + private_tenant_enabled: true + default_tenant: "" server_username: "kibanaserver" index: ".kibana" http: diff --git a/src/test/resources/restapi/config.yml b/src/test/resources/restapi/config.yml index 899a17a1fc..2ed865657a 100644 --- a/src/test/resources/restapi/config.yml +++ b/src/test/resources/restapi/config.yml @@ -10,6 +10,8 @@ config: license: null kibana: multitenancy_enabled: true + private_tenant_enabled: true + default_tenant: "" 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 76ac5e7c0f..7bbbf2201f 100644 --- a/src/test/resources/restapi/invalid_config.json +++ b/src/test/resources/restapi/invalid_config.json @@ -7,6 +7,8 @@ "respect_request_indices_options":false, "kibana":{ "multitenancy_enabled":true, + "private_tenant_enabled" : true, + "default_tenant" : "", "server_username":"kibanaserver", "index":".kibana" }, diff --git a/src/test/resources/restapi/security_config.json b/src/test/resources/restapi/security_config.json index 8a230f283c..e8acc0a22a 100644 --- a/src/test/resources/restapi/security_config.json +++ b/src/test/resources/restapi/security_config.json @@ -7,6 +7,8 @@ "respect_request_indices_options":false, "kibana":{ "multitenancy_enabled":true, + "private_tenant_enabled" : true, + "default_tenant" : "", "server_username":"kibanaserver", "index":".kibana" }, diff --git a/src/test/resources/restapi/securityconfig.json b/src/test/resources/restapi/securityconfig.json index 9e7fb32342..4e4b1bba63 100644 --- a/src/test/resources/restapi/securityconfig.json +++ b/src/test/resources/restapi/securityconfig.json @@ -7,6 +7,8 @@ "respect_request_indices_options":false, "kibana":{ "multitenancy_enabled":true, + "private_tenant_enabled" : true, + "default_tenant" : "", "server_username":"kibanaserver", "index":".kibana" }, diff --git a/src/test/resources/restapi/securityconfig_nondefault.json b/src/test/resources/restapi/securityconfig_nondefault.json index c9e6aaec5e..6fb297be37 100644 --- a/src/test/resources/restapi/securityconfig_nondefault.json +++ b/src/test/resources/restapi/securityconfig_nondefault.json @@ -6,6 +6,8 @@ "respect_request_indices_options" : false, "kibana" : { "multitenancy_enabled" : true, + "private_tenant_enabled" : true, + "default_tenant" : "", "server_username" : "kibanaserver", "index" : ".kibana" }, diff --git a/src/test/resources/security_passive/config.yml b/src/test/resources/security_passive/config.yml index 34f4aff093..3663b3c706 100644 --- a/src/test/resources/security_passive/config.yml +++ b/src/test/resources/security_passive/config.yml @@ -10,6 +10,8 @@ config: respect_request_indices_options: false kibana: multitenancy_enabled: true + private_tenant_enabled: true + default_tenant: "" server_username: "kibanaserver" index: ".kibana" http: