From f86f1fef52c371224ce59a36e07bf3b2a7f20335 Mon Sep 17 00:00:00 2001 From: Nils Bandener Date: Sun, 30 Jun 2024 09:31:18 +0200 Subject: [PATCH 1/9] V6 configurations now get auto-converted into V7 configurations Signed-off-by: Nils Bandener --- .../LegacyConfigV6AutoConversionTest.java | 79 ++++++ .../test/framework/TestSecurityConfig.java | 80 +++++- .../security/DefaultObjectMapper.java | 16 ++ .../ConfigurationChangeListener.java | 7 +- .../ConfigurationLoaderSecurity7.java | 35 ++- .../configuration/ConfigurationMap.java | 93 +++++++ .../ConfigurationRepository.java | 61 ++-- .../dlic/rest/api/AbstractApiAction.java | 14 +- .../dlic/rest/api/AccountApiAction.java | 2 +- .../dlic/rest/api/ActionGroupsApiAction.java | 2 +- .../dlic/rest/api/AllowlistApiAction.java | 2 +- .../dlic/rest/api/AuditApiAction.java | 2 +- .../rest/api/AuthTokenProcessorAction.java | 2 +- .../dlic/rest/api/CertificatesApiAction.java | 2 +- .../dlic/rest/api/ConfigUpgradeApiAction.java | 28 +- .../dlic/rest/api/FlushCacheApiAction.java | 2 +- .../dlic/rest/api/InternalUsersApiAction.java | 2 +- .../dlic/rest/api/MigrateApiAction.java | 2 +- .../rest/api/MultiTenancyConfigApiAction.java | 2 +- .../dlic/rest/api/NodesDnApiAction.java | 2 +- .../dlic/rest/api/RateLimitersApiAction.java | 2 +- .../dlic/rest/api/RolesApiAction.java | 2 +- .../dlic/rest/api/RolesMappingApiAction.java | 2 +- .../rest/api/SecurityConfigApiAction.java | 2 +- .../rest/api/SecuritySSLCertsApiAction.java | 2 +- .../dlic/rest/api/TenantsApiAction.java | 2 +- .../dlic/rest/api/ValidateApiAction.java | 4 +- .../dlic/rest/api/WhitelistApiAction.java | 2 +- .../security/rest/TenantInfoAction.java | 2 +- .../securityconf/DynamicConfigFactory.java | 138 ++++----- .../security/securityconf/Migration.java | 88 +++--- .../security/securityconf/impl/CType.java | 261 +++++++++++++++--- .../security/securityconf/impl/Meta.java | 4 +- .../impl/SecurityDynamicConfiguration.java | 118 ++++++-- .../securityconf/impl/v6/ConfigV6.java | 9 + .../securityconf/impl/v7/ActionGroupsV7.java | 8 + .../security/state/SecurityConfig.java | 12 +- .../security/support/ConfigHelper.java | 26 +- .../support/SecurityIndexHandler.java | 24 +- .../security/support/YamlConfigReader.java | 15 +- .../opensearch/security/tools/Migrater.java | 38 +-- .../security/tools/SecurityAdmin.java | 87 +----- .../opensearch/security/user/UserService.java | 6 +- .../org/opensearch/security/ConfigTests.java | 16 +- .../security/UserServiceUnitTests.java | 2 +- .../ConfigurationRepositoryTest.java | 7 +- .../api/AbstractApiActionValidationTest.java | 9 +- .../ActionGroupsApiActionValidationTest.java | 59 ++-- .../InternalUsersApiActionValidationTest.java | 6 +- .../RolesMappingApiActionValidationTest.java | 4 +- .../validation/EndpointValidatorTest.java | 7 +- .../FlattenedActionGroupsTest.java | 6 +- .../SecurityRolesPermissionsTest.java | 12 +- .../SecurityRolesPermissionsV6Test.java | 193 ------------- .../security/support/ConfigReaderTest.java | 4 +- .../support/SecurityIndexHandlerTest.java | 59 +--- 56 files changed, 934 insertions(+), 739 deletions(-) create mode 100644 src/integrationTest/java/org/opensearch/security/legacy/LegacyConfigV6AutoConversionTest.java create mode 100644 src/main/java/org/opensearch/security/configuration/ConfigurationMap.java delete mode 100644 src/test/java/org/opensearch/security/securityconf/SecurityRolesPermissionsV6Test.java diff --git a/src/integrationTest/java/org/opensearch/security/legacy/LegacyConfigV6AutoConversionTest.java b/src/integrationTest/java/org/opensearch/security/legacy/LegacyConfigV6AutoConversionTest.java new file mode 100644 index 0000000000..35fd179019 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/security/legacy/LegacyConfigV6AutoConversionTest.java @@ -0,0 +1,79 @@ +/* + * Copyright OpenSearch Contributors + * 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. + * + */ +package org.opensearch.security.legacy; + +import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.opensearch.test.framework.TestSecurityConfig; +import org.opensearch.test.framework.cluster.ClusterManager; +import org.opensearch.test.framework.cluster.LocalCluster; +import org.opensearch.test.framework.cluster.TestRestClient; + +@RunWith(com.carrotsearch.randomizedtesting.RandomizedRunner.class) +@ThreadLeakScope(ThreadLeakScope.Scope.NONE) +public class LegacyConfigV6AutoConversionTest { + static final TestSecurityConfig LEGACY_CONFIG = new TestSecurityConfig()// + .rawConfigurationDocumentYaml("config", """ + opendistro_security: + dynamic: + authc: + basic_internal_auth_domain: + http_enabled: true + order: 4 + http_authenticator: + type: basic + challenge: true + authentication_backend: + type: intern + """)// + .rawConfigurationDocumentYaml("internalusers", """ + admin: + readonly: true + hash: $2a$12$VcCDgh2NDk07JGN0rjGbM.Ad41qVR/YFJcgHp0UGns5JDymv..TOG + roles: + - admin + attributes: + attribute1: value1 + """)// + .rawConfigurationDocumentYaml("roles", """ + all_access: + readonly: true + cluster: + - UNLIMITED + indices: + '*': + '*': + - UNLIMITED + tenants: + admin_tenant: RW + """)// + .rawConfigurationDocumentYaml("rolesmapping", """ + all_access: + readonly: true + backendroles: + - admin + """); + + @ClassRule + public static LocalCluster cluster = new LocalCluster.Builder().clusterManager(ClusterManager.THREE_CLUSTER_MANAGERS) + .config(LEGACY_CONFIG) + .build(); + + @Test + public void checkAuthc() { + try (TestRestClient client = cluster.getRestClient("admin", "admin")) { + TestRestClient.HttpResponse response = client.get("_opendistro/_security/authinfo"); + System.out.println(response.getBody()); + } + } +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/TestSecurityConfig.java b/src/integrationTest/java/org/opensearch/test/framework/TestSecurityConfig.java index a1ea3720ba..9edf77f75c 100644 --- a/src/integrationTest/java/org/opensearch/test/framework/TestSecurityConfig.java +++ b/src/integrationTest/java/org/opensearch/test/framework/TestSecurityConfig.java @@ -43,6 +43,9 @@ import java.util.function.Supplier; import java.util.stream.Collectors; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -94,6 +97,13 @@ public class TestSecurityConfig { private Map actionGroups = new LinkedHashMap<>(); + /** + * A map from document id to a string containing config JSON. + * If this is not null, it will be used ALTERNATIVELY to all other configuration contained in this class. + * Can be used to simulate invalid configuration or legacy configuration. + */ + private Map rawConfigurationDocuments; + private String indexName = ".opendistro_security"; public TestSecurityConfig() { @@ -212,6 +222,27 @@ public List actionGroups() { return List.copyOf(actionGroups.values()); } + /** + * Specifies raw document content for the configuration index as YAML document. If this method is used, + * then ONLY the raw documents will be written to the configuration index. Any other configuration specified + * by the roles() or users() method will be ignored. + * Can be used to simulate invalid configuration or legacy configuration. + */ + public TestSecurityConfig rawConfigurationDocumentYaml(String configTypeId, String configDocumentAsYaml) { + try { + if (this.rawConfigurationDocuments == null) { + this.rawConfigurationDocuments = new LinkedHashMap<>(); + } + + JsonNode node = new ObjectMapper(new YAMLFactory()).readTree(configDocumentAsYaml); + + this.rawConfigurationDocuments.put(configTypeId, new ObjectMapper().writeValueAsString(node)); + return this; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + public static class Config implements ToXContentObject { private boolean anonymousAuth; @@ -964,15 +995,24 @@ public void initIndex(Client client) { } client.admin().indices().create(new CreateIndexRequest(indexName).settings(settings)).actionGet(); - writeSingleEntryConfigToIndex(client, CType.CONFIG, config); - if (auditConfiguration != null) { - writeSingleEntryConfigToIndex(client, CType.AUDIT, "config", auditConfiguration); + if (rawConfigurationDocuments == null) { + writeSingleEntryConfigToIndex(client, CType.CONFIG, config); + if (auditConfiguration != null) { + writeSingleEntryConfigToIndex(client, CType.AUDIT, "config", auditConfiguration); + } + writeConfigToIndex(client, CType.ROLES, roles); + writeConfigToIndex(client, CType.INTERNALUSERS, internalUsers); + writeConfigToIndex(client, CType.ROLESMAPPING, rolesMapping); + writeEmptyConfigToIndex(client, CType.ACTIONGROUPS); + writeEmptyConfigToIndex(client, CType.TENANTS); + } else { + // Write raw configuration alternatively to the normal configuration + + for (Map.Entry entry : this.rawConfigurationDocuments.entrySet()) { + writeConfigToIndex(client, entry.getKey(), entry.getValue()); + } } - writeConfigToIndex(client, CType.ROLES, roles); - writeConfigToIndex(client, CType.INTERNALUSERS, internalUsers); - writeConfigToIndex(client, CType.ROLESMAPPING, rolesMapping); - writeEmptyConfigToIndex(client, CType.ACTIONGROUPS); - writeEmptyConfigToIndex(client, CType.TENANTS); + } public void updateInternalUsersConfiguration(Client client, List users) { @@ -987,11 +1027,11 @@ static String hashPassword(final String clearTextPassword) { return passwordHasher.hash(clearTextPassword.toCharArray()); } - private void writeEmptyConfigToIndex(Client client, CType configType) { + private void writeEmptyConfigToIndex(Client client, CType configType) { writeConfigToIndex(client, configType, Collections.emptyMap()); } - private void writeConfigToIndex(Client client, CType configType, Map config) { + private void writeConfigToIndex(Client client, CType configType, Map config) { try { String json = configToJson(configType, config); @@ -1008,11 +1048,23 @@ private void writeConfigToIndex(Client client, CType configType, Map config) { + private void updateConfigInIndex(Client client, CType configType, Map config) { try { String json = configToJson(configType, config); BytesReference bytesReference = toByteReference(json); @@ -1025,7 +1077,7 @@ private void updateConfigInIndex(Client client, CType configType, Map config) throws IOException { + private static String configToJson(CType configType, Map config) throws IOException { XContentBuilder builder = XContentFactory.jsonBuilder(); builder.startObject(); @@ -1043,11 +1095,11 @@ private static String configToJson(CType configType, Map configType, ToXContentObject config) { writeSingleEntryConfigToIndex(client, configType, configType.toLCString(), config); } - private void writeSingleEntryConfigToIndex(Client client, CType configType, String configurationRoot, ToXContentObject config) { + private void writeSingleEntryConfigToIndex(Client client, CType configType, String configurationRoot, ToXContentObject config) { try { XContentBuilder builder = XContentFactory.jsonBuilder(); diff --git a/src/main/java/org/opensearch/security/DefaultObjectMapper.java b/src/main/java/org/opensearch/security/DefaultObjectMapper.java index d7ec09f5d9..68a537c669 100644 --- a/src/main/java/org/opensearch/security/DefaultObjectMapper.java +++ b/src/main/java/org/opensearch/security/DefaultObjectMapper.java @@ -267,6 +267,22 @@ public static T readValue(String string, JavaType jt) throws IOException { } } + @SuppressWarnings("removal") + public static T convertValue(JsonNode jsonNode, JavaType jt) throws IOException { + + final SecurityManager sm = System.getSecurityManager(); + + if (sm != null) { + sm.checkPermission(new SpecialPermission()); + } + + try { + return AccessController.doPrivileged((PrivilegedExceptionAction) () -> objectMapper.convertValue(jsonNode, jt)); + } catch (final PrivilegedActionException e) { + throw (IOException) e.getCause(); + } + } + public static TypeFactory getTypeFactory() { return objectMapper.getTypeFactory(); } diff --git a/src/main/java/org/opensearch/security/configuration/ConfigurationChangeListener.java b/src/main/java/org/opensearch/security/configuration/ConfigurationChangeListener.java index 024bd55157..cc410c0158 100644 --- a/src/main/java/org/opensearch/security/configuration/ConfigurationChangeListener.java +++ b/src/main/java/org/opensearch/security/configuration/ConfigurationChangeListener.java @@ -26,11 +26,6 @@ package org.opensearch.security.configuration; -import java.util.Map; - -import org.opensearch.security.securityconf.impl.CType; -import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; - /** * Callback function on change particular configuration */ @@ -39,5 +34,5 @@ public interface ConfigurationChangeListener { /** * @param configuration not null updated configuration on that was subscribe current listener */ - void onChange(Map> typeToConfig); + void onChange(ConfigurationMap typeToConfig); } diff --git a/src/main/java/org/opensearch/security/configuration/ConfigurationLoaderSecurity7.java b/src/main/java/org/opensearch/security/configuration/ConfigurationLoaderSecurity7.java index 8a3047385b..77d4585c21 100644 --- a/src/main/java/org/opensearch/security/configuration/ConfigurationLoaderSecurity7.java +++ b/src/main/java/org/opensearch/security/configuration/ConfigurationLoaderSecurity7.java @@ -29,8 +29,6 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -56,8 +54,11 @@ import org.opensearch.core.xcontent.XContentParser; import org.opensearch.security.DefaultObjectMapper; import org.opensearch.security.auditlog.config.AuditConfig; +import org.opensearch.security.securityconf.Migration; import org.opensearch.security.securityconf.impl.CType; import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; +import org.opensearch.security.securityconf.impl.v6.RoleV6; +import org.opensearch.security.securityconf.impl.v7.TenantV7; import org.opensearch.security.support.ConfigConstants; import org.opensearch.security.support.ConfigHelper; import org.opensearch.security.support.SecurityUtils; @@ -94,10 +95,10 @@ boolean isAuditConfigDocPresentInIndex() { return isAuditConfigDocPresentInIndex.get(); } - Map> load(final CType[] events, long timeout, TimeUnit timeUnit, boolean acceptInvalid) - throws InterruptedException, TimeoutException { + ConfigurationMap load(final CType[] events, long timeout, TimeUnit timeUnit, boolean acceptInvalid) throws InterruptedException, + TimeoutException { final CountDownLatch latch = new CountDownLatch(events.length); - final Map> rs = new HashMap<>(events.length); + ConfigurationMap.Builder result = new ConfigurationMap.Builder(); final boolean isDebugEnabled = log.isDebugEnabled(); loadAsync(events, new ConfigCallback() { @@ -118,7 +119,19 @@ public void success(SecurityDynamicConfiguration dConf) { isAuditConfigDocPresentInIndex.set(true); } - rs.put(dConf.getCType(), dConf); + result.with(dConf); + + if (dConf.getCType() == CType.ROLES && dConf.getAutoConvertedFrom() != null) { + // Special case for configuration that was auto-converted from v6: + // We need to generate tenant config from role config. + // Having such a special case here is not optimal, but IMHO acceptable, as this + // should be only a temporary measure until V6 configuration is completely discontinued. + @SuppressWarnings("unchecked") + SecurityDynamicConfiguration roleV6config = (SecurityDynamicConfiguration) dConf.getAutoConvertedFrom(); + SecurityDynamicConfiguration tenants = Migration.createTenants(roleV6config); + result.with(tenants); + } + latch.countDown(); if (isDebugEnabled) { log.debug( @@ -142,7 +155,7 @@ public void singleFailure(Failure failure) { @Override public void noData(String id) { - CType cType = CType.fromString(id); + CType cType = CType.fromString(id); // Since NODESDN is newly introduced data-type applying for existing clusters as well, we make it backward compatible by // returning valid empty @@ -154,7 +167,7 @@ public void noData(String id) { cType, ConfigurationRepository.getDefaultConfigVersion() ); - rs.put(cType, empty); + result.with(empty); latch.countDown(); return; } catch (Exception e) { @@ -172,7 +185,7 @@ public void noData(String id) { ConfigurationRepository.getDefaultConfigVersion() ); empty.putCObject("config", AuditConfig.from(settings)); - rs.put(cType, empty); + result.with(empty); latch.countDown(); return; } catch (Exception e) { @@ -204,10 +217,10 @@ public void failure(Throwable t) { ); } - return rs; + return result.build(); } - void loadAsync(final CType[] events, final ConfigCallback callback, boolean acceptInvalid) { + void loadAsync(final CType[] events, final ConfigCallback callback, boolean acceptInvalid) { if (events == null || events.length == 0) { log.warn("No config events requested to load"); return; diff --git a/src/main/java/org/opensearch/security/configuration/ConfigurationMap.java b/src/main/java/org/opensearch/security/configuration/ConfigurationMap.java new file mode 100644 index 0000000000..bd023fc4ef --- /dev/null +++ b/src/main/java/org/opensearch/security/configuration/ConfigurationMap.java @@ -0,0 +1,93 @@ +/* + * 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.configuration; + +import java.util.Set; + +import com.google.common.collect.ImmutableMap; + +import org.opensearch.security.securityconf.impl.CType; +import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; + +/** + * Allows type safe access of configuration instances via the configuration type + */ +public class ConfigurationMap { + public static final ConfigurationMap EMPTY = new ConfigurationMap(ImmutableMap.of()); + + private final ImmutableMap, SecurityDynamicConfiguration> map; + + private ConfigurationMap(ImmutableMap, SecurityDynamicConfiguration> map) { + this.map = map; + } + + public SecurityDynamicConfiguration get(CType ctype) { + @SuppressWarnings("unchecked") + SecurityDynamicConfiguration config = (SecurityDynamicConfiguration) map.get(ctype); + + if (config == null) { + return null; + } + + if (!config.getCType().equals(ctype)) { + throw new RuntimeException("Stored configuration does not match type: " + ctype + "; " + config); + } + + return config; + } + + public boolean containsKey(CType ctype) { + return map.containsKey(ctype); + } + + public Set> keySet() { + return map.keySet(); + } + + public int size() { + return this.map.size(); + } + + public ImmutableMap, SecurityDynamicConfiguration> rawMap() { + return this.map; + } + + public static ConfigurationMap of(SecurityDynamicConfiguration... configs) { + Builder builder = new Builder(); + + for (SecurityDynamicConfiguration config : configs) { + builder.with(config); + } + + return builder.build(); + } + + public static class Builder { + private ImmutableMap.Builder, SecurityDynamicConfiguration> map = new ImmutableMap.Builder<>(); + + public Builder() {} + + public Builder with(SecurityDynamicConfiguration config) { + map.put(config.getCType(), config); + return this; + } + + public Builder with(ConfigurationMap configurationMap) { + map.putAll(configurationMap.map); + return this; + } + + public ConfigurationMap build() { + return new ConfigurationMap(this.map.build()); + } + } +} diff --git a/src/main/java/org/opensearch/security/configuration/ConfigurationRepository.java b/src/main/java/org/opensearch/security/configuration/ConfigurationRepository.java index d7c5fcaed4..a546db131e 100644 --- a/src/main/java/org/opensearch/security/configuration/ConfigurationRepository.java +++ b/src/main/java/org/opensearch/security/configuration/ConfigurationRepository.java @@ -33,7 +33,6 @@ import java.text.SimpleDateFormat; import java.time.Instant; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.HashMap; @@ -100,7 +99,7 @@ public class ConfigurationRepository implements ClusterStateListener { private final String securityIndex; private final Client client; - private final Cache> configCache; + private final Cache, SecurityDynamicConfiguration> configCache; private final List configurationChangedListener; private final ConfigurationLoaderSecurity7 cl; private final Settings settings; @@ -283,7 +282,7 @@ private void initalizeClusterConfiguration(final boolean installDefaultConfig) { while (!dynamicConfigFactory.isInitialized()) { try { LOGGER.debug("Try to load config ..."); - reloadConfiguration(Arrays.asList(CType.values()), true); + reloadConfiguration(CType.values(), true); break; } catch (Exception e) { LOGGER.debug("Unable to load configuration due to {}", String.valueOf(ExceptionUtils.getRootCause(e))); @@ -510,21 +509,23 @@ public void setDynamicConfigFactory(DynamicConfigFactory dynamicConfigFactory) { * @param configurationType * @return can also return empty in case it was never loaded */ - public SecurityDynamicConfiguration getConfiguration(CType configurationType) { + public SecurityDynamicConfiguration getConfiguration(CType configurationType) { SecurityDynamicConfiguration conf = configCache.getIfPresent(configurationType); if (conf != null) { - return conf.deepClone(); + @SuppressWarnings("unchecked") + SecurityDynamicConfiguration result = (SecurityDynamicConfiguration) conf.deepClone(); + return result; } - return SecurityDynamicConfiguration.empty(); + return SecurityDynamicConfiguration.empty(configurationType); } private final Lock LOCK = new ReentrantLock(); - public boolean reloadConfiguration(final Collection configTypes) throws ConfigUpdateAlreadyInProgressException { + public boolean reloadConfiguration(final Collection> configTypes) throws ConfigUpdateAlreadyInProgressException { return reloadConfiguration(configTypes, false); } - private boolean reloadConfiguration(final Collection configTypes, final boolean fromBackgroundThread) + private boolean reloadConfiguration(final Collection> configTypes, final boolean fromBackgroundThread) throws ConfigUpdateAlreadyInProgressException { if (!fromBackgroundThread && !initalizeConfigTask.isDone()) { LOGGER.warn("Unable to reload configuration, initalization thread has not yet completed."); @@ -533,7 +534,7 @@ private boolean reloadConfiguration(final Collection configTypes, final b return loadConfigurationWithLock(configTypes); } - private boolean loadConfigurationWithLock(Collection configTypes) { + private boolean loadConfigurationWithLock(Collection> configTypes) { try { if (LOCK.tryLock(60, TimeUnit.SECONDS)) { try { @@ -551,13 +552,13 @@ private boolean loadConfigurationWithLock(Collection configTypes) { } } - private void reloadConfiguration0(Collection configTypes, boolean acceptInvalid) { - final Map> loaded = getConfigurationsFromIndex(configTypes, false, acceptInvalid); + private void reloadConfiguration0(Collection> configTypes, boolean acceptInvalid) { + ConfigurationMap loaded = getConfigurationsFromIndex(configTypes, false, acceptInvalid); notifyConfigurationListeners(loaded); } - private void notifyConfigurationListeners(final Map> configuration) { - configCache.putAll(configuration); + private void notifyConfigurationListeners(ConfigurationMap configuration) { + configCache.putAll(configuration.rawMap()); notifyAboutChanges(configuration); } @@ -565,7 +566,7 @@ public synchronized void subscribeOnChange(ConfigurationChangeListener listener) configurationChangedListener.add(listener); } - private synchronized void notifyAboutChanges(Map> typeToConfig) { + private synchronized void notifyAboutChanges(ConfigurationMap typeToConfig) { for (ConfigurationChangeListener listener : configurationChangedListener) { try { LOGGER.debug("Notify {} listener about change configuration with type {}", listener); @@ -583,21 +584,18 @@ private synchronized void notifyAboutChanges(Map> getConfigurationsFromIndex( - Collection configTypes, - boolean logComplianceEvent - ) { + public ConfigurationMap getConfigurationsFromIndex(Collection> configTypes, boolean logComplianceEvent) { return getConfigurationsFromIndex(configTypes, logComplianceEvent, this.acceptInvalid); } - public Map> getConfigurationsFromIndex( - Collection configTypes, + public ConfigurationMap getConfigurationsFromIndex( + Collection> configTypes, boolean logComplianceEvent, boolean acceptInvalid ) { final ThreadContext threadContext = threadPool.getThreadContext(); - final Map> retVal = new HashMap<>(); + final ConfigurationMap.Builder resultBuilder = new ConfigurationMap.Builder(); try (StoredContext ctx = threadContext.stashContext()) { threadContext.putHeader(ConfigConstants.OPENDISTRO_SECURITY_CONF_REQUEST_HEADER, "true"); @@ -611,15 +609,15 @@ public Map> getConfigurationsFromIndex( } else { LOGGER.debug("security index exists and was created with ES 7 (new layout)"); } - retVal.putAll( - validate(cl.load(configTypes.toArray(new CType[0]), 10, TimeUnit.SECONDS, acceptInvalid), configTypes.size()) + resultBuilder.with( + validate(cl.load(configTypes.toArray(new CType[0]), 10, TimeUnit.SECONDS, acceptInvalid), configTypes.size()) ); } else { // wait (and use new layout) LOGGER.debug("security index not exists (yet)"); - retVal.putAll( - validate(cl.load(configTypes.toArray(new CType[0]), 10, TimeUnit.SECONDS, acceptInvalid), configTypes.size()) + resultBuilder.with( + validate(cl.load(configTypes.toArray(new CType[0]), 10, TimeUnit.SECONDS, acceptInvalid), configTypes.size()) ); } @@ -627,20 +625,21 @@ public Map> getConfigurationsFromIndex( throw new OpenSearchException(e); } + ConfigurationMap result = resultBuilder.build(); + if (logComplianceEvent && auditLog.getComplianceConfig() != null && auditLog.getComplianceConfig().isEnabled()) { - CType configurationType = configTypes.iterator().next(); + CType configurationType = configTypes.iterator().next(); Map fields = new HashMap(); - fields.put(configurationType.toLCString(), Strings.toString(MediaTypeRegistry.JSON, retVal.get(configurationType))); + fields.put(configurationType.toLCString(), Strings.toString(MediaTypeRegistry.JSON, result.get(configurationType))); auditLog.logDocumentRead(this.securityIndex, configurationType.toLCString(), null, fields); } - return retVal; + return result; } - private Map> validate(Map> conf, int expectedSize) - throws InvalidConfigException { + private ConfigurationMap validate(ConfigurationMap conf, int expectedSize) throws InvalidConfigException { - if (conf == null || conf.size() != expectedSize) { + if (conf == null || conf.size() < expectedSize) { throw new InvalidConfigException("Retrieved only partial configuration"); } 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 eb82bed908..160a7f708d 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 @@ -376,7 +376,7 @@ protected final ValidationResult loadConfiguration(final } protected ValidationResult> loadConfiguration( - final CType cType, + final CType cType, boolean omitSensitiveData, final boolean logComplianceEvent ) { @@ -451,9 +451,9 @@ public RequestContentValidator createRequestContentValidator(Object... params) { }; } - protected abstract CType getConfigType(); + protected abstract CType getConfigType(); - protected final SecurityDynamicConfiguration load(final CType config, boolean logComplianceEvent) { + protected final SecurityDynamicConfiguration load(final CType config, boolean logComplianceEvent) { SecurityDynamicConfiguration loaded = securityApiDependencies.configurationRepository() .getConfigurationsFromIndex(List.of(config), logComplianceEvent) .get(config) @@ -461,7 +461,7 @@ protected final SecurityDynamicConfiguration load(final CType config, boolean return DynamicConfigFactory.addStatics(loaded); } - protected final SecurityDynamicConfiguration loadAndRedact(final CType config, boolean logComplianceEvent) { + protected final SecurityDynamicConfiguration loadAndRedact(final CType config, boolean logComplianceEvent) { SecurityDynamicConfiguration loaded = securityApiDependencies.configurationRepository() .getConfigurationsFromIndex(List.of(config), logComplianceEvent) .get(config) @@ -496,7 +496,7 @@ public final void onFailure(Exception e) { public static ActionFuture saveAndUpdateConfigs( final SecurityApiDependencies dependencies, final Client client, - final CType cType, + final CType cType, final SecurityDynamicConfiguration configuration ) { final var request = createIndexRequestForConfig(dependencies, cType, configuration); @@ -506,7 +506,7 @@ public static ActionFuture saveAndUpdateConfigs( public static void saveAndUpdateConfigsAsync( final SecurityApiDependencies dependencies, final Client client, - final CType cType, + final CType cType, final SecurityDynamicConfiguration configuration, final ActionListener actionListener ) { @@ -516,7 +516,7 @@ public static void saveAndUpdateConfigsAsync( private static IndexRequest createIndexRequestForConfig( final SecurityApiDependencies dependencies, - final CType cType, + final CType cType, final SecurityDynamicConfiguration configuration ) { configuration.removeStatic(); 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 199f6b088a..ad9aa656da 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 @@ -74,7 +74,7 @@ public List routes() { } @Override - protected CType getConfigType() { + protected CType getConfigType() { return CType.INTERNALUSERS; } diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/ActionGroupsApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/ActionGroupsApiAction.java index 0b5dfd1499..e54e7d87af 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/ActionGroupsApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/ActionGroupsApiAction.java @@ -88,7 +88,7 @@ public List routes() { } @Override - protected CType getConfigType() { + protected CType getConfigType() { return CType.ACTIONGROUPS; } 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 b7d4761993..8462ec3fcf 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 @@ -106,7 +106,7 @@ public List routes() { } @Override - protected CType getConfigType() { + protected CType getConfigType() { return CType.ALLOWLIST; } diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/AuditApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/AuditApiAction.java index 997bd85bdd..a5bf9c6b9b 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/AuditApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/AuditApiAction.java @@ -233,7 +233,7 @@ public List routes() { } @Override - protected CType getConfigType() { + protected CType getConfigType() { return CType.AUDIT; } diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/AuthTokenProcessorAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/AuthTokenProcessorAction.java index bc37f41d6e..b77af85db3 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/AuthTokenProcessorAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/AuthTokenProcessorAction.java @@ -44,7 +44,7 @@ public List routes() { } @Override - protected CType getConfigType() { + protected CType getConfigType() { return null; } diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/CertificatesApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/CertificatesApiAction.java index e1cd51e95d..1a769a9b71 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/CertificatesApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/CertificatesApiAction.java @@ -63,7 +63,7 @@ public String getName() { } @Override - protected CType getConfigType() { + protected CType getConfigType() { return null; } diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/ConfigUpgradeApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/ConfigUpgradeApiAction.java index 8559ab49d7..0ece877b20 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/ConfigUpgradeApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/ConfigUpgradeApiAction.java @@ -69,7 +69,7 @@ public class ConfigUpgradeApiAction extends AbstractApiAction { private final static Logger LOGGER = LogManager.getLogger(ConfigUpgradeApiAction.class); - private final static Set SUPPORTED_CTYPES = ImmutableSet.of(CType.ROLES); + private final static Set> SUPPORTED_CTYPES = ImmutableSet.of(CType.ROLES); private final static String REQUEST_PARAM_CONFIGS_KEY = "configs"; @@ -130,11 +130,11 @@ void performUpgrade(final RestChannel channel, final RestRequest request, final private ValidationResult> applyDifferences( final RestRequest request, final Client client, - final List> differencesToUpdate + final List, JsonNode>> differencesToUpdate ) { try { final var updatedResources = new ArrayList>(); - for (final Tuple difference : differencesToUpdate) { + for (final Tuple, JsonNode> difference : differencesToUpdate) { updatedResources.add( loadConfiguration(difference.v1(), false, false).map( configuration -> patchEntities(request, difference.v2(), SecurityConfiguration.of(null, configuration)).map( @@ -167,7 +167,7 @@ private ValidationResult> applyDifferences( } - ValidationResult>> verifyHasDifferences(List> diffs) { + ValidationResult, JsonNode>>> verifyHasDifferences(List, JsonNode>> diffs) { if (diffs.isEmpty()) { return ValidationResult.error(RestStatus.BAD_REQUEST, badRequestMessage("Unable to upgrade, no differences found")); } @@ -183,9 +183,9 @@ ValidationResult>> verifyHasDifferences(List>> configurationDifferences(final Set configurations) { + private ValidationResult, JsonNode>>> configurationDifferences(final Set> configurations) { try { - final var differences = new ArrayList>>(); + final var differences = new ArrayList, JsonNode>>>(); for (final var configuration : configurations) { differences.add(computeDifferenceToUpdate(configuration)); } @@ -199,7 +199,7 @@ private ValidationResult>> configurationDifferences( } } - ValidationResult> computeDifferenceToUpdate(final CType configType) { + ValidationResult, JsonNode>> computeDifferenceToUpdate(final CType configType) { return withIOException(() -> loadConfiguration(configType, false, false).map(activeRoles -> { final var activeRolesJson = Utils.convertJsonToJackson(activeRoles, true); final var defaultRolesJson = loadConfigFileAsJson(configType); @@ -208,10 +208,10 @@ ValidationResult> computeDifferenceToUpdate(final CType c })); } - private ValidationResult> getAndValidateConfigurationsToUpgrade(final RestRequest request) { + private ValidationResult>> getAndValidateConfigurationsToUpgrade(final RestRequest request) { final String[] configs = request.paramAsStringArray(REQUEST_PARAM_CONFIGS_KEY, null); - final Set configurations; + final Set> configurations; try { configurations = Optional.ofNullable(configs).map(CType::fromStringValues).orElse(SUPPORTED_CTYPES); } catch (final IllegalArgumentException iae) { @@ -261,12 +261,12 @@ private static boolean isRemoveOperation(final JsonNode node) { return node.get("op").asText().equals("remove"); } - private SecurityDynamicConfiguration loadYamlFile(final String filepath, final CType cType) throws IOException { + private SecurityDynamicConfiguration loadYamlFile(final String filepath, final CType cType) throws IOException { return ConfigHelper.fromYamlFile(filepath, cType, ConfigurationRepository.DEFAULT_CONFIG_VERSION, 0, 0); } @SuppressWarnings("removal") - JsonNode loadConfigFileAsJson(final CType cType) throws IOException { + JsonNode loadConfigFileAsJson(final CType cType) throws IOException { final var cd = securityApiDependencies.configurationRepository().getConfigDirectory(); final var filepath = cType.configFile(Path.of(cd)).toString(); try { @@ -286,7 +286,7 @@ public List routes() { } @Override - protected CType getConfigType() { + protected CType getConfigType() { throw new UnsupportedOperationException("This class supports multiple configuration types"); } @@ -354,10 +354,10 @@ public ValidationResult validate(final RestRequest request, final Json /** Tranforms config changes from a raw PATCH into simplier view */ static class ConfigItemChanges { - private final CType config; + private final CType config; private final Map> itemsGroupedByOperation; - public ConfigItemChanges(final CType config, final JsonNode differences) { + public ConfigItemChanges(final CType config, final JsonNode differences) { this.config = config; this.itemsGroupedByOperation = classifyChanges(differences); } diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/FlushCacheApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/FlushCacheApiAction.java index 2f579ecbd9..766b57c62a 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/FlushCacheApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/FlushCacheApiAction.java @@ -95,7 +95,7 @@ public void onFailure(final Exception e) { } @Override - protected CType getConfigType() { + protected CType getConfigType() { return null; } 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 3a16028b54..00c33e0f4f 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 @@ -104,7 +104,7 @@ public List routes() { } @Override - protected CType getConfigType() { + protected CType getConfigType() { return CType.INTERNALUSERS; } 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 b66ff0d5f3..4dc5128751 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 @@ -87,7 +87,7 @@ public List routes() { } @Override - protected CType getConfigType() { + protected CType getConfigType() { return null; } diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/MultiTenancyConfigApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/MultiTenancyConfigApiAction.java index 2be5778956..53945432d9 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/MultiTenancyConfigApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/MultiTenancyConfigApiAction.java @@ -84,7 +84,7 @@ public MultiTenancyConfigApiAction( } @Override - protected CType getConfigType() { + protected CType getConfigType() { return CType.CONFIG; } diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/NodesDnApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/NodesDnApiAction.java index ff44867bd2..d1c53721d8 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/NodesDnApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/NodesDnApiAction.java @@ -90,7 +90,7 @@ public List routes() { } @Override - protected CType getConfigType() { + protected CType getConfigType() { return CType.NODESDN; } diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/RateLimitersApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/RateLimitersApiAction.java index 6dc51bf6e1..7ef5c59c1e 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/RateLimitersApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/RateLimitersApiAction.java @@ -91,7 +91,7 @@ public List routes() { } @Override - protected CType getConfigType() { + protected CType getConfigType() { return CType.CONFIG; } diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/RolesApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/RolesApiAction.java index 4c29d6e934..10abd83f7b 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/RolesApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/RolesApiAction.java @@ -121,7 +121,7 @@ public List routes() { } @Override - protected CType getConfigType() { + protected CType getConfigType() { return CType.ROLES; } 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 ccfac457aa..5b117ec48d 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 @@ -65,7 +65,7 @@ public List routes() { } @Override - protected CType getConfigType() { + protected CType getConfigType() { return CType.ROLESMAPPING; } diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/SecurityConfigApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/SecurityConfigApiAction.java index 2141a35460..57c23f86af 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/SecurityConfigApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/SecurityConfigApiAction.java @@ -67,7 +67,7 @@ public List routes() { } @Override - protected CType getConfigType() { + protected CType getConfigType() { return CType.CONFIG; } diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/SecuritySSLCertsApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/SecuritySSLCertsApiAction.java index 30b6c862ee..7f4bff50ab 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/SecuritySSLCertsApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/SecuritySSLCertsApiAction.java @@ -98,7 +98,7 @@ protected void consumeParameters(RestRequest request) { } @Override - protected CType getConfigType() { + protected CType getConfigType() { return null; } diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/TenantsApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/TenantsApiAction.java index df2289b44c..7cccd04bb1 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/TenantsApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/TenantsApiAction.java @@ -74,7 +74,7 @@ public List routes() { } @Override - protected CType getConfigType() { + protected CType getConfigType() { return CType.TENANTS; } diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/ValidateApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/ValidateApiAction.java index 1d56ed80f9..58ab3884ae 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/ValidateApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/ValidateApiAction.java @@ -63,7 +63,7 @@ public List routes() { } @Override - protected CType getConfigType() { + protected CType getConfigType() { return null; } @@ -129,7 +129,7 @@ protected void validate(RestChannel channel, RestRequest request) throws IOExcep } } - private SecurityDynamicConfiguration load(final CType config, boolean logComplianceEvent, boolean acceptInvalid) { + private SecurityDynamicConfiguration load(final CType config, boolean logComplianceEvent, boolean acceptInvalid) { SecurityDynamicConfiguration loaded = securityApiDependencies.configurationRepository() .getConfigurationsFromIndex(Collections.singleton(config), logComplianceEvent, acceptInvalid) .get(config) diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/WhitelistApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/WhitelistApiAction.java index 2545bb2e23..fd71312910 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/WhitelistApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/WhitelistApiAction.java @@ -104,7 +104,7 @@ public List deprecatedRoutes() { } @Override - protected CType getConfigType() { + protected CType getConfigType() { return CType.WHITELIST; } diff --git a/src/main/java/org/opensearch/security/rest/TenantInfoAction.java b/src/main/java/org/opensearch/security/rest/TenantInfoAction.java index bd911463d4..d7b3ef3d1f 100644 --- a/src/main/java/org/opensearch/security/rest/TenantInfoAction.java +++ b/src/main/java/org/opensearch/security/rest/TenantInfoAction.java @@ -179,7 +179,7 @@ private boolean isAuthorized() { return false; } - private final SecurityDynamicConfiguration load(final CType config, boolean logComplianceEvent) { + private final SecurityDynamicConfiguration load(final CType config, boolean logComplianceEvent) { SecurityDynamicConfiguration loaded = configurationRepository.getConfigurationsFromIndex( Collections.singleton(config), logComplianceEvent diff --git a/src/main/java/org/opensearch/security/securityconf/DynamicConfigFactory.java b/src/main/java/org/opensearch/security/securityconf/DynamicConfigFactory.java index 335e5283ba..ccd1502532 100644 --- a/src/main/java/org/opensearch/security/securityconf/DynamicConfigFactory.java +++ b/src/main/java/org/opensearch/security/securityconf/DynamicConfigFactory.java @@ -48,6 +48,7 @@ import org.opensearch.security.auth.internal.InternalAuthenticationBackend; import org.opensearch.security.configuration.ClusterInfoHolder; import org.opensearch.security.configuration.ConfigurationChangeListener; +import org.opensearch.security.configuration.ConfigurationMap; import org.opensearch.security.configuration.ConfigurationRepository; import org.opensearch.security.configuration.StaticResourceException; import org.opensearch.security.hasher.PasswordHasher; @@ -56,11 +57,8 @@ import org.opensearch.security.securityconf.impl.NodesDn; import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; import org.opensearch.security.securityconf.impl.WhitelistingSettings; -import org.opensearch.security.securityconf.impl.v6.ActionGroupsV6; import org.opensearch.security.securityconf.impl.v6.ConfigV6; import org.opensearch.security.securityconf.impl.v6.InternalUserV6; -import org.opensearch.security.securityconf.impl.v6.RoleMappingsV6; -import org.opensearch.security.securityconf.impl.v6.RoleV6; import org.opensearch.security.securityconf.impl.v7.ActionGroupsV7; import org.opensearch.security.securityconf.impl.v7.ConfigV7; import org.opensearch.security.securityconf.impl.v7.InternalUserV7; @@ -78,17 +76,17 @@ public class DynamicConfigFactory implements Initializable, ConfigurationChangeListener { public static final EventBusBuilder EVENT_BUS_BUILDER = EventBus.builder(); - private static SecurityDynamicConfiguration staticRoles = SecurityDynamicConfiguration.empty(); - private static SecurityDynamicConfiguration staticActionGroups = SecurityDynamicConfiguration.empty(); - private static SecurityDynamicConfiguration staticTenants = SecurityDynamicConfiguration.empty(); + private static SecurityDynamicConfiguration staticRoles = SecurityDynamicConfiguration.empty(CType.ROLES); + private static SecurityDynamicConfiguration staticActionGroups = SecurityDynamicConfiguration.empty(CType.ACTIONGROUPS); + private static SecurityDynamicConfiguration staticTenants = SecurityDynamicConfiguration.empty(CType.TENANTS); private static final WhitelistingSettings defaultWhitelistingSettings = new WhitelistingSettings(); private static final AllowlistingSettings defaultAllowlistingSettings = new AllowlistingSettings(); private static final AuditConfig defaultAuditConfig = AuditConfig.from(Settings.EMPTY); static void resetStatics() { - staticRoles = SecurityDynamicConfiguration.empty(); - staticActionGroups = SecurityDynamicConfiguration.empty(); - staticTenants = SecurityDynamicConfiguration.empty(); + staticRoles = SecurityDynamicConfiguration.empty(CType.ROLES); + staticActionGroups = SecurityDynamicConfiguration.empty(CType.ACTIONGROUPS); + staticTenants = SecurityDynamicConfiguration.empty(CType.TENANTS); } private void loadStaticConfig() throws IOException { @@ -167,17 +165,17 @@ public DynamicConfigFactory( @Override @SuppressWarnings("unchecked") - public void onChange(Map> typeToConfig) { + public void onChange(ConfigurationMap typeToConfig) { - SecurityDynamicConfiguration actionGroups = cr.getConfiguration(CType.ACTIONGROUPS); + SecurityDynamicConfiguration actionGroups = cr.getConfiguration(CType.ACTIONGROUPS); config = cr.getConfiguration(CType.CONFIG); - SecurityDynamicConfiguration internalusers = cr.getConfiguration(CType.INTERNALUSERS); - SecurityDynamicConfiguration roles = cr.getConfiguration(CType.ROLES); - SecurityDynamicConfiguration rolesmapping = cr.getConfiguration(CType.ROLESMAPPING); - SecurityDynamicConfiguration tenants = cr.getConfiguration(CType.TENANTS); - SecurityDynamicConfiguration nodesDn = cr.getConfiguration(CType.NODESDN); - SecurityDynamicConfiguration whitelistingSetting = cr.getConfiguration(CType.WHITELIST); - SecurityDynamicConfiguration allowlistingSetting = cr.getConfiguration(CType.ALLOWLIST); + SecurityDynamicConfiguration internalusers = cr.getConfiguration(CType.INTERNALUSERS); + SecurityDynamicConfiguration roles = cr.getConfiguration(CType.ROLES); + SecurityDynamicConfiguration rolesmapping = cr.getConfiguration(CType.ROLESMAPPING); + SecurityDynamicConfiguration tenants = cr.getConfiguration(CType.TENANTS); + SecurityDynamicConfiguration nodesDn = cr.getConfiguration(CType.NODESDN); + SecurityDynamicConfiguration whitelistingSetting = cr.getConfiguration(CType.WHITELIST); + SecurityDynamicConfiguration allowlistingSetting = cr.getConfiguration(CType.ALLOWLIST); if (log.isDebugEnabled()) { String logmsg = "current config (because of " @@ -235,77 +233,48 @@ public void onChange(Map> typeToConfig) { final InternalUsersModel ium; final ConfigModel cm; final NodesDnModel nm = new NodesDnModelImpl(nodesDn); - final WhitelistingSettings whitelist = (WhitelistingSettings) cr.getConfiguration(CType.WHITELIST).getCEntry("config"); - final AllowlistingSettings allowlist = (AllowlistingSettings) cr.getConfiguration(CType.ALLOWLIST).getCEntry("config"); - final AuditConfig audit = (AuditConfig) cr.getConfiguration(CType.AUDIT).getCEntry("config"); + final WhitelistingSettings whitelist = cr.getConfiguration(CType.WHITELIST).getCEntry("config"); + final AllowlistingSettings allowlist = cr.getConfiguration(CType.ALLOWLIST).getCEntry("config"); + final AuditConfig audit = cr.getConfiguration(CType.AUDIT).getCEntry("config"); - if (config.getImplementingClass() == ConfigV7.class) { - // statics - - if (roles.containsAny(staticRoles)) { - throw new StaticResourceException("Cannot override static roles"); - } - if (!roles.add(staticRoles) && !staticRoles.getCEntries().isEmpty()) { - throw new StaticResourceException("Unable to load static roles"); - } - - log.debug("Static roles loaded ({})", staticRoles.getCEntries().size()); + if (roles.containsAny(staticRoles)) { + throw new StaticResourceException("Cannot override static roles"); + } + if (!roles.add(staticRoles) && !staticRoles.getCEntries().isEmpty()) { + throw new StaticResourceException("Unable to load static roles"); + } - if (actionGroups.containsAny(staticActionGroups)) { - throw new StaticResourceException("Cannot override static action groups"); - } - if (!actionGroups.add(staticActionGroups) && !staticActionGroups.getCEntries().isEmpty()) { - throw new StaticResourceException("Unable to load static action groups"); - } + log.debug("Static roles loaded ({})", staticRoles.getCEntries().size()); - log.debug("Static action groups loaded ({})", staticActionGroups.getCEntries().size()); + if (actionGroups.containsAny(staticActionGroups)) { + throw new StaticResourceException("Cannot override static action groups"); + } + if (!actionGroups.add(staticActionGroups) && !staticActionGroups.getCEntries().isEmpty()) { + throw new StaticResourceException("Unable to load static action groups"); + } - if (tenants.containsAny(staticTenants)) { - throw new StaticResourceException("Cannot override static tenants"); - } - if (!tenants.add(staticTenants) && !staticTenants.getCEntries().isEmpty()) { - throw new StaticResourceException("Unable to load static tenants"); - } + log.debug("Static action groups loaded ({})", staticActionGroups.getCEntries().size()); - log.debug("Static tenants loaded ({})", staticTenants.getCEntries().size()); - - log.debug( - "Static configuration loaded (total roles: {}/total action groups: {}/total tenants: {})", - roles.getCEntries().size(), - actionGroups.getCEntries().size(), - tenants.getCEntries().size() - ); - - // rebuild v7 Models - dcm = new DynamicConfigModelV7(getConfigV7(config), opensearchSettings, configPath, iab, this.cih); - ium = new InternalUsersModelV7( - (SecurityDynamicConfiguration) internalusers, - (SecurityDynamicConfiguration) roles, - (SecurityDynamicConfiguration) rolesmapping - ); - cm = new ConfigModelV7( - (SecurityDynamicConfiguration) roles, - (SecurityDynamicConfiguration) rolesmapping, - (SecurityDynamicConfiguration) actionGroups, - (SecurityDynamicConfiguration) tenants, - dcm, - opensearchSettings - ); + if (tenants.containsAny(staticTenants)) { + throw new StaticResourceException("Cannot override static tenants"); + } + if (!tenants.add(staticTenants) && !staticTenants.getCEntries().isEmpty()) { + throw new StaticResourceException("Unable to load static tenants"); + } - } else { + log.debug("Static tenants loaded ({})", staticTenants.getCEntries().size()); - // rebuild v6 Models - dcm = new DynamicConfigModelV6(getConfigV6(config), opensearchSettings, configPath, iab); - ium = new InternalUsersModelV6((SecurityDynamicConfiguration) internalusers); - cm = new ConfigModelV6( - (SecurityDynamicConfiguration) roles, - (SecurityDynamicConfiguration) actionGroups, - (SecurityDynamicConfiguration) rolesmapping, - dcm, - opensearchSettings - ); + log.debug( + "Static configuration loaded (total roles: {}/total action groups: {}/total tenants: {})", + roles.getCEntries().size(), + actionGroups.getCEntries().size(), + tenants.getCEntries().size() + ); - } + // rebuild v7 Models + dcm = new DynamicConfigModelV7(getConfigV7(config), opensearchSettings, configPath, iab, this.cih); + ium = new InternalUsersModelV7(internalusers, roles, rolesmapping); + cm = new ConfigModelV7(roles, rolesmapping, actionGroups, tenants, dcm, opensearchSettings); // notify subscribers eventBus.post(cm); @@ -460,12 +429,9 @@ private static class NodesDnModelImpl extends NodesDnModel { SecurityDynamicConfiguration configuration; - @SuppressWarnings("unchecked") - public NodesDnModelImpl(SecurityDynamicConfiguration configuration) { + public NodesDnModelImpl(SecurityDynamicConfiguration configuration) { super(); - this.configuration = null == configuration.getCType() - ? SecurityDynamicConfiguration.empty() - : (SecurityDynamicConfiguration) configuration; + this.configuration = null == configuration.getCType() ? SecurityDynamicConfiguration.empty(CType.NODESDN) : configuration; } @Override diff --git a/src/main/java/org/opensearch/security/securityconf/Migration.java b/src/main/java/org/opensearch/security/securityconf/Migration.java index b5e3ef973d..57df2c2ac1 100644 --- a/src/main/java/org/opensearch/security/securityconf/Migration.java +++ b/src/main/java/org/opensearch/security/securityconf/Migration.java @@ -27,13 +27,18 @@ package org.opensearch.security.securityconf; +import java.io.File; +import java.io.IOException; import java.util.HashSet; import java.util.List; import java.util.Map.Entry; import java.util.Set; +import com.fasterxml.jackson.databind.JavaType; + import org.opensearch.common.collect.Tuple; import org.opensearch.core.common.Strings; +import org.opensearch.security.DefaultObjectMapper; import org.opensearch.security.auditlog.config.AuditConfig; import org.opensearch.security.securityconf.impl.AllowlistingSettings; import org.opensearch.security.securityconf.impl.CType; @@ -60,20 +65,11 @@ public static Tuple, SecurityDynamicConfigu SecurityDynamicConfiguration rms6 ) throws MigrationException { - final SecurityDynamicConfiguration r7 = SecurityDynamicConfiguration.empty(); - r7.setCType(r6cs.getCType()); + final SecurityDynamicConfiguration r7 = SecurityDynamicConfiguration.empty(CType.ROLES); r7.set_meta(new Meta()); r7.get_meta().setConfig_version(2); r7.get_meta().setType("roles"); - final SecurityDynamicConfiguration t7 = SecurityDynamicConfiguration.empty(); - t7.setCType(CType.TENANTS); - t7.set_meta(new Meta()); - t7.get_meta().setConfig_version(2); - t7.get_meta().setType("tenants"); - - Set dedupTenants = new HashSet<>(); - for (final Entry r6e : r6cs.getCEntries().entrySet()) { final String roleName = r6e.getKey(); final RoleV6 r6 = r6e.getValue(); @@ -86,10 +82,6 @@ public static Tuple, SecurityDynamicConfigu } r7.putCEntry(roleName, new RoleV7(r6)); - - for (Entry tenant : r6.getTenants().entrySet()) { - dedupTenants.add(tenant.getKey()); - } } if (rms6 != null) { @@ -107,20 +99,40 @@ public static Tuple, SecurityDynamicConfigu } } + return new Tuple, SecurityDynamicConfiguration>(r7, createTenants(r6cs)); + } + + /** + * This creates a tenant configuration from the tenant names in a `SecurityDynamicConfiguration` configuration. + * The v6 configuration did not yet have an explicit file to specify all tenants. Thus, these need to be collected + * this way. + */ + public static SecurityDynamicConfiguration createTenants(SecurityDynamicConfiguration r6cs) { + Set dedupTenants = new HashSet<>(); + + for (final Entry entry : r6cs.getCEntries().entrySet()) { + for (Entry tenant : entry.getValue().getTenants().entrySet()) { + dedupTenants.add(tenant.getKey()); + } + } + + SecurityDynamicConfiguration tenantConfig = SecurityDynamicConfiguration.empty(CType.TENANTS); + tenantConfig.set_meta(new Meta()); + tenantConfig.get_meta().setConfig_version(2); + tenantConfig.get_meta().setType("tenants"); + for (String tenantName : dedupTenants) { TenantV7 entry = new TenantV7(); entry.setDescription("Migrated from v6"); - t7.putCEntry(tenantName, entry); + tenantConfig.putCEntry(tenantName, entry); } - return new Tuple, SecurityDynamicConfiguration>(r7, t7); - + return tenantConfig; } public static SecurityDynamicConfiguration migrateConfig(SecurityDynamicConfiguration r6cs) throws MigrationException { - final SecurityDynamicConfiguration c7 = SecurityDynamicConfiguration.empty(); - c7.setCType(r6cs.getCType()); + final SecurityDynamicConfiguration c7 = SecurityDynamicConfiguration.empty(CType.CONFIG); c7.set_meta(new Meta()); c7.get_meta().setConfig_version(2); c7.get_meta().setType("config"); @@ -142,8 +154,7 @@ public static SecurityDynamicConfiguration migrateConfig(SecurityDynam } public static SecurityDynamicConfiguration migrateNodesDn(SecurityDynamicConfiguration nodesDn) { - final SecurityDynamicConfiguration migrated = SecurityDynamicConfiguration.empty(); - migrated.setCType(nodesDn.getCType()); + final SecurityDynamicConfiguration migrated = SecurityDynamicConfiguration.empty(CType.NODESDN); migrated.set_meta(new Meta()); migrated.get_meta().setConfig_version(2); migrated.get_meta().setType("nodesdn"); @@ -157,8 +168,7 @@ public static SecurityDynamicConfiguration migrateNodesDn(SecurityDynam public static SecurityDynamicConfiguration migrateWhitelistingSetting( SecurityDynamicConfiguration whitelistingSetting ) { - final SecurityDynamicConfiguration migrated = SecurityDynamicConfiguration.empty(); - migrated.setCType(whitelistingSetting.getCType()); + final SecurityDynamicConfiguration migrated = SecurityDynamicConfiguration.empty(CType.WHITELIST); migrated.set_meta(new Meta()); migrated.get_meta().setConfig_version(2); migrated.get_meta().setType("whitelist"); @@ -172,8 +182,7 @@ public static SecurityDynamicConfiguration migrateWhitelis public static SecurityDynamicConfiguration migrateAllowlistingSetting( SecurityDynamicConfiguration allowlistingSetting ) { - final SecurityDynamicConfiguration migrated = SecurityDynamicConfiguration.empty(); - migrated.setCType(allowlistingSetting.getCType()); + final SecurityDynamicConfiguration migrated = SecurityDynamicConfiguration.empty(CType.ALLOWLIST); migrated.set_meta(new Meta()); migrated.get_meta().setConfig_version(2); migrated.get_meta().setType("whitelist"); @@ -186,8 +195,7 @@ public static SecurityDynamicConfiguration migrateAllowlis public static SecurityDynamicConfiguration migrateInternalUsers(SecurityDynamicConfiguration r6is) throws MigrationException { - final SecurityDynamicConfiguration i7 = SecurityDynamicConfiguration.empty(); - i7.setCType(r6is.getCType()); + final SecurityDynamicConfiguration i7 = SecurityDynamicConfiguration.empty(CType.INTERNALUSERS); i7.set_meta(new Meta()); i7.get_meta().setConfig_version(2); i7.get_meta().setType("internalusers"); @@ -204,18 +212,15 @@ public static SecurityDynamicConfiguration migrateInternalUsers( public static SecurityDynamicConfiguration migrateActionGroups(SecurityDynamicConfiguration r6as) throws MigrationException { - final SecurityDynamicConfiguration a7 = SecurityDynamicConfiguration.empty(); - a7.setCType(r6as.getCType()); + final SecurityDynamicConfiguration a7 = SecurityDynamicConfiguration.empty(CType.ACTIONGROUPS); a7.set_meta(new Meta()); a7.get_meta().setConfig_version(2); a7.get_meta().setType("actiongroups"); - if (r6as.getImplementingClass().isAssignableFrom(List.class)) { - for (final Entry r6a : r6as.getCEntries().entrySet()) { + for (final Entry r6a : r6as.getCEntries().entrySet()) { + if (r6a.getValue() instanceof List) { a7.putCEntry(r6a.getKey(), new ActionGroupsV7(r6a.getKey(), (List) r6a.getValue())); - } - } else { - for (final Entry r6a : r6as.getCEntries().entrySet()) { + } else { a7.putCEntry(r6a.getKey(), new ActionGroupsV7(r6a.getKey(), (ActionGroupsV6) r6a.getValue())); } } @@ -225,8 +230,7 @@ public static SecurityDynamicConfiguration migrateActionGroups(S public static SecurityDynamicConfiguration migrateRoleMappings(SecurityDynamicConfiguration r6rms) throws MigrationException { - final SecurityDynamicConfiguration rms7 = SecurityDynamicConfiguration.empty(); - rms7.setCType(r6rms.getCType()); + final SecurityDynamicConfiguration rms7 = SecurityDynamicConfiguration.empty(CType.ROLESMAPPING); rms7.set_meta(new Meta()); rms7.get_meta().setConfig_version(2); rms7.get_meta().setType("rolesmapping"); @@ -239,8 +243,7 @@ public static SecurityDynamicConfiguration migrateRoleMappings(S } public static SecurityDynamicConfiguration migrateAudit(SecurityDynamicConfiguration audit) { - final SecurityDynamicConfiguration migrated = SecurityDynamicConfiguration.empty(); - migrated.setCType(audit.getCType()); + final SecurityDynamicConfiguration migrated = SecurityDynamicConfiguration.empty(CType.AUDIT); migrated.set_meta(new Meta()); migrated.get_meta().setConfig_version(2); migrated.get_meta().setType("audit"); @@ -251,4 +254,13 @@ public static SecurityDynamicConfiguration migrateAudit(SecurityDyn return migrated; } + public static SecurityDynamicConfiguration readYaml(File file, Class type) throws IOException { + if (!file.exists()) { + return SecurityDynamicConfiguration.empty(null); + } + + JavaType javaType = DefaultObjectMapper.getTypeFactory().constructParametricType(SecurityDynamicConfiguration.class, type); + return DefaultObjectMapper.YAML_MAPPER.readValue(file, javaType); + } + } diff --git a/src/main/java/org/opensearch/security/securityconf/impl/CType.java b/src/main/java/org/opensearch/security/securityconf/impl/CType.java index 8a51686225..292104e6be 100644 --- a/src/main/java/org/opensearch/security/securityconf/impl/CType.java +++ b/src/main/java/org/opensearch/security/securityconf/impl/CType.java @@ -27,17 +27,22 @@ package org.opensearch.security.securityconf.impl; +import java.io.IOException; import java.nio.file.Path; import java.util.Arrays; import java.util.Collections; -import java.util.List; +import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; -import com.google.common.collect.ImmutableMap; +import com.fasterxml.jackson.databind.JavaType; +import org.opensearch.security.DefaultObjectMapper; +import org.opensearch.security.NonValidatingObjectMapper; import org.opensearch.security.auditlog.config.AuditConfig; import org.opensearch.security.securityconf.impl.v6.ActionGroupsV6; import org.opensearch.security.securityconf.impl.v6.ConfigV6; @@ -51,63 +56,154 @@ import org.opensearch.security.securityconf.impl.v7.RoleV7; import org.opensearch.security.securityconf.impl.v7.TenantV7; -public enum CType { - - ACTIONGROUPS(toMap(0, List.class, 1, ActionGroupsV6.class, 2, ActionGroupsV7.class), "action_groups.yml", false), - ALLOWLIST(toMap(1, AllowlistingSettings.class, 2, AllowlistingSettings.class), "allowlist.yml", true), - AUDIT(toMap(1, AuditConfig.class, 2, AuditConfig.class), "audit.yml", true), - CONFIG(toMap(1, ConfigV6.class, 2, ConfigV7.class), "config.yml", false), - INTERNALUSERS(toMap(1, InternalUserV6.class, 2, InternalUserV7.class), "internal_users.yml", false), - NODESDN(toMap(1, NodesDn.class, 2, NodesDn.class), "nodes_dn.yml", true), - ROLES(toMap(1, RoleV6.class, 2, RoleV7.class), "roles.yml", false), - ROLESMAPPING(toMap(1, RoleMappingsV6.class, 2, RoleMappingsV7.class), "roles_mapping.yml", false), - TENANTS(toMap(2, TenantV7.class), "tenants.yml", false), - WHITELIST(toMap(1, WhitelistingSettings.class, 2, WhitelistingSettings.class), "whitelist.yml", true); - - public static final List REQUIRED_CONFIG_FILES = Arrays.stream(CType.values()) - .filter(Predicate.not(CType::emptyIfMissing)) - .collect(Collectors.toList()); +/** + * Identifies configuration types. A `CType` instance has a 1-to-1 relationship with a configuration + * business object class, which is referenced in its generic parameter. + *

+ * Additionally, a `CType` can reference older versions of these business objects via the `oldConfigVersions` + * property. + *

+ * In earlier versions, `CType` was an `enum` which leads to a few peculiarities: + *

    + *
  • Each instance needs to have a unique `id` which corresponds to the `ord` property + * from Java enums. OpenSearch uses these ids for its own transport message serialization. + * These ids must not be changed, otherwise backward compatibility will be broken. + *
  • Each instance has several names. One, that is used for identifying the document + * in document ids. Another one that is used for identifying the config type by yaml file names. There + * are a few inconsistencies - for the future, it is recommendable to use identical names. + *
+ */ +public class CType implements Comparable> { - public static final List NOT_REQUIRED_CONFIG_FILES = Arrays.stream(CType.values()) - .filter(CType::emptyIfMissing) - .collect(Collectors.toList()); + private final static Set> allSet = new HashSet<>(); + private static Map> nameToInstanceMap = new HashMap<>(); + private static Map> ordToInstanceMap = new HashMap<>(); - private final Map> implementations; + public static final CType ACTIONGROUPS = new CType<>( + "actiongroups", + "action_groups", + ActionGroupsV7.class, + 0, + false, + new OldConfigVersion<>(1, ActionGroupsV6.class, ActionGroupsV7::new) + ); + public static final CType ALLOWLIST = new CType<>("allowlist", "allowlist", AllowlistingSettings.class, 1, true); + public static final CType AUDIT = new CType<>("audit", "audit", AuditConfig.class, 2, true); + public static final CType CONFIG = new CType<>( + "config", + "config", + ConfigV7.class, + 3, + false, + new OldConfigVersion<>(1, ConfigV6.class, ConfigV7::new, ConfigV6::convertMapKeyToV7) + ); + public static final CType INTERNALUSERS = new CType<>( + "internalusers", + "internal_users", + InternalUserV7.class, + 4, + false, + new OldConfigVersion<>(1, InternalUserV6.class, InternalUserV7::new) + ); + public static final CType NODESDN = new CType<>("nodesdn", "nodes_dn", NodesDn.class, 5, true); + public static final CType ROLES = new CType<>( + "roles", + "roles", + RoleV7.class, + 6, + false, + new OldConfigVersion<>(1, RoleV6.class, RoleV7::new) + ); + public static final CType ROLESMAPPING = new CType<>( + "rolesmapping", + "roles_mapping", + RoleMappingsV7.class, + 7, + false, + new OldConfigVersion<>(1, RoleMappingsV6.class, RoleMappingsV7::new) + ); + public static final CType TENANTS = new CType<>("tenants", "tenants", TenantV7.class, 8, false); + public static final CType WHITELIST = new CType<>("whitelist", "whitelist", WhitelistingSettings.class, 9, true); + private final String name; + private final String nameUpperCase; + private final Class configClass; private final String configFileName; - private final boolean emptyIfMissing; + private final OldConfigVersion[] oldConfigVersions; + private final int id; - private CType(Map> implementations, final String configFileName, final boolean emptyIfMissing) { - this.implementations = implementations; - this.configFileName = configFileName; + @SafeVarargs + @SuppressWarnings("varargs") + private CType( + String name, + String configFileName, + Class configClass, + int id, + boolean emptyIfMissing, + OldConfigVersion... oldConfigVersions + ) { + this.name = name; + this.nameUpperCase = name.toUpperCase(); + this.configClass = configClass; + this.id = id; + this.configFileName = configFileName + ".yml"; this.emptyIfMissing = emptyIfMissing; + this.oldConfigVersions = oldConfigVersions; + + allSet.add(this); + nameToInstanceMap.put(name, this); + ordToInstanceMap.put(id, this); + } + + public Class getConfigClass() { + return this.configClass; } public boolean emptyIfMissing() { return emptyIfMissing; } - public Map> getImplementationClass() { - return Collections.unmodifiableMap(implementations); + public String toLCString() { + return this.name; } - public static CType fromString(String value) { - return CType.valueOf(value.toUpperCase()); + public String name() { + return this.name; } - public String toLCString() { - return this.toString().toLowerCase(); + public int getOrd() { + return id; + } + + public static CType fromString(String value) { + return nameToInstanceMap.get(value.toLowerCase()); + } + + public static Set> values() { + return Collections.unmodifiableSet(allSet); } public static Set lcStringValues() { - return Arrays.stream(CType.values()).map(CType::toLCString).collect(Collectors.toSet()); + return CType.values().stream().map(CType::toLCString).collect(Collectors.toSet()); } - public static Set fromStringValues(String[] strings) { + public static Set> fromStringValues(String[] strings) { return Arrays.stream(strings).map(CType::fromString).collect(Collectors.toSet()); } + public static CType fromOrd(int ord) { + return ordToInstanceMap.get(ord); + } + + public static Set> requiredConfigTypes() { + return values().stream().filter(Predicate.not(CType::emptyIfMissing)).collect(Collectors.toUnmodifiableSet()); + } + + public static Set> notRequiredConfigTypes() { + return values().stream().filter(CType::emptyIfMissing).collect(Collectors.toUnmodifiableSet()); + } + public Path configFile(final Path configDir) { return configDir.resolve(this.configFileName); } @@ -116,11 +212,98 @@ public String configFileName() { return configFileName; } - private static Map> toMap(Object... objects) { - final ImmutableMap.Builder> map = ImmutableMap.builder(); - for (int i = 0; i < objects.length; i = i + 2) { - map.put((Integer) objects[i], (Class) objects[i + 1]); + public OldConfigVersion findOldConfigVersion(int versionNumber) { + for (OldConfigVersion oldConfigVersion : this.oldConfigVersions) { + if (versionNumber == oldConfigVersion.versionNumber) { + return oldConfigVersion; + } } - return map.build(); + + return null; + } + + @Override + public int compareTo(CType cType) { + return this.id - cType.id; + } + + @Override + public String toString() { + return this.nameUpperCase; + } + + @Override + public boolean equals(Object other) { + if (other instanceof CType) { + return ((CType) other).id == this.id; + } else { + return false; + } + } + + @Override + public int hashCode() { + return id; + } + + public static class OldConfigVersion { + private final int versionNumber; + private final Class oldType; + private final Function entryConversionFunction; + private final Function mapKeyConversionFunction; + + public OldConfigVersion(int versionNumber, Class oldType, Function entryConversionFunction) { + this.versionNumber = versionNumber; + this.oldType = oldType; + this.entryConversionFunction = entryConversionFunction; + this.mapKeyConversionFunction = Function.identity(); + } + + public OldConfigVersion( + int versionNumber, + Class oldType, + Function entryConversionFunction, + Function mapKeyConversionFunction + ) { + this.versionNumber = versionNumber; + this.oldType = oldType; + this.entryConversionFunction = entryConversionFunction; + this.mapKeyConversionFunction = mapKeyConversionFunction; + } + + public int getVersionNumber() { + return versionNumber; + } + + public Class getOldType() { + return oldType; + } + + public Function getEntryConversionFunction() { + return entryConversionFunction; + } + + public SecurityDynamicConfiguration parseJson(CType ctype, String json, boolean acceptInvalid) + throws IOException { + JavaType javaType = DefaultObjectMapper.getTypeFactory().constructParametricType(SecurityDynamicConfiguration.class, oldType); + + if (acceptInvalid) { + return convert(NonValidatingObjectMapper.readValue(json, javaType), ctype); + } else { + return convert(DefaultObjectMapper.readValue(json, javaType), ctype); + } + } + + public SecurityDynamicConfiguration convert(SecurityDynamicConfiguration oldConfig, CType ctype) { + SecurityDynamicConfiguration newConfig = SecurityDynamicConfiguration.empty(ctype); + newConfig.setAutoConvertedFrom(oldConfig); + + for (Map.Entry oldEntry : oldConfig.getCEntries().entrySet()) { + newConfig.putCEntry(mapKeyConversionFunction.apply(oldEntry.getKey()), entryConversionFunction.apply(oldEntry.getValue())); + } + + return newConfig; + } + } } diff --git a/src/main/java/org/opensearch/security/securityconf/impl/Meta.java b/src/main/java/org/opensearch/security/securityconf/impl/Meta.java index 2fa4ced06a..c97f72e6b8 100644 --- a/src/main/java/org/opensearch/security/securityconf/impl/Meta.java +++ b/src/main/java/org/opensearch/security/securityconf/impl/Meta.java @@ -34,7 +34,7 @@ public class Meta { private String type; private int config_version; - private CType cType; + private CType cType; public String getType() { return type; @@ -54,7 +54,7 @@ public void setConfig_version(int config_version) { } @JsonIgnore - public CType getCType() { + public CType getCType() { return cType; } diff --git a/src/main/java/org/opensearch/security/securityconf/impl/SecurityDynamicConfiguration.java b/src/main/java/org/opensearch/security/securityconf/impl/SecurityDynamicConfiguration.java index e1dd1602ac..bc03d59500 100644 --- a/src/main/java/org/opensearch/security/securityconf/impl/SecurityDynamicConfiguration.java +++ b/src/main/java/org/opensearch/security/securityconf/impl/SecurityDynamicConfiguration.java @@ -46,13 +46,14 @@ import org.opensearch.core.xcontent.ToXContent; import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.security.DefaultObjectMapper; -import org.opensearch.security.NonValidatingObjectMapper; import org.opensearch.security.securityconf.Hashed; import org.opensearch.security.securityconf.Hideable; import org.opensearch.security.securityconf.StaticDefinable; public class SecurityDynamicConfiguration implements ToXContent { + public static final int CURRENT_VERSION = 2; + private static final TypeReference> typeRefMSO = new TypeReference>() { }; @@ -62,11 +63,13 @@ public class SecurityDynamicConfiguration implements ToXContent { private final Object modificationLock = new Object(); private long seqNo = -1; private long primaryTerm = -1; - private CType ctype; + private CType ctype; private int version = -1; - public static SecurityDynamicConfiguration empty() { - return new SecurityDynamicConfiguration(); + private SecurityDynamicConfiguration autoConvertedFrom; + + public static SecurityDynamicConfiguration empty(CType ctype) { + return new SecurityDynamicConfiguration(ctype); } @JsonIgnore @@ -74,14 +77,18 @@ public boolean notEmpty() { return !centries.isEmpty(); } - public static SecurityDynamicConfiguration fromJson(String json, CType ctype, int version, long seqNo, long primaryTerm) + public static SecurityDynamicConfiguration fromJson(String json, CType ctype, int version, long seqNo, long primaryTerm) throws IOException { return fromJson(json, ctype, version, seqNo, primaryTerm, false); } + /** + * Creates the SecurityDynamicConfiguration instance from the given JSON. If a config version is found, which + * is not the current one, it will be automatically converted into the current configuration version. + */ public static SecurityDynamicConfiguration fromJson( String json, - CType ctype, + CType ctype, int version, long seqNo, long primaryTerm, @@ -89,22 +96,23 @@ public static SecurityDynamicConfiguration fromJson( ) throws IOException { SecurityDynamicConfiguration sdc = null; if (ctype != null) { - final Class implementationClass = ctype.getImplementationClass().get(version); - if (implementationClass == null) { - throw new IllegalArgumentException("No implementation class found for " + ctype + " and config version " + version); - } - if (acceptInvalid && version < 2) { - sdc = NonValidatingObjectMapper.readValue( - json, - NonValidatingObjectMapper.getTypeFactory() - .constructParametricType(SecurityDynamicConfiguration.class, implementationClass) - ); + CType.OldConfigVersion oldConfigVersion = ctype.findOldConfigVersion(version); + + if (oldConfigVersion != null) { + sdc = oldConfigVersion.parseJson(ctype, json, acceptInvalid); + if (sdc._meta == null) { + sdc._meta = new Meta(); + sdc._meta.setConfig_version(CURRENT_VERSION); + sdc._meta.setType(ctype.toLCString()); + } + version = CURRENT_VERSION; } else { sdc = DefaultObjectMapper.readValue( json, - DefaultObjectMapper.getTypeFactory().constructParametricType(SecurityDynamicConfiguration.class, implementationClass) + DefaultObjectMapper.getTypeFactory().constructParametricType(SecurityDynamicConfiguration.class, ctype.getConfigClass()) ); } + validate(sdc, version, ctype); } else { @@ -119,21 +127,53 @@ public static SecurityDynamicConfiguration fromJson( return sdc; } + /** + * Creates the SecurityDynamicConfiguration instance from the given JsonNode. If a config version is found, which + * is not the current one, no conversion will be performed. The configuration will be returned as it was found. + */ + public static SecurityDynamicConfiguration fromNodeWithoutAutoConversion( + JsonNode jsonNode, + CType ctype, + int version, + long seqNo, + long primaryTerm + ) throws IOException { + Class configClass; + CType.OldConfigVersion oldConfigVersion = ctype.findOldConfigVersion(version); + + if (oldConfigVersion != null) { + configClass = oldConfigVersion.getOldType(); + } else { + configClass = ctype.getConfigClass(); + } + + SecurityDynamicConfiguration sdc = DefaultObjectMapper.convertValue( + jsonNode, + DefaultObjectMapper.getTypeFactory().constructParametricType(SecurityDynamicConfiguration.class, configClass) + ); + + validate(sdc, version, ctype); + + sdc.seqNo = seqNo; + sdc.primaryTerm = primaryTerm; + sdc.version = version; + + return sdc; + } + /** * For testing only */ - public static SecurityDynamicConfiguration fromMap(Map map, CType ctype, int version) - throws JsonProcessingException { - Class implementationClass = ctype.getImplementationClass().get(version); + public static SecurityDynamicConfiguration fromMap(Map map, CType ctype) throws JsonProcessingException { SecurityDynamicConfiguration result = DefaultObjectMapper.objectMapper.convertValue( map, - DefaultObjectMapper.getTypeFactory().constructParametricType(SecurityDynamicConfiguration.class, implementationClass) + DefaultObjectMapper.getTypeFactory().constructParametricType(SecurityDynamicConfiguration.class, ctype.getConfigClass()) ); result.ctype = ctype; return result; } - public static void validate(SecurityDynamicConfiguration sdc, int version, CType ctype) throws IOException { + public static void validate(SecurityDynamicConfiguration sdc, int version, CType ctype) throws IOException { if (version < 2 && sdc.get_meta() != null) { throw new IOException("A version of " + version + " can not have a _meta key for " + ctype); } @@ -154,9 +194,15 @@ public static void validate(SecurityDynamicConfiguration sdc, int version, CT } - public static SecurityDynamicConfiguration fromNode(JsonNode json, CType ctype, int version, long seqNo, long primaryTerm) + public static SecurityDynamicConfiguration fromNode(JsonNode json, CType ctype, int version, long seqNo, long primaryTerm) throws IOException { - return fromJson(DefaultObjectMapper.writeValueAsString(json, false), ctype, version, seqNo, primaryTerm); + return SecurityDynamicConfiguration.fromJson( + DefaultObjectMapper.writeValueAsString(json, false), + ctype, + version, + seqNo, + primaryTerm + ); } // for Jackson @@ -164,6 +210,11 @@ private SecurityDynamicConfiguration() { super(); } + private SecurityDynamicConfiguration(CType ctype) { + super(); + this.ctype = ctype; + } + private Meta _meta; public Meta get_meta() { @@ -295,25 +346,31 @@ public long getPrimaryTerm() { } @JsonIgnore - public CType getCType() { + public CType getCType() { return ctype; } @JsonIgnore - public void setCType(CType ctype) { - this.ctype = ctype; + public int getVersion() { + return version; } @JsonIgnore - public int getVersion() { - return version; + public SecurityDynamicConfiguration getAutoConvertedFrom() { + return autoConvertedFrom; + } + + @JsonIgnore + public void setAutoConvertedFrom(SecurityDynamicConfiguration autoConvertedFrom) { + this.autoConvertedFrom = autoConvertedFrom; } @JsonIgnore public Class getImplementingClass() { - return getCType() == null ? null : getCType().getImplementationClass().get(getVersion()); + return getCType() == null ? null : getCType().getConfigClass(); } + @SuppressWarnings("unchecked") @JsonIgnore public SecurityDynamicConfiguration deepClone() { try { @@ -323,6 +380,7 @@ public SecurityDynamicConfiguration deepClone() { } } + @SuppressWarnings("unchecked") @JsonIgnore public SecurityDynamicConfiguration deepCloneWithRedaction() { try { 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 78758e0603..59b759970e 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 @@ -45,11 +45,20 @@ import org.opensearch.security.DefaultObjectMapper; import org.opensearch.security.auth.internal.InternalAuthenticationBackend; +import org.opensearch.security.securityconf.impl.CType; import org.opensearch.security.securityconf.impl.DashboardSignInOption; import org.opensearch.security.setting.DeprecatedSettings; public class ConfigV6 { + public static String convertMapKeyToV7(String mapKey) { + if (mapKey.equals("opendistro_security")) { + return CType.CONFIG.toLCString(); + } else { + return mapKey; + } + } + public Dynamic dynamic; @Override diff --git a/src/main/java/org/opensearch/security/securityconf/impl/v7/ActionGroupsV7.java b/src/main/java/org/opensearch/security/securityconf/impl/v7/ActionGroupsV7.java index 9ec9c25e5d..44382cb07e 100644 --- a/src/main/java/org/opensearch/security/securityconf/impl/v7/ActionGroupsV7.java +++ b/src/main/java/org/opensearch/security/securityconf/impl/v7/ActionGroupsV7.java @@ -58,6 +58,14 @@ public ActionGroupsV7(String agName, ActionGroupsV6 ag6) { description = "Migrated from v6"; } + public ActionGroupsV7(ActionGroupsV6 ag6) { + reserved = ag6.isReserved(); + hidden = ag6.isHidden(); + allowed_actions = ag6.getPermissions(); + type = "unknown"; + description = "Migrated from v6"; + } + public ActionGroupsV7(String key, List allowed_actions) { this.allowed_actions = allowed_actions; type = "unknown"; diff --git a/src/main/java/org/opensearch/security/state/SecurityConfig.java b/src/main/java/org/opensearch/security/state/SecurityConfig.java index f8de098365..79f9461875 100644 --- a/src/main/java/org/opensearch/security/state/SecurityConfig.java +++ b/src/main/java/org/opensearch/security/state/SecurityConfig.java @@ -26,20 +26,20 @@ public class SecurityConfig implements Writeable, ToXContent { - private final CType type; + private final CType type; private final Instant lastModified; private final String hash; - public SecurityConfig(final CType type, final String hash, final Instant lastModified) { + public SecurityConfig(final CType type, final String hash, final Instant lastModified) { this.type = type; this.hash = hash; this.lastModified = lastModified; } public SecurityConfig(final StreamInput in) throws IOException { - this.type = in.readEnum(CType.class); + this.type = CType.fromOrd(in.readVInt()); this.hash = in.readString(); this.lastModified = in.readOptionalInstant(); } @@ -48,7 +48,7 @@ public Optional lastModified() { return Optional.ofNullable(lastModified); } - public CType type() { + public CType type() { return type; } @@ -58,7 +58,7 @@ public String hash() { @Override public void writeTo(final StreamOutput out) throws IOException { - out.writeEnum(type); + out.writeVInt(type.getOrd()); out.writeString(hash); out.writeOptionalInstant(lastModified); } @@ -89,7 +89,7 @@ public int hashCode() { public final static class Builder { - private final CType type; + private final CType type; private Instant lastModified; diff --git a/src/main/java/org/opensearch/security/support/ConfigHelper.java b/src/main/java/org/opensearch/security/support/ConfigHelper.java index d9e2921da6..48edb6850c 100644 --- a/src/main/java/org/opensearch/security/support/ConfigHelper.java +++ b/src/main/java/org/opensearch/security/support/ConfigHelper.java @@ -62,7 +62,7 @@ public class ConfigHelper { private static final Logger LOGGER = LogManager.getLogger(ConfigHelper.class); - public static void uploadFile(Client tc, String filepath, String index, CType cType, int configVersion) throws Exception { + public static void uploadFile(Client tc, String filepath, String index, CType cType, int configVersion) throws Exception { uploadFile(tc, filepath, index, cType, configVersion, false); } @@ -71,7 +71,7 @@ public static void uploadFile( Client tc, String filepath, String index, - CType cType, + CType cType, int configVersion, boolean populateEmptyIfFileMissing ) throws Exception { @@ -111,7 +111,7 @@ public static void uploadFile( }); } - public static Reader createFileOrStringReader(CType cType, int configVersion, String filepath, boolean populateEmptyIfFileMissing) + public static Reader createFileOrStringReader(CType cType, int configVersion, String filepath, boolean populateEmptyIfFileMissing) throws Exception { Reader reader; if (!populateEmptyIfFileMissing || new File(filepath).exists()) { @@ -122,10 +122,9 @@ public static Reader createFileOrStringReader(CType cType, int configVersion, St return reader; } - public static SecurityDynamicConfiguration createEmptySdc(CType cType, int configVersion) throws Exception { - SecurityDynamicConfiguration empty = SecurityDynamicConfiguration.empty(); + public static SecurityDynamicConfiguration createEmptySdc(CType cType, int configVersion) throws Exception { + SecurityDynamicConfiguration empty = SecurityDynamicConfiguration.empty(cType); if (configVersion == 2) { - empty.setCType(cType); empty.set_meta(new Meta()); empty.get_meta().setConfig_version(configVersion); empty.get_meta().setType(cType.toLCString()); @@ -135,7 +134,7 @@ public static SecurityDynamicConfiguration createEmptySdc(CType cType, int co return c; } - public static String createEmptySdcYaml(CType cType, int configVersion) throws Exception { + public static String createEmptySdcYaml(CType cType, int configVersion) throws Exception { return DefaultObjectMapper.YAML_MAPPER.writeValueAsString(createEmptySdc(cType, configVersion)); } @@ -158,7 +157,7 @@ public static BytesReference readXContent(final Reader reader, final MediaType m public static SecurityDynamicConfiguration fromYamlReader( Reader yamlReader, - CType ctype, + CType ctype, int version, long seqNo, long primaryTerm @@ -178,14 +177,19 @@ public static SecurityDynamicConfiguration fromYamlReader( } } - public static SecurityDynamicConfiguration fromYamlFile(String filepath, CType ctype, int version, long seqNo, long primaryTerm) - throws IOException { + public static SecurityDynamicConfiguration fromYamlFile( + String filepath, + CType ctype, + int version, + long seqNo, + long primaryTerm + ) throws IOException { return fromYamlReader(new FileReader(filepath, StandardCharsets.UTF_8), ctype, version, seqNo, primaryTerm); } public static SecurityDynamicConfiguration fromYamlString( String yamlString, - CType ctype, + CType ctype, int version, long seqNo, long primaryTerm diff --git a/src/main/java/org/opensearch/security/support/SecurityIndexHandler.java b/src/main/java/org/opensearch/security/support/SecurityIndexHandler.java index 73af39a348..1ff1b9222e 100644 --- a/src/main/java/org/opensearch/security/support/SecurityIndexHandler.java +++ b/src/main/java/org/opensearch/security/support/SecurityIndexHandler.java @@ -22,7 +22,6 @@ import java.util.Set; import java.util.stream.Collectors; -import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSortedSet; import com.google.common.hash.Hashing; import org.apache.logging.log4j.LogManager; @@ -43,6 +42,7 @@ import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.security.DefaultObjectMapper; +import org.opensearch.security.configuration.ConfigurationMap; import org.opensearch.security.securityconf.impl.CType; import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; import org.opensearch.security.state.SecurityConfig; @@ -128,17 +128,14 @@ public void uploadDefaultConfiguration(final Path configDir, final ActionListene } } - public void loadConfiguration( - final Set configuration, - final ActionListener>> listener - ) { + public void loadConfiguration(final Set configuration, final ActionListener listener) { try (final ThreadContext.StoredContext threadContext = client.threadPool().getThreadContext().stashContext()) { client.threadPool().getThreadContext().putHeader(ConfigConstants.OPENDISTRO_SECURITY_CONF_REQUEST_HEADER, "true"); - final var configurationTypes = configuration.stream().map(SecurityConfig::type).collect(Collectors.toUnmodifiableList()); + final List> configurationTypes = configuration.stream() + .map(SecurityConfig::type) + .collect(Collectors.toUnmodifiableList()); client.multiGet(newMultiGetRequest(configurationTypes), ActionListener.runBefore(ActionListener.wrap(r -> { - final var cTypeConfigsBuilder = ImmutableMap.>builderWithExpectedSize( - configuration.size() - ); + final var cTypeConfigsBuilder = new ConfigurationMap.Builder(); var hasFailures = false; for (final var item : r.getResponses()) { if (item.isFailed()) { @@ -162,15 +159,14 @@ public void loadConfiguration( hasFailures = true; break; } - cTypeConfigsBuilder.put(cType, config); + cTypeConfigsBuilder.with(config); } else { if (!cType.emptyIfMissing()) { listener.onFailure(new SecurityException("Missing required configuration for type: " + cType)); hasFailures = true; break; } - cTypeConfigsBuilder.put( - cType, + cTypeConfigsBuilder.with( SecurityDynamicConfiguration.fromJson( emptyJsonConfigFor(cType), cType, @@ -188,7 +184,7 @@ public void loadConfiguration( } } - private MultiGetRequest newMultiGetRequest(final List configurationTypes) { + private MultiGetRequest newMultiGetRequest(final List> configurationTypes) { final var request = new MultiGetRequest().realtime(true).refresh(true); for (final var cType : configurationTypes) { request.add(indexName, cType.toLCString()); @@ -197,7 +193,7 @@ private MultiGetRequest newMultiGetRequest(final List configurationTypes) } private SecurityDynamicConfiguration buildDynamicConfiguration( - final CType cType, + final CType cType, final BytesReference bytesRef, final long seqNo, final long primaryTerm diff --git a/src/main/java/org/opensearch/security/support/YamlConfigReader.java b/src/main/java/org/opensearch/security/support/YamlConfigReader.java index 237e5b5bfb..c8096f744c 100644 --- a/src/main/java/org/opensearch/security/support/YamlConfigReader.java +++ b/src/main/java/org/opensearch/security/support/YamlConfigReader.java @@ -40,7 +40,7 @@ public final class YamlConfigReader { private static final Logger LOGGER = LogManager.getLogger(YamlConfigReader.class); - public static BytesReference yamlContentFor(final CType cType, final Path configDir) throws IOException { + public static BytesReference yamlContentFor(final CType cType, final Path configDir) throws IOException { final var yamlXContent = XContentType.YAML.xContent(); try ( final var r = newReader(cType, configDir); @@ -56,7 +56,7 @@ public static BytesReference yamlContentFor(final CType cType, final Path config } } - public static Reader newReader(final CType cType, final Path configDir) throws IOException { + public static Reader newReader(final CType cType, final Path configDir) throws IOException { final var cTypeFile = cType.configFile(configDir); final var fileExists = Files.exists(cTypeFile); if (!fileExists && !cType.emptyIfMissing()) { @@ -71,24 +71,23 @@ public static Reader newReader(final CType cType, final Path configDir) throws I } } - private static SecurityDynamicConfiguration emptyConfigFor(final CType cType) { - final var emptyConfiguration = SecurityDynamicConfiguration.empty(); - emptyConfiguration.setCType(cType); + private static SecurityDynamicConfiguration emptyConfigFor(final CType cType) { + final var emptyConfiguration = SecurityDynamicConfiguration.empty(cType); emptyConfiguration.set_meta(new Meta()); emptyConfiguration.get_meta().setConfig_version(DEFAULT_CONFIG_VERSION); emptyConfiguration.get_meta().setType(cType.toLCString()); return emptyConfiguration; } - public static String emptyJsonConfigFor(final CType cType) throws IOException { + public static String emptyJsonConfigFor(final CType cType) throws IOException { return DefaultObjectMapper.writeValueAsString(emptyConfigFor(cType), false); } - public static String emptyYamlConfigFor(final CType cType) throws IOException { + public static String emptyYamlConfigFor(final CType cType) throws IOException { return DefaultObjectMapper.YAML_MAPPER.writeValueAsString(emptyConfigFor(cType)); } - private static void validateYamlContent(final CType cType, final InputStream in) throws IOException { + private static void validateYamlContent(final CType cType, final InputStream in) throws IOException { SecurityDynamicConfiguration.fromNode(DefaultObjectMapper.YAML_MAPPER.readTree(in), cType, DEFAULT_CONFIG_VERSION, -1, -1); } diff --git a/src/main/java/org/opensearch/security/tools/Migrater.java b/src/main/java/org/opensearch/security/tools/Migrater.java index 96b79a5993..2dbc009706 100644 --- a/src/main/java/org/opensearch/security/tools/Migrater.java +++ b/src/main/java/org/opensearch/security/tools/Migrater.java @@ -29,13 +29,17 @@ import org.opensearch.common.collect.Tuple; import org.opensearch.security.DefaultObjectMapper; +import org.opensearch.security.auditlog.config.AuditConfig; import org.opensearch.security.securityconf.Migration; import org.opensearch.security.securityconf.impl.CType; import org.opensearch.security.securityconf.impl.NodesDn; import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; +import org.opensearch.security.securityconf.impl.v6.ConfigV6; +import org.opensearch.security.securityconf.impl.v6.InternalUserV6; +import org.opensearch.security.securityconf.impl.v6.RoleMappingsV6; +import org.opensearch.security.securityconf.impl.v6.RoleV6; import org.opensearch.security.securityconf.impl.v7.RoleV7; import org.opensearch.security.securityconf.impl.v7.TenantV7; -import org.opensearch.security.support.ConfigHelper; public class Migrater { @@ -89,7 +93,7 @@ public static boolean migrateDirectory(File dir, boolean backup) { return retVal; } - public static boolean migrateFile(File file, CType cType, boolean backup) { + public static boolean migrateFile(File file, CType cType, boolean backup) { final String absolutePath = file.getAbsolutePath(); // NODESDN is newer type and supports populating empty content if file is missing if (!file.exists() && cType != CType.NODESDN) { @@ -118,15 +122,13 @@ public static boolean migrateFile(File file, CType cType, boolean backup) { } if (cType == CType.CONFIG) { - SecurityDynamicConfiguration val = Migration.migrateConfig( - SecurityDynamicConfiguration.fromNode(DefaultObjectMapper.YAML_MAPPER.readTree(file), CType.CONFIG, 1, 0, 0) - ); + SecurityDynamicConfiguration val = Migration.migrateConfig(Migration.readYaml(file, ConfigV6.class)); return backupAndWrite(file, val, backup); } if (cType == CType.ROLES) { Tuple, SecurityDynamicConfiguration> tup = Migration.migrateRoles( - SecurityDynamicConfiguration.fromNode(DefaultObjectMapper.YAML_MAPPER.readTree(file), CType.ROLES, 1, 0, 0), + Migration.readYaml(file, RoleV6.class), null ); boolean roles = backupAndWrite(file, tup.v1(), backup); @@ -134,38 +136,22 @@ public static boolean migrateFile(File file, CType cType, boolean backup) { } if (cType == CType.ROLESMAPPING) { - SecurityDynamicConfiguration val = Migration.migrateRoleMappings( - SecurityDynamicConfiguration.fromNode(DefaultObjectMapper.YAML_MAPPER.readTree(file), CType.ROLESMAPPING, 1, 0, 0) - ); + SecurityDynamicConfiguration val = Migration.migrateRoleMappings(Migration.readYaml(file, RoleMappingsV6.class)); return backupAndWrite(file, val, backup); } if (cType == CType.INTERNALUSERS) { - SecurityDynamicConfiguration val = Migration.migrateInternalUsers( - SecurityDynamicConfiguration.fromNode(DefaultObjectMapper.YAML_MAPPER.readTree(file), CType.INTERNALUSERS, 1, 0, 0) - ); + SecurityDynamicConfiguration val = Migration.migrateInternalUsers(Migration.readYaml(file, InternalUserV6.class)); return backupAndWrite(file, val, backup); } if (cType == CType.AUDIT) { - SecurityDynamicConfiguration val = Migration.migrateAudit( - SecurityDynamicConfiguration.fromNode(DefaultObjectMapper.YAML_MAPPER.readTree(file), CType.AUDIT, 1, 0, 0) - ); + SecurityDynamicConfiguration val = Migration.migrateAudit(Migration.readYaml(file, AuditConfig.class)); return backupAndWrite(file, val, backup); } if (cType == CType.NODESDN) { - SecurityDynamicConfiguration val = Migration.migrateNodesDn( - SecurityDynamicConfiguration.fromNode( - DefaultObjectMapper.YAML_MAPPER.readTree( - ConfigHelper.createFileOrStringReader(CType.NODESDN, 1, file.getAbsolutePath(), true) - ), - CType.NODESDN, - 1, - 0, - 0 - ) - ); + SecurityDynamicConfiguration val = Migration.migrateNodesDn(Migration.readYaml(file, NodesDn.class)); return backupAndWrite(file, val, backup); } } catch (Exception e) { diff --git a/src/main/java/org/opensearch/security/tools/SecurityAdmin.java b/src/main/java/org/opensearch/security/tools/SecurityAdmin.java index b862f455fe..240d7fad25 100644 --- a/src/main/java/org/opensearch/security/tools/SecurityAdmin.java +++ b/src/main/java/org/opensearch/security/tools/SecurityAdmin.java @@ -132,7 +132,10 @@ import org.opensearch.security.securityconf.impl.NodesDn; import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; import org.opensearch.security.securityconf.impl.WhitelistingSettings; +import org.opensearch.security.securityconf.impl.v6.ConfigV6; +import org.opensearch.security.securityconf.impl.v6.InternalUserV6; import org.opensearch.security.securityconf.impl.v6.RoleMappingsV6; +import org.opensearch.security.securityconf.impl.v6.RoleV6; import org.opensearch.security.securityconf.impl.v7.ActionGroupsV7; import org.opensearch.security.securityconf.impl.v7.ConfigV7; import org.opensearch.security.securityconf.impl.v7.InternalUserV7; @@ -1555,92 +1558,32 @@ private static int migrate(RestHighLevelClient tc, String index, File backupDir, ) ); SecurityDynamicConfiguration configV7 = Migration.migrateConfig( - SecurityDynamicConfiguration.fromNode( - DefaultObjectMapper.YAML_MAPPER.readTree(new File(backupDir, "config.yml")), - CType.CONFIG, - 1, - 0, - 0 - ) + Migration.readYaml(new File(backupDir, "config.yml"), ConfigV6.class) ); SecurityDynamicConfiguration internalUsersV7 = Migration.migrateInternalUsers( - SecurityDynamicConfiguration.fromNode( - DefaultObjectMapper.YAML_MAPPER.readTree(new File(backupDir, "internal_users.yml")), - CType.INTERNALUSERS, - 1, - 0, - 0 - ) + Migration.readYaml(new File(backupDir, "internal_users.yml"), InternalUserV6.class) ); - SecurityDynamicConfiguration rolesmappingV6 = SecurityDynamicConfiguration.fromNode( - DefaultObjectMapper.YAML_MAPPER.readTree(new File(backupDir, "roles_mapping.yml")), - CType.ROLESMAPPING, - 1, - 0, - 0 + SecurityDynamicConfiguration rolesmappingV6 = Migration.readYaml( + new File(backupDir, "roles_mapping.yml"), + RoleMappingsV6.class ); + Tuple, SecurityDynamicConfiguration> rolesTenantsV7 = Migration.migrateRoles( - SecurityDynamicConfiguration.fromNode( - DefaultObjectMapper.YAML_MAPPER.readTree(new File(backupDir, "roles.yml")), - CType.ROLES, - 1, - 0, - 0 - ), + Migration.readYaml(new File(backupDir, "roles.yml"), RoleV6.class), rolesmappingV6 ); SecurityDynamicConfiguration rolesmappingV7 = Migration.migrateRoleMappings(rolesmappingV6); SecurityDynamicConfiguration nodesDn = Migration.migrateNodesDn( - SecurityDynamicConfiguration.fromNode( - DefaultObjectMapper.YAML_MAPPER.readTree( - ConfigHelper.createFileOrStringReader(CType.NODESDN, 1, new File(backupDir, "nodes_dn.yml").getAbsolutePath(), true) - ), - CType.NODESDN, - 1, - 0, - 0 - ) + Migration.readYaml(new File(backupDir, "nodes_dn.yml"), NodesDn.class) ); SecurityDynamicConfiguration whitelistingSettings = Migration.migrateWhitelistingSetting( - SecurityDynamicConfiguration.fromNode( - DefaultObjectMapper.YAML_MAPPER.readTree( - ConfigHelper.createFileOrStringReader( - CType.WHITELIST, - 1, - new File(backupDir, "whitelist.yml").getAbsolutePath(), - true - ) - ), - CType.WHITELIST, - 1, - 0, - 0 - ) + Migration.readYaml(new File(backupDir, "whitelist.yml"), WhitelistingSettings.class) ); SecurityDynamicConfiguration allowlistingSettings = Migration.migrateAllowlistingSetting( - SecurityDynamicConfiguration.fromNode( - DefaultObjectMapper.YAML_MAPPER.readTree( - ConfigHelper.createFileOrStringReader( - CType.ALLOWLIST, - 1, - new File(backupDir, "allowlist.yml").getAbsolutePath(), - true - ) - ), - CType.ALLOWLIST, - 1, - 0, - 0 - ) + Migration.readYaml(new File(backupDir, "allowlist.yml"), AllowlistingSettings.class) ); SecurityDynamicConfiguration audit = Migration.migrateAudit( - SecurityDynamicConfiguration.fromNode( - DefaultObjectMapper.YAML_MAPPER.readTree(new File(backupDir, "audit.yml")), - CType.AUDIT, - 1, - 0, - 0 - ) + Migration.readYaml(new File(backupDir, "audit.yml"), AuditConfig.class) ); DefaultObjectMapper.YAML_MAPPER.writeValue(new File(v7Dir, "/action_groups.yml"), actionGroupsV7); @@ -1727,7 +1670,7 @@ private static int validateConfig(String cd, String file, String type, int versi return -1; } - private static boolean validateConfigFile(String file, CType cType, int version) { + private static boolean validateConfigFile(String file, CType cType, int version) { try { ConfigHelper.fromYamlFile(file, cType, version == 7 ? 2 : 1, 0, 0); System.out.println(file + " OK"); diff --git a/src/main/java/org/opensearch/security/user/UserService.java b/src/main/java/org/opensearch/security/user/UserService.java index f380fdb328..60d15a4cc8 100644 --- a/src/main/java/org/opensearch/security/user/UserService.java +++ b/src/main/java/org/opensearch/security/user/UserService.java @@ -85,7 +85,7 @@ public class UserService { final static String FAILED_ACCOUNT_RETRIEVAL_MESSAGE = "The account specified could not be accessed at this time."; final static String AUTH_TOKEN_GENERATION_MESSAGE = "An auth token could not be generated for the specified account."; - private static CType getUserConfigName() { + private static CType getUserConfigName() { return CType.INTERNALUSERS; } @@ -116,7 +116,7 @@ public UserService( * @param config CType whose data is to be loaded in-memory * @return configuration loaded with given CType data */ - protected final SecurityDynamicConfiguration load(final CType config, boolean logComplianceEvent) { + protected final SecurityDynamicConfiguration load(final CType config, boolean logComplianceEvent) { SecurityDynamicConfiguration loaded = configurationRepository.getConfigurationsFromIndex( Collections.singleton(config), logComplianceEvent @@ -311,7 +311,7 @@ public AuthToken generateAuthToken(String accountName) throws IOException { public static void saveAndUpdateConfigs( final String indexName, final Client client, - final CType cType, + final CType cType, final SecurityDynamicConfiguration configuration ) { final IndexRequest ir = new IndexRequest(indexName); diff --git a/src/test/java/org/opensearch/security/ConfigTests.java b/src/test/java/org/opensearch/security/ConfigTests.java index 7a4e626d1b..c30da8ed9f 100644 --- a/src/test/java/org/opensearch/security/ConfigTests.java +++ b/src/test/java/org/opensearch/security/ConfigTests.java @@ -54,7 +54,7 @@ public class ConfigTests { @Test public void testEmptyConfig() throws Exception { - Assert.assertNotSame(SecurityDynamicConfiguration.empty().deepClone(), SecurityDynamicConfiguration.empty()); + Assert.assertNotSame(SecurityDynamicConfiguration.empty(CType.ROLES).deepClone(), SecurityDynamicConfiguration.empty(CType.ROLES)); } @Test @@ -102,7 +102,7 @@ public void testParseSg67Config() throws Exception { } - private void check(String file, CType cType) throws Exception { + private void check(String file, CType cType) throws Exception { final String adjustedFilePath = SingleClusterTest.TEST_RESOURCE_RELATIVE_PATH + file; JsonNode jsonNode = YAML.readTree(Files.readString(new File(adjustedFilePath).toPath(), StandardCharsets.UTF_8)); int configVersion = 1; @@ -116,11 +116,17 @@ private void check(String file, CType cType) throws Exception { // Assert.assertTrue(dc.getCEntries().size() > 0); String jsonSerialize = DefaultObjectMapper.objectMapper.writeValueAsString(dc); SecurityDynamicConfiguration conf = SecurityDynamicConfiguration.fromJson(jsonSerialize, cType, configVersion, 0, 0); - SecurityDynamicConfiguration.fromJson(Strings.toString(XContentType.JSON, conf), cType, configVersion, 0, 0); + SecurityDynamicConfiguration.fromJson( + Strings.toString(XContentType.JSON, conf), + cType, + SecurityDynamicConfiguration.CURRENT_VERSION, + 0, + 0 + ); } - private SecurityDynamicConfiguration load(String file, CType cType) throws Exception { + private SecurityDynamicConfiguration load(String file, CType cType) throws Exception { final String adjustedFilePath = SingleClusterTest.TEST_RESOURCE_RELATIVE_PATH + file; JsonNode jsonNode = YAML.readTree(Files.readString(new File(adjustedFilePath).toPath(), StandardCharsets.UTF_8)); int configVersion = 1; @@ -129,6 +135,6 @@ private SecurityDynamicConfiguration load(String file, CType cType) throws Ex assertThat(cType.toLCString(), is(jsonNode.get("_meta").get("type").asText())); configVersion = jsonNode.get("_meta").get("config_version").asInt(); } - return SecurityDynamicConfiguration.fromNode(jsonNode, cType, configVersion, 0, 0); + return SecurityDynamicConfiguration.fromNodeWithoutAutoConversion(jsonNode, cType, configVersion, 0, 0); } } diff --git a/src/test/java/org/opensearch/security/UserServiceUnitTests.java b/src/test/java/org/opensearch/security/UserServiceUnitTests.java index 578273d291..5b18f7ec3f 100644 --- a/src/test/java/org/opensearch/security/UserServiceUnitTests.java +++ b/src/test/java/org/opensearch/security/UserServiceUnitTests.java @@ -100,7 +100,7 @@ public void testAnyUserTypeFilter() { assertThat(true, is(config.getCEntries().containsKey(internalAccountUsername))); } - private SecurityDynamicConfiguration readConfigFromYml(String file, CType cType) throws Exception { + private SecurityDynamicConfiguration readConfigFromYml(String file, CType cType) throws Exception { final ObjectMapper YAML = new ObjectMapper(new YAMLFactory()); final String TEST_RESOURCE_RELATIVE_PATH = "../../resources/test/"; diff --git a/src/test/java/org/opensearch/security/configuration/ConfigurationRepositoryTest.java b/src/test/java/org/opensearch/security/configuration/ConfigurationRepositoryTest.java index ac1f57c0f1..3bf6e36dac 100644 --- a/src/test/java/org/opensearch/security/configuration/ConfigurationRepositoryTest.java +++ b/src/test/java/org/opensearch/security/configuration/ConfigurationRepositoryTest.java @@ -14,7 +14,6 @@ import java.io.IOException; import java.nio.file.Path; import java.time.Instant; -import java.util.Map; import java.util.Set; import org.junit.Before; @@ -154,7 +153,7 @@ public void getConfiguration_withInvalidConfigurationShouldReturnNewEmptyConfigu ConfigurationRepository configRepository = createConfigurationRepository(Settings.EMPTY); SecurityDynamicConfiguration config = configRepository.getConfiguration(CType.CONFIG); - SecurityDynamicConfiguration emptyConfig = SecurityDynamicConfiguration.empty(); + SecurityDynamicConfiguration emptyConfig = SecurityDynamicConfiguration.empty(CType.CONFIG); assertThat(config, is(instanceOf(SecurityDynamicConfiguration.class))); assertThat(config.getCEntries().size(), is(equalTo(0))); @@ -256,8 +255,8 @@ public void testInitSecurityIndex_shouldUploadConfigIfIndexCreated() throws Exce public void testExecuteConfigurationInitialization_executeInitializationOnlyOnce() throws Exception { doAnswer(invocation -> { @SuppressWarnings("unchecked") - final var listener = (ActionListener>>) invocation.getArgument(1); - listener.onResponse(Map.of()); + final var listener = (ActionListener) invocation.getArgument(1); + listener.onResponse(ConfigurationMap.EMPTY); return null; }).when(securityIndexHandler).loadConfiguration(any(), any()); diff --git a/src/test/java/org/opensearch/security/dlic/rest/api/AbstractApiActionValidationTest.java b/src/test/java/org/opensearch/security/dlic/rest/api/AbstractApiActionValidationTest.java index 39ff609c06..9278551efa 100644 --- a/src/test/java/org/opensearch/security/dlic/rest/api/AbstractApiActionValidationTest.java +++ b/src/test/java/org/opensearch/security/dlic/rest/api/AbstractApiActionValidationTest.java @@ -13,7 +13,6 @@ import java.io.IOException; import java.util.List; -import java.util.Map; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -27,11 +26,13 @@ import org.opensearch.core.rest.RestStatus; import org.opensearch.core.xcontent.ToXContent; import org.opensearch.security.DefaultObjectMapper; +import org.opensearch.security.configuration.ConfigurationMap; import org.opensearch.security.configuration.ConfigurationRepository; import org.opensearch.security.hasher.PasswordHasher; import org.opensearch.security.hasher.PasswordHasherFactory; import org.opensearch.security.securityconf.impl.CType; import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; +import org.opensearch.security.securityconf.impl.v7.RoleV7; import org.opensearch.security.support.ConfigConstants; import org.opensearch.threadpool.ThreadPool; @@ -62,7 +63,7 @@ public abstract class AbstractApiActionValidationTest { @Mock SecurityDynamicConfiguration configuration; - SecurityDynamicConfiguration rolesConfiguration; + SecurityDynamicConfiguration rolesConfiguration; ObjectMapper objectMapper = DefaultObjectMapper.objectMapper; @@ -101,7 +102,7 @@ void setupRolesConfiguration() throws IOException { rolesConfiguration = SecurityDynamicConfiguration.fromJson(objectMapper.writeValueAsString(config), CType.ROLES, 2, 1, 1); when(configurationRepository.getConfigurationsFromIndex(List.of(CType.ROLES), false)).thenReturn( - Map.of(CType.ROLES, rolesConfiguration) + ConfigurationMap.of(rolesConfiguration) ); } @@ -110,7 +111,7 @@ public void allCrudActionsForDefaultValidatorAreForbidden() throws Exception { final var defaultPessimisticValidator = new AbstractApiAction(null, clusterService, threadPool, securityApiDependencies) { @Override - protected CType getConfigType() { + protected CType getConfigType() { return CType.CONFIG; } }.createEndpointValidator(); diff --git a/src/test/java/org/opensearch/security/dlic/rest/api/ActionGroupsApiActionValidationTest.java b/src/test/java/org/opensearch/security/dlic/rest/api/ActionGroupsApiActionValidationTest.java index 72d77b9a68..0ad73ef61c 100644 --- a/src/test/java/org/opensearch/security/dlic/rest/api/ActionGroupsApiActionValidationTest.java +++ b/src/test/java/org/opensearch/security/dlic/rest/api/ActionGroupsApiActionValidationTest.java @@ -23,6 +23,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.when; public class ActionGroupsApiActionValidationTest extends AbstractApiActionValidationTest { @@ -68,34 +69,36 @@ public void hasNoRightsToChangeImmutableEntityForRegularUser() throws Exception @Test public void onConfigChangeActionGroupHasSameNameAsRole() throws Exception { - when(configuration.getCType()).thenReturn(CType.ACTIONGROUPS); - when(configuration.getVersion()).thenReturn(2); + doReturn(CType.ACTIONGROUPS).when(configuration).getCType(); when(configuration.getImplementingClass()).thenCallRealMethod(); final var ag = objectMapper.createObjectNode() - .put("type", ActionGroupsApiAction.CLUSTER_TYPE) - .set("allowed_actions", objectMapper.createArrayNode().add("indices:*")); + .put("type", ActionGroupsApiAction.CLUSTER_TYPE) + .set("allowed_actions", objectMapper.createArrayNode().add("indices:*")); final var actionGroupsApiActionEndpointValidator = new ActionGroupsApiAction(clusterService, threadPool, securityApiDependencies) - .createEndpointValidator(); + .createEndpointValidator(); - final var result = actionGroupsApiActionEndpointValidator.onConfigChange(SecurityConfiguration.of(ag,"kibana_read_only", configuration)); + final var result = actionGroupsApiActionEndpointValidator.onConfigChange( + SecurityConfiguration.of(ag, "kibana_read_only", configuration) + ); assertFalse(result.isValid()); assertThat(result.status(), is(RestStatus.BAD_REQUEST)); - assertThat(xContentToJsonNode(result.errorMessage()).get("message").asText(), is("kibana_read_only is an existing role. A action group cannot be named with an existing role name.")); + assertThat( + xContentToJsonNode(result.errorMessage()).get("message").asText(), + is("kibana_read_only is an existing role. A action group cannot be named with an existing role name.") + ); } @Test public void onConfigChangeActionGroupHasSelfReference() throws Exception { - when(configuration.getCType()).thenReturn(CType.ACTIONGROUPS); - when(configuration.getVersion()).thenReturn(2); + doReturn(CType.ACTIONGROUPS).when(configuration).getCType(); when(configuration.getImplementingClass()).thenCallRealMethod(); final var ag = objectMapper.createObjectNode() - .put("type", ActionGroupsApiAction.INDEX_TYPE) - .set("allowed_actions", objectMapper.createArrayNode().add("ag")); + .put("type", ActionGroupsApiAction.INDEX_TYPE) + .set("allowed_actions", objectMapper.createArrayNode().add("ag")); final var actionGroupsApiActionEndpointValidator = new ActionGroupsApiAction(clusterService, threadPool, securityApiDependencies) - .createEndpointValidator(); + .createEndpointValidator(); - final var result = actionGroupsApiActionEndpointValidator - .onConfigChange(SecurityConfiguration.of(ag,"ag", configuration)); + final var result = actionGroupsApiActionEndpointValidator.onConfigChange(SecurityConfiguration.of(ag, "ag", configuration)); assertFalse(result.isValid()); assertThat(result.status(), is(RestStatus.BAD_REQUEST)); assertThat(xContentToJsonNode(result.errorMessage()).get("message").asText(), is("ag cannot be an allowed_action of itself")); @@ -103,34 +106,32 @@ public void onConfigChangeActionGroupHasSelfReference() throws Exception { @Test public void validateInvalidType() throws Exception { - when(configuration.getCType()).thenReturn(CType.ACTIONGROUPS); - when(configuration.getVersion()).thenReturn(2); + doReturn(CType.ACTIONGROUPS).when(configuration).getCType(); when(configuration.getImplementingClass()).thenCallRealMethod(); final var ag = objectMapper.createObjectNode() - .put("type", "some_type_we_know_nothing_about") - .set("allowed_actions", objectMapper.createArrayNode().add("ag")); + .put("type", "some_type_we_know_nothing_about") + .set("allowed_actions", objectMapper.createArrayNode().add("ag")); final var actionGroupsApiActionEndpointValidator = new ActionGroupsApiAction(clusterService, threadPool, securityApiDependencies) - .createEndpointValidator(); + .createEndpointValidator(); - final var result = actionGroupsApiActionEndpointValidator - .onConfigChange(SecurityConfiguration.of(ag,"ag", configuration)); + final var result = actionGroupsApiActionEndpointValidator.onConfigChange(SecurityConfiguration.of(ag, "ag", configuration)); assertFalse(result.isValid()); assertThat(result.status(), is(RestStatus.BAD_REQUEST)); - assertThat(xContentToJsonNode(result.errorMessage()).get("message").asText(), is("Invalid action group type: some_type_we_know_nothing_about. Supported types are: cluster, index.")); + assertThat( + xContentToJsonNode(result.errorMessage()).get("message").asText(), + is("Invalid action group type: some_type_we_know_nothing_about. Supported types are: cluster, index.") + ); } @Test public void passActionGroupWithoutType() throws Exception { - when(configuration.getCType()).thenReturn(CType.ACTIONGROUPS); - when(configuration.getVersion()).thenReturn(2); + doReturn(CType.ACTIONGROUPS).when(configuration).getCType(); when(configuration.getImplementingClass()).thenCallRealMethod(); - final var ag = objectMapper.createObjectNode() - .set("allowed_actions", objectMapper.createArrayNode().add("ag")); + final var ag = objectMapper.createObjectNode().set("allowed_actions", objectMapper.createArrayNode().add("ag")); final var actionGroupsApiActionEndpointValidator = new ActionGroupsApiAction(clusterService, threadPool, securityApiDependencies) - .createEndpointValidator(); + .createEndpointValidator(); - final var result = actionGroupsApiActionEndpointValidator - .onConfigChange(SecurityConfiguration.of(ag,"some_ag", configuration)); + final var result = actionGroupsApiActionEndpointValidator.onConfigChange(SecurityConfiguration.of(ag, "some_ag", configuration)); assertTrue(result.isValid()); } diff --git a/src/test/java/org/opensearch/security/dlic/rest/api/InternalUsersApiActionValidationTest.java b/src/test/java/org/opensearch/security/dlic/rest/api/InternalUsersApiActionValidationTest.java index d6efaa9dd4..ffe3461951 100644 --- a/src/test/java/org/opensearch/security/dlic/rest/api/InternalUsersApiActionValidationTest.java +++ b/src/test/java/org/opensearch/security/dlic/rest/api/InternalUsersApiActionValidationTest.java @@ -22,6 +22,7 @@ import org.opensearch.core.rest.RestStatus; import org.opensearch.rest.RestRequest; import org.opensearch.security.DefaultObjectMapper; +import org.opensearch.security.configuration.ConfigurationMap; import org.opensearch.security.dlic.rest.validation.ValidationResult; import org.opensearch.security.hasher.PasswordHasherFactory; import org.opensearch.security.securityconf.impl.CType; @@ -58,8 +59,7 @@ public void setupRolesAndMappings() throws IOException { final var allClusterPermissions = new RoleV7(); allClusterPermissions.setCluster_permissions(List.of("*")); - @SuppressWarnings("unchecked") - final var c = (SecurityDynamicConfiguration) rolesConfiguration; + final var c = rolesConfiguration; c.putCEntry("some_role_with_reserved_mapping", allClusterPermissions); c.putCEntry("some_role_with_hidden_mapping", allClusterPermissions); @@ -82,7 +82,7 @@ public void setupRolesAndMappings() throws IOException { 1 ); when(configurationRepository.getConfigurationsFromIndex(List.of(CType.ROLESMAPPING), false)).thenReturn( - Map.of(CType.ROLESMAPPING, rolesMappingConfiguration) + ConfigurationMap.of(rolesMappingConfiguration) ); } diff --git a/src/test/java/org/opensearch/security/dlic/rest/api/RolesMappingApiActionValidationTest.java b/src/test/java/org/opensearch/security/dlic/rest/api/RolesMappingApiActionValidationTest.java index 1caa39a4ff..21dd372265 100644 --- a/src/test/java/org/opensearch/security/dlic/rest/api/RolesMappingApiActionValidationTest.java +++ b/src/test/java/org/opensearch/security/dlic/rest/api/RolesMappingApiActionValidationTest.java @@ -12,12 +12,12 @@ package org.opensearch.security.dlic.rest.api; import java.util.List; -import java.util.Map; import org.junit.Before; import org.junit.Test; import org.opensearch.core.rest.RestStatus; +import org.opensearch.security.configuration.ConfigurationMap; import org.opensearch.security.securityconf.impl.CType; import static org.hamcrest.MatcherAssert.assertThat; @@ -62,7 +62,7 @@ public void isNotAllowedNoRightsToChangeRoleEntity() throws Exception { public void onConfigChangeShouldCheckRoles() throws Exception { when(restApiAdminPrivilegesEvaluator.containsRestApiAdminPermissions(any(Object.class))).thenCallRealMethod(); when(configurationRepository.getConfigurationsFromIndex(List.of(CType.ROLES), false)) - .thenReturn(Map.of(CType.ROLES, rolesConfiguration)); + .thenReturn(ConfigurationMap.of(rolesConfiguration)); final var rolesApiActionEndpointValidator = new RolesMappingApiAction(clusterService, threadPool, securityApiDependencies).createEndpointValidator(); diff --git a/src/test/java/org/opensearch/security/dlic/rest/validation/EndpointValidatorTest.java b/src/test/java/org/opensearch/security/dlic/rest/validation/EndpointValidatorTest.java index a38b28fa5c..19fba4caee 100644 --- a/src/test/java/org/opensearch/security/dlic/rest/validation/EndpointValidatorTest.java +++ b/src/test/java/org/opensearch/security/dlic/rest/validation/EndpointValidatorTest.java @@ -38,6 +38,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) @@ -351,7 +352,7 @@ public void regularUserCanNotChangeObjectWithRestAdminPermissionsForNewRoles() t final var array = objectMapper.createArrayNode(); restAdminPermissions().forEach(array::add); - when(configuration.getCType()).thenReturn(CType.ROLES); + doReturn(CType.ROLES).when(configuration).getCType(); when(configuration.getVersion()).thenReturn(2); when(configuration.getImplementingClass()).thenCallRealMethod(); when(configuration.exists("some_role")).thenReturn(false); @@ -378,7 +379,7 @@ public void regularUserCanNotChangeObjectWithRestAdminPermissionsForExitingActio @Test public void regularUserCanNotChangeObjectWithRestAdminPermissionsForMewActionGroups() throws Exception { - when(configuration.getCType()).thenReturn(CType.ACTIONGROUPS); + doReturn(CType.ACTIONGROUPS).when(configuration).getCType(); when(configuration.getVersion()).thenReturn(2); when(configuration.getImplementingClass()).thenCallRealMethod(); when(configuration.exists("some_ag")).thenReturn(false); @@ -389,7 +390,7 @@ public void regularUserCanNotChangeObjectWithRestAdminPermissionsForMewActionGro restAdminPermissions().forEach(array::add); var agCheckResult = endpointValidator.isAllowedToChangeEntityWithRestAdminPermissions( - SecurityConfiguration.of(objectMapper.createObjectNode().set("allowed_actions", array), "some_ag", configuration) + SecurityConfiguration.of(objectMapper.createObjectNode().set("allowed_actions", array), "some_ag", configuration) ); assertFalse(agCheckResult.isValid()); assertThat(agCheckResult.status(), is(RestStatus.FORBIDDEN)); diff --git a/src/test/java/org/opensearch/security/securityconf/FlattenedActionGroupsTest.java b/src/test/java/org/opensearch/security/securityconf/FlattenedActionGroupsTest.java index 7cea117d25..cc0d635686 100644 --- a/src/test/java/org/opensearch/security/securityconf/FlattenedActionGroupsTest.java +++ b/src/test/java/org/opensearch/security/securityconf/FlattenedActionGroupsTest.java @@ -38,8 +38,7 @@ public void basicTest() throws Exception { ); SecurityDynamicConfiguration config = SecurityDynamicConfiguration.fromMap( testActionGroups.map, - CType.ACTIONGROUPS, - 2 + CType.ACTIONGROUPS ); FlattenedActionGroups actionGroups = new FlattenedActionGroups(config); @@ -66,8 +65,7 @@ public void cycleTest() throws Exception { SecurityDynamicConfiguration config = SecurityDynamicConfiguration.fromMap( testActionGroups.map, - CType.ACTIONGROUPS, - 2 + CType.ACTIONGROUPS ); FlattenedActionGroups actionGroups = new FlattenedActionGroups(config); diff --git a/src/test/java/org/opensearch/security/securityconf/SecurityRolesPermissionsTest.java b/src/test/java/org/opensearch/security/securityconf/SecurityRolesPermissionsTest.java index f469d1989c..f21a3e98a2 100644 --- a/src/test/java/org/opensearch/security/securityconf/SecurityRolesPermissionsTest.java +++ b/src/test/java/org/opensearch/security/securityconf/SecurityRolesPermissionsTest.java @@ -48,6 +48,10 @@ import org.opensearch.security.dlic.rest.api.RestApiAdminPrivilegesEvaluator.PermissionBuilder; import org.opensearch.security.securityconf.impl.CType; import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; +import org.opensearch.security.securityconf.impl.v7.ActionGroupsV7; +import org.opensearch.security.securityconf.impl.v7.RoleMappingsV7; +import org.opensearch.security.securityconf.impl.v7.RoleV7; +import org.opensearch.security.securityconf.impl.v7.TenantV7; import org.mockito.Mockito; @@ -243,7 +247,7 @@ static ObjectNode meta(final String type) { return DefaultObjectMapper.objectMapper.createObjectNode().put("type", type).put("config_version", 2); } - static SecurityDynamicConfiguration createRolesConfig() throws IOException { + static SecurityDynamicConfiguration createRolesConfig() throws IOException { final ObjectNode rolesNode = DefaultObjectMapper.objectMapper.createObjectNode(); rolesNode.set("_meta", meta("roles")); NO_REST_ADMIN_PERMISSIONS_ROLES.forEach(rolesNode::set); @@ -252,19 +256,19 @@ static SecurityDynamicConfiguration createRolesConfig() throws IOExceptio return SecurityDynamicConfiguration.fromNode(rolesNode, CType.ROLES, 2, 0, 0); } - static SecurityDynamicConfiguration createRoleMappingsConfig() throws IOException { + static SecurityDynamicConfiguration createRoleMappingsConfig() throws IOException { final ObjectNode metaNode = DefaultObjectMapper.objectMapper.createObjectNode(); metaNode.set("_meta", meta("rolesmapping")); return SecurityDynamicConfiguration.fromNode(metaNode, CType.ROLESMAPPING, 2, 0, 0); } - static SecurityDynamicConfiguration createActionGroupsConfig() throws IOException { + static SecurityDynamicConfiguration createActionGroupsConfig() throws IOException { final ObjectNode metaNode = DefaultObjectMapper.objectMapper.createObjectNode(); metaNode.set("_meta", meta("actiongroups")); return SecurityDynamicConfiguration.fromNode(metaNode, CType.ACTIONGROUPS, 2, 0, 0); } - static SecurityDynamicConfiguration createTenantsConfig() throws IOException { + static SecurityDynamicConfiguration createTenantsConfig() throws IOException { final ObjectNode metaNode = DefaultObjectMapper.objectMapper.createObjectNode(); metaNode.set("_meta", meta("tenants")); return SecurityDynamicConfiguration.fromNode(metaNode, CType.TENANTS, 2, 0, 0); diff --git a/src/test/java/org/opensearch/security/securityconf/SecurityRolesPermissionsV6Test.java b/src/test/java/org/opensearch/security/securityconf/SecurityRolesPermissionsV6Test.java deleted file mode 100644 index 530db3211b..0000000000 --- a/src/test/java/org/opensearch/security/securityconf/SecurityRolesPermissionsV6Test.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.security.securityconf; - -import java.io.IOException; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; - -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import org.junit.Assert; -import org.junit.Test; - -import org.opensearch.action.support.IndicesOptions; -import org.opensearch.cluster.ClusterState; -import org.opensearch.cluster.metadata.IndexAbstraction; -import org.opensearch.cluster.metadata.IndexNameExpressionResolver; -import org.opensearch.cluster.metadata.Metadata; -import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.settings.Settings; -import org.opensearch.security.DefaultObjectMapper; -import org.opensearch.security.resolver.IndexResolverReplacer; -import org.opensearch.security.securityconf.impl.CType; -import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; -import org.opensearch.security.support.ConfigConstants; -import org.opensearch.security.user.User; - -import org.mockito.quality.Strictness; - -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import static org.mockito.Mockito.withSettings; - -public class SecurityRolesPermissionsV6Test { - static final String TEST_INDEX = ".test"; - - // a role with * permission but no system:admin/system_index permission - static final Map NO_EXPLICIT_SYSTEM_INDEX_PERMISSION = ImmutableMap.builder() - .put("all_access_without_system_index_permission", role(new String[] { "*" }, new String[] { TEST_INDEX }, new String[] { "*" })) - .build(); - - static final Map HAS_SYSTEM_INDEX_PERMISSION = ImmutableMap.builder() - .put( - "has_system_index_permission", - role(new String[] { "*" }, new String[] { TEST_INDEX }, new String[] { ConfigConstants.SYSTEM_INDEX_PERMISSION }) - ) - .build(); - - static ObjectNode role(final String[] clusterPermissions, final String[] indexPatterns, final String[] allowedActions) { - ObjectMapper objectMapper = DefaultObjectMapper.objectMapper; - // form cluster permissions - final ArrayNode clusterPermissionsArrayNode = objectMapper.createArrayNode(); - Arrays.stream(clusterPermissions).forEach(clusterPermissionsArrayNode::add); - - // form index_permissions - ArrayNode permissions = objectMapper.createArrayNode(); - Arrays.stream(allowedActions).forEach(permissions::add); // permission in v6 format - - ObjectNode permissionNode = objectMapper.createObjectNode(); - permissionNode.set("*", permissions); // type : "*" - - ObjectNode indexPermission = objectMapper.createObjectNode(); - indexPermission.set("*", permissionNode); // '*' -> all indices - - // add both to the role - ObjectNode role = objectMapper.createObjectNode(); - role.put("readonly", true); - role.set("cluster", clusterPermissionsArrayNode); - role.set("indices", indexPermission); - - return role; - } - - final ConfigModel configModel; - - public SecurityRolesPermissionsV6Test() throws IOException { - this.configModel = new ConfigModelV6( - createRolesConfig(), - createRoleMappingsConfig(), - createActionGroupsConfig(), - mock(DynamicConfigModel.class), - Settings.EMPTY - ); - } - - @Test - public void hasExplicitIndexPermission() { - IndexNameExpressionResolver resolver = mock(IndexNameExpressionResolver.class); - User user = new User("test"); - ClusterService cs = mock(ClusterService.class); - doReturn(createClusterState(new IndexShorthand(TEST_INDEX, IndexAbstraction.Type.ALIAS))).when(cs).state(); - IndexResolverReplacer.Resolved resolved = createResolved(TEST_INDEX); - - // test hasExplicitIndexPermission - final SecurityRoles securityRoleWithStarAccess = configModel.getSecurityRoles() - .filter(ImmutableSet.of("all_access_without_system_index_permission")); - user.addSecurityRoles(List.of("all_access_without_system_index_permission")); - - Assert.assertFalse( - "Should not allow system index access with * only", - securityRoleWithStarAccess.hasExplicitIndexPermission(resolved, user, new String[] {}, resolver, cs) - ); - - final SecurityRoles securityRoleWithExplicitAccess = configModel.getSecurityRoles() - .filter(ImmutableSet.of("has_system_index_permission")); - user.addSecurityRoles(List.of("has_system_index_permission")); - - Assert.assertTrue( - "Should allow system index access with explicit only", - securityRoleWithExplicitAccess.hasExplicitIndexPermission(resolved, user, new String[] {}, resolver, cs) - ); - } - - @Test - public void isPermittedOnSystemIndex() { - final SecurityRoles securityRoleWithExplicitAccess = configModel.getSecurityRoles() - .filter(ImmutableSet.of("has_system_index_permission")); - Assert.assertTrue(securityRoleWithExplicitAccess.isPermittedOnSystemIndex(TEST_INDEX)); - - final SecurityRoles securityRoleWithStarAccess = configModel.getSecurityRoles() - .filter(ImmutableSet.of("all_access_without_system_index_permission")); - Assert.assertFalse(securityRoleWithStarAccess.isPermittedOnSystemIndex(TEST_INDEX)); - } - - static SecurityDynamicConfiguration createRolesConfig() throws IOException { - final ObjectNode rolesNode = DefaultObjectMapper.objectMapper.createObjectNode(); - NO_EXPLICIT_SYSTEM_INDEX_PERMISSION.forEach(rolesNode::set); - HAS_SYSTEM_INDEX_PERMISSION.forEach(rolesNode::set); - return SecurityDynamicConfiguration.fromNode(rolesNode, CType.ROLES, 1, 0, 0); - } - - static SecurityDynamicConfiguration createRoleMappingsConfig() throws IOException { - final ObjectNode metaNode = DefaultObjectMapper.objectMapper.createObjectNode(); - return SecurityDynamicConfiguration.fromNode(metaNode, CType.ROLESMAPPING, 1, 0, 0); - } - - static SecurityDynamicConfiguration createActionGroupsConfig() throws IOException { - final ObjectNode metaNode = DefaultObjectMapper.objectMapper.createObjectNode(); - return SecurityDynamicConfiguration.fromNode(metaNode, CType.ACTIONGROUPS, 1, 0, 0); - } - - private IndexResolverReplacer.Resolved createResolved(final String... indexes) { - return new IndexResolverReplacer.Resolved( - ImmutableSet.of(), - ImmutableSet.copyOf(indexes), - ImmutableSet.copyOf(indexes), - ImmutableSet.of(), - IndicesOptions.STRICT_EXPAND_OPEN - ); - } - - private ClusterState createClusterState(final IndexShorthand... indices) { - final TreeMap indexMap = new TreeMap(); - Arrays.stream(indices).forEach(indexShorthand -> { - final IndexAbstraction indexAbstraction = mock(IndexAbstraction.class); - when(indexAbstraction.getType()).thenReturn(indexShorthand.type); - indexMap.put(indexShorthand.name, indexAbstraction); - }); - - final Metadata mockMetadata = mock(Metadata.class, withSettings().strictness(Strictness.LENIENT)); - when(mockMetadata.getIndicesLookup()).thenReturn(indexMap); - - final ClusterState mockClusterState = mock(ClusterState.class, withSettings().strictness(Strictness.LENIENT)); - when(mockClusterState.getMetadata()).thenReturn(mockMetadata); - - return mockClusterState; - } - - private class IndexShorthand { - public final String name; - public final IndexAbstraction.Type type; - - public IndexShorthand(final String name, final IndexAbstraction.Type type) { - this.name = name; - this.type = type; - } - } -} diff --git a/src/test/java/org/opensearch/security/support/ConfigReaderTest.java b/src/test/java/org/opensearch/security/support/ConfigReaderTest.java index b75d5a6e35..dfdfa76e75 100644 --- a/src/test/java/org/opensearch/security/support/ConfigReaderTest.java +++ b/src/test/java/org/opensearch/security/support/ConfigReaderTest.java @@ -41,7 +41,7 @@ public static void createConfigFile() throws IOException { @Test public void testThrowsIOExceptionForMandatoryCTypes() { - for (final var cType : CType.REQUIRED_CONFIG_FILES) { + for (final var cType : CType.requiredConfigTypes()) { assertThrows(IOException.class, () -> YamlConfigReader.newReader(cType, configDir.toPath())); } } @@ -49,7 +49,7 @@ public void testThrowsIOExceptionForMandatoryCTypes() { @Test public void testCreateReaderForNonMandatoryCTypes() throws IOException { final var yamlMapper = DefaultObjectMapper.YAML_MAPPER; - for (final var cType : CType.NOT_REQUIRED_CONFIG_FILES) { + for (final var cType : CType.notRequiredConfigTypes()) { try (final var reader = new BufferedReader(YamlConfigReader.newReader(cType, configDir.toPath()))) { final var emptyYaml = yamlMapper.readTree(reader); assertTrue(emptyYaml.has("_meta")); diff --git a/src/test/java/org/opensearch/security/support/SecurityIndexHandlerTest.java b/src/test/java/org/opensearch/security/support/SecurityIndexHandlerTest.java index e121218af4..f1d62fc5f4 100644 --- a/src/test/java/org/opensearch/security/support/SecurityIndexHandlerTest.java +++ b/src/test/java/org/opensearch/security/support/SecurityIndexHandlerTest.java @@ -47,8 +47,8 @@ import org.opensearch.core.common.bytes.BytesArray; import org.opensearch.index.get.GetResult; import org.opensearch.security.DefaultObjectMapper; +import org.opensearch.security.configuration.ConfigurationMap; import org.opensearch.security.securityconf.impl.CType; -import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; import org.opensearch.security.state.SecurityConfig; import org.opensearch.threadpool.ThreadPool; @@ -104,7 +104,7 @@ public class SecurityIndexHandlerTest { + "all_access: \n" + " reserved: false\n"; - static final Map> YAML = Map.of( + static final Map, CheckedSupplier> YAML = Map.of( CType.ACTIONGROUPS, () -> emptyYamlConfigFor(CType.ACTIONGROUPS), CType.ALLOWLIST, @@ -226,7 +226,7 @@ public void testUploadDefaultConfiguration_shouldFailIfBulkHasFailures() throws actionListener.onResponse(failedBulkResponse); return null; }).when(client).bulk(any(BulkRequest.class), any()); - for (final var c : CType.REQUIRED_CONFIG_FILES) { + for (final var c : CType.requiredConfigTypes()) { try (final var io = Files.newBufferedWriter(c.configFile(configFolder))) { io.write(YAML.get(c).get()); io.flush(); @@ -246,7 +246,7 @@ public void testUploadDefaultConfiguration_shouldCreateSetOfSecurityConfigs() th } }, e -> fail("Unexpected behave"))); - for (final var c : CType.REQUIRED_CONFIG_FILES) { + for (final var c : CType.requiredConfigTypes()) { try (final var io = Files.newBufferedWriter(c.configFile(configFolder))) { final var source = YAML.get(c).get(); io.write(source); @@ -283,7 +283,7 @@ public void testUploadDefaultConfiguration_shouldSkipAudit() throws IOException ) ); - for (final var c : CType.REQUIRED_CONFIG_FILES) { + for (final var c : CType.requiredConfigTypes()) { if (c == CType.AUDIT) continue; try (final var io = Files.newBufferedWriter(c.configFile(configFolder))) { final var source = YAML.get(c).get(); @@ -312,7 +312,7 @@ public void testUploadDefaultConfiguration_shouldSkipWhitelist() throws IOExcept ) ); - for (final var c : CType.REQUIRED_CONFIG_FILES) { + for (final var c : CType.requiredConfigTypes()) { if (c == CType.WHITELIST) continue; try (final var io = Files.newBufferedWriter(c.configFile(configFolder))) { final var source = YAML.get(c).get(); @@ -335,7 +335,7 @@ public void testUploadDefaultConfiguration_shouldSkipWhitelist() throws IOExcept @Test public void testLoadConfiguration_shouldFailIfResponseHasFailures() { final var listener = spy( - ActionListener.>>wrap( + ActionListener.wrap( r -> fail("Unexpected behave"), e -> assertThat(e.getClass(), is(SecurityException.class)) ) @@ -359,7 +359,7 @@ public void testLoadConfiguration_shouldFailIfResponseHasFailures() { @Test public void testLoadConfiguration_shouldFailIfNoRequiredConfigInResponse() { final var listener = spy( - ActionListener.>>wrap( + ActionListener.wrap( r -> fail("Unexpected behave"), e -> assertThat(e.getMessage(), is("Missing required configuration for type: CONFIG")) ) @@ -379,43 +379,10 @@ public void testLoadConfiguration_shouldFailIfNoRequiredConfigInResponse() { verify(listener).onFailure(any()); } - @Test - public void testLoadConfiguration_shouldFailForUnsupportedVersion() { - final var listener = spy( - ActionListener.>>wrap( - r -> fail("Unexpected behave"), - e -> assertThat(e.getMessage(), is("Version 1 is not supported for CONFIG")) - ) - ); - doAnswer(invocation -> { - - final var objectMapper = DefaultObjectMapper.objectMapper; - - ActionListener actionListener = invocation.getArgument(1); - final var getResult = mock(GetResult.class); - final var r = new MultiGetResponse(new MultiGetItemResponse[] { new MultiGetItemResponse(new GetResponse(getResult), null) }); - when(getResult.getId()).thenReturn(CType.CONFIG.toLCString()); - when(getResult.isExists()).thenReturn(true); - - final var oldVersionJson = objectMapper.createObjectNode() - .set("opendistro_security", objectMapper.createObjectNode().set("dynamic", objectMapper.createObjectNode())) - .toString() - .getBytes(StandardCharsets.UTF_8); - final var configResponse = objectMapper.createObjectNode().put(CType.CONFIG.toLCString(), oldVersionJson); - final var source = objectMapper.writeValueAsBytes(configResponse); - when(getResult.sourceRef()).thenReturn(new BytesArray(source, 0, source.length)); - actionListener.onResponse(r); - return null; - }).when(client).multiGet(any(MultiGetRequest.class), any()); - securityIndexHandler.loadConfiguration(configuration(), listener); - - verify(listener).onFailure(any()); - } - @Test public void testLoadConfiguration_shouldFailForUnparseableConfig() { final var listener = spy( - ActionListener.>>wrap( + ActionListener.wrap( r -> fail("Unexpected behave"), e -> assertThat(e.getMessage(), is("Couldn't parse content for CONFIG")) ) @@ -450,8 +417,8 @@ public void testLoadConfiguration_shouldFailForUnparseableConfig() { @Test public void testLoadConfiguration_shouldBuildSecurityConfig() { - final var listener = spy(ActionListener.>>wrap(config -> { - assertThat(config.keySet().size(), is(CType.values().length)); + final var listener = spy(ActionListener.wrap(config -> { + assertThat(config.keySet().size(), is(CType.values().size())); for (final var c : CType.values()) { assertTrue(c.toLCString(), config.containsKey(c)); } @@ -460,7 +427,7 @@ public void testLoadConfiguration_shouldBuildSecurityConfig() { final var objectMapper = DefaultObjectMapper.objectMapper; ActionListener actionListener = invocation.getArgument(1); - final var responses = new MultiGetItemResponse[CType.values().length]; + final var responses = new MultiGetItemResponse[CType.values().size()]; var counter = 0; for (final var c : CType.values()) { final var getResult = mock(GetResult.class); @@ -497,7 +464,7 @@ public void testLoadConfiguration_shouldBuildSecurityConfig() { verify(listener).onResponse(any()); } - private ObjectNode minimumRequiredConfig(final CType cType) { + private ObjectNode minimumRequiredConfig(final CType cType) { final var objectMapper = DefaultObjectMapper.objectMapper; return objectMapper.createObjectNode() .set("_meta", objectMapper.createObjectNode().put("type", cType.toLCString()).put("config_version", DEFAULT_CONFIG_VERSION)); From abbc3e7466a9be11d3fd3ce97ff898cfb2988896 Mon Sep 17 00:00:00 2001 From: Nils Bandener Date: Fri, 12 Jul 2024 09:24:15 +0200 Subject: [PATCH 2/9] Deleted unused classes Signed-off-by: Nils Bandener --- .../security/securityconf/ConfigModelV6.java | 1316 ----------------- .../securityconf/DynamicConfigModelV6.java | 485 ------ 2 files changed, 1801 deletions(-) delete mode 100644 src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java delete mode 100644 src/main/java/org/opensearch/security/securityconf/DynamicConfigModelV6.java diff --git a/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java b/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java deleted file mode 100644 index e35fb40a24..0000000000 --- a/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java +++ /dev/null @@ -1,1316 +0,0 @@ -/* - * Copyright 2015-2018 floragunn GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package org.opensearch.security.securityconf; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -import com.google.common.base.Joiner; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; -import com.google.common.collect.ListMultimap; -import com.google.common.collect.MultimapBuilder.SetMultimapBuilder; -import com.google.common.collect.SetMultimap; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import org.opensearch.ExceptionsHelper; -import org.opensearch.action.support.IndicesOptions; -import org.opensearch.cluster.metadata.IndexNameExpressionResolver; -import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.collect.Tuple; -import org.opensearch.common.settings.Settings; -import org.opensearch.common.util.set.Sets; -import org.opensearch.core.common.transport.TransportAddress; -import org.opensearch.core.xcontent.NamedXContentRegistry; -import org.opensearch.security.resolver.IndexResolverReplacer.Resolved; -import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; -import org.opensearch.security.securityconf.impl.v6.ActionGroupsV6; -import org.opensearch.security.securityconf.impl.v6.RoleMappingsV6; -import org.opensearch.security.securityconf.impl.v6.RoleV6; -import org.opensearch.security.securityconf.impl.v6.RoleV6.Index; -import org.opensearch.security.support.ConfigConstants; -import org.opensearch.security.support.WildcardMatcher; -import org.opensearch.security.user.User; - -import static org.opensearch.cluster.metadata.IndexAbstraction.Type.ALIAS; - -public class ConfigModelV6 extends ConfigModel { - - protected final Logger log = LogManager.getLogger(this.getClass()); - private ConfigConstants.RolesMappingResolution rolesMappingResolution; - private ActionGroupResolver agr = null; - private SecurityRoles securityRoles = null; - private TenantHolder tenantHolder; - private RoleMappingHolder roleMappingHolder; - private SecurityDynamicConfiguration roles; - - public ConfigModelV6( - SecurityDynamicConfiguration roles, - SecurityDynamicConfiguration actiongroups, - SecurityDynamicConfiguration rolesmapping, - DynamicConfigModel dcm, - Settings opensearchSettings - ) { - - this.roles = roles; - - try { - rolesMappingResolution = ConfigConstants.RolesMappingResolution.valueOf( - opensearchSettings.get( - ConfigConstants.SECURITY_ROLES_MAPPING_RESOLUTION, - ConfigConstants.RolesMappingResolution.MAPPING_ONLY.toString() - ).toUpperCase() - ); - } catch (Exception e) { - log.error("Cannot apply roles mapping resolution", e); - rolesMappingResolution = ConfigConstants.RolesMappingResolution.MAPPING_ONLY; - } - - agr = reloadActionGroups(actiongroups); - securityRoles = reload(roles); - tenantHolder = new TenantHolder(roles); - roleMappingHolder = new RoleMappingHolder(rolesmapping, dcm.getHostsResolverMode()); - } - - public Set getAllConfiguredTenantNames() { - final Set configuredTenants = new HashSet<>(); - for (Entry securityRole : roles.getCEntries().entrySet()) { - Map tenants = securityRole.getValue().getTenants(); - - if (tenants != null) { - configuredTenants.addAll(tenants.keySet()); - } - - } - - return Collections.unmodifiableSet(configuredTenants); - } - - public SecurityRoles getSecurityRoles() { - return securityRoles; - } - - private static interface ActionGroupResolver { - Set resolvedActions(final List actions); - } - - private ActionGroupResolver reloadActionGroups(SecurityDynamicConfiguration actionGroups) { - return new ActionGroupResolver() { - - private Set getGroupMembers(final String groupname) { - - if (actionGroups == null) { - return Collections.emptySet(); - } - - return Collections.unmodifiableSet(resolve(actionGroups, groupname)); - } - - private Set resolve(final SecurityDynamicConfiguration actionGroups, final String entry) { - - // SG5 format, plain array - // List en = actionGroups.getAsList(DotPath.of(entry)); - // if (en.isEmpty()) { - // try SG6 format including readonly and permissions key - // en = actionGroups.getAsList(DotPath.of(entry + "." + ConfigConstants.CONFIGKEY_ACTION_GROUPS_PERMISSIONS)); - // } - - if (!actionGroups.getCEntries().containsKey(entry)) { - return Collections.emptySet(); - } - - final Set ret = new HashSet(); - - final Object actionGroupAsObject = actionGroups.getCEntries().get(entry); - - if (actionGroupAsObject instanceof List) { - @SuppressWarnings("unchecked") - final List actionGroupPermissions = (List) actionGroupAsObject; - for (final String perm : actionGroupPermissions) { - if (actionGroups.getCEntries().containsKey(perm)) { - ret.addAll(resolve(actionGroups, perm)); - } else { - ret.add(perm); - } - } - - } else if (actionGroupAsObject instanceof ActionGroupsV6) { - for (final String perm : ((ActionGroupsV6) actionGroupAsObject).getPermissions()) { - if (actionGroups.getCEntries().containsKey(perm)) { - ret.addAll(resolve(actionGroups, perm)); - } else { - ret.add(perm); - } - } - } else { - throw new RuntimeException("Unable to handle " + actionGroupAsObject); - } - - return Collections.unmodifiableSet(ret); - } - - @Override - public Set resolvedActions(final List actions) { - final Set resolvedActions = new HashSet(); - for (String string : actions) { - final Set groups = getGroupMembers(string); - if (groups.isEmpty()) { - resolvedActions.add(string); - } else { - resolvedActions.addAll(groups); - } - } - - return Collections.unmodifiableSet(resolvedActions); - } - }; - } - - private SecurityRoles reload(SecurityDynamicConfiguration settings) { - - final Set> futures = new HashSet<>(5000); - final ExecutorService execs = Executors.newFixedThreadPool(10); - - for (Entry securityRole : settings.getCEntries().entrySet()) { - - Future future = execs.submit(new Callable() { - - @Override - public SecurityRole call() throws Exception { - SecurityRole _securityRole = new SecurityRole(securityRole.getKey()); - - if (securityRole.getValue() == null) { - return null; - } - - final Set permittedClusterActions = agr.resolvedActions(securityRole.getValue().getCluster()); - _securityRole.addClusterPerms(permittedClusterActions); - - // if(tenants != null) { - for (Entry tenant : securityRole.getValue().getTenants().entrySet()) { - - // if(tenant.equals(user.getName())) { - // continue; - // } - - if ("RW".equalsIgnoreCase(tenant.getValue())) { - _securityRole.addTenant(new Tenant(tenant.getKey(), true)); - } else { - _securityRole.addTenant(new Tenant(tenant.getKey(), false)); - // if(_securityRole.tenants.stream().filter(t->t.tenant.equals(tenant)).count() > 0) { //RW outperforms RO - // _securityRole.addTenant(new Tenant(tenant, false)); - // } - } - } - // } - - // final Map permittedAliasesIndices = - // securityRoleSettings.getGroups(DotPath.of("indices")); - - for (final Entry permittedAliasesIndex : securityRole.getValue().getIndices().entrySet()) { - - // final String resolvedRole = securityRole; - // final String indexPattern = permittedAliasesIndex; - - final String dls = permittedAliasesIndex.getValue().get_dls_(); - final List fls = permittedAliasesIndex.getValue().get_fls_(); - final List maskedFields = permittedAliasesIndex.getValue().get_masked_fields_(); - - IndexPattern _indexPattern = new IndexPattern(permittedAliasesIndex.getKey()); - _indexPattern.setDlsQuery(dls); - _indexPattern.addFlsFields(fls); - _indexPattern.addMaskedFields(maskedFields); - - for (Entry> type : permittedAliasesIndex.getValue().getTypes().entrySet()) { - TypePerm typePerm = new TypePerm(type.getKey()); - final List perms = type.getValue(); - typePerm.addPerms(agr.resolvedActions(perms)); - _indexPattern.addTypePerms(typePerm); - } - - _securityRole.addIndexPattern(_indexPattern); - - } - - return _securityRole; - } - }); - - futures.add(future); - } - - execs.shutdown(); - try { - execs.awaitTermination(30, TimeUnit.SECONDS); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - log.error("Thread interrupted (1) while loading roles"); - return null; - } - - try { - SecurityRoles _securityRoles = new SecurityRoles(futures.size()); - for (Future future : futures) { - _securityRoles.addSecurityRole(future.get()); - } - - return _securityRoles; - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - log.error("Thread interrupted (2) while loading roles"); - return null; - } catch (ExecutionException e) { - log.error("Error while updating roles: {}", e.getCause(), e.getCause()); - throw ExceptionsHelper.convertToOpenSearchException(e); - } - } - - // beans - - public static class SecurityRoles implements org.opensearch.security.securityconf.SecurityRoles { - - protected final Logger log = LogManager.getLogger(this.getClass()); - - final Set roles; - - private SecurityRoles(int roleCount) { - roles = new HashSet<>(roleCount); - } - - private SecurityRoles addSecurityRole(SecurityRole securityRole) { - if (securityRole != null) { - this.roles.add(securityRole); - } - return this; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((roles == null) ? 0 : roles.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - SecurityRoles other = (SecurityRoles) obj; - if (roles == null) { - if (other.roles != null) return false; - } else if (!roles.equals(other.roles)) return false; - return true; - } - - @Override - public String toString() { - return "roles=" + roles; - } - - public Set getRoles() { - return Collections.unmodifiableSet(roles); - } - - public Set getRoleNames() { - return getRoles().stream().map(r -> r.getName()).collect(Collectors.toSet()); - } - - public SecurityRoles filter(Set keep) { - final SecurityRoles retVal = new SecurityRoles(roles.size()); - for (SecurityRole sr : roles) { - if (keep.contains(sr.getName())) { - retVal.addSecurityRole(sr); - } - } - return retVal; - } - - @Override - public EvaluatedDlsFlsConfig getDlsFls( - User user, - boolean dfmEmptyOverwritesAll, - IndexNameExpressionResolver resolver, - ClusterService cs, - NamedXContentRegistry namedXContentRegistry - ) { - - final Map> dlsQueries = new HashMap>(); - final Map> flsFields = new HashMap>(); - final Map> maskedFieldsMap = new HashMap>(); - - for (SecurityRole sr : roles) { - for (IndexPattern ip : sr.getIpatterns()) { - final Set fls = ip.getFls(); - final String dls = ip.getDlsQuery(user); - final String indexPattern = ip.getUnresolvedIndexPattern(user); - final Set maskedFields = ip.getMaskedFields(); - Set concreteIndices = new HashSet<>(); - - if ((dls != null && dls.length() > 0) - || (fls != null && fls.size() > 0) - || (maskedFields != null && maskedFields.size() > 0)) { - concreteIndices = ip.getResolvedIndexPattern(user, resolver, cs); - } - - if (dls != null && dls.length() > 0) { - - Set dlsQuery = dlsQueries.get(indexPattern); - if (dlsQuery != null) { - dlsQuery.add(dls); - } else { - dlsQueries.put(indexPattern, new HashSet<>(Arrays.asList(dls))); - } - - for (String concreteIndex : concreteIndices) { - dlsQuery = dlsQueries.get(concreteIndex); - if (dlsQuery != null) { - dlsQuery.add(dls); - } else { - dlsQueries.put(concreteIndex, new HashSet<>(Arrays.asList(dls))); - } - } - - } - - if (fls != null && fls.size() > 0) { - - Set flsField = flsFields.get(indexPattern); - if (flsField != null) { - flsField.addAll(fls); - } else { - flsFields.put(indexPattern, new HashSet<>(fls)); - } - - for (String concreteIndex : concreteIndices) { - flsField = flsFields.get(concreteIndex); - if (flsField != null) { - flsField.addAll(fls); - } else { - flsFields.put(concreteIndex, new HashSet<>(fls)); - } - } - } - - if (maskedFields != null && maskedFields.size() > 0) { - - if (maskedFieldsMap.containsKey(indexPattern)) { - maskedFieldsMap.get(indexPattern).addAll(Sets.newHashSet(maskedFields)); - } else { - maskedFieldsMap.put(indexPattern, new HashSet()); - maskedFieldsMap.get(indexPattern).addAll(Sets.newHashSet(maskedFields)); - } - - for (String concreteIndex : concreteIndices) { - if (maskedFieldsMap.containsKey(concreteIndex)) { - maskedFieldsMap.get(concreteIndex).addAll(Sets.newHashSet(maskedFields)); - } else { - maskedFieldsMap.put(concreteIndex, new HashSet()); - maskedFieldsMap.get(concreteIndex).addAll(Sets.newHashSet(maskedFields)); - } - } - } - } - } - - return new EvaluatedDlsFlsConfig(dlsQueries, flsFields, maskedFieldsMap); - } - - public boolean hasExplicitIndexPermission( - Resolved resolved, - User user, - String[] actions, - IndexNameExpressionResolver resolver, - ClusterService cs - ) { - final Set indicesForRequest = new HashSet<>(resolved.getAllIndicesResolved(cs, resolver)); - if (indicesForRequest.isEmpty()) { - // If no indices could be found on the request there is no way to check for the explicit permissions - return false; - } - - final Set explicitlyAllowedIndices = roles.stream() - .map(role -> role.getAllResolvedPermittedIndices(resolved, user, actions, resolver, cs, true)) - .flatMap(Collection::stream) - .collect(Collectors.toSet()); - - if (log.isDebugEnabled()) { - log.debug( - "ExplicitIndexPermission check indices for request {}, explicitly allowed indices {}", - indicesForRequest.toString(), - explicitlyAllowedIndices.toString() - ); - } - - indicesForRequest.removeAll(explicitlyAllowedIndices); - return indicesForRequest.isEmpty(); - } - - // opensearchDashboards special only, terms eval - public Set getAllPermittedIndicesForDashboards( - Resolved resolved, - User user, - String[] actions, - IndexNameExpressionResolver resolver, - ClusterService cs - ) { - Set retVal = new HashSet<>(); - for (SecurityRole sr : roles) { - retVal.addAll(sr.getAllResolvedPermittedIndices(Resolved._LOCAL_ALL, user, actions, resolver, cs, false)); - retVal.addAll(resolved.getRemoteIndices()); - } - return Collections.unmodifiableSet(retVal); - } - - // dnfof only - public Set reduce(Resolved resolved, User user, String[] actions, IndexNameExpressionResolver resolver, ClusterService cs) { - Set retVal = new HashSet<>(); - for (SecurityRole sr : roles) { - retVal.addAll(sr.getAllResolvedPermittedIndices(resolved, user, actions, resolver, cs, false)); - } - if (log.isDebugEnabled()) { - log.debug("Reduced requested resolved indices {} to permitted indices {}.", resolved, retVal.toString()); - } - return Collections.unmodifiableSet(retVal); - } - - // return true on success - public boolean get(Resolved resolved, User user, String[] actions, IndexNameExpressionResolver resolver, ClusterService cs) { - for (SecurityRole sr : roles) { - if (ConfigModelV6.impliesTypePerm(sr.getIpatterns(), resolved, user, actions, resolver, cs)) { - return true; - } - } - return false; - } - - @Override - public boolean impliesClusterPermissionPermission(String action) { - return roles.stream().filter(r -> r.impliesClusterPermission(action)).count() > 0; - } - - @Override - public boolean hasExplicitClusterPermissionPermission(String action) { - return roles.stream().map(r -> { - final WildcardMatcher m = WildcardMatcher.from(r.clusterPerms); - return m == WildcardMatcher.ANY ? WildcardMatcher.NONE : m; - }).filter(m -> m.test(action)).count() > 0; - } - - // rolespan - public boolean impliesTypePermGlobal( - Resolved resolved, - User user, - String[] actions, - IndexNameExpressionResolver resolver, - ClusterService cs - ) { - Set ipatterns = new HashSet(); - roles.stream().forEach(p -> ipatterns.addAll(p.getIpatterns())); - return ConfigModelV6.impliesTypePerm(ipatterns, resolved, user, actions, resolver, cs); - } - - @Override - public boolean isPermittedOnSystemIndex(String indexName) { - boolean isPatternMatched = false; - boolean isPermitted = false; - for (SecurityRole role : roles) { - for (IndexPattern ip : role.getIpatterns()) { - WildcardMatcher wildcardMatcher = WildcardMatcher.from(ip.indexPattern); - if (wildcardMatcher.test(indexName)) { - isPatternMatched = true; - } - for (TypePerm tp : ip.getTypePerms()) { - if (tp.perms.contains(ConfigConstants.SYSTEM_INDEX_PERMISSION)) { - isPermitted = true; - } - } - } - } - return isPatternMatched && isPermitted; - } - } - - public static class SecurityRole { - - private final String name; - private final Set tenants = new HashSet<>(); - private final Set ipatterns = new HashSet<>(); - private final Set clusterPerms = new HashSet<>(); - - private SecurityRole(String name) { - super(); - this.name = Objects.requireNonNull(name); - } - - private boolean impliesClusterPermission(String action) { - return WildcardMatcher.from(clusterPerms).test(action); - } - - // get indices which are permitted for the given types and actions - // dnfof + opensearchDashboards special only - private Set getAllResolvedPermittedIndices( - Resolved resolved, - User user, - String[] actions, - IndexNameExpressionResolver resolver, - ClusterService cs, - boolean matchExplicitly - ) { - - final Set retVal = new HashSet<>(); - for (IndexPattern p : ipatterns) { - // what if we cannot resolve one (for create purposes) - boolean patternMatch = false; - final Set tperms = p.getTypePerms(); - for (TypePerm tp : tperms) { - // if matchExplicitly is true we don't want to match against `*` pattern - WildcardMatcher matcher = matchExplicitly && (tp.getPerms() == WildcardMatcher.ANY) - ? WildcardMatcher.NONE - : tp.getTypeMatcher(); - if (matcher.matchAny(resolved.getTypes())) { - patternMatch = tp.getPerms().matchAll(actions); - } - } - if (patternMatch) { - // resolved but can contain patterns for nonexistent indices - final WildcardMatcher permitted = WildcardMatcher.from(p.getResolvedIndexPattern(user, resolver, cs)); // maybe they do - // not exist - final Set res = new HashSet<>(); - if (!resolved.isLocalAll() && !resolved.getAllIndices().contains("*") && !resolved.getAllIndices().contains("_all")) { - // resolved but can contain patterns for nonexistent indices - resolved.getAllIndices().stream().filter(permitted).forEach(res::add); - } else { - // we want all indices so just return what's permitted - - // #557 - // final String[] allIndices = resolver.concreteIndexNames(cs.state(), IndicesOptions.lenientExpandOpen(), "*"); - Arrays.stream(cs.state().metadata().getConcreteAllOpenIndices()).filter(permitted).forEach(res::add); - } - retVal.addAll(res); - } - } - - // all that we want and all thats permitted of them - return Collections.unmodifiableSet(retVal); - } - - private SecurityRole addTenant(Tenant tenant) { - if (tenant != null) { - this.tenants.add(tenant); - } - return this; - } - - private SecurityRole addIndexPattern(IndexPattern indexPattern) { - if (indexPattern != null) { - this.ipatterns.add(indexPattern); - } - return this; - } - - private SecurityRole addClusterPerms(Collection clusterPerms) { - if (clusterPerms != null) { - this.clusterPerms.addAll(clusterPerms); - } - return this; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((clusterPerms == null) ? 0 : clusterPerms.hashCode()); - result = prime * result + ((ipatterns == null) ? 0 : ipatterns.hashCode()); - result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + ((tenants == null) ? 0 : tenants.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - SecurityRole other = (SecurityRole) obj; - if (clusterPerms == null) { - if (other.clusterPerms != null) return false; - } else if (!clusterPerms.equals(other.clusterPerms)) return false; - if (ipatterns == null) { - if (other.ipatterns != null) return false; - } else if (!ipatterns.equals(other.ipatterns)) return false; - if (name == null) { - if (other.name != null) return false; - } else if (!name.equals(other.name)) return false; - if (tenants == null) { - if (other.tenants != null) return false; - } else if (!tenants.equals(other.tenants)) return false; - return true; - } - - @Override - public String toString() { - return System.lineSeparator() - + " " - + name - + System.lineSeparator() - + " tenants=" - + tenants - + System.lineSeparator() - + " ipatterns=" - + ipatterns - + System.lineSeparator() - + " clusterPerms=" - + clusterPerms; - } - - public Set getTenants(User user) { - // TODO filter out user tenants - return Collections.unmodifiableSet(tenants); - } - - public Set getIpatterns() { - return Collections.unmodifiableSet(ipatterns); - } - - public Set getClusterPerms() { - return Collections.unmodifiableSet(clusterPerms); - } - - public String getName() { - return name; - } - - } - - // sg roles - public static class IndexPattern { - private final String indexPattern; - private String dlsQuery; - private final Set fls = new HashSet<>(); - private final Set maskedFields = new HashSet<>(); - private final Set typePerms = new HashSet<>(); - - public IndexPattern(String indexPattern) { - super(); - this.indexPattern = Objects.requireNonNull(indexPattern); - } - - public IndexPattern addFlsFields(List flsFields) { - if (flsFields != null) { - this.fls.addAll(flsFields); - } - return this; - } - - public IndexPattern addMaskedFields(List maskedFields) { - if (maskedFields != null) { - this.maskedFields.addAll(maskedFields); - } - return this; - } - - public IndexPattern addTypePerms(TypePerm typePerm) { - if (typePerm != null) { - this.typePerms.add(typePerm); - } - return this; - } - - public IndexPattern setDlsQuery(String dlsQuery) { - if (dlsQuery != null) { - this.dlsQuery = dlsQuery; - } - return this; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((dlsQuery == null) ? 0 : dlsQuery.hashCode()); - result = prime * result + ((fls == null) ? 0 : fls.hashCode()); - result = prime * result + ((maskedFields == null) ? 0 : maskedFields.hashCode()); - result = prime * result + ((indexPattern == null) ? 0 : indexPattern.hashCode()); - result = prime * result + ((typePerms == null) ? 0 : typePerms.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - IndexPattern other = (IndexPattern) obj; - if (dlsQuery == null) { - if (other.dlsQuery != null) return false; - } else if (!dlsQuery.equals(other.dlsQuery)) return false; - if (fls == null) { - if (other.fls != null) return false; - } else if (!fls.equals(other.fls)) return false; - if (maskedFields == null) { - if (other.maskedFields != null) return false; - } else if (!maskedFields.equals(other.maskedFields)) return false; - if (indexPattern == null) { - if (other.indexPattern != null) return false; - } else if (!indexPattern.equals(other.indexPattern)) return false; - if (typePerms == null) { - if (other.typePerms != null) return false; - } else if (!typePerms.equals(other.typePerms)) return false; - return true; - } - - @Override - public String toString() { - return System.lineSeparator() - + " indexPattern=" - + indexPattern - + System.lineSeparator() - + " dlsQuery=" - + dlsQuery - + System.lineSeparator() - + " fls=" - + fls - + System.lineSeparator() - + " typePerms=" - + typePerms; - } - - public String getUnresolvedIndexPattern(User user) { - return replaceProperties(indexPattern, user); - } - - private Set getResolvedIndexPattern(User user, IndexNameExpressionResolver resolver, ClusterService cs) { - String unresolved = getUnresolvedIndexPattern(user); - WildcardMatcher matcher = WildcardMatcher.from(unresolved); - String[] resolved = null; - if (!(matcher instanceof WildcardMatcher.Exact)) { - final String[] aliasesForPermittedPattern = cs.state() - .getMetadata() - .getIndicesLookup() - .entrySet() - .stream() - .filter(e -> e.getValue().getType() == ALIAS) - .filter(e -> matcher.test(e.getKey())) - .map(e -> e.getKey()) - .toArray(String[]::new); - - if (aliasesForPermittedPattern.length > 0) { - resolved = resolver.concreteIndexNames(cs.state(), IndicesOptions.lenientExpandOpen(), aliasesForPermittedPattern); - } - } - - if (resolved == null && !unresolved.isEmpty()) { - resolved = resolver.concreteIndexNames(cs.state(), IndicesOptions.lenientExpandOpen(), unresolved); - } - if (resolved == null || resolved.length == 0) { - return ImmutableSet.of(unresolved); - } else { - return ImmutableSet.builder().addAll(Arrays.asList(resolved)).add(unresolved).build(); - } - } - - public String getDlsQuery(User user) { - return replaceProperties(dlsQuery, user); - } - - public Set getFls() { - return Collections.unmodifiableSet(fls); - } - - public Set getMaskedFields() { - return Collections.unmodifiableSet(maskedFields); - } - - public Set getTypePerms() { - return Collections.unmodifiableSet(typePerms); - } - - } - - public static class TypePerm { - private final WildcardMatcher typeMatcher; - private final Set perms = new HashSet<>(); - - private TypePerm(String typePattern) { - this.typeMatcher = WildcardMatcher.ANY; - } - - private TypePerm addPerms(Collection perms) { - if (perms != null) { - this.perms.addAll(perms); - } - return this; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((perms == null) ? 0 : perms.hashCode()); - result = prime * result + ((typeMatcher == null) ? 0 : typeMatcher.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - TypePerm other = (TypePerm) obj; - if (perms == null) { - if (other.perms != null) return false; - } else if (!perms.equals(other.perms)) return false; - if (typeMatcher == null) { - if (other.typeMatcher != null) return false; - } else if (!typeMatcher.equals(other.typeMatcher)) return false; - return true; - } - - @Override - public String toString() { - return System.lineSeparator() - + " typePattern=" - + typeMatcher - + System.lineSeparator() - + " perms=" - + perms; - } - - public WildcardMatcher getTypeMatcher() { - return typeMatcher; - } - - public WildcardMatcher getPerms() { - return WildcardMatcher.from(perms); - } - - } - - public static class Tenant { - private final String tenant; - private final boolean readWrite; - - private Tenant(String tenant, boolean readWrite) { - super(); - this.tenant = tenant; - this.readWrite = readWrite; - } - - public String getTenant() { - return tenant; - } - - public boolean isReadWrite() { - return readWrite; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (readWrite ? 1231 : 1237); - result = prime * result + ((tenant == null) ? 0 : tenant.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - Tenant other = (Tenant) obj; - if (readWrite != other.readWrite) return false; - if (tenant == null) { - if (other.tenant != null) return false; - } else if (!tenant.equals(other.tenant)) return false; - return true; - } - - @Override - public String toString() { - return System.lineSeparator() - + " tenant=" - + tenant - + System.lineSeparator() - + " readWrite=" - + readWrite; - } - } - - private static String replaceProperties(String orig, User user) { - - if (user == null || orig == null) { - return orig; - } - - orig = orig.replace("${user.name}", user.getName()).replace("${user_name}", user.getName()); - orig = replaceRoles(orig, user); - for (Entry entry : user.getCustomAttributesMap().entrySet()) { - if (entry == null || entry.getKey() == null || entry.getValue() == null) { - continue; - } - orig = orig.replace("${" + entry.getKey() + "}", entry.getValue()); - orig = orig.replace("${" + entry.getKey().replace('.', '_') + "}", entry.getValue()); - } - return orig; - } - - private static String replaceRoles(final String orig, final User user) { - String retVal = orig; - if (orig.contains("${user.roles}") || orig.contains("${user_roles}")) { - final String commaSeparatedRoles = toQuotedCommaSeparatedString(user.getRoles()); - retVal = orig.replace("${user.roles}", commaSeparatedRoles).replace("${user_roles}", commaSeparatedRoles); - } - return retVal; - } - - private static String toQuotedCommaSeparatedString(final Set roles) { - return Joiner.on(',').join(Iterables.transform(roles, s -> { - return new StringBuilder(s.length() + 2).append('"').append(s).append('"').toString(); - })); - } - - private static final class IndexMatcherAndTypePermissions { - private static final Logger log = LogManager.getLogger(IndexMatcherAndTypePermissions.class); - - private final WildcardMatcher matcher; - private final Set typePerms; - - public IndexMatcherAndTypePermissions(Set pattern, Set typePerms) { - this.matcher = WildcardMatcher.from(pattern); - this.typePerms = typePerms; - } - - private static String b2s(boolean matches) { - return matches ? "matches" : "does not match"; - } - - public boolean matches(String index, String type, String action) { - final boolean isDebugEnabled = log.isDebugEnabled(); - boolean matchIndex = matcher.test(index); - if (isDebugEnabled) { - log.debug("index {} {} index pattern {}", index, b2s(matchIndex), matcher); - } - if (matchIndex) { - return typePerms.stream().anyMatch(tp -> { - boolean matchType = tp.getTypeMatcher().test(type); - if (isDebugEnabled) { - log.debug("type {} {} type pattern {}", type, b2s(matchType), tp.getTypeMatcher()); - } - if (matchType) { - boolean matchAction = tp.getPerms().test(action); - if (isDebugEnabled) { - log.debug("action {} {} action pattern {}", action, b2s(matchAction), tp.getPerms()); - } - return matchAction; - } - return false; - }); - } - return false; - } - } - - private static boolean impliesTypePerm( - Set ipatterns, - Resolved resolved, - User user, - String[] requestedActions, - IndexNameExpressionResolver resolver, - ClusterService cs - ) { - IndexMatcherAndTypePermissions[] indexMatcherAndTypePermissions; - if (resolved.isLocalAll()) { - // Only let localAll pass if there is an explicit privilege for a * index pattern - indexMatcherAndTypePermissions = ipatterns.stream() - .filter(indexPattern -> "*".equals(indexPattern.getUnresolvedIndexPattern(user))) - .map(p -> new IndexMatcherAndTypePermissions(p.getResolvedIndexPattern(user, resolver, cs), p.getTypePerms())) - .toArray(IndexMatcherAndTypePermissions[]::new); - } else { - indexMatcherAndTypePermissions = ipatterns.stream() - .map(p -> new IndexMatcherAndTypePermissions(p.getResolvedIndexPattern(user, resolver, cs), p.getTypePerms())) - .toArray(IndexMatcherAndTypePermissions[]::new); - } - - return resolved.getAllIndices() - .stream() - .allMatch( - index -> resolved.getTypes() - .stream() - .allMatch( - type -> Arrays.stream(requestedActions) - .allMatch( - action -> Arrays.stream(indexMatcherAndTypePermissions) - .anyMatch(ipatp -> ipatp.matches(index, type, action)) - ) - ) - ); - } - - // ####### - - private class TenantHolder { - - private SetMultimap> tenantsMM = null; - - public TenantHolder(SecurityDynamicConfiguration roles) { - final Set>>>> futures = new HashSet<>(roles.getCEntries().size()); - - final ExecutorService execs = Executors.newFixedThreadPool(10); - - for (Entry securityRole : roles.getCEntries().entrySet()) { - - if (securityRole.getValue() == null) { - continue; - } - - Future>>> future = execs.submit( - new Callable>>>() { - @Override - public Tuple>> call() throws Exception { - final Set> tuples = new HashSet<>(); - final Map tenants = securityRole.getValue().getTenants(); - - if (tenants != null) { - - for (String tenant : tenants.keySet()) { - - if ("RW".equalsIgnoreCase(tenants.get(tenant))) { - // RW - tuples.add(new Tuple(tenant, true)); - } else { - // RO - // if(!tenantsMM.containsValue(value)) { //RW outperforms RO - tuples.add(new Tuple(tenant, false)); - // } - } - } - } - - return new Tuple>>(securityRole.getKey(), tuples); - } - } - ); - - futures.add(future); - - } - - execs.shutdown(); - try { - execs.awaitTermination(30, TimeUnit.SECONDS); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - log.error("Thread interrupted (1) while loading roles"); - return; - } - - try { - final SetMultimap> tenantsMM_ = SetMultimapBuilder.hashKeys(futures.size()) - .hashSetValues(16) - .build(); - - for (Future>>> future : futures) { - Tuple>> result = future.get(); - tenantsMM_.putAll(result.v1(), result.v2()); - } - - tenantsMM = tenantsMM_; - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - log.error("Thread interrupted (2) while loading roles"); - return; - } catch (ExecutionException e) { - log.error("Error while updating roles: {}", e.getCause(), e.getCause()); - throw ExceptionsHelper.convertToOpenSearchException(e); - } - - } - - public Map mapTenants(final User user, Set roles) { - - if (user == null || tenantsMM == null) { - return Collections.emptyMap(); - } - - final Map result = new HashMap<>(roles.size()); - result.put(user.getName(), true); - - tenantsMM.entries() - .stream() - .filter(e -> roles.contains(e.getKey())) - .filter(e -> !user.getName().equals(e.getValue().v1())) - .forEach(e -> { - final String tenant = e.getValue().v1(); - final boolean rw = e.getValue().v2(); - - if (rw || !result.containsKey(tenant)) { // RW outperforms RO - result.put(tenant, rw); - } - }); - - return Collections.unmodifiableMap(result); - } - } - - private class RoleMappingHolder { - - private ListMultimap users; - private ListMultimap, String> abars; - private ListMultimap bars; - private ListMultimap hosts; - private final String hostResolverMode; - - private List userMatchers; - private List barMatchers; - private List hostMatchers; - - private RoleMappingHolder(final SecurityDynamicConfiguration rolesMapping, final String hostResolverMode) { - - this.hostResolverMode = hostResolverMode; - - if (rolesMapping != null) { - - users = ArrayListMultimap.create(); - abars = ArrayListMultimap.create(); - bars = ArrayListMultimap.create(); - hosts = ArrayListMultimap.create(); - - for (final Entry roleMap : rolesMapping.getCEntries().entrySet()) { - final String roleMapKey = roleMap.getKey(); - final RoleMappingsV6 roleMapValue = roleMap.getValue(); - - for (String u : roleMapValue.getUsers()) { - users.put(u, roleMapKey); - } - - final Set abar = new HashSet<>(roleMapValue.getAndBackendroles()); - - if (!abar.isEmpty()) { - abars.put(WildcardMatcher.matchers(abar), roleMapKey); - } - - for (String bar : roleMapValue.getBackendroles()) { - bars.put(bar, roleMapKey); - } - - for (String host : roleMapValue.getHosts()) { - hosts.put(host, roleMapKey); - } - } - - userMatchers = WildcardMatcher.matchers(users.keySet()); - barMatchers = WildcardMatcher.matchers(bars.keySet()); - hostMatchers = WildcardMatcher.matchers(hosts.keySet()); - } - } - - private Set map(final User user, final TransportAddress caller) { - - if (user == null || users == null || abars == null || bars == null || hosts == null) { - return Collections.emptySet(); - } - - final Set securityRoles = new HashSet<>(); - - if (rolesMappingResolution == ConfigConstants.RolesMappingResolution.BOTH - || rolesMappingResolution == ConfigConstants.RolesMappingResolution.BACKENDROLES_ONLY) { - if (log.isDebugEnabled()) { - log.debug("Pass backendroles from {}", user); - } - securityRoles.addAll(user.getRoles()); - } - - if (((rolesMappingResolution == ConfigConstants.RolesMappingResolution.BOTH - || rolesMappingResolution == ConfigConstants.RolesMappingResolution.MAPPING_ONLY))) { - - for (String p : WildcardMatcher.getAllMatchingPatterns(userMatchers, user.getName())) { - securityRoles.addAll(users.get(p)); - } - - for (String p : WildcardMatcher.getAllMatchingPatterns(barMatchers, user.getRoles())) { - securityRoles.addAll(bars.get(p)); - } - - for (List patterns : abars.keySet()) { - if (patterns.stream().allMatch(p -> p.matchAny(user.getRoles()))) { - securityRoles.addAll(abars.get(patterns)); - } - } - - if (caller != null) { - // IPV4 or IPv6 (compressed and without scope identifiers) - final String ipAddress = caller.getAddress(); - - final List hostMatchers = WildcardMatcher.matchers(hosts.keySet()); - for (String p : WildcardMatcher.getAllMatchingPatterns(hostMatchers, ipAddress)) { - securityRoles.addAll(hosts.get(p)); - } - - if (caller.address() != null - && (hostResolverMode.equalsIgnoreCase("ip-hostname") || hostResolverMode.equalsIgnoreCase("ip-hostname-lookup"))) { - final String hostName = caller.address().getHostString(); - - for (String p : WildcardMatcher.getAllMatchingPatterns(hostMatchers, hostName)) { - securityRoles.addAll(hosts.get(p)); - } - } - - if (caller.address() != null && hostResolverMode.equalsIgnoreCase("ip-hostname-lookup")) { - - final String resolvedHostName = caller.address().getHostName(); - - for (String p : WildcardMatcher.getAllMatchingPatterns(hostMatchers, resolvedHostName)) { - securityRoles.addAll(hosts.get(p)); - } - } - } - } - - return Collections.unmodifiableSet(securityRoles); - - } - } - - public Map mapTenants(User user, Set roles) { - return tenantHolder.mapTenants(user, roles); - } - - public Set mapSecurityRoles(User user, TransportAddress caller) { - return roleMappingHolder.map(user, caller); - } -} diff --git a/src/main/java/org/opensearch/security/securityconf/DynamicConfigModelV6.java b/src/main/java/org/opensearch/security/securityconf/DynamicConfigModelV6.java deleted file mode 100644 index c7edaf938c..0000000000 --- a/src/main/java/org/opensearch/security/securityconf/DynamicConfigModelV6.java +++ /dev/null @@ -1,485 +0,0 @@ -/* - * Copyright 2015-2017 floragunn GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.security.securityconf; - -import java.net.InetAddress; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map.Entry; -import java.util.Set; -import java.util.SortedSet; -import java.util.TreeSet; - -import com.google.common.base.Strings; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Multimap; -import com.google.common.collect.Multimaps; - -import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentType; -import org.opensearch.security.auth.AuthDomain; -import org.opensearch.security.auth.AuthFailureListener; -import org.opensearch.security.auth.AuthenticationBackend; -import org.opensearch.security.auth.AuthorizationBackend; -import org.opensearch.security.auth.Destroyable; -import org.opensearch.security.auth.HTTPAuthenticator; -import org.opensearch.security.auth.blocking.ClientBlockRegistry; -import org.opensearch.security.auth.internal.InternalAuthenticationBackend; -import org.opensearch.security.securityconf.impl.DashboardSignInOption; -import org.opensearch.security.securityconf.impl.v6.ConfigV6; -import org.opensearch.security.securityconf.impl.v6.ConfigV6.Authc; -import org.opensearch.security.securityconf.impl.v6.ConfigV6.AuthcDomain; -import org.opensearch.security.securityconf.impl.v6.ConfigV6.Authz; -import org.opensearch.security.securityconf.impl.v6.ConfigV6.AuthzDomain; -import org.opensearch.security.support.ReflectionHelper; - -public class DynamicConfigModelV6 extends DynamicConfigModel { - - private final ConfigV6 config; - private final Settings opensearchSettings; - private final Path configPath; - private SortedSet restAuthDomains; - private Set restAuthorizers; - private List destroyableComponents; - private final InternalAuthenticationBackend iab; - - private List ipAuthFailureListeners; - private Multimap authBackendFailureListeners; - private List> ipClientBlockRegistries; - private Multimap> authBackendClientBlockRegistries; - - public DynamicConfigModelV6(ConfigV6 config, Settings opensearchSettings, Path configPath, InternalAuthenticationBackend iab) { - super(); - this.config = config; - this.opensearchSettings = opensearchSettings; - this.configPath = configPath; - this.iab = iab; - buildAAA(); - } - - @Override - public SortedSet getRestAuthDomains() { - return Collections.unmodifiableSortedSet(restAuthDomains); - } - - @Override - public Set getRestAuthorizers() { - return Collections.unmodifiableSet(restAuthorizers); - } - - @Override - public boolean isAnonymousAuthenticationEnabled() { - return config.dynamic.http.anonymous_auth_enabled; - } - - @Override - public boolean isXffEnabled() { - return config.dynamic.http.xff.enabled; - } - - @Override - public String getInternalProxies() { - return config.dynamic.http.xff.internalProxies; - } - - @Override - public String getRemoteIpHeader() { - return config.dynamic.http.xff.remoteIpHeader; - } - - @Override - public boolean isRestAuthDisabled() { - return config.dynamic.disable_rest_auth; - } - - @Override - public boolean isInterTransportAuthDisabled() { - return config.dynamic.disable_intertransport_auth; - } - - @Override - public boolean isRespectRequestIndicesEnabled() { - return config.dynamic.respect_request_indices_options; - } - - @Override - public String getDashboardsServerUsername() { - return config.dynamic.kibana.server_username; - } - - @Override - public String getDashboardsOpenSearchRole() { - return config.dynamic.kibana.opendistro_role; - } - - @Override - public String getDashboardsIndexname() { - return config.dynamic.kibana.index; - } - - @Override - 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; - } - - @Override - public boolean isMultiRolespanEnabled() { - return config.dynamic.multi_rolespan_enabled; - } - - @Override - public String getFilteredAliasMode() { - return config.dynamic.filtered_alias_mode; - } - - @Override - public boolean isDnfofForEmptyResultsEnabled() { - return config.dynamic.do_not_fail_on_forbidden_empty; - } - - @Override - public String getHostsResolverMode() { - return config.dynamic.hosts_resolver_mode; - } - - @Override - public List getIpAuthFailureListeners() { - return Collections.unmodifiableList(ipAuthFailureListeners); - } - - @Override - public Multimap getAuthBackendFailureListeners() { - return Multimaps.unmodifiableMultimap(authBackendFailureListeners); - } - - @Override - public List> getIpClientBlockRegistries() { - return Collections.unmodifiableList(ipClientBlockRegistries); - } - - @Override - public Multimap> getAuthBackendClientBlockRegistries() { - return Multimaps.unmodifiableMultimap(authBackendClientBlockRegistries); - } - - @Override - public List getSignInOptions() { - return config.dynamic.kibana.sign_in_options; - } - - @Override - public Settings getDynamicOnBehalfOfSettings() { - return Settings.EMPTY; - } - - private void buildAAA() { - - final SortedSet restAuthDomains0 = new TreeSet<>(); - final Set restAuthorizers0 = new HashSet<>(); - final List destroyableComponents0 = new LinkedList<>(); - final List ipAuthFailureListeners0 = new ArrayList<>(); - final Multimap authBackendFailureListeners0 = ArrayListMultimap.create(); - final List> ipClientBlockRegistries0 = new ArrayList<>(); - final Multimap> authBackendClientBlockRegistries0 = ArrayListMultimap.create(); - - final Authz authzDyn = config.dynamic.authz; - - for (final Entry ad : authzDyn.getDomains().entrySet()) { - final boolean enabled = ad.getValue().enabled; - final boolean httpEnabled = enabled && ad.getValue().http_enabled; - - if (httpEnabled) { - try { - - final String authzBackendClazz = ad.getValue().authorization_backend.type; - final AuthorizationBackend authorizationBackend; - - if (authzBackendClazz.equals(InternalAuthenticationBackend.class.getName()) // NOSONAR - || authzBackendClazz.equals("internal") - || authzBackendClazz.equals("intern")) { - authorizationBackend = iab; - ReflectionHelper.addLoadedModule(InternalAuthenticationBackend.class); - } else { - authorizationBackend = newInstance( - authzBackendClazz, - "z", - Settings.builder() - .put(opensearchSettings) - // .putProperties(ads.getAsStringMap(DotPath.of("authorization_backend.config")), - // DynamicConfiguration.checkKeyFunction()).build(), configPath); - .put( - Settings.builder() - .loadFromSource(ad.getValue().authorization_backend.configAsJson(), XContentType.JSON) - .build() - ) - .build(), - configPath - ); - } - - if (httpEnabled) { - restAuthorizers0.add(authorizationBackend); - } - - if (authorizationBackend instanceof Destroyable) { - destroyableComponents0.add((Destroyable) authorizationBackend); - } - } catch (final Exception e) { - log.error("Unable to initialize AuthorizationBackend {} due to {}", ad, e.toString(), e); - } - } - } - - final Authc authcDyn = config.dynamic.authc; - - for (final Entry ad : authcDyn.getDomains().entrySet()) { - final boolean enabled = ad.getValue().enabled; - final boolean httpEnabled = enabled && ad.getValue().http_enabled; - - if (httpEnabled) { - try { - AuthenticationBackend authenticationBackend; - final String authBackendClazz = ad.getValue().authentication_backend.type; - if (authBackendClazz.equals(InternalAuthenticationBackend.class.getName()) // NOSONAR - || authBackendClazz.equals("internal") - || authBackendClazz.equals("intern")) { - authenticationBackend = iab; - ReflectionHelper.addLoadedModule(InternalAuthenticationBackend.class); - } else { - authenticationBackend = newInstance( - authBackendClazz, - "c", - Settings.builder() - .put(opensearchSettings) - // .putProperties(ads.getAsStringMap(DotPath.of("authentication_backend.config")), - // DynamicConfiguration.checkKeyFunction()).build() - .put( - Settings.builder() - .loadFromSource(ad.getValue().authentication_backend.configAsJson(), XContentType.JSON) - .build() - ) - .build(), - configPath - ); - } - - String httpAuthenticatorType = ad.getValue().http_authenticator.type; // no default - HTTPAuthenticator httpAuthenticator = httpAuthenticatorType == null - ? null - : (HTTPAuthenticator) newInstance( - httpAuthenticatorType, - "h", - Settings.builder() - .put(opensearchSettings) - // .putProperties(ads.getAsStringMap(DotPath.of("http_authenticator.config")), - // DynamicConfiguration.checkKeyFunction()).build(), - .put( - Settings.builder() - .loadFromSource(ad.getValue().http_authenticator.configAsJson(), XContentType.JSON) - .build() - ) - .build() - - , - configPath - ); - - final AuthDomain _ad = new AuthDomain( - authenticationBackend, - httpAuthenticator, - ad.getValue().http_authenticator.challenge, - ad.getValue().order - ); - - if (httpEnabled && _ad.getHttpAuthenticator() != null) { - restAuthDomains0.add(_ad); - } - - if (httpAuthenticator instanceof Destroyable) { - destroyableComponents0.add((Destroyable) httpAuthenticator); - } - - if (authenticationBackend instanceof Destroyable) { - destroyableComponents0.add((Destroyable) authenticationBackend); - } - - } catch (final Exception e) { - log.error("Unable to initialize auth domain {} due to {}", ad, e.toString(), e); - } - - } - } - - List originalDestroyableComponents = destroyableComponents; - - restAuthDomains = Collections.unmodifiableSortedSet(restAuthDomains0); - restAuthorizers = Collections.unmodifiableSet(restAuthorizers0); - - destroyableComponents = Collections.unmodifiableList(destroyableComponents0); - - if (originalDestroyableComponents != null) { - destroyDestroyables(originalDestroyableComponents); - } - - originalDestroyableComponents = null; - - createAuthFailureListeners( - ipAuthFailureListeners0, - authBackendFailureListeners0, - ipClientBlockRegistries0, - authBackendClientBlockRegistries0, - destroyableComponents0 - ); - - ipAuthFailureListeners = Collections.unmodifiableList(ipAuthFailureListeners0); - ipClientBlockRegistries = Collections.unmodifiableList(ipClientBlockRegistries0); - authBackendClientBlockRegistries = Multimaps.unmodifiableMultimap(authBackendClientBlockRegistries0); - authBackendFailureListeners = Multimaps.unmodifiableMultimap(authBackendFailureListeners0); - - } - - private void destroyDestroyables(List destroyableComponents) { - for (Destroyable destroyable : destroyableComponents) { - try { - destroyable.destroy(); - } catch (Exception e) { - log.error("Error while destroying " + destroyable, e); - } - } - } - - private T newInstance(final String clazzOrShortcut, String type, final Settings settings, final Path configPath) { - - String clazz = clazzOrShortcut; - - if (authImplMap.containsKey(clazz + "_" + type)) { - clazz = authImplMap.get(clazz + "_" + type); - } - - return ReflectionHelper.instantiateAAA(clazz, settings, configPath); - } - - private String translateShortcutToClassName(final String clazzOrShortcut, final String type) { - - if (authImplMap.containsKey(clazzOrShortcut + "_" + type)) { - return authImplMap.get(clazzOrShortcut + "_" + type); - } else { - return clazzOrShortcut; - } - } - - private void createAuthFailureListeners( - List ipAuthFailureListeners, - Multimap authBackendFailureListeners, - List> ipClientBlockRegistries, - Multimap> authBackendUserClientBlockRegistries, - List destroyableComponents0 - ) { - - for (Entry entry : config.dynamic.auth_failure_listeners.getListeners().entrySet()) { - - Settings entrySettings = Settings.builder() - .put(opensearchSettings) - .put(Settings.builder().loadFromSource(entry.getValue().asJson(), XContentType.JSON).build()) - .build(); - - String type = entry.getValue().type; - String authenticationBackend = entry.getValue().authentication_backend; - - AuthFailureListener authFailureListener = newInstance(type, "authFailureListener", entrySettings, configPath); - - if (Strings.isNullOrEmpty(authenticationBackend)) { - ipAuthFailureListeners.add(authFailureListener); - - if (authFailureListener instanceof ClientBlockRegistry) { - if (InetAddress.class.isAssignableFrom(((ClientBlockRegistry) authFailureListener).getClientIdType())) { - @SuppressWarnings("unchecked") - ClientBlockRegistry clientBlockRegistry = (ClientBlockRegistry) authFailureListener; - - ipClientBlockRegistries.add(clientBlockRegistry); - } else { - log.error( - "Illegal ClientIdType for AuthFailureListener" - + entry.getKey() - + ": " - + ((ClientBlockRegistry) authFailureListener).getClientIdType() - + "; must be InetAddress." - ); - } - } - - } else { - - authenticationBackend = translateShortcutToClassName(authenticationBackend, "c"); - - authBackendFailureListeners.put(authenticationBackend, authFailureListener); - - if (authFailureListener instanceof ClientBlockRegistry) { - if (String.class.isAssignableFrom(((ClientBlockRegistry) authFailureListener).getClientIdType())) { - @SuppressWarnings("unchecked") - ClientBlockRegistry clientBlockRegistry = (ClientBlockRegistry) authFailureListener; - - authBackendUserClientBlockRegistries.put(authenticationBackend, clientBlockRegistry); - } else { - log.error( - "Illegal ClientIdType for AuthFailureListener" - + entry.getKey() - + ": " - + ((ClientBlockRegistry) authFailureListener).getClientIdType() - + "; must be InetAddress." - ); - } - } - } - - if (authFailureListener instanceof Destroyable) { - destroyableComponents0.add((Destroyable) authFailureListener); - } - } - - } -} From f3a6f446c104b7c24bedea6e29bcc32a31e5008a Mon Sep 17 00:00:00 2001 From: Nils Bandener Date: Tue, 24 Sep 2024 09:54:08 +0200 Subject: [PATCH 3/9] Removed v6 configuration related code Signed-off-by: Nils Bandener --- .../ConfigurationLoaderSecurity7.java | 14 - .../dlic/rest/api/MigrateApiAction.java | 272 ---------- .../dlic/rest/api/SecurityRestApiActions.java | 2 - .../dlic/rest/api/ValidateApiAction.java | 140 ------ .../securityconf/DynamicConfigFactory.java | 58 +-- .../security/securityconf/Migration.java | 266 ---------- .../securityconf/MigrationException.java | 54 -- .../security/securityconf/impl/CType.java | 133 +---- .../impl/SecurityDynamicConfiguration.java | 82 +--- .../securityconf/impl/v6/ActionGroupsV6.java | 80 --- .../securityconf/impl/v6/ConfigV6.java | 464 ------------------ .../securityconf/impl/v6/InternalUserV6.java | 148 ------ .../securityconf/impl/v6/RoleMappingsV6.java | 105 ---- .../security/securityconf/impl/v6/RoleV6.java | 147 ------ .../securityconf/impl/v7/ActionGroupsV7.java | 17 - .../securityconf/impl/v7/ConfigV7.java | 107 ---- .../securityconf/impl/v7/InternalUserV7.java | 10 - .../securityconf/impl/v7/RoleMappingsV7.java | 12 - .../security/securityconf/impl/v7/RoleV7.java | 62 --- .../opensearch/security/tools/Migrater.java | 183 ------- .../security/tools/SecurityAdmin.java | 144 +----- .../org/opensearch/security/ConfigTests.java | 107 ---- .../securityconf/impl/v6/ConfigV6Test.java | 127 ----- .../securityconfig_v6/action_groups.yml | 136 ----- .../legacy/securityconfig_v6/audit.yml | 42 -- .../legacy/securityconfig_v6/config.yml | 218 -------- .../securityconfig_v6/internal_users.yml | 45 -- .../migration/action_groups.yml | 136 ----- .../securityconfig_v6/migration/audit.yml | 42 -- .../securityconfig_v6/migration/config.yml | 210 -------- .../migration/internal_users.yml | 45 -- .../migration/internal_users2.yml | 46 -- .../securityconfig_v6/migration/roles.yml | 177 ------- .../migration/roles_mapping.yml | 31 -- .../securityconfig_v6/migration/whitelist.yml | 12 - .../legacy/securityconfig_v6/nodes_dn.yml | 4 - .../legacy/securityconfig_v6/roles.yml | 182 ------- .../securityconfig_v6/roles_mapping.yml | 34 -- .../legacy/securityconfig_v6/whitelist.yml | 12 - 39 files changed, 21 insertions(+), 4085 deletions(-) delete mode 100644 src/main/java/org/opensearch/security/dlic/rest/api/MigrateApiAction.java delete mode 100644 src/main/java/org/opensearch/security/dlic/rest/api/ValidateApiAction.java delete mode 100644 src/main/java/org/opensearch/security/securityconf/Migration.java delete mode 100644 src/main/java/org/opensearch/security/securityconf/MigrationException.java delete mode 100644 src/main/java/org/opensearch/security/securityconf/impl/v6/ActionGroupsV6.java delete mode 100644 src/main/java/org/opensearch/security/securityconf/impl/v6/ConfigV6.java delete mode 100644 src/main/java/org/opensearch/security/securityconf/impl/v6/InternalUserV6.java delete mode 100644 src/main/java/org/opensearch/security/securityconf/impl/v6/RoleMappingsV6.java delete mode 100644 src/main/java/org/opensearch/security/securityconf/impl/v6/RoleV6.java delete mode 100644 src/main/java/org/opensearch/security/tools/Migrater.java delete mode 100644 src/test/java/org/opensearch/security/securityconf/impl/v6/ConfigV6Test.java delete mode 100644 src/test/resources/legacy/securityconfig_v6/action_groups.yml delete mode 100644 src/test/resources/legacy/securityconfig_v6/audit.yml delete mode 100644 src/test/resources/legacy/securityconfig_v6/config.yml delete mode 100644 src/test/resources/legacy/securityconfig_v6/internal_users.yml delete mode 100644 src/test/resources/legacy/securityconfig_v6/migration/action_groups.yml delete mode 100644 src/test/resources/legacy/securityconfig_v6/migration/audit.yml delete mode 100644 src/test/resources/legacy/securityconfig_v6/migration/config.yml delete mode 100644 src/test/resources/legacy/securityconfig_v6/migration/internal_users.yml delete mode 100644 src/test/resources/legacy/securityconfig_v6/migration/internal_users2.yml delete mode 100644 src/test/resources/legacy/securityconfig_v6/migration/roles.yml delete mode 100644 src/test/resources/legacy/securityconfig_v6/migration/roles_mapping.yml delete mode 100644 src/test/resources/legacy/securityconfig_v6/migration/whitelist.yml delete mode 100644 src/test/resources/legacy/securityconfig_v6/nodes_dn.yml delete mode 100644 src/test/resources/legacy/securityconfig_v6/roles.yml delete mode 100644 src/test/resources/legacy/securityconfig_v6/roles_mapping.yml delete mode 100644 src/test/resources/legacy/securityconfig_v6/whitelist.yml diff --git a/src/main/java/org/opensearch/security/configuration/ConfigurationLoaderSecurity7.java b/src/main/java/org/opensearch/security/configuration/ConfigurationLoaderSecurity7.java index 77d4585c21..2ac2bf8cdb 100644 --- a/src/main/java/org/opensearch/security/configuration/ConfigurationLoaderSecurity7.java +++ b/src/main/java/org/opensearch/security/configuration/ConfigurationLoaderSecurity7.java @@ -54,11 +54,8 @@ import org.opensearch.core.xcontent.XContentParser; import org.opensearch.security.DefaultObjectMapper; import org.opensearch.security.auditlog.config.AuditConfig; -import org.opensearch.security.securityconf.Migration; import org.opensearch.security.securityconf.impl.CType; import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; -import org.opensearch.security.securityconf.impl.v6.RoleV6; -import org.opensearch.security.securityconf.impl.v7.TenantV7; import org.opensearch.security.support.ConfigConstants; import org.opensearch.security.support.ConfigHelper; import org.opensearch.security.support.SecurityUtils; @@ -121,17 +118,6 @@ public void success(SecurityDynamicConfiguration dConf) { result.with(dConf); - if (dConf.getCType() == CType.ROLES && dConf.getAutoConvertedFrom() != null) { - // Special case for configuration that was auto-converted from v6: - // We need to generate tenant config from role config. - // Having such a special case here is not optimal, but IMHO acceptable, as this - // should be only a temporary measure until V6 configuration is completely discontinued. - @SuppressWarnings("unchecked") - SecurityDynamicConfiguration roleV6config = (SecurityDynamicConfiguration) dConf.getAutoConvertedFrom(); - SecurityDynamicConfiguration tenants = Migration.createTenants(roleV6config); - result.with(tenants); - } - latch.countDown(); if (isDebugEnabled) { log.debug( 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 deleted file mode 100644 index 4dc5128751..0000000000 --- a/src/main/java/org/opensearch/security/dlic/rest/api/MigrateApiAction.java +++ /dev/null @@ -1,272 +0,0 @@ -/* - * 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.dlic.rest.api; - -// CS-SUPPRESS-SINGLE: RegexpSingleline https://github.com/opensearch-project/OpenSearch/issues/3663 - -import java.io.IOException; -import java.util.Collections; -import java.util.List; - -import com.google.common.collect.ImmutableList; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import org.opensearch.action.admin.indices.create.CreateIndexResponse; -import org.opensearch.action.bulk.BulkRequestBuilder; -import org.opensearch.action.bulk.BulkResponse; -import org.opensearch.action.index.IndexRequest; -import org.opensearch.action.support.WriteRequest.RefreshPolicy; -import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.client.Client; -import org.opensearch.cluster.metadata.IndexMetadata; -import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.collect.Tuple; -import org.opensearch.common.inject.Inject; -import org.opensearch.common.settings.Settings; -import org.opensearch.common.settings.Settings.Builder; -import org.opensearch.common.xcontent.XContentHelper; -import org.opensearch.common.xcontent.XContentType; -import org.opensearch.core.action.ActionListener; -import org.opensearch.core.common.bytes.BytesReference; -import org.opensearch.rest.RestChannel; -import org.opensearch.rest.RestRequest; -import org.opensearch.rest.RestRequest.Method; -import org.opensearch.security.auditlog.config.AuditConfig; -import org.opensearch.security.securityconf.Migration; -import org.opensearch.security.securityconf.impl.CType; -import org.opensearch.security.securityconf.impl.NodesDn; -import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; -import org.opensearch.security.securityconf.impl.WhitelistingSettings; -import org.opensearch.security.securityconf.impl.v6.ActionGroupsV6; -import org.opensearch.security.securityconf.impl.v6.ConfigV6; -import org.opensearch.security.securityconf.impl.v6.InternalUserV6; -import org.opensearch.security.securityconf.impl.v6.RoleMappingsV6; -import org.opensearch.security.securityconf.impl.v6.RoleV6; -import org.opensearch.security.securityconf.impl.v7.ActionGroupsV7; -import org.opensearch.security.securityconf.impl.v7.ConfigV7; -import org.opensearch.security.securityconf.impl.v7.InternalUserV7; -import org.opensearch.security.securityconf.impl.v7.RoleMappingsV7; -import org.opensearch.security.securityconf.impl.v7.RoleV7; -import org.opensearch.security.securityconf.impl.v7.TenantV7; -import org.opensearch.threadpool.ThreadPool; - -import static org.opensearch.security.dlic.rest.api.Responses.badRequest; -import static org.opensearch.security.dlic.rest.api.Responses.internalServerError; -import static org.opensearch.security.dlic.rest.api.Responses.ok; -import static org.opensearch.security.dlic.rest.support.Utils.addRoutesPrefix; -// CS-ENFORCE-SINGLE - -public class MigrateApiAction extends AbstractApiAction { - private final static Logger LOGGER = LogManager.getLogger(MigrateApiAction.class); - - private static final List routes = addRoutesPrefix(Collections.singletonList(new Route(Method.POST, "/migrate"))); - - @Inject - public MigrateApiAction( - final ClusterService clusterService, - final ThreadPool threadPool, - final SecurityApiDependencies securityApiDependencies - ) { - super(Endpoint.MIGRATE, clusterService, threadPool, securityApiDependencies); - this.requestHandlersBuilder.configureRequestHandlers(this::migrateApiRequestHandlers); - } - - @Override - public List routes() { - return routes; - } - - @Override - protected CType getConfigType() { - return null; - } - - @Override - protected void consumeParameters(final RestRequest request) { - // not needed - } - - private void migrateApiRequestHandlers(RequestHandler.RequestHandlersBuilder requestHandlersBuilder) { - requestHandlersBuilder.allMethodsNotImplemented().override(Method.POST, (channel, request, client) -> migrate(channel, client)); - } - - @SuppressWarnings("unchecked") - protected void migrate(final RestChannel channel, final Client client) throws IOException { - - final SecurityDynamicConfiguration loadedConfig = load(CType.CONFIG, true); - - if (loadedConfig.getVersion() != 1) { - badRequest(channel, "Can not migrate configuration because it was already migrated."); - return; - } - - final SecurityDynamicConfiguration configV6 = (SecurityDynamicConfiguration) loadedConfig; - final SecurityDynamicConfiguration actionGroupsV6 = (SecurityDynamicConfiguration) load( - CType.ACTIONGROUPS, - true - ); - final SecurityDynamicConfiguration internalUsersV6 = (SecurityDynamicConfiguration) load( - CType.INTERNALUSERS, - true - ); - final SecurityDynamicConfiguration rolesV6 = (SecurityDynamicConfiguration) load(CType.ROLES, true); - final SecurityDynamicConfiguration rolesmappingV6 = (SecurityDynamicConfiguration) load( - CType.ROLESMAPPING, - true - ); - final SecurityDynamicConfiguration nodesDnV6 = (SecurityDynamicConfiguration) load(CType.NODESDN, true); - final SecurityDynamicConfiguration whitelistingSettingV6 = (SecurityDynamicConfiguration< - WhitelistingSettings>) load(CType.WHITELIST, true); - final SecurityDynamicConfiguration auditConfigV6 = (SecurityDynamicConfiguration) load(CType.AUDIT, true); - - final ImmutableList.Builder> builder = ImmutableList.builder(); - - final SecurityDynamicConfiguration actionGroupsV7 = Migration.migrateActionGroups(actionGroupsV6); - builder.add(actionGroupsV7); - final SecurityDynamicConfiguration configV7 = Migration.migrateConfig(configV6); - builder.add(configV7); - final SecurityDynamicConfiguration internalUsersV7 = Migration.migrateInternalUsers(internalUsersV6); - builder.add(internalUsersV7); - final Tuple, SecurityDynamicConfiguration> rolesTenantsV7 = Migration.migrateRoles( - rolesV6, - rolesmappingV6 - ); - builder.add(rolesTenantsV7.v1()); - builder.add(rolesTenantsV7.v2()); - final SecurityDynamicConfiguration rolesmappingV7 = Migration.migrateRoleMappings(rolesmappingV6); - builder.add(rolesmappingV7); - final SecurityDynamicConfiguration nodesDnV7 = Migration.migrateNodesDn(nodesDnV6); - builder.add(nodesDnV7); - final SecurityDynamicConfiguration whitelistingSettingV7 = Migration.migrateWhitelistingSetting( - whitelistingSettingV6 - ); - builder.add(whitelistingSettingV7); - final SecurityDynamicConfiguration auditConfigV7 = Migration.migrateAudit(auditConfigV6); - builder.add(auditConfigV7); - - final int replicas = clusterService.state().metadata().index(securityApiDependencies.securityIndexName()).getNumberOfReplicas(); - final String autoExpandReplicas = clusterService.state() - .metadata() - .index(securityApiDependencies.securityIndexName()) - .getSettings() - .get(IndexMetadata.SETTING_AUTO_EXPAND_REPLICAS); - - final Builder securityIndexSettings = Settings.builder(); - - if (autoExpandReplicas == null) { - securityIndexSettings.put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, replicas); - } else { - securityIndexSettings.put(IndexMetadata.SETTING_AUTO_EXPAND_REPLICAS, autoExpandReplicas); - } - - securityIndexSettings.put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1); - - client.admin() - .indices() - .prepareDelete(securityApiDependencies.securityIndexName()) - .execute(new ActionListener() { - - @Override - public void onResponse(AcknowledgedResponse response) { - - if (response.isAcknowledged()) { - LOGGER.debug("opendistro_security index deleted successfully"); - - client.admin() - .indices() - .prepareCreate(securityApiDependencies.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(securityApiDependencies.securityIndexName()); - br.setRefreshPolicy(RefreshPolicy.IMMEDIATE); - try { - for (SecurityDynamicConfiguration dynamicConfiguration : dynamicConfigurations) { - final String id = dynamicConfiguration.getCType().toLCString(); - final BytesReference xContent = XContentHelper.toXContent( - dynamicConfiguration, - XContentType.JSON, - false - ); - br.add(new IndexRequest().id(id).source(id, xContent)); - cTypes.add(id); - } - } catch (final IOException e1) { - LOGGER.error("Unable to create bulk request " + e1, e1); - internalServerError(channel, "Unable to create bulk request."); - return; - } - - br.execute( - new ConfigUpdatingActionListener<>( - cTypes.build().toArray(new String[0]), - client, - new ActionListener() { - - @Override - public void onResponse(BulkResponse response) { - if (response.hasFailures()) { - LOGGER.error( - "Unable to upload migrated configuration because of " - + response.buildFailureMessage() - ); - internalServerError( - channel, - "Unable to upload migrated configuration (bulk index failed)." - ); - } else { - LOGGER.debug("Migration completed"); - ok(channel, "Migration completed."); - } - - } - - @Override - public void onFailure(Exception e) { - LOGGER.error("Unable to upload migrated configuration because of " + e, e); - internalServerError(channel, "Unable to upload migrated configuration."); - } - } - ) - ); - - } - - @Override - public void onFailure(Exception e) { - LOGGER.error("Unable to create opendistro_security index because of " + e, e); - internalServerError(channel, "Unable to create opendistro_security index."); - } - }); - - } else { - LOGGER.error("Unable to create opendistro_security index."); - } - } - - @Override - public void onFailure(Exception e) { - LOGGER.error("Unable to delete opendistro_security index because of " + e, e); - internalServerError(channel, "Unable to delete opendistro_security index."); - } - }); - - } - -} diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/SecurityRestApiActions.java b/src/main/java/org/opensearch/security/dlic/rest/api/SecurityRestApiActions.java index ceb99a9401..3963e443d8 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/SecurityRestApiActions.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/SecurityRestApiActions.java @@ -88,8 +88,6 @@ public static Collection getHandler( ), new AuthTokenProcessorAction(clusterService, threadPool, securityApiDependencies), new TenantsApiAction(clusterService, threadPool, securityApiDependencies), - new MigrateApiAction(clusterService, threadPool, securityApiDependencies), - new ValidateApiAction(clusterService, threadPool, securityApiDependencies), new AccountApiAction(clusterService, threadPool, securityApiDependencies, passwordHasher), new NodesDnApiAction(clusterService, threadPool, securityApiDependencies), new WhitelistApiAction(clusterService, threadPool, securityApiDependencies), diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/ValidateApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/ValidateApiAction.java deleted file mode 100644 index 58ab3884ae..0000000000 --- a/src/main/java/org/opensearch/security/dlic/rest/api/ValidateApiAction.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * 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.dlic.rest.api; - -import java.io.IOException; -import java.util.Collections; -import java.util.List; - -import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.collect.Tuple; -import org.opensearch.common.inject.Inject; -import org.opensearch.rest.RestChannel; -import org.opensearch.rest.RestRequest; -import org.opensearch.rest.RestRequest.Method; -import org.opensearch.security.auditlog.config.AuditConfig; -import org.opensearch.security.securityconf.DynamicConfigFactory; -import org.opensearch.security.securityconf.Migration; -import org.opensearch.security.securityconf.impl.CType; -import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; -import org.opensearch.security.securityconf.impl.v6.ActionGroupsV6; -import org.opensearch.security.securityconf.impl.v6.ConfigV6; -import org.opensearch.security.securityconf.impl.v6.InternalUserV6; -import org.opensearch.security.securityconf.impl.v6.RoleMappingsV6; -import org.opensearch.security.securityconf.impl.v6.RoleV6; -import org.opensearch.security.securityconf.impl.v7.ActionGroupsV7; -import org.opensearch.security.securityconf.impl.v7.ConfigV7; -import org.opensearch.security.securityconf.impl.v7.InternalUserV7; -import org.opensearch.security.securityconf.impl.v7.RoleMappingsV7; -import org.opensearch.security.securityconf.impl.v7.RoleV7; -import org.opensearch.security.securityconf.impl.v7.TenantV7; -import org.opensearch.threadpool.ThreadPool; - -import static org.opensearch.security.dlic.rest.api.Responses.badRequest; -import static org.opensearch.security.dlic.rest.api.Responses.internalServerError; -import static org.opensearch.security.dlic.rest.api.Responses.ok; -import static org.opensearch.security.dlic.rest.support.Utils.addRoutesPrefix; - -public class ValidateApiAction extends AbstractApiAction { - private static final List routes = addRoutesPrefix(Collections.singletonList(new Route(Method.GET, "/validate"))); - - @Inject - public ValidateApiAction( - final ClusterService clusterService, - final ThreadPool threadPool, - final SecurityApiDependencies securityApiDependencies - ) { - super(Endpoint.VALIDATE, clusterService, threadPool, securityApiDependencies); - this.requestHandlersBuilder.configureRequestHandlers(this::validateApiRequestHandlers); - } - - @Override - public List routes() { - return routes; - } - - @Override - protected CType getConfigType() { - return null; - } - - @Override - protected void consumeParameters(final RestRequest request) { - request.paramAsBoolean("accept_invalid", false); - } - - private void validateApiRequestHandlers(RequestHandler.RequestHandlersBuilder requestHandlersBuilder) { - requestHandlersBuilder.allMethodsNotImplemented().override(Method.GET, (channel, request, client) -> validate(channel, request)); - } - - @SuppressWarnings("unchecked") - protected void validate(RestChannel channel, RestRequest request) throws IOException { - - final boolean acceptInvalid = request.paramAsBoolean("accept_invalid", false); - - final SecurityDynamicConfiguration loadedConfig = load(CType.CONFIG, true, acceptInvalid); - - if (loadedConfig.getVersion() != 1) { - badRequest(channel, "Can not migrate configuration because it was already migrated."); - return; - } - - try { - final SecurityDynamicConfiguration configV6 = (SecurityDynamicConfiguration) loadedConfig; - final SecurityDynamicConfiguration actionGroupsV6 = (SecurityDynamicConfiguration) load( - CType.ACTIONGROUPS, - true, - acceptInvalid - ); - final SecurityDynamicConfiguration internalUsersV6 = (SecurityDynamicConfiguration) load( - CType.INTERNALUSERS, - true, - acceptInvalid - ); - final SecurityDynamicConfiguration rolesV6 = (SecurityDynamicConfiguration) load( - CType.ROLES, - true, - acceptInvalid - ); - final SecurityDynamicConfiguration rolesmappingV6 = (SecurityDynamicConfiguration) load( - CType.ROLESMAPPING, - true, - acceptInvalid - ); - final SecurityDynamicConfiguration auditConfigV6 = (SecurityDynamicConfiguration) load( - CType.AUDIT, - true - ); - - final SecurityDynamicConfiguration actionGroupsV7 = Migration.migrateActionGroups(actionGroupsV6); - final SecurityDynamicConfiguration configV7 = Migration.migrateConfig(configV6); - final SecurityDynamicConfiguration internalUsersV7 = Migration.migrateInternalUsers(internalUsersV6); - final Tuple, SecurityDynamicConfiguration> rolesTenantsV7 = Migration - .migrateRoles(rolesV6, rolesmappingV6); - final SecurityDynamicConfiguration rolesmappingV7 = Migration.migrateRoleMappings(rolesmappingV6); - final SecurityDynamicConfiguration auditConfigV7 = Migration.migrateAudit(auditConfigV6); - - ok(channel, "OK."); - } catch (Exception e) { - internalServerError(channel, "Configuration is not valid."); - } - } - - private SecurityDynamicConfiguration load(final CType config, boolean logComplianceEvent, boolean acceptInvalid) { - SecurityDynamicConfiguration loaded = securityApiDependencies.configurationRepository() - .getConfigurationsFromIndex(Collections.singleton(config), logComplianceEvent, acceptInvalid) - .get(config) - .deepClone(); - return DynamicConfigFactory.addStatics(loaded); - } - -} diff --git a/src/main/java/org/opensearch/security/securityconf/DynamicConfigFactory.java b/src/main/java/org/opensearch/security/securityconf/DynamicConfigFactory.java index ccd1502532..17e08cfcfd 100644 --- a/src/main/java/org/opensearch/security/securityconf/DynamicConfigFactory.java +++ b/src/main/java/org/opensearch/security/securityconf/DynamicConfigFactory.java @@ -29,7 +29,6 @@ import java.io.IOException; import java.nio.file.Path; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -57,8 +56,6 @@ import org.opensearch.security.securityconf.impl.NodesDn; import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; import org.opensearch.security.securityconf.impl.WhitelistingSettings; -import org.opensearch.security.securityconf.impl.v6.ConfigV6; -import org.opensearch.security.securityconf.impl.v6.InternalUserV6; import org.opensearch.security.securityconf.impl.v7.ActionGroupsV7; import org.opensearch.security.securityconf.impl.v7.ConfigV7; import org.opensearch.security.securityconf.impl.v7.InternalUserV7; @@ -290,12 +287,6 @@ public void onChange(ConfigurationMap typeToConfig) { initialized.set(true); } - private static ConfigV6 getConfigV6(SecurityDynamicConfiguration sdc) { - @SuppressWarnings("unchecked") - SecurityDynamicConfiguration c = (SecurityDynamicConfiguration) sdc; - return c.getCEntry("opendistro_security"); - } - private static ConfigV7 getConfigV7(SecurityDynamicConfiguration sdc) { @SuppressWarnings("unchecked") SecurityDynamicConfiguration c = (SecurityDynamicConfiguration) sdc; @@ -383,55 +374,16 @@ private boolean isRolesMappingHidden(String rolename) { } } - private static class InternalUsersModelV6 extends InternalUsersModel { - - SecurityDynamicConfiguration configuration; - - public InternalUsersModelV6(SecurityDynamicConfiguration configuration) { - super(); - this.configuration = configuration; - } - - @Override - public boolean exists(String user) { - return configuration.exists(user); - } - - @Override - public List getBackenRoles(String user) { - InternalUserV6 tmp = configuration.getCEntry(user); - return tmp == null ? null : tmp.getRoles(); - } - - @Override - public Map getAttributes(String user) { - InternalUserV6 tmp = configuration.getCEntry(user); - return tmp == null ? null : tmp.getAttributes(); - } - - @Override - public String getDescription(String user) { - return null; - } - - @Override - public String getHash(String user) { - InternalUserV6 tmp = configuration.getCEntry(user); - return tmp == null ? null : tmp.getHash(); - } - - public List getSecurityRoles(String user) { - return Collections.emptyList(); - } - } - private static class NodesDnModelImpl extends NodesDnModel { SecurityDynamicConfiguration configuration; - public NodesDnModelImpl(SecurityDynamicConfiguration configuration) { + @SuppressWarnings("unchecked") + public NodesDnModelImpl(SecurityDynamicConfiguration configuration) { super(); - this.configuration = null == configuration.getCType() ? SecurityDynamicConfiguration.empty(CType.NODESDN) : configuration; + this.configuration = null == configuration.getCType() + ? SecurityDynamicConfiguration.empty(CType.NODESDN) + : (SecurityDynamicConfiguration) configuration; } @Override diff --git a/src/main/java/org/opensearch/security/securityconf/Migration.java b/src/main/java/org/opensearch/security/securityconf/Migration.java deleted file mode 100644 index 57df2c2ac1..0000000000 --- a/src/main/java/org/opensearch/security/securityconf/Migration.java +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Copyright 2015-2017 floragunn GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.security.securityconf; - -import java.io.File; -import java.io.IOException; -import java.util.HashSet; -import java.util.List; -import java.util.Map.Entry; -import java.util.Set; - -import com.fasterxml.jackson.databind.JavaType; - -import org.opensearch.common.collect.Tuple; -import org.opensearch.core.common.Strings; -import org.opensearch.security.DefaultObjectMapper; -import org.opensearch.security.auditlog.config.AuditConfig; -import org.opensearch.security.securityconf.impl.AllowlistingSettings; -import org.opensearch.security.securityconf.impl.CType; -import org.opensearch.security.securityconf.impl.Meta; -import org.opensearch.security.securityconf.impl.NodesDn; -import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; -import org.opensearch.security.securityconf.impl.WhitelistingSettings; -import org.opensearch.security.securityconf.impl.v6.ActionGroupsV6; -import org.opensearch.security.securityconf.impl.v6.ConfigV6; -import org.opensearch.security.securityconf.impl.v6.InternalUserV6; -import org.opensearch.security.securityconf.impl.v6.RoleMappingsV6; -import org.opensearch.security.securityconf.impl.v6.RoleV6; -import org.opensearch.security.securityconf.impl.v7.ActionGroupsV7; -import org.opensearch.security.securityconf.impl.v7.ConfigV7; -import org.opensearch.security.securityconf.impl.v7.InternalUserV7; -import org.opensearch.security.securityconf.impl.v7.RoleMappingsV7; -import org.opensearch.security.securityconf.impl.v7.RoleV7; -import org.opensearch.security.securityconf.impl.v7.TenantV7; - -public class Migration { - - public static Tuple, SecurityDynamicConfiguration> migrateRoles( - SecurityDynamicConfiguration r6cs, - SecurityDynamicConfiguration rms6 - ) throws MigrationException { - - final SecurityDynamicConfiguration r7 = SecurityDynamicConfiguration.empty(CType.ROLES); - r7.set_meta(new Meta()); - r7.get_meta().setConfig_version(2); - r7.get_meta().setType("roles"); - - for (final Entry r6e : r6cs.getCEntries().entrySet()) { - final String roleName = r6e.getKey(); - final RoleV6 r6 = r6e.getValue(); - - if (r6 == null) { - RoleV7 noPermRole = new RoleV7(); - noPermRole.setDescription("Migrated from v6, was empty"); - r7.putCEntry(roleName, noPermRole); - continue; - } - - r7.putCEntry(roleName, new RoleV7(r6)); - } - - if (rms6 != null) { - for (final Entry r6m : rms6.getCEntries().entrySet()) { - final String roleName = r6m.getKey(); - // final RoleMappingsV6 r6 = r6m.getValue(); - - if (!r7.exists(roleName)) { - // rolemapping but role does not exists - RoleV7 noPermRole = new RoleV7(); - noPermRole.setDescription("Migrated from v6, was in rolemappings but no role existed"); - r7.putCEntry(roleName, noPermRole); - } - - } - } - - return new Tuple, SecurityDynamicConfiguration>(r7, createTenants(r6cs)); - } - - /** - * This creates a tenant configuration from the tenant names in a `SecurityDynamicConfiguration` configuration. - * The v6 configuration did not yet have an explicit file to specify all tenants. Thus, these need to be collected - * this way. - */ - public static SecurityDynamicConfiguration createTenants(SecurityDynamicConfiguration r6cs) { - Set dedupTenants = new HashSet<>(); - - for (final Entry entry : r6cs.getCEntries().entrySet()) { - for (Entry tenant : entry.getValue().getTenants().entrySet()) { - dedupTenants.add(tenant.getKey()); - } - } - - SecurityDynamicConfiguration tenantConfig = SecurityDynamicConfiguration.empty(CType.TENANTS); - tenantConfig.set_meta(new Meta()); - tenantConfig.get_meta().setConfig_version(2); - tenantConfig.get_meta().setType("tenants"); - - for (String tenantName : dedupTenants) { - TenantV7 entry = new TenantV7(); - entry.setDescription("Migrated from v6"); - tenantConfig.putCEntry(tenantName, entry); - } - - return tenantConfig; - } - - public static SecurityDynamicConfiguration migrateConfig(SecurityDynamicConfiguration r6cs) - throws MigrationException { - final SecurityDynamicConfiguration c7 = SecurityDynamicConfiguration.empty(CType.CONFIG); - c7.set_meta(new Meta()); - c7.get_meta().setConfig_version(2); - c7.get_meta().setType("config"); - - if (r6cs.getCEntries().size() != 1) { - throw new MigrationException( - "Unable to migrate config because expected size was 1 but actual size is " + r6cs.getCEntries().size() - ); - } - - if (r6cs.getCEntries().get("opendistro_security") == null) { - throw new MigrationException("Unable to migrate config because 'opendistro_security' key not found"); - } - - for (final Entry r6c : r6cs.getCEntries().entrySet()) { - c7.putCEntry("config", new ConfigV7(r6c.getValue())); - } - return c7; - } - - public static SecurityDynamicConfiguration migrateNodesDn(SecurityDynamicConfiguration nodesDn) { - final SecurityDynamicConfiguration migrated = SecurityDynamicConfiguration.empty(CType.NODESDN); - migrated.set_meta(new Meta()); - migrated.get_meta().setConfig_version(2); - migrated.get_meta().setType("nodesdn"); - - for (final Entry entry : nodesDn.getCEntries().entrySet()) { - migrated.putCEntry(entry.getKey(), new NodesDn(entry.getValue())); - } - return migrated; - } - - public static SecurityDynamicConfiguration migrateWhitelistingSetting( - SecurityDynamicConfiguration whitelistingSetting - ) { - final SecurityDynamicConfiguration migrated = SecurityDynamicConfiguration.empty(CType.WHITELIST); - migrated.set_meta(new Meta()); - migrated.get_meta().setConfig_version(2); - migrated.get_meta().setType("whitelist"); - - for (final Entry entry : whitelistingSetting.getCEntries().entrySet()) { - migrated.putCEntry(entry.getKey(), new WhitelistingSettings(entry.getValue())); - } - return migrated; - } - - public static SecurityDynamicConfiguration migrateAllowlistingSetting( - SecurityDynamicConfiguration allowlistingSetting - ) { - final SecurityDynamicConfiguration migrated = SecurityDynamicConfiguration.empty(CType.ALLOWLIST); - migrated.set_meta(new Meta()); - migrated.get_meta().setConfig_version(2); - migrated.get_meta().setType("whitelist"); - - for (final Entry entry : allowlistingSetting.getCEntries().entrySet()) { - migrated.putCEntry(entry.getKey(), new AllowlistingSettings(entry.getValue())); - } - return migrated; - } - - public static SecurityDynamicConfiguration migrateInternalUsers(SecurityDynamicConfiguration r6is) - throws MigrationException { - final SecurityDynamicConfiguration i7 = SecurityDynamicConfiguration.empty(CType.INTERNALUSERS); - i7.set_meta(new Meta()); - i7.get_meta().setConfig_version(2); - i7.get_meta().setType("internalusers"); - - for (final Entry r6i : r6is.getCEntries().entrySet()) { - final String username = !Strings.isNullOrEmpty(r6i.getValue().getUsername()) ? r6i.getValue().getUsername() : r6i.getKey(); - i7.putCEntry(username, new InternalUserV7(r6i.getValue())); - } - - return i7; - } - - @SuppressWarnings("unchecked") - public static SecurityDynamicConfiguration migrateActionGroups(SecurityDynamicConfiguration r6as) - throws MigrationException { - - final SecurityDynamicConfiguration a7 = SecurityDynamicConfiguration.empty(CType.ACTIONGROUPS); - a7.set_meta(new Meta()); - a7.get_meta().setConfig_version(2); - a7.get_meta().setType("actiongroups"); - - for (final Entry r6a : r6as.getCEntries().entrySet()) { - if (r6a.getValue() instanceof List) { - a7.putCEntry(r6a.getKey(), new ActionGroupsV7(r6a.getKey(), (List) r6a.getValue())); - } else { - a7.putCEntry(r6a.getKey(), new ActionGroupsV7(r6a.getKey(), (ActionGroupsV6) r6a.getValue())); - } - } - - return a7; - } - - public static SecurityDynamicConfiguration migrateRoleMappings(SecurityDynamicConfiguration r6rms) - throws MigrationException { - final SecurityDynamicConfiguration rms7 = SecurityDynamicConfiguration.empty(CType.ROLESMAPPING); - rms7.set_meta(new Meta()); - rms7.get_meta().setConfig_version(2); - rms7.get_meta().setType("rolesmapping"); - - for (final Entry r6m : r6rms.getCEntries().entrySet()) { - rms7.putCEntry(r6m.getKey(), new RoleMappingsV7(r6m.getValue())); - } - - return rms7; - } - - public static SecurityDynamicConfiguration migrateAudit(SecurityDynamicConfiguration audit) { - final SecurityDynamicConfiguration migrated = SecurityDynamicConfiguration.empty(CType.AUDIT); - migrated.set_meta(new Meta()); - migrated.get_meta().setConfig_version(2); - migrated.get_meta().setType("audit"); - - for (final Entry entry : audit.getCEntries().entrySet()) { - migrated.putCEntry(entry.getKey(), entry.getValue()); - } - return migrated; - } - - public static SecurityDynamicConfiguration readYaml(File file, Class type) throws IOException { - if (!file.exists()) { - return SecurityDynamicConfiguration.empty(null); - } - - JavaType javaType = DefaultObjectMapper.getTypeFactory().constructParametricType(SecurityDynamicConfiguration.class, type); - return DefaultObjectMapper.YAML_MAPPER.readValue(file, javaType); - } - -} diff --git a/src/main/java/org/opensearch/security/securityconf/MigrationException.java b/src/main/java/org/opensearch/security/securityconf/MigrationException.java deleted file mode 100644 index 5be57aa4b8..0000000000 --- a/src/main/java/org/opensearch/security/securityconf/MigrationException.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2015-2017 floragunn GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.security.securityconf; - -import java.io.IOException; - -public class MigrationException extends IOException { - - public MigrationException() { - super(); - // TODO Auto-generated constructor stub - } - - public MigrationException(String message, Throwable cause) { - super(message, cause); - // TODO Auto-generated constructor stub - } - - public MigrationException(String message) { - super(message); - // TODO Auto-generated constructor stub - } - - public MigrationException(Throwable cause) { - super(cause); - // TODO Auto-generated constructor stub - } - -} diff --git a/src/main/java/org/opensearch/security/securityconf/impl/CType.java b/src/main/java/org/opensearch/security/securityconf/impl/CType.java index 292104e6be..fbd05b372d 100644 --- a/src/main/java/org/opensearch/security/securityconf/impl/CType.java +++ b/src/main/java/org/opensearch/security/securityconf/impl/CType.java @@ -27,7 +27,6 @@ package org.opensearch.security.securityconf.impl; -import java.io.IOException; import java.nio.file.Path; import java.util.Arrays; import java.util.Collections; @@ -35,20 +34,10 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; -import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; -import com.fasterxml.jackson.databind.JavaType; - -import org.opensearch.security.DefaultObjectMapper; -import org.opensearch.security.NonValidatingObjectMapper; import org.opensearch.security.auditlog.config.AuditConfig; -import org.opensearch.security.securityconf.impl.v6.ActionGroupsV6; -import org.opensearch.security.securityconf.impl.v6.ConfigV6; -import org.opensearch.security.securityconf.impl.v6.InternalUserV6; -import org.opensearch.security.securityconf.impl.v6.RoleMappingsV6; -import org.opensearch.security.securityconf.impl.v6.RoleV6; import org.opensearch.security.securityconf.impl.v7.ActionGroupsV7; import org.opensearch.security.securityconf.impl.v7.ConfigV7; import org.opensearch.security.securityconf.impl.v7.InternalUserV7; @@ -79,49 +68,20 @@ public class CType implements Comparable> { private static Map> nameToInstanceMap = new HashMap<>(); private static Map> ordToInstanceMap = new HashMap<>(); - public static final CType ACTIONGROUPS = new CType<>( - "actiongroups", - "action_groups", - ActionGroupsV7.class, - 0, - false, - new OldConfigVersion<>(1, ActionGroupsV6.class, ActionGroupsV7::new) - ); + public static final CType ACTIONGROUPS = new CType<>("actiongroups", "action_groups", ActionGroupsV7.class, 0, false); public static final CType ALLOWLIST = new CType<>("allowlist", "allowlist", AllowlistingSettings.class, 1, true); public static final CType AUDIT = new CType<>("audit", "audit", AuditConfig.class, 2, true); - public static final CType CONFIG = new CType<>( - "config", - "config", - ConfigV7.class, - 3, - false, - new OldConfigVersion<>(1, ConfigV6.class, ConfigV7::new, ConfigV6::convertMapKeyToV7) - ); + public static final CType CONFIG = new CType<>("config", "config", ConfigV7.class, 3, false); public static final CType INTERNALUSERS = new CType<>( "internalusers", "internal_users", InternalUserV7.class, 4, - false, - new OldConfigVersion<>(1, InternalUserV6.class, InternalUserV7::new) + false ); public static final CType NODESDN = new CType<>("nodesdn", "nodes_dn", NodesDn.class, 5, true); - public static final CType ROLES = new CType<>( - "roles", - "roles", - RoleV7.class, - 6, - false, - new OldConfigVersion<>(1, RoleV6.class, RoleV7::new) - ); - public static final CType ROLESMAPPING = new CType<>( - "rolesmapping", - "roles_mapping", - RoleMappingsV7.class, - 7, - false, - new OldConfigVersion<>(1, RoleMappingsV6.class, RoleMappingsV7::new) - ); + public static final CType ROLES = new CType<>("roles", "roles", RoleV7.class, 6, false); + public static final CType ROLESMAPPING = new CType<>("rolesmapping", "roles_mapping", RoleMappingsV7.class, 7, false); public static final CType TENANTS = new CType<>("tenants", "tenants", TenantV7.class, 8, false); public static final CType WHITELIST = new CType<>("whitelist", "whitelist", WhitelistingSettings.class, 9, true); @@ -130,26 +90,16 @@ public class CType implements Comparable> { private final Class configClass; private final String configFileName; private final boolean emptyIfMissing; - private final OldConfigVersion[] oldConfigVersions; private final int id; - @SafeVarargs @SuppressWarnings("varargs") - private CType( - String name, - String configFileName, - Class configClass, - int id, - boolean emptyIfMissing, - OldConfigVersion... oldConfigVersions - ) { + private CType(String name, String configFileName, Class configClass, int id, boolean emptyIfMissing) { this.name = name; this.nameUpperCase = name.toUpperCase(); this.configClass = configClass; this.id = id; this.configFileName = configFileName + ".yml"; this.emptyIfMissing = emptyIfMissing; - this.oldConfigVersions = oldConfigVersions; allSet.add(this); nameToInstanceMap.put(name, this); @@ -212,16 +162,6 @@ public String configFileName() { return configFileName; } - public OldConfigVersion findOldConfigVersion(int versionNumber) { - for (OldConfigVersion oldConfigVersion : this.oldConfigVersions) { - if (versionNumber == oldConfigVersion.versionNumber) { - return oldConfigVersion; - } - } - - return null; - } - @Override public int compareTo(CType cType) { return this.id - cType.id; @@ -245,65 +185,4 @@ public boolean equals(Object other) { public int hashCode() { return id; } - - public static class OldConfigVersion { - private final int versionNumber; - private final Class oldType; - private final Function entryConversionFunction; - private final Function mapKeyConversionFunction; - - public OldConfigVersion(int versionNumber, Class oldType, Function entryConversionFunction) { - this.versionNumber = versionNumber; - this.oldType = oldType; - this.entryConversionFunction = entryConversionFunction; - this.mapKeyConversionFunction = Function.identity(); - } - - public OldConfigVersion( - int versionNumber, - Class oldType, - Function entryConversionFunction, - Function mapKeyConversionFunction - ) { - this.versionNumber = versionNumber; - this.oldType = oldType; - this.entryConversionFunction = entryConversionFunction; - this.mapKeyConversionFunction = mapKeyConversionFunction; - } - - public int getVersionNumber() { - return versionNumber; - } - - public Class getOldType() { - return oldType; - } - - public Function getEntryConversionFunction() { - return entryConversionFunction; - } - - public SecurityDynamicConfiguration parseJson(CType ctype, String json, boolean acceptInvalid) - throws IOException { - JavaType javaType = DefaultObjectMapper.getTypeFactory().constructParametricType(SecurityDynamicConfiguration.class, oldType); - - if (acceptInvalid) { - return convert(NonValidatingObjectMapper.readValue(json, javaType), ctype); - } else { - return convert(DefaultObjectMapper.readValue(json, javaType), ctype); - } - } - - public SecurityDynamicConfiguration convert(SecurityDynamicConfiguration oldConfig, CType ctype) { - SecurityDynamicConfiguration newConfig = SecurityDynamicConfiguration.empty(ctype); - newConfig.setAutoConvertedFrom(oldConfig); - - for (Map.Entry oldEntry : oldConfig.getCEntries().entrySet()) { - newConfig.putCEntry(mapKeyConversionFunction.apply(oldEntry.getKey()), entryConversionFunction.apply(oldEntry.getValue())); - } - - return newConfig; - } - - } } diff --git a/src/main/java/org/opensearch/security/securityconf/impl/SecurityDynamicConfiguration.java b/src/main/java/org/opensearch/security/securityconf/impl/SecurityDynamicConfiguration.java index bc03d59500..a56d8e8aa3 100644 --- a/src/main/java/org/opensearch/security/securityconf/impl/SecurityDynamicConfiguration.java +++ b/src/main/java/org/opensearch/security/securityconf/impl/SecurityDynamicConfiguration.java @@ -64,9 +64,7 @@ public class SecurityDynamicConfiguration implements ToXContent { private long seqNo = -1; private long primaryTerm = -1; private CType ctype; - private int version = -1; - - private SecurityDynamicConfiguration autoConvertedFrom; + private int version = CURRENT_VERSION; public static SecurityDynamicConfiguration empty(CType ctype) { return new SecurityDynamicConfiguration(ctype); @@ -96,22 +94,10 @@ public static SecurityDynamicConfiguration fromJson( ) throws IOException { SecurityDynamicConfiguration sdc = null; if (ctype != null) { - CType.OldConfigVersion oldConfigVersion = ctype.findOldConfigVersion(version); - - if (oldConfigVersion != null) { - sdc = oldConfigVersion.parseJson(ctype, json, acceptInvalid); - if (sdc._meta == null) { - sdc._meta = new Meta(); - sdc._meta.setConfig_version(CURRENT_VERSION); - sdc._meta.setType(ctype.toLCString()); - } - version = CURRENT_VERSION; - } else { - sdc = DefaultObjectMapper.readValue( - json, - DefaultObjectMapper.getTypeFactory().constructParametricType(SecurityDynamicConfiguration.class, ctype.getConfigClass()) - ); - } + sdc = DefaultObjectMapper.readValue( + json, + DefaultObjectMapper.getTypeFactory().constructParametricType(SecurityDynamicConfiguration.class, ctype.getConfigClass()) + ); validate(sdc, version, ctype); @@ -127,40 +113,6 @@ public static SecurityDynamicConfiguration fromJson( return sdc; } - /** - * Creates the SecurityDynamicConfiguration instance from the given JsonNode. If a config version is found, which - * is not the current one, no conversion will be performed. The configuration will be returned as it was found. - */ - public static SecurityDynamicConfiguration fromNodeWithoutAutoConversion( - JsonNode jsonNode, - CType ctype, - int version, - long seqNo, - long primaryTerm - ) throws IOException { - Class configClass; - CType.OldConfigVersion oldConfigVersion = ctype.findOldConfigVersion(version); - - if (oldConfigVersion != null) { - configClass = oldConfigVersion.getOldType(); - } else { - configClass = ctype.getConfigClass(); - } - - SecurityDynamicConfiguration sdc = DefaultObjectMapper.convertValue( - jsonNode, - DefaultObjectMapper.getTypeFactory().constructParametricType(SecurityDynamicConfiguration.class, configClass) - ); - - validate(sdc, version, ctype); - - sdc.seqNo = seqNo; - sdc.primaryTerm = primaryTerm; - sdc.version = version; - - return sdc; - } - /** * For testing only */ @@ -174,21 +126,15 @@ public static SecurityDynamicConfiguration fromMap(Map ma } public static void validate(SecurityDynamicConfiguration sdc, int version, CType ctype) throws IOException { - if (version < 2 && sdc.get_meta() != null) { - throw new IOException("A version of " + version + " can not have a _meta key for " + ctype); + if (version < 2) { + throw new IOException("Config version " + version + " is not supported"); } - if (version >= 2 && sdc.get_meta() == null) { + if (sdc.get_meta() == null) { throw new IOException("A version of " + version + " must have a _meta key for " + ctype); } - if (version < 2 - && ctype == CType.CONFIG - && (sdc.getCEntries().size() != 1 || !sdc.getCEntries().keySet().contains("opendistro_security"))) { - throw new IOException("A version of " + version + " must have a single toplevel key named 'opendistro_security' for " + ctype); - } - - if (version >= 2 && ctype == CType.CONFIG && (sdc.getCEntries().size() != 1 || !sdc.getCEntries().keySet().contains("config"))) { + if (ctype == CType.CONFIG && (sdc.getCEntries().size() != 1 || !sdc.getCEntries().keySet().contains("config"))) { throw new IOException("A version of " + version + " must have a single toplevel key named 'config' for " + ctype); } @@ -355,16 +301,6 @@ public int getVersion() { return version; } - @JsonIgnore - public SecurityDynamicConfiguration getAutoConvertedFrom() { - return autoConvertedFrom; - } - - @JsonIgnore - public void setAutoConvertedFrom(SecurityDynamicConfiguration autoConvertedFrom) { - this.autoConvertedFrom = autoConvertedFrom; - } - @JsonIgnore public Class getImplementingClass() { return getCType() == null ? null : getCType().getConfigClass(); diff --git a/src/main/java/org/opensearch/security/securityconf/impl/v6/ActionGroupsV6.java b/src/main/java/org/opensearch/security/securityconf/impl/v6/ActionGroupsV6.java deleted file mode 100644 index 45fced168c..0000000000 --- a/src/main/java/org/opensearch/security/securityconf/impl/v6/ActionGroupsV6.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2015-2017 floragunn GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ -package org.opensearch.security.securityconf.impl.v6; - -import java.util.Collections; -import java.util.List; - -import com.fasterxml.jackson.annotation.JsonIgnore; - -import org.opensearch.security.securityconf.Hideable; - -public class ActionGroupsV6 implements Hideable { - - private boolean readonly; - private boolean hidden; - private List permissions = Collections.emptyList(); - - public ActionGroupsV6() { - super(); - } - - @JsonIgnore - public boolean isReserved() { - return readonly; - } - - public boolean isReadonly() { - return readonly; - } - - public void setReadonly(boolean readonly) { - this.readonly = readonly; - } - - public boolean isHidden() { - return hidden; - } - - public void setHidden(boolean hidden) { - this.hidden = hidden; - } - - public List getPermissions() { - return permissions; - } - - public void setPermissions(List permissions) { - this.permissions = permissions; - } - - @Override - public String toString() { - return "ActionGroups [readonly=" + readonly + ", hidden=" + hidden + ", permissions=" + permissions + "]"; - } - -} 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 deleted file mode 100644 index 59b759970e..0000000000 --- a/src/main/java/org/opensearch/security/securityconf/impl/v6/ConfigV6.java +++ /dev/null @@ -1,464 +0,0 @@ -/* - * Copyright 2015-2017 floragunn GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.security.securityconf.impl.v6; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.regex.Pattern; - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException; - -import org.opensearch.security.DefaultObjectMapper; -import org.opensearch.security.auth.internal.InternalAuthenticationBackend; -import org.opensearch.security.securityconf.impl.CType; -import org.opensearch.security.securityconf.impl.DashboardSignInOption; -import org.opensearch.security.setting.DeprecatedSettings; - -public class ConfigV6 { - - public static String convertMapKeyToV7(String mapKey) { - if (mapKey.equals("opendistro_security")) { - return CType.CONFIG.toLCString(); - } else { - return mapKey; - } - } - - public Dynamic dynamic; - - @Override - public String toString() { - return "Config [dynamic=" + dynamic + "]"; - } - - public static class Dynamic { - - public String filtered_alias_mode = "warn"; - public boolean disable_rest_auth; - public boolean disable_intertransport_auth; - public boolean respect_request_indices_options; - public String license; - public Kibana kibana = new Kibana(); - public Http http = new Http(); - public Authc authc = new Authc(); - public Authz authz = new Authz(); - public AuthFailureListeners auth_failure_listeners = new AuthFailureListeners(); - public boolean do_not_fail_on_forbidden; - public boolean multi_rolespan_enabled; - public String hosts_resolver_mode = "ip-only"; - public String transport_userrname_attribute; - public boolean do_not_fail_on_forbidden_empty; - - @Override - public String toString() { - return "Dynamic [filtered_alias_mode=" - + filtered_alias_mode - + ", kibana=" - + kibana - + ", http=" - + http - + ", authc=" - + authc - + ", authz=" - + authz - + "]"; - } - } - - 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"; - public boolean do_not_fail_on_forbidden; - @JsonInclude(JsonInclude.Include.NON_NULL) - public List sign_in_options = Arrays.asList(DashboardSignInOption.BASIC); - - @Override - public String toString() { - return "Kibana [multitenancy_enabled=" - + multitenancy_enabled - + ", server_username=" - + server_username - + ", opendistro_role=" - + opendistro_role - + ", index=" - + index - + ", do_not_fail_on_forbidden=" - + do_not_fail_on_forbidden - + ", sign_in_options=" - + sign_in_options - + "]"; - } - - } - - public static class Http { - public boolean anonymous_auth_enabled = false; - public Xff xff = new Xff(); - - @Override - public String toString() { - return "Http [anonymous_auth_enabled=" + anonymous_auth_enabled + ", xff=" + xff + "]"; - } - - } - - public static class AuthFailureListeners { - @JsonIgnore - private final Map listeners = new HashMap<>(); - - @JsonAnySetter - void setListeners(String key, AuthFailureListener value) { - listeners.put(key, value); - } - - @JsonAnyGetter - public Map getListeners() { - return listeners; - } - - } - - public static class AuthFailureListener { - public String type; - public String authentication_backend; - public int allowed_tries = 10; - public int time_window_seconds = 60 * 60; - public int block_expiry_seconds = 60 * 10; - public int max_blocked_clients = 100_000; - public int max_tracked_clients = 100_000; - - public AuthFailureListener() { - super(); - } - - @JsonIgnore - public String asJson() { - try { - return DefaultObjectMapper.writeValueAsString(this, false); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - } - } - - public static class Xff { - @JsonInclude(JsonInclude.Include.NON_NULL) - public boolean enabled = true; - public String internalProxies = Pattern.compile( - "10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" - + "192\\.168\\.\\d{1,3}\\.\\d{1,3}|" - + "169\\.254\\.\\d{1,3}\\.\\d{1,3}|" - + "127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" - + "172\\.1[6-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" - + "172\\.2[0-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" - + "172\\.3[0-1]{1}\\.\\d{1,3}\\.\\d{1,3}" - ).toString(); - public String remoteIpHeader = "X-Forwarded-For"; - public String proxiesHeader = "X-Forwarded-By"; - public String trustedProxies; - - @Override - public String toString() { - return "Xff [enabled=" - + enabled - + ", internalProxies=" - + internalProxies - + ", remoteIpHeader=" - + remoteIpHeader - + ", proxiesHeader=" - + proxiesHeader - + ", trustedProxies=" - + trustedProxies - + "]"; - } - - } - - public static class Authc { - - @JsonIgnore - private final Map domains = new HashMap<>(); - - @JsonAnySetter - void setDomains(String key, AuthcDomain value) { - domains.put(key, value); - } - - @JsonAnyGetter - public Map getDomains() { - return domains; - } - - @Override - public String toString() { - return "Authc [domains=" + domains + "]"; - } - - } - - public static class AuthcDomain { - @JsonInclude(JsonInclude.Include.NON_NULL) - public boolean http_enabled = true; - @JsonInclude(JsonInclude.Include.NON_NULL) - public boolean enabled = true; - public int order = 0; - public HttpAuthenticator http_authenticator = new HttpAuthenticator(); - public AuthcBackend authentication_backend = new AuthcBackend(); - - @Override - public String toString() { - return "AuthcDomain [http_enabled=" - + http_enabled - + ", enabled=" - + enabled - + ", order=" - + order - + ", http_authenticator=" - + http_authenticator - + ", authentication_backend=" - + authentication_backend - + "]"; - } - - @JsonAnySetter - public void unknownPropertiesHandler(String name, Object value) throws JsonMappingException { - switch (name) { - case "transport_enabled": - DeprecatedSettings.logCustomDeprecationMessage( - String.format( - "In AuthcDomain, using http_authenticator=%s, authentication_backend=%s", - http_authenticator, - authentication_backend - ), - name - ); - break; - default: - throw new UnrecognizedPropertyException( - null, - "Unrecognized field " + name + " present in the input data for AuthcDomain config", - null, - AuthcDomain.class, - name, - null - ); - } - } - - } - - public static class HttpAuthenticator { - @JsonInclude(JsonInclude.Include.NON_NULL) - public boolean challenge = true; - public String type; - public Map config = Collections.emptyMap(); - - @JsonIgnore - public String configAsJson() { - try { - return DefaultObjectMapper.writeValueAsString(config, false); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - } - - @Override - public String toString() { - return "HttpAuthenticator [challenge=" + challenge + ", type=" + type + ", config=" + config + "]"; - } - - } - - public static class AuthzBackend { - public String type = "noop"; - public Map config = Collections.emptyMap(); - - @JsonIgnore - public String configAsJson() { - try { - return DefaultObjectMapper.writeValueAsString(config, false); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - } - - @Override - public String toString() { - return "AuthzBackend [type=" + type + ", config=" + config + "]"; - } - - } - - public static class AuthcBackend { - public String type = InternalAuthenticationBackend.class.getName(); - public Map config = Collections.emptyMap(); - - @JsonIgnore - public String configAsJson() { - try { - return DefaultObjectMapper.writeValueAsString(config, false); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - } - - @Override - public String toString() { - return "AuthcBackend [type=" + type + ", config=" + config + "]"; - } - - } - - public static class Authz { - @JsonIgnore - private final Map domains = new HashMap<>(); - - @JsonAnySetter - void setDomains(String key, AuthzDomain value) { - domains.put(key, value); - } - - @JsonAnyGetter - public Map getDomains() { - return domains; - } - - @Override - public String toString() { - return "Authz [domains=" + domains + "]"; - } - - } - - public static class AuthzDomain { - @JsonInclude(JsonInclude.Include.NON_NULL) - public boolean http_enabled = true; - @JsonInclude(JsonInclude.Include.NON_NULL) - public boolean enabled = true; - public AuthzBackend authorization_backend = new AuthzBackend(); - - @Override - public String toString() { - return "AuthzDomain [http_enabled=" - + http_enabled - + ", enabled=" - + enabled - + ", authorization_backend=" - + authorization_backend - + "]"; - } - - @JsonAnySetter - public void unknownPropertiesHandler(String name, Object value) throws JsonMappingException { - switch (name) { - case "transport_enabled": - DeprecatedSettings.logCustomDeprecationMessage( - String.format("In AuthzDomain, using authorization_backend=%s", authorization_backend), - name - ); - break; - default: - throw new UnrecognizedPropertyException( - null, - "Unrecognized field " + name + " present in the input data for AuthzDomain config", - null, - AuthzDomain.class, - name, - null - ); - } - } - - } - - public static class OnBehalfOfSettings { - @JsonProperty("enabled") - private Boolean oboEnabled = Boolean.FALSE; - @JsonProperty("signing_key") - private String signingKey; - @JsonProperty("encryption_key") - private String encryptionKey; - - @JsonIgnore - public String configAsJson() { - try { - return DefaultObjectMapper.writeValueAsString(this, false); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - } - - public Boolean getOboEnabled() { - return oboEnabled; - } - - public void setOboEnabled(Boolean oboEnabled) { - this.oboEnabled = oboEnabled; - } - - public String getSigningKey() { - return signingKey; - } - - public void setSigningKey(String signingKey) { - this.signingKey = signingKey; - } - - public String getEncryptionKey() { - return encryptionKey; - } - - public void setEncryptionKey(String encryptionKey) { - this.encryptionKey = encryptionKey; - } - - @Override - public String toString() { - return "OnBehalfOfSettings [ enabled=" + oboEnabled + ", signing_key=" + signingKey + ", encryption_key=" + encryptionKey + "]"; - } - } - -} diff --git a/src/main/java/org/opensearch/security/securityconf/impl/v6/InternalUserV6.java b/src/main/java/org/opensearch/security/securityconf/impl/v6/InternalUserV6.java deleted file mode 100644 index 0db727ad99..0000000000 --- a/src/main/java/org/opensearch/security/securityconf/impl/v6/InternalUserV6.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2015-2017 floragunn GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.security.securityconf.impl.v6; - -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import com.fasterxml.jackson.annotation.JsonIgnore; - -import org.opensearch.security.securityconf.Hashed; -import org.opensearch.security.securityconf.Hideable; - -public class InternalUserV6 implements Hideable, Hashed { - - private String hash; - private boolean readonly; - private boolean hidden; - private List roles = Collections.emptyList(); - private Map attributes = Collections.emptyMap(); - private String username; - - public InternalUserV6( - String hash, - boolean readonly, - boolean hidden, - List roles, - Map attributes, - String username - ) { - super(); - this.hash = hash; - this.readonly = readonly; - this.hidden = hidden; - this.roles = roles; - this.attributes = attributes; - this.username = username; - } - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public InternalUserV6() { - super(); - // default constructor - } - - public String getHash() { - return hash; - } - - public void setHash(String hash) { - this.hash = hash; - } - - public void setPassword(String password) { - // no-op setter. Due to a bug in 6.x, empty "password" may be saved to the internalusers doc. Ignore it. - } - - public boolean isReadonly() { - return readonly; - } - - public void setReadonly(boolean readonly) { - this.readonly = readonly; - } - - public boolean isHidden() { - return hidden; - } - - public void setHidden(boolean hidden) { - this.hidden = hidden; - } - - public List getRoles() { - return roles; - } - - public void setRoles(List roles) { - this.roles = roles; - } - - public Map getAttributes() { - return attributes; - } - - public void setAttributes(Map attributes) { - this.attributes = attributes; - } - - @Override - public String toString() { - return "SgInternalUser [hash=" - + hash - + ", readonly=" - + readonly - + ", hidden=" - + hidden - + ", roles=" - + roles - + ", attributes=" - + attributes - + "]"; - } - - @JsonIgnore - public boolean isReserved() { - return readonly; - } - - @Override - @JsonIgnore - public void clearHash() { - hash = ""; - } - -} diff --git a/src/main/java/org/opensearch/security/securityconf/impl/v6/RoleMappingsV6.java b/src/main/java/org/opensearch/security/securityconf/impl/v6/RoleMappingsV6.java deleted file mode 100644 index f78c2f4305..0000000000 --- a/src/main/java/org/opensearch/security/securityconf/impl/v6/RoleMappingsV6.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2015-2017 floragunn GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.security.securityconf.impl.v6; - -import java.util.Collections; -import java.util.List; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; - -import org.opensearch.security.securityconf.Hideable; -import org.opensearch.security.securityconf.RoleMappings; - -public class RoleMappingsV6 extends RoleMappings implements Hideable { - - private boolean readonly; - private boolean hidden; - private List backendroles = Collections.emptyList(); - private List andBackendroles = Collections.emptyList(); - - public RoleMappingsV6() { - super(); - } - - public boolean isReadonly() { - return readonly; - } - - public void setReadonly(boolean readonly) { - this.readonly = readonly; - } - - public boolean isHidden() { - return hidden; - } - - public void setHidden(boolean hidden) { - this.hidden = hidden; - } - - public List getBackendroles() { - return backendroles; - } - - public void setBackendroles(List backendroles) { - this.backendroles = backendroles; - } - - @JsonProperty(value = "and_backendroles") - public List getAndBackendroles() { - return andBackendroles; - } - - public void setAndBackendroles(List andBackendroles) { - this.andBackendroles = andBackendroles; - } - - @Override - public String toString() { - return "RoleMappings [readonly=" - + readonly - + ", hidden=" - + hidden - + ", backendroles=" - + backendroles - + ", hosts=" - + getHosts() - + ", users=" - + getUsers() - + ", andBackendroles=" - + andBackendroles - + "]"; - } - - @JsonIgnore - public boolean isReserved() { - return readonly; - } - -} diff --git a/src/main/java/org/opensearch/security/securityconf/impl/v6/RoleV6.java b/src/main/java/org/opensearch/security/securityconf/impl/v6/RoleV6.java deleted file mode 100644 index 8bc8b46249..0000000000 --- a/src/main/java/org/opensearch/security/securityconf/impl/v6/RoleV6.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2015-2017 floragunn GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.security.securityconf.impl.v6; - -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonIgnore; - -import org.opensearch.security.securityconf.Hideable; - -public class RoleV6 implements Hideable { - - private boolean readonly; - private boolean hidden; - private List cluster = Collections.emptyList(); - private Map tenants = Collections.emptyMap(); - private Map indices = Collections.emptyMap(); - - public static class Index { - - @JsonIgnore - private final Map> types = new HashMap<>(); - - @JsonAnySetter - void setTypes0(String key, List value) { - types.put(key, value); - } - - @JsonAnyGetter - public Map> getTypes() { - return types; - } - - private String _dls_; - private List _fls_; - private List _masked_fields_; - - public String get_dls_() { - return _dls_; - } - - public List get_fls_() { - return _fls_; - } - - public List get_masked_fields_() { - return _masked_fields_; - } - - @Override - public String toString() { - return "Index [types=" + types + ", _dls_=" + _dls_ + ", _fls_=" + _fls_ + ", _masked_fields_=" + _masked_fields_ + "]"; - } - - } - - public boolean isReadonly() { - return readonly; - } - - public void setReadonly(boolean readonly) { - this.readonly = readonly; - } - - public boolean isHidden() { - return hidden; - } - - public void setHidden(boolean hidden) { - this.hidden = hidden; - } - - public List getCluster() { - return cluster; - } - - public void setCluster(List cluster) { - this.cluster = cluster; - } - - public Map getTenants() { - return tenants; - } - - public void setTenants(Map tenants) { - this.tenants = tenants; - } - - public Map getIndices() { - return indices; - } - - public void setIndices(Map indices) { - this.indices = indices; - } - - @Override - public String toString() { - return "Role [readonly=" - + readonly - + ", hidden=" - + hidden - + ", cluster=" - + cluster - + ", tenants=" - + tenants - + ", indices=" - + indices - + "]"; - } - - @JsonIgnore - public boolean isReserved() { - return readonly; - } - -} diff --git a/src/main/java/org/opensearch/security/securityconf/impl/v7/ActionGroupsV7.java b/src/main/java/org/opensearch/security/securityconf/impl/v7/ActionGroupsV7.java index 44382cb07e..0a86f2672e 100644 --- a/src/main/java/org/opensearch/security/securityconf/impl/v7/ActionGroupsV7.java +++ b/src/main/java/org/opensearch/security/securityconf/impl/v7/ActionGroupsV7.java @@ -34,7 +34,6 @@ import org.opensearch.security.securityconf.Hideable; import org.opensearch.security.securityconf.StaticDefinable; -import org.opensearch.security.securityconf.impl.v6.ActionGroupsV6; public class ActionGroupsV7 implements Hideable, StaticDefinable { @@ -50,22 +49,6 @@ public ActionGroupsV7() { super(); } - public ActionGroupsV7(String agName, ActionGroupsV6 ag6) { - reserved = ag6.isReserved(); - hidden = ag6.isHidden(); - allowed_actions = ag6.getPermissions(); - type = agName.toLowerCase().contains("cluster") ? "cluster" : "index"; - description = "Migrated from v6"; - } - - public ActionGroupsV7(ActionGroupsV6 ag6) { - reserved = ag6.isReserved(); - hidden = ag6.isHidden(); - allowed_actions = ag6.getPermissions(); - type = "unknown"; - description = "Migrated from v6"; - } - public ActionGroupsV7(String key, List allowed_actions) { this.allowed_actions = allowed_actions; type = "unknown"; 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 fb406fd83a..77fb973a52 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 @@ -33,7 +33,6 @@ import java.util.List; import java.util.Map; import java.util.regex.Pattern; -import java.util.stream.Collectors; import com.fasterxml.jackson.annotation.JsonAnyGetter; import com.fasterxml.jackson.annotation.JsonAnySetter; @@ -47,7 +46,6 @@ import org.opensearch.security.DefaultObjectMapper; import org.opensearch.security.auth.internal.InternalAuthenticationBackend; import org.opensearch.security.securityconf.impl.DashboardSignInOption; -import org.opensearch.security.securityconf.impl.v6.ConfigV6; import org.opensearch.security.setting.DeprecatedSettings; public class ConfigV7 { @@ -64,66 +62,6 @@ public ConfigV7() { super(); } - public ConfigV7(ConfigV6 c6) { - dynamic = new Dynamic(); - - dynamic.filtered_alias_mode = c6.dynamic.filtered_alias_mode; - dynamic.disable_rest_auth = c6.dynamic.disable_rest_auth; - dynamic.disable_intertransport_auth = c6.dynamic.disable_intertransport_auth; - dynamic.respect_request_indices_options = c6.dynamic.respect_request_indices_options; - dynamic.license = c6.dynamic.license; - dynamic.do_not_fail_on_forbidden = c6.dynamic.do_not_fail_on_forbidden || c6.dynamic.kibana.do_not_fail_on_forbidden; - dynamic.do_not_fail_on_forbidden_empty = c6.dynamic.do_not_fail_on_forbidden_empty; - dynamic.multi_rolespan_enabled = c6.dynamic.multi_rolespan_enabled; - dynamic.hosts_resolver_mode = c6.dynamic.hosts_resolver_mode; - dynamic.transport_userrname_attribute = c6.dynamic.transport_userrname_attribute; - - dynamic.kibana = new Kibana(); - - 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.kibana.sign_in_options = c6.dynamic.kibana.sign_in_options; - - dynamic.http = new Http(); - - dynamic.http.anonymous_auth_enabled = c6.dynamic.http.anonymous_auth_enabled; - - dynamic.http.xff = new Xff(); - - dynamic.http.xff.enabled = c6.dynamic.http.xff.enabled; - dynamic.http.xff.internalProxies = c6.dynamic.http.xff.internalProxies; - dynamic.http.xff.remoteIpHeader = c6.dynamic.http.xff.remoteIpHeader; - - dynamic.authc = new Authc(); - - dynamic.authc.domains.putAll( - c6.dynamic.authc.getDomains() - .entrySet() - .stream() - .collect(Collectors.toMap(entry -> entry.getKey(), entry -> new AuthcDomain(entry.getValue()))) - ); - - dynamic.authz = new Authz(); - - dynamic.authz.domains.putAll( - c6.dynamic.authz.getDomains() - .entrySet() - .stream() - .collect(Collectors.toMap(entry -> entry.getKey(), entry -> new AuthzDomain(entry.getValue()))) - ); - - dynamic.auth_failure_listeners = new AuthFailureListeners(); - dynamic.auth_failure_listeners.listeners.putAll( - c6.dynamic.auth_failure_listeners.getListeners() - .entrySet() - .stream() - .collect(Collectors.toMap(entry -> entry.getKey(), entry -> new AuthFailureListener(entry.getValue()))) - ); - } - @Override public String toString() { return "Config [dynamic=" + dynamic + "]"; @@ -243,17 +181,6 @@ public AuthFailureListener() { super(); } - public AuthFailureListener(ConfigV6.AuthFailureListener v6) { - super(); - this.type = v6.type; - this.authentication_backend = v6.authentication_backend; - this.allowed_tries = v6.allowed_tries; - this.time_window_seconds = v6.time_window_seconds; - this.block_expiry_seconds = v6.block_expiry_seconds; - this.max_blocked_clients = v6.max_blocked_clients; - this.max_tracked_clients = v6.max_tracked_clients; - } - public AuthFailureListener( String type, String authentication_backend, @@ -341,18 +268,6 @@ public AuthcDomain() { super(); } - public AuthcDomain(ConfigV6.AuthcDomain v6) { - super(); - http_enabled = v6.http_enabled && v6.enabled; - // if(v6.enabled)vv { - // http_enabled = true; - // } - order = v6.order; - http_authenticator = new HttpAuthenticator(v6.http_authenticator); - authentication_backend = new AuthcBackend(v6.authentication_backend); - description = "Migrated from v6"; - } - @Override public String toString() { return "AuthcDomain [http_enabled=" @@ -405,12 +320,6 @@ public HttpAuthenticator() { super(); } - public HttpAuthenticator(ConfigV6.HttpAuthenticator v6) { - this.challenge = v6.challenge; - this.type = v6.type; - this.config = v6.config; - } - @JsonIgnore public String configAsJson() { try { @@ -435,11 +344,6 @@ public AuthzBackend() { super(); } - public AuthzBackend(ConfigV6.AuthzBackend v6) { - this.type = v6.type; - this.config = v6.config; - } - @JsonIgnore public String configAsJson() { try { @@ -464,11 +368,6 @@ public AuthcBackend() { super(); } - public AuthcBackend(ConfigV6.AuthcBackend v6) { - this.type = v6.type; - this.config = v6.config; - } - @JsonIgnore public String configAsJson() { try { @@ -516,12 +415,6 @@ public AuthzDomain() { super(); } - public AuthzDomain(ConfigV6.AuthzDomain v6) { - http_enabled = v6.http_enabled && v6.enabled; - authorization_backend = new AuthzBackend(v6.authorization_backend); - description = "Migrated from v6"; - } - @Override public String toString() { return "AuthzDomain [http_enabled=" diff --git a/src/main/java/org/opensearch/security/securityconf/impl/v7/InternalUserV7.java b/src/main/java/org/opensearch/security/securityconf/impl/v7/InternalUserV7.java index 8f10df04a4..cf8212b92f 100644 --- a/src/main/java/org/opensearch/security/securityconf/impl/v7/InternalUserV7.java +++ b/src/main/java/org/opensearch/security/securityconf/impl/v7/InternalUserV7.java @@ -37,7 +37,6 @@ import org.opensearch.security.securityconf.Hashed; import org.opensearch.security.securityconf.Hideable; import org.opensearch.security.securityconf.StaticDefinable; -import org.opensearch.security.securityconf.impl.v6.InternalUserV6; public class InternalUserV7 implements Hideable, Hashed, StaticDefinable { @@ -88,15 +87,6 @@ public InternalUserV7() { // default constructor } - public InternalUserV7(InternalUserV6 u6) { - hash = u6.getHash(); - reserved = u6.isReserved(); - hidden = u6.isHidden(); - backend_roles = u6.getRoles(); - attributes = u6.getAttributes(); - description = "Migrated from v6"; - } - public String getHash() { return hash; } diff --git a/src/main/java/org/opensearch/security/securityconf/impl/v7/RoleMappingsV7.java b/src/main/java/org/opensearch/security/securityconf/impl/v7/RoleMappingsV7.java index cc53a0b9b1..c9d15bc42f 100644 --- a/src/main/java/org/opensearch/security/securityconf/impl/v7/RoleMappingsV7.java +++ b/src/main/java/org/opensearch/security/securityconf/impl/v7/RoleMappingsV7.java @@ -32,7 +32,6 @@ import org.opensearch.security.securityconf.Hideable; import org.opensearch.security.securityconf.RoleMappings; -import org.opensearch.security.securityconf.impl.v6.RoleMappingsV6; public class RoleMappingsV7 extends RoleMappings implements Hideable { @@ -46,17 +45,6 @@ public RoleMappingsV7() { super(); } - public RoleMappingsV7(RoleMappingsV6 roleMappingsV6) { - super(); - this.reserved = roleMappingsV6.isReserved(); - this.hidden = roleMappingsV6.isHidden(); - this.backend_roles = roleMappingsV6.getBackendroles(); - this.and_backend_roles = roleMappingsV6.getAndBackendroles(); - this.description = "Migrated from v6"; - setHosts(roleMappingsV6.getHosts()); - setUsers(roleMappingsV6.getUsers()); - } - public boolean isReserved() { return reserved; } diff --git a/src/main/java/org/opensearch/security/securityconf/impl/v7/RoleV7.java b/src/main/java/org/opensearch/security/securityconf/impl/v7/RoleV7.java index 229cae0e6f..2b2da40927 100644 --- a/src/main/java/org/opensearch/security/securityconf/impl/v7/RoleV7.java +++ b/src/main/java/org/opensearch/security/securityconf/impl/v7/RoleV7.java @@ -27,19 +27,13 @@ package org.opensearch.security.securityconf.impl.v7; -import java.util.ArrayList; import java.util.Collections; -import java.util.HashSet; import java.util.List; -import java.util.Map.Entry; -import java.util.Set; -import java.util.stream.Collectors; import com.fasterxml.jackson.annotation.JsonProperty; import org.opensearch.security.securityconf.Hideable; import org.opensearch.security.securityconf.StaticDefinable; -import org.opensearch.security.securityconf.impl.v6.RoleV6; public class RoleV7 implements Hideable, StaticDefinable { @@ -56,49 +50,6 @@ public RoleV7() { } - public RoleV7(RoleV6 roleV6) { - this.reserved = roleV6.isReserved(); - this.hidden = roleV6.isHidden(); - this.description = "Migrated from v6 (all types mapped)"; - this.cluster_permissions = roleV6.getCluster(); - index_permissions = new ArrayList<>(); - tenant_permissions = new ArrayList<>(); - - for (Entry v6i : roleV6.getIndices().entrySet()) { - index_permissions.add(new Index(v6i.getKey(), v6i.getValue())); - } - - // rw tenants - List rwTenants = roleV6.getTenants() - .entrySet() - .stream() - .filter(e -> "rw".equalsIgnoreCase(e.getValue())) - .map(e -> e.getKey()) - .collect(Collectors.toList()); - - if (rwTenants != null && !rwTenants.isEmpty()) { - Tenant t = new Tenant(); - t.setAllowed_actions(Collections.singletonList("kibana_all_write")); - t.setTenant_patterns(rwTenants); - tenant_permissions.add(t); - } - - List roTenants = roleV6.getTenants() - .entrySet() - .stream() - .filter(e -> "ro".equalsIgnoreCase(e.getValue())) - .map(e -> e.getKey()) - .collect(Collectors.toList()); - - if (roTenants != null && !roTenants.isEmpty()) { - Tenant t = new Tenant(); - t.setAllowed_actions(Collections.singletonList("kibana_all_read")); - t.setTenant_patterns(roTenants); - tenant_permissions.add(t); - } - - } - public static class Index { private List index_patterns = Collections.emptyList(); @@ -107,19 +58,6 @@ public static class Index { private List masked_fields = Collections.emptyList(); private List allowed_actions = Collections.emptyList(); - public Index(String pattern, RoleV6.Index v6Index) { - super(); - index_patterns = Collections.singletonList(pattern); - dls = v6Index.get_dls_(); - fls = v6Index.get_fls_(); - masked_fields = v6Index.get_masked_fields_(); - Set tmpActions = new HashSet<>(); - for (Entry> type : v6Index.getTypes().entrySet()) { - tmpActions.addAll(type.getValue()); - } - allowed_actions = new ArrayList<>(tmpActions); - } - public Index() { super(); } diff --git a/src/main/java/org/opensearch/security/tools/Migrater.java b/src/main/java/org/opensearch/security/tools/Migrater.java deleted file mode 100644 index 2dbc009706..0000000000 --- a/src/main/java/org/opensearch/security/tools/Migrater.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright 2015-2017 floragunn GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package org.opensearch.security.tools; - -import java.io.File; - -import com.google.common.io.Files; -import org.apache.commons.cli.CommandLine; -import org.apache.commons.cli.CommandLineParser; -import org.apache.commons.cli.DefaultParser; -import org.apache.commons.cli.HelpFormatter; -import org.apache.commons.cli.Option; -import org.apache.commons.cli.Options; - -import org.opensearch.common.collect.Tuple; -import org.opensearch.security.DefaultObjectMapper; -import org.opensearch.security.auditlog.config.AuditConfig; -import org.opensearch.security.securityconf.Migration; -import org.opensearch.security.securityconf.impl.CType; -import org.opensearch.security.securityconf.impl.NodesDn; -import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; -import org.opensearch.security.securityconf.impl.v6.ConfigV6; -import org.opensearch.security.securityconf.impl.v6.InternalUserV6; -import org.opensearch.security.securityconf.impl.v6.RoleMappingsV6; -import org.opensearch.security.securityconf.impl.v6.RoleV6; -import org.opensearch.security.securityconf.impl.v7.RoleV7; -import org.opensearch.security.securityconf.impl.v7.TenantV7; - -public class Migrater { - - public static void main(final String[] args) { - - final Options options = new Options(); - final HelpFormatter formatter = new HelpFormatter(); - options.addOption( - Option.builder("dir").argName("directory").hasArg().required().desc("Directory containing file to be migrated").build() - ); - - final CommandLineParser parser = new DefaultParser(); - try { - final CommandLine line = parser.parse(options, args); - - if (line.hasOption("dir")) { - final File dir = new File(line.getOptionValue("dir")); - if (!migrateDirectory(dir, true)) { - System.exit(-1); - } else { - System.exit(0); - } - } - } catch (final Exception exp) { - System.err.println("Parsing failed. Reason: " + exp.getMessage()); - formatter.printHelp("migrater.sh", options, true); - } - - System.exit(-1); - } - - public static boolean migrateDirectory(File dir, boolean backup) { - if (!dir.exists()) { - System.out.println(dir.getAbsolutePath() + " does not exist"); - return false; - } - - if (!dir.isDirectory()) { - System.out.println(dir.getAbsolutePath() + " is not a directory"); - return false; - } - - boolean retVal = migrateFile(new File(dir, "config.yml"), CType.CONFIG, backup); - retVal = migrateFile(new File(dir, "action_groups.yml"), CType.ACTIONGROUPS, backup) && retVal; - retVal = migrateFile(new File(dir, "roles.yml"), CType.ROLES, backup) && retVal; - retVal = migrateFile(new File(dir, "roles_mapping.yml"), CType.ROLESMAPPING, backup) && retVal; - retVal = migrateFile(new File(dir, "internal_users.yml"), CType.INTERNALUSERS, backup) && retVal; - retVal = migrateFile(new File(dir, "nodes_dn.yml"), CType.NODESDN, backup) && retVal; - retVal = migrateFile(new File(dir, "audit.yml"), CType.AUDIT, backup) && retVal; - - return retVal; - } - - public static boolean migrateFile(File file, CType cType, boolean backup) { - final String absolutePath = file.getAbsolutePath(); - // NODESDN is newer type and supports populating empty content if file is missing - if (!file.exists() && cType != CType.NODESDN) { - System.out.println("Skip " + absolutePath + " because it does not exist"); - return false; - } - - if (!file.isFile()) { - System.out.println("Skip " + absolutePath + " because it is a directory or a special file"); - return false; - } - - try { - if (cType == CType.ACTIONGROUPS) { - SecurityDynamicConfiguration val; - try { - val = Migration.migrateActionGroups( - SecurityDynamicConfiguration.fromNode(DefaultObjectMapper.YAML_MAPPER.readTree(file), CType.ACTIONGROUPS, 0, 0, 0) - ); - } catch (Exception e) { - val = Migration.migrateActionGroups( - SecurityDynamicConfiguration.fromNode(DefaultObjectMapper.YAML_MAPPER.readTree(file), CType.ACTIONGROUPS, 1, 0, 0) - ); - } - return backupAndWrite(file, val, backup); - } - - if (cType == CType.CONFIG) { - SecurityDynamicConfiguration val = Migration.migrateConfig(Migration.readYaml(file, ConfigV6.class)); - return backupAndWrite(file, val, backup); - } - - if (cType == CType.ROLES) { - Tuple, SecurityDynamicConfiguration> tup = Migration.migrateRoles( - Migration.readYaml(file, RoleV6.class), - null - ); - boolean roles = backupAndWrite(file, tup.v1(), backup); - return roles && backupAndWrite(new File(file.getParent(), "tenants.yml"), tup.v2(), backup); - } - - if (cType == CType.ROLESMAPPING) { - SecurityDynamicConfiguration val = Migration.migrateRoleMappings(Migration.readYaml(file, RoleMappingsV6.class)); - return backupAndWrite(file, val, backup); - } - - if (cType == CType.INTERNALUSERS) { - SecurityDynamicConfiguration val = Migration.migrateInternalUsers(Migration.readYaml(file, InternalUserV6.class)); - return backupAndWrite(file, val, backup); - } - - if (cType == CType.AUDIT) { - SecurityDynamicConfiguration val = Migration.migrateAudit(Migration.readYaml(file, AuditConfig.class)); - return backupAndWrite(file, val, backup); - } - - if (cType == CType.NODESDN) { - SecurityDynamicConfiguration val = Migration.migrateNodesDn(Migration.readYaml(file, NodesDn.class)); - return backupAndWrite(file, val, backup); - } - } catch (Exception e) { - System.out.println("Can not migrate " + file + " due to " + e); - } - - return false; - } - - private static boolean backupAndWrite(File file, SecurityDynamicConfiguration val, boolean backup) { - try { - if (val == null) { - System.out.println("NULL object for " + file.getAbsolutePath()); - return false; - } - if (backup && file.exists()) { - Files.copy(file, new File(file.getParent(), file.getName() + ".bck6")); - } - DefaultObjectMapper.YAML_MAPPER.writeValue(file, val); - System.out.println("Migrated (as " + val.getCType() + ") " + file.getAbsolutePath()); - return true; - } catch (Exception e) { - System.out.println("Unable to write " + file.getAbsolutePath() + ". This is unexpected and we will abort migration."); - System.out.println(" Details: " + e.getMessage()); - } - - return false; - } -} diff --git a/src/main/java/org/opensearch/security/tools/SecurityAdmin.java b/src/main/java/org/opensearch/security/tools/SecurityAdmin.java index 240d7fad25..f0755e2162 100644 --- a/src/main/java/org/opensearch/security/tools/SecurityAdmin.java +++ b/src/main/java/org/opensearch/security/tools/SecurityAdmin.java @@ -27,6 +27,7 @@ package org.opensearch.security.tools; // CS-SUPPRESS-SINGLE: RegexpSingleline https://github.com/opensearch-project/OpenSearch/issues/3663 + import java.io.ByteArrayInputStream; import java.io.Console; import java.io.File; @@ -108,7 +109,6 @@ import org.opensearch.client.indices.GetIndexResponse; import org.opensearch.client.transport.NoNodeAvailableException; import org.opensearch.cluster.health.ClusterHealthStatus; -import org.opensearch.common.collect.Tuple; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.xcontent.XContentFactory; @@ -125,23 +125,7 @@ import org.opensearch.index.IndexNotFoundException; import org.opensearch.security.DefaultObjectMapper; import org.opensearch.security.NonValidatingObjectMapper; -import org.opensearch.security.auditlog.config.AuditConfig; -import org.opensearch.security.securityconf.Migration; -import org.opensearch.security.securityconf.impl.AllowlistingSettings; import org.opensearch.security.securityconf.impl.CType; -import org.opensearch.security.securityconf.impl.NodesDn; -import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; -import org.opensearch.security.securityconf.impl.WhitelistingSettings; -import org.opensearch.security.securityconf.impl.v6.ConfigV6; -import org.opensearch.security.securityconf.impl.v6.InternalUserV6; -import org.opensearch.security.securityconf.impl.v6.RoleMappingsV6; -import org.opensearch.security.securityconf.impl.v6.RoleV6; -import org.opensearch.security.securityconf.impl.v7.ActionGroupsV7; -import org.opensearch.security.securityconf.impl.v7.ConfigV7; -import org.opensearch.security.securityconf.impl.v7.InternalUserV7; -import org.opensearch.security.securityconf.impl.v7.RoleMappingsV7; -import org.opensearch.security.securityconf.impl.v7.RoleV7; -import org.opensearch.security.securityconf.impl.v7.TenantV7; import org.opensearch.security.ssl.util.ExceptionUtils; import org.opensearch.security.support.ConfigConstants; import org.opensearch.security.support.ConfigHelper; @@ -337,10 +321,6 @@ public static int execute(final String[] args) throws Exception { options.addOption(Option.builder("backup").hasArg().argName("folder").desc("Backup configuration to folder").build()); - options.addOption( - Option.builder("migrate").hasArg().argName("folder").desc("Migrate and use folder to store migrated files").build() - ); - options.addOption( Option.builder("rev") .longOpt("resolve-env-vars") @@ -358,15 +338,6 @@ public static int execute(final String[] args) throws Exception { .build() ); - options.addOption( - Option.builder("mo") - .longOpt("migrate-offline") - .hasArg() - .argName("folder") - .desc("Migrate and use folder to store migrated files") - .build() - ); - // when adding new options also adjust validate(CommandLine line) String hostname = "localhost"; @@ -406,10 +377,8 @@ public static int execute(final String[] args) throws Exception { final boolean promptForPassword; String explicitReplicas = null; String backup = null; - String migrate = null; final boolean resolveEnvVars; Integer validateConfig = null; - String migrateOffline = null; InjectableValues.Std injectableValues = new InjectableValues.Std(); injectableValues.addValue(Settings.class, Settings.builder().build()); @@ -495,8 +464,6 @@ public static int execute(final String[] args) throws Exception { backup = line.getOptionValue("backup"); - migrate = line.getOptionValue("migrate"); - resolveEnvVars = line.hasOption("rev"); validateConfig = !line.hasOption("vc") ? null : Integer.parseInt(line.getOptionValue("vc", "7")); @@ -505,8 +472,6 @@ public static int execute(final String[] args) throws Exception { throw new ParseException("version must be 6 or 7"); } - migrateOffline = line.getOptionValue("mo"); - } catch (ParseException exp) { System.out.println("ERR: Parsing failed. Reason: " + exp.getMessage()); formatter.printHelp("securityadmin.sh", options, true); @@ -518,12 +483,6 @@ public static int execute(final String[] args) throws Exception { return validateConfig(cd, file, type, validateConfig.intValue()); } - if (migrateOffline != null) { - System.out.println("Migrate " + migrateOffline + " offline"); - final boolean retVal = Migrater.migrateDirectory(new File(migrateOffline), true); - return retVal ? 0 : -1; - } - System.out.print("Will connect to " + hostname + ":" + port); Socket socket = new Socket(); @@ -940,14 +899,6 @@ public static int execute(final String[] args) throws Exception { return backup(restHighLevelClient, index, new File(backup), legacy); } - if (migrate != null) { - if (!legacy) { - System.out.println("ERR: Seems cluster is already migrated"); - return -1; - } - return migrate(restHighLevelClient, index, new File(migrate), expectedNodeCount, resolveEnvVars); - } - boolean isCdAbs = new File(cd).isAbsolute(); System.out.println("Populate config from " + (isCdAbs ? cd : new File(".", cd).getCanonicalPath())); @@ -1528,99 +1479,6 @@ private static int upload( return (success ? 0 : -1); } - private static int migrate(RestHighLevelClient tc, String index, File backupDir, int expectedNodeCount, boolean resolveEnvVars) - throws IOException { - - System.out.println("== Migration started =="); - System.out.println("======================="); - - System.out.println("-> Backup current configuration to " + backupDir.getAbsolutePath()); - - if (backup(tc, index, backupDir, true) != 0) { - return -1; - } - - System.out.println(" done"); - - File v7Dir = new File(backupDir, "v7"); - v7Dir.mkdirs(); - - try { - - System.out.println("-> Migrate configuration to new format and store it here: " + v7Dir.getAbsolutePath()); - SecurityDynamicConfiguration actionGroupsV7 = Migration.migrateActionGroups( - SecurityDynamicConfiguration.fromNode( - DefaultObjectMapper.YAML_MAPPER.readTree(new File(backupDir, "action_groups.yml")), - CType.ACTIONGROUPS, - 1, - 0, - 0 - ) - ); - SecurityDynamicConfiguration configV7 = Migration.migrateConfig( - Migration.readYaml(new File(backupDir, "config.yml"), ConfigV6.class) - ); - SecurityDynamicConfiguration internalUsersV7 = Migration.migrateInternalUsers( - Migration.readYaml(new File(backupDir, "internal_users.yml"), InternalUserV6.class) - ); - SecurityDynamicConfiguration rolesmappingV6 = Migration.readYaml( - new File(backupDir, "roles_mapping.yml"), - RoleMappingsV6.class - ); - - Tuple, SecurityDynamicConfiguration> rolesTenantsV7 = Migration.migrateRoles( - Migration.readYaml(new File(backupDir, "roles.yml"), RoleV6.class), - rolesmappingV6 - ); - SecurityDynamicConfiguration rolesmappingV7 = Migration.migrateRoleMappings(rolesmappingV6); - SecurityDynamicConfiguration nodesDn = Migration.migrateNodesDn( - Migration.readYaml(new File(backupDir, "nodes_dn.yml"), NodesDn.class) - ); - SecurityDynamicConfiguration whitelistingSettings = Migration.migrateWhitelistingSetting( - Migration.readYaml(new File(backupDir, "whitelist.yml"), WhitelistingSettings.class) - ); - SecurityDynamicConfiguration allowlistingSettings = Migration.migrateAllowlistingSetting( - Migration.readYaml(new File(backupDir, "allowlist.yml"), AllowlistingSettings.class) - ); - SecurityDynamicConfiguration audit = Migration.migrateAudit( - Migration.readYaml(new File(backupDir, "audit.yml"), AuditConfig.class) - ); - - DefaultObjectMapper.YAML_MAPPER.writeValue(new File(v7Dir, "/action_groups.yml"), actionGroupsV7); - DefaultObjectMapper.YAML_MAPPER.writeValue(new File(v7Dir, "/config.yml"), configV7); - DefaultObjectMapper.YAML_MAPPER.writeValue(new File(v7Dir, "/internal_users.yml"), internalUsersV7); - DefaultObjectMapper.YAML_MAPPER.writeValue(new File(v7Dir, "/roles.yml"), rolesTenantsV7.v1()); - DefaultObjectMapper.YAML_MAPPER.writeValue(new File(v7Dir, "/tenants.yml"), rolesTenantsV7.v2()); - DefaultObjectMapper.YAML_MAPPER.writeValue(new File(v7Dir, "/roles_mapping.yml"), rolesmappingV7); - DefaultObjectMapper.YAML_MAPPER.writeValue(new File(v7Dir, "/nodes_dn.yml"), nodesDn); - DefaultObjectMapper.YAML_MAPPER.writeValue(new File(v7Dir, "/whitelist.yml"), whitelistingSettings); - DefaultObjectMapper.YAML_MAPPER.writeValue(new File(v7Dir, "/allowlist.yml"), allowlistingSettings); - DefaultObjectMapper.YAML_MAPPER.writeValue(new File(v7Dir, "/audit.yml"), audit); - - } catch (Exception e) { - System.out.println("ERR: Unable to migrate config files due to " + e); - return -1; - } - - System.out.println(" done"); - - System.out.println("-> Delete old " + index + " index"); - deleteConfigIndex(tc, index, true); - System.out.println(" done"); - - System.out.println("-> Upload new configuration into OpenSearch cluster"); - - int uploadResult = upload(tc, index, v7Dir.getAbsolutePath() + "/", false, expectedNodeCount, resolveEnvVars); - - if (uploadResult == 0) { - System.out.println(" done"); - } else { - System.out.println(" ERR: unable to upload"); - } - - return uploadResult; - } - private static String readTypeFromFile(File file) throws IOException { if (!file.exists() || !file.isFile()) { System.out.println("ERR: No such file " + file.getAbsolutePath()); diff --git a/src/test/java/org/opensearch/security/ConfigTests.java b/src/test/java/org/opensearch/security/ConfigTests.java index c30da8ed9f..48d9cf3c9d 100644 --- a/src/test/java/org/opensearch/security/ConfigTests.java +++ b/src/test/java/org/opensearch/security/ConfigTests.java @@ -17,124 +17,17 @@ package org.opensearch.security; -import java.io.File; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import org.junit.Assert; import org.junit.Test; -import org.opensearch.common.collect.Tuple; -import org.opensearch.common.xcontent.XContentType; -import org.opensearch.core.common.Strings; -import org.opensearch.security.securityconf.Migration; import org.opensearch.security.securityconf.impl.CType; import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; -import org.opensearch.security.securityconf.impl.v6.ConfigV6; -import org.opensearch.security.securityconf.impl.v6.InternalUserV6; -import org.opensearch.security.securityconf.impl.v6.RoleMappingsV6; -import org.opensearch.security.securityconf.impl.v6.RoleV6; -import org.opensearch.security.securityconf.impl.v7.ActionGroupsV7; -import org.opensearch.security.securityconf.impl.v7.ConfigV7; -import org.opensearch.security.securityconf.impl.v7.InternalUserV7; -import org.opensearch.security.securityconf.impl.v7.RoleMappingsV7; -import org.opensearch.security.securityconf.impl.v7.RoleV7; -import org.opensearch.security.securityconf.impl.v7.TenantV7; -import org.opensearch.security.test.SingleClusterTest; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; public class ConfigTests { - private static final ObjectMapper YAML = new ObjectMapper(new YAMLFactory()); - @Test public void testEmptyConfig() throws Exception { Assert.assertNotSame(SecurityDynamicConfiguration.empty(CType.ROLES).deepClone(), SecurityDynamicConfiguration.empty(CType.ROLES)); } - @Test - @SuppressWarnings("unchecked") - public void testMigrate() throws Exception { - - Tuple, SecurityDynamicConfiguration> rolesResult = Migration.migrateRoles( - (SecurityDynamicConfiguration) load("./legacy/securityconfig_v6/roles.yml", CType.ROLES), - (SecurityDynamicConfiguration) load("./legacy/securityconfig_v6/roles_mapping.yml", CType.ROLESMAPPING) - ); - - SecurityDynamicConfiguration actionGroupsResult = Migration.migrateActionGroups( - load("./legacy/securityconfig_v6/action_groups.yml", CType.ACTIONGROUPS) - ); - SecurityDynamicConfiguration configResult = Migration.migrateConfig( - (SecurityDynamicConfiguration) load("./legacy/securityconfig_v6/config.yml", CType.CONFIG) - ); - SecurityDynamicConfiguration internalUsersResult = Migration.migrateInternalUsers( - (SecurityDynamicConfiguration) load("./legacy/securityconfig_v6/internal_users.yml", CType.INTERNALUSERS) - ); - SecurityDynamicConfiguration rolemappingsResult = Migration.migrateRoleMappings( - (SecurityDynamicConfiguration) load("./legacy/securityconfig_v6/roles_mapping.yml", CType.ROLESMAPPING) - ); - } - - @Test - public void testParseSg67Config() throws Exception { - - check("./legacy/securityconfig_v6/action_groups.yml", CType.ACTIONGROUPS); - check("./action_groups.yml", CType.ACTIONGROUPS); - - check("./legacy/securityconfig_v6/config.yml", CType.CONFIG); - check("./config.yml", CType.CONFIG); - - check("./legacy/securityconfig_v6/roles.yml", CType.ROLES); - check("./roles.yml", CType.ROLES); - - check("./legacy/securityconfig_v6/internal_users.yml", CType.INTERNALUSERS); - check("./internal_users.yml", CType.INTERNALUSERS); - - check("./legacy/securityconfig_v6/roles_mapping.yml", CType.ROLESMAPPING); - check("./roles_mapping.yml", CType.ROLESMAPPING); - - check("./tenants.yml", CType.TENANTS); - - } - - private void check(String file, CType cType) throws Exception { - final String adjustedFilePath = SingleClusterTest.TEST_RESOURCE_RELATIVE_PATH + file; - JsonNode jsonNode = YAML.readTree(Files.readString(new File(adjustedFilePath).toPath(), StandardCharsets.UTF_8)); - int configVersion = 1; - if (jsonNode.get("_meta") != null) { - assertThat(cType.toLCString(), is(jsonNode.get("_meta").get("type").asText())); - configVersion = jsonNode.get("_meta").get("config_version").asInt(); - } - - SecurityDynamicConfiguration dc = load(file, cType); - Assert.assertNotNull(dc); - // Assert.assertTrue(dc.getCEntries().size() > 0); - String jsonSerialize = DefaultObjectMapper.objectMapper.writeValueAsString(dc); - SecurityDynamicConfiguration conf = SecurityDynamicConfiguration.fromJson(jsonSerialize, cType, configVersion, 0, 0); - SecurityDynamicConfiguration.fromJson( - Strings.toString(XContentType.JSON, conf), - cType, - SecurityDynamicConfiguration.CURRENT_VERSION, - 0, - 0 - ); - - } - - private SecurityDynamicConfiguration load(String file, CType cType) throws Exception { - final String adjustedFilePath = SingleClusterTest.TEST_RESOURCE_RELATIVE_PATH + file; - JsonNode jsonNode = YAML.readTree(Files.readString(new File(adjustedFilePath).toPath(), StandardCharsets.UTF_8)); - int configVersion = 1; - - if (jsonNode.get("_meta") != null) { - assertThat(cType.toLCString(), is(jsonNode.get("_meta").get("type").asText())); - configVersion = jsonNode.get("_meta").get("config_version").asInt(); - } - return SecurityDynamicConfiguration.fromNodeWithoutAutoConversion(jsonNode, cType, configVersion, 0, 0); - } } diff --git a/src/test/java/org/opensearch/security/securityconf/impl/v6/ConfigV6Test.java b/src/test/java/org/opensearch/security/securityconf/impl/v6/ConfigV6Test.java deleted file mode 100644 index f2e5351019..0000000000 --- a/src/test/java/org/opensearch/security/securityconf/impl/v6/ConfigV6Test.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.security.securityconf.impl.v6; - -import com.google.common.collect.ImmutableList; -import com.fasterxml.jackson.databind.JsonNode; -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -import org.opensearch.security.DefaultObjectMapper; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.is; - -@RunWith(Parameterized.class) -public class ConfigV6Test { - private final boolean omitDefaults; - - @Parameterized.Parameters - public static Iterable omitDefaults() { - return ImmutableList.of(Boolean.FALSE, Boolean.TRUE); - } - - public void assertEquals(ConfigV6.Kibana expected, JsonNode node) { - assertThat(node.get("multitenancy_enabled").asBoolean(), is(expected.multitenancy_enabled)); - assertThat(node.get("sign_in_options").isArray(), is(true)); - assertThat(node.get("sign_in_options").toString(), containsString(expected.sign_in_options.get(0).toString())); - - if (expected.server_username == null) { - Assert.assertNull(node.get("server_username")); - } else { - assertThat(node.get("server_username").asText(), is(expected.server_username)); - } - if (expected.index == null) { - // null is not persisted - Assert.assertNull(node.get("index")); - } else { - assertThat(node.get("index").asText(), is(expected.index)); - } - if (expected.opendistro_role == null) { - Assert.assertNull(node.get("opendistro_role")); - } else { - assertThat(node.get("opendistro_role").asText(), is(expected.opendistro_role)); - } - if (omitDefaults && !expected.do_not_fail_on_forbidden) { - // false (default) is not persisted - Assert.assertNull(node.get("do_not_fail_on_forbidden")); - } else { - assertThat(node.get("do_not_fail_on_forbidden").asBoolean(), is(expected.do_not_fail_on_forbidden)); - } - } - - private void assertEquals(ConfigV6.Kibana expected, ConfigV6.Kibana actual) { - assertThat(actual.multitenancy_enabled, is(expected.multitenancy_enabled)); - assertThat(expected.sign_in_options, is(actual.sign_in_options)); - if (expected.server_username == null) { - // null is restored to default instead of null - assertThat(actual.server_username, is(new ConfigV6.Kibana().server_username)); - } else { - assertThat(actual.server_username, is(expected.server_username)); - } - // null is restored to default (which is null). - assertThat(actual.opendistro_role, is(expected.opendistro_role)); - if (expected.index == null) { - // null is restored to default instead of null - assertThat(actual.index, is(new ConfigV6.Kibana().index)); - } else { - assertThat(actual.index, is(expected.index)); - } - assertThat(actual.do_not_fail_on_forbidden, is(expected.do_not_fail_on_forbidden)); - } - - public ConfigV6Test(boolean omitDefaults) { - this.omitDefaults = omitDefaults; - } - - @Test - public void testDashboards() throws Exception { - ConfigV6.Kibana kibana; - String json; - - kibana = new ConfigV6.Kibana(); - json = DefaultObjectMapper.writeValueAsString(kibana, omitDefaults); - assertEquals(kibana, DefaultObjectMapper.readTree(json)); - assertEquals(kibana, DefaultObjectMapper.readValue(json, ConfigV6.Kibana.class)); - - kibana.multitenancy_enabled = false; - kibana.server_username = null; - kibana.opendistro_role = null; - kibana.index = null; - kibana.do_not_fail_on_forbidden = false; - json = DefaultObjectMapper.writeValueAsString(kibana, omitDefaults); - assertEquals(kibana, DefaultObjectMapper.readTree(json)); - assertEquals(kibana, DefaultObjectMapper.readValue(json, ConfigV6.Kibana.class)); - - kibana.multitenancy_enabled = true; - kibana.server_username = "user"; - kibana.opendistro_role = "role"; - kibana.index = "index"; - kibana.do_not_fail_on_forbidden = true; - json = DefaultObjectMapper.writeValueAsString(kibana, omitDefaults); - assertEquals(kibana, DefaultObjectMapper.readTree(json)); - assertEquals(kibana, DefaultObjectMapper.readValue(json, ConfigV6.Kibana.class)); - } - - @Test - public void testOnBehalfOfSettings() { - ConfigV6.OnBehalfOfSettings oboSettings; - - oboSettings = new ConfigV6.OnBehalfOfSettings(); - assertThat(Boolean.FALSE, is(oboSettings.getOboEnabled())); - Assert.assertNull(oboSettings.getSigningKey()); - Assert.assertNull(oboSettings.getEncryptionKey()); - } -} diff --git a/src/test/resources/legacy/securityconfig_v6/action_groups.yml b/src/test/resources/legacy/securityconfig_v6/action_groups.yml deleted file mode 100644 index ac564e7421..0000000000 --- a/src/test/resources/legacy/securityconfig_v6/action_groups.yml +++ /dev/null @@ -1,136 +0,0 @@ -OPENDISTRO_SECURITY_UNLIMITED: - readonly: true - permissions: - - "*" - -###### INDEX LEVEL ###### - -OPENDISTRO_SECURITY_INDICES_ALL: - readonly: true - permissions: - - "indices:*" - -# for backward compatibility -ALL: - readonly: true - permissions: - - INDICES_ALL - -OPENDISTRO_SECURITY_MANAGE: - readonly: true - permissions: - - "indices:monitor/*" - - "indices:admin/*" - -OPENDISTRO_SECURITY_CREATE_INDEX: - readonly: true - permissions: - - "indices:admin/create" - - "indices:admin/mapping/put" - -OPENDISTRO_SECURITY_MANAGE_ALIASES: - readonly: true - permissions: - - "indices:admin/aliases*" - -# for backward compatibility -MONITOR: - readonly: true - permissions: - - INDICES_MONITOR - -OPENDISTRO_SECURITY_INDICES_MONITOR: - readonly: true - permissions: - - "indices:monitor/*" - -OPENDISTRO_SECURITY_DATA_ACCESS: - readonly: true - permissions: - - "indices:data/*" - - CRUD - -OPENDISTRO_SECURITY_WRITE: - readonly: true - permissions: - - "indices:data/write*" - - "indices:admin/mapping/put" - -OPENDISTRO_SECURITY_READ: - readonly: true - permissions: - - "indices:data/read*" - - "indices:admin/mappings/fields/get*" - -OPENDISTRO_SECURITY_DELETE: - readonly: true - permissions: - - "indices:data/write/delete*" - -OPENDISTRO_SECURITY_CRUD: - readonly: true - permissions: - - READ - - WRITE - -OPENDISTRO_SECURITY_SEARCH: - readonly: true - permissions: - - "indices:data/read/search*" - - "indices:data/read/msearch*" - - SUGGEST - -OPENDISTRO_SECURITY_SUGGEST: - readonly: true - permissions: - - "indices:data/read/suggest*" - -OPENDISTRO_SECURITY_INDEX: - readonly: true - permissions: - - "indices:data/write/index*" - - "indices:data/write/update*" - - "indices:admin/mapping/put" - - "indices:data/write/bulk*" - -OPENDISTRO_SECURITY_GET: - readonly: true - permissions: - - "indices:data/read/get*" - - "indices:data/read/mget*" - -###### CLUSTER LEVEL ###### - -OPENDISTRO_SECURITY_CLUSTER_ALL: - readonly: true - permissions: - - "cluster:*" - -OPENDISTRO_SECURITY_CLUSTER_MONITOR: - readonly: true - permissions: - - "cluster:monitor/*" - -OPENDISTRO_SECURITY_CLUSTER_COMPOSITE_OPS_RO: - readonly: true - permissions: - - "indices:data/read/mget" - - "indices:data/read/msearch" - - "indices:data/read/mtv" - - "indices:admin/aliases/exists*" - - "indices:admin/aliases/get*" - - "indices:data/read/scroll" - -OPENDISTRO_SECURITY_CLUSTER_COMPOSITE_OPS: - readonly: true - permissions: - - "indices:data/write/bulk" - - "indices:admin/aliases*" - - "indices:data/write/reindex" - - CLUSTER_COMPOSITE_OPS_RO - -OPENDISTRO_SECURITY_MANAGE_SNAPSHOTS: - readonly: true - permissions: - - "cluster:admin/snapshot/*" - - "cluster:admin/repository/*" diff --git a/src/test/resources/legacy/securityconfig_v6/audit.yml b/src/test/resources/legacy/securityconfig_v6/audit.yml deleted file mode 100644 index d46efdfa03..0000000000 --- a/src/test/resources/legacy/securityconfig_v6/audit.yml +++ /dev/null @@ -1,42 +0,0 @@ -config: - # enable/disable auditlog - enabled: true - - audit: - # rest - enable_rest: false - disabled_rest_categories: [] - - # transport - enable_transport: false - disabled_transport_categories: [] - - # ignore - ignore_users: - - kibanaserver - ignore_requests: [] - - # verbose attributes - resolve_bulk_requests: false - log_request_body: false - resolve_indices: false - exclude_sensitive_headers: false - - compliance: - # enable/disable compliance - enabled: true - - # configs - internal_config: true - external_config: false - - # compliance read - read_metadata_only: false - read_watched_fields: {} - read_ignore_users: [] - - # compliance write - write_metadata_only: false - write_log_diffs: false - write_watched_indices: [] - write_ignore_users: [] diff --git a/src/test/resources/legacy/securityconfig_v6/config.yml b/src/test/resources/legacy/securityconfig_v6/config.yml deleted file mode 100644 index 19b1fd76cd..0000000000 --- a/src/test/resources/legacy/securityconfig_v6/config.yml +++ /dev/null @@ -1,218 +0,0 @@ -# This is the main OpenSearch Security configuration file where authentication -# and authorization is defined. -# -# You need to configure at least one authentication domain in the authc of this file. -# An authentication domain is responsible for extracting the user credentials from -# the request and for validating them against an authentication backend like Active Directory for example. -# -# If more than one authentication domain is configured the first one which succeeds wins. -# If all authentication domains fail then the request is unauthenticated. -# In this case an exception is thrown and/or the HTTP status is set to 401. -# -# After authentication authorization (authz) will be applied. There can be zero or more authorizers which collect -# the roles from a given backend for the authenticated user. -# -# Both, authc and auth can be enabled/disabled separately for REST and TRANSPORT layer. Default is true for both. -# http_enabled: true -# transport_enabled: true -# -# 5.x Migration: "enabled: true/false" will also be respected currently but only to provide backward compatibility. -# -# For HTTP it is possible to allow anonymous authentication. If that is the case then the HTTP authenticators try to -# find user credentials in the HTTP request. If credentials are found then the user gets regularly authenticated. -# If none can be found the user will be authenticated as an "anonymous" user. This user has always the username "opendistro_security_anonymous" -# and one role named "opendistro_security_anonymous_backendrole". -# If you enable anonymous authentication all HTTP authenticators will not challenge. -# -# -# Note: If you define more than one HTTP authenticators make sure to put non-challenging authenticators like "proxy" or "clientcert" -# first and the challenging one last. -# Because it's not possible to challenge a client with two different authentication methods (for example -# Kerberos and Basic) only one can have the challenge flag set to true. You can cope with this situation -# by using pre-authentication, e.g. sending a HTTP Basic authentication header in the request. -# -# Default value of the challenge flag is true. -# -# -# HTTP -# basic (challenging) -# proxy (not challenging, needs xff) -# kerberos (challenging) -# clientcert (not challenging, needs https) -# jwt (not challenging) -# host (not challenging) #DEPRECATED, will be removed in a future version. -# host based authentication is configurable in opendistro_security_roles_mapping - -# Authc -# internal -# noop -# ldap - -# Authz -# ldap -# noop - -opendistro_security: - dynamic: - # Set filtered_alias_mode to 'disallow' to forbid more than 2 filtered aliases per index - # Set filtered_alias_mode to 'warn' to allow more than 2 filtered aliases per index but warns about it (default) - # Set filtered_alias_mode to 'nowarn' to allow more than 2 filtered aliases per index silently - #filtered_alias_mode: warn - #kibana: - #multitenancy_enabled: true - #server_username: kibanaserver - #index: '.kibana' - #do_not_fail_on_forbidden: false - http: - anonymous_auth_enabled: false - xff: - enabled: false - internalProxies: '192\.168\.0\.10|192\.168\.0\.11' # regex pattern - #internalProxies: '.*' # trust all internal proxies, regex pattern - remoteIpHeader: 'x-forwarded-for' - proxiesHeader: 'x-forwarded-by' - trustedProxies: '.*' # trust all external proxies, regex pattern - ###### see https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html for regex help - ###### more information about XFF https://en.wikipedia.org/wiki/X-Forwarded-For - ###### and here https://tools.ietf.org/html/rfc7239 - ###### and https://tomcat.apache.org/tomcat-8.0-doc/config/valve.html#Remote_IP_Valve - authc: - kerberos_auth_domain: - http_enabled: false - transport_enabled: false - order: 6 - http_authenticator: - type: kerberos # - challenge: true - config: - # If true a lot of kerberos/security related debugging output will be logged to standard out - krb_debug: false - # If true then the realm will be stripped from the user name - strip_realm_from_principal: true - authentication_backend: - type: noop - basic_internal_auth_domain: - http_enabled: true - transport_enabled: true - order: 4 - http_authenticator: - type: basic - challenge: true - authentication_backend: - type: intern - proxy_auth_domain: - http_enabled: false - transport_enabled: false - order: 3 - http_authenticator: - type: proxy - challenge: false - config: - user_header: "x-proxy-user" - roles_header: "x-proxy-roles" - authentication_backend: - type: noop - jwt_auth_domain: - http_enabled: false - transport_enabled: false - order: 0 - http_authenticator: - type: jwt - challenge: false - config: - signing_key: "base64 encoded HMAC key or public RSA/ECDSA pem key" - jwt_header: "Authorization" - jwt_url_parameter: null - roles_key: null - subject_key: null - authentication_backend: - type: noop - clientcert_auth_domain: - http_enabled: false - transport_enabled: false - order: 2 - http_authenticator: - type: clientcert - config: - username_attribute: cn #optional, if omitted DN becomes username - challenge: false - authentication_backend: - type: noop - ldap: - http_enabled: false - transport_enabled: false - order: 5 - http_authenticator: - type: basic - challenge: false - authentication_backend: - # LDAP authentication backend (authenticate users against a LDAP or Active Directory) - type: ldap # - config: - # enable ldaps - enable_ssl: false - # enable start tls, enable_ssl should be false - enable_start_tls: false - # send client certificate - enable_ssl_client_auth: false - # verify ldap hostname - verify_hostnames: true - hosts: - - localhost:8389 - bind_dn: null - password: null - userbase: 'ou=people,dc=example,dc=com' - # Filter to search for users (currently in the whole subtree beneath userbase) - # {0} is substituted with the username - usersearch: '(sAMAccountName={0})' - # Use this attribute from the user as username (if not set then DN is used) - username_attribute: null - authz: - roles_from_myldap: - http_enabled: false - transport_enabled: false - authorization_backend: - # LDAP authorization backend (gather roles from a LDAP or Active Directory, you have to configure the above LDAP authentication backend settings too) - type: ldap # - config: - # enable ldaps - enable_ssl: false - # enable start tls, enable_ssl should be false - enable_start_tls: false - # send client certificate - enable_ssl_client_auth: false - # verify ldap hostname - verify_hostnames: true - hosts: - - localhost:8389 - bind_dn: null - password: null - rolebase: 'ou=groups,dc=example,dc=com' - # Filter to search for roles (currently in the whole subtree beneath rolebase) - # {0} is substituted with the DN of the user - # {1} is substituted with the username - # {2} is substituted with an attribute value from user's directory entry, of the authenticated user. Use userroleattribute to specify the name of the attribute - rolesearch: '(member={0})' - # Specify the name of the attribute which value should be substituted with {2} above - userroleattribute: null - # Roles as an attribute of the user entry - userrolename: disabled - #userrolename: memberOf - # The attribute in a role entry containing the name of that role, Default is "name". - # Can also be "dn" to use the full DN as rolename. - rolename: cn - # Resolve nested roles transitive (roles which are members of other roles and so on ...) - resolve_nested_roles: true - userbase: 'ou=people,dc=example,dc=com' - # Filter to search for users (currently in the whole subtree beneath userbase) - # {0} is substituted with the username - usersearch: '(uid={0})' - # Skip users matching a user name, a wildcard or a regex pattern - #skip_users: - # - 'cn=Michael Jackson,ou*people,o=TEST' - # - '/\S*/' - roles_from_another_ldap: - enabled: false - authorization_backend: - type: ldap # - #config goes here ... diff --git a/src/test/resources/legacy/securityconfig_v6/internal_users.yml b/src/test/resources/legacy/securityconfig_v6/internal_users.yml deleted file mode 100644 index 64b6295bbe..0000000000 --- a/src/test/resources/legacy/securityconfig_v6/internal_users.yml +++ /dev/null @@ -1,45 +0,0 @@ -# This is the internal user database -# The hash value is a bcrypt hash and can be generated with plugin/tools/hash.sh - -#password is: admin -admin: - readonly: true - hash: $2a$12$VcCDgh2NDk07JGN0rjGbM.Ad41qVR/YFJcgHp0UGns5JDymv..TOG - roles: - - admin - attributes: - #no dots allowed in attribute names - attribute1: value1 - attribute2: value2 - attribute3: value3 - -#password is: logstash -opendistro_security_logstash: - hash: $2a$12$u1ShR4l4uBS3Uv59Pa2y5.1uQuZBrZtmNfqB3iM/.jL0XoV9sghS2 - roles: - - logstash - -#password is: kibanaserver -kibanaserver: - readonly: true - hash: $2a$12$4AcgAt3xwOWadA5s5blL6ev39OXDNhmOesEoo33eZtrq2N0YrU3H. - -#password is: kibanaro -kibanaro: - hash: $2a$12$JJSXNfTowz7Uu5ttXfeYpeYE0arACvcwlPBStB1F.MI7f0U9Z4DGC - roles: - - kibanauser - - readall - -#password is: readall -opendistro_security_readall: - hash: $2a$12$ae4ycwzwvLtZxwZ82RmiEunBbIPiAmGZduBAjKN0TXdwQFtCwARz2 - #password is: readall - roles: - - readall - -#password is: snapshotrestore -snapshotrestore: - hash: $2y$12$DpwmetHKwgYnorbgdvORCenv4NAK8cPUg8AI6pxLCuWf/ALc0.v7W - roles: - - snapshotrestore diff --git a/src/test/resources/legacy/securityconfig_v6/migration/action_groups.yml b/src/test/resources/legacy/securityconfig_v6/migration/action_groups.yml deleted file mode 100644 index 3faa4c5e31..0000000000 --- a/src/test/resources/legacy/securityconfig_v6/migration/action_groups.yml +++ /dev/null @@ -1,136 +0,0 @@ -UNLIMITED: - readonly: true - permissions: - - "*" - -###### INDEX LEVEL ###### - -INDICES_ALL: - readonly: true - permissions: - - "indices:*" - -# for backward compatibility -ALL: - readonly: true - permissions: - - INDICES_ALL - -MANAGE: - readonly: true - permissions: - - "indices:monitor/*" - - "indices:admin/*" - -CREATE_INDEX: - readonly: true - permissions: - - "indices:admin/create" - - "indices:admin/mapping/put" - -MANAGE_ALIASES: - readonly: true - permissions: - - "indices:admin/aliases*" - -# for backward compatibility -MONITOR: - readonly: true - permissions: - - INDICES_MONITOR - -INDICES_MONITOR: - readonly: true - permissions: - - "indices:monitor/*" - -DATA_ACCESS: - readonly: true - permissions: - - "indices:data/*" - - CRUD - -WRITE: - readonly: true - permissions: - - "indices:data/write*" - - "indices:admin/mapping/put" - -READ: - readonly: true - permissions: - - "indices:data/read*" - - "indices:admin/mappings/fields/get*" - -DELETE: - readonly: true - permissions: - - "indices:data/write/delete*" - -CRUD: - readonly: true - permissions: - - READ - - WRITE - -SEARCH: - readonly: true - permissions: - - "indices:data/read/search*" - - "indices:data/read/msearch*" - - SUGGEST - -SUGGEST: - readonly: true - permissions: - - "indices:data/read/suggest*" - -INDEX: - readonly: true - permissions: - - "indices:data/write/index*" - - "indices:data/write/update*" - - "indices:admin/mapping/put" - - "indices:data/write/bulk*" - -GET: - readonly: true - permissions: - - "indices:data/read/get*" - - "indices:data/read/mget*" - -###### CLUSTER LEVEL ###### - -CLUSTER_ALL: - readonly: true - permissions: - - "cluster:*" - -CLUSTER_MONITOR: - readonly: true - permissions: - - "cluster:monitor/*" - -CLUSTER_COMPOSITE_OPS_RO: - readonly: true - permissions: - - "indices:data/read/mget" - - "indices:data/read/msearch" - - "indices:data/read/mtv" - - "indices:admin/aliases/exists*" - - "indices:admin/aliases/get*" - - "indices:data/read/scroll" - -CLUSTER_COMPOSITE_OPS: - readonly: true - permissions: - - "indices:data/write/bulk" - - "indices:admin/aliases*" - - "indices:data/write/reindex" - - CLUSTER_COMPOSITE_OPS_RO - -MANAGE_SNAPSHOTS: - readonly: true - permissions: - - "cluster:admin/snapshot/*" - - "cluster:admin/repository/*" diff --git a/src/test/resources/legacy/securityconfig_v6/migration/audit.yml b/src/test/resources/legacy/securityconfig_v6/migration/audit.yml deleted file mode 100644 index d46efdfa03..0000000000 --- a/src/test/resources/legacy/securityconfig_v6/migration/audit.yml +++ /dev/null @@ -1,42 +0,0 @@ -config: - # enable/disable auditlog - enabled: true - - audit: - # rest - enable_rest: false - disabled_rest_categories: [] - - # transport - enable_transport: false - disabled_transport_categories: [] - - # ignore - ignore_users: - - kibanaserver - ignore_requests: [] - - # verbose attributes - resolve_bulk_requests: false - log_request_body: false - resolve_indices: false - exclude_sensitive_headers: false - - compliance: - # enable/disable compliance - enabled: true - - # configs - internal_config: true - external_config: false - - # compliance read - read_metadata_only: false - read_watched_fields: {} - read_ignore_users: [] - - # compliance write - write_metadata_only: false - write_log_diffs: false - write_watched_indices: [] - write_ignore_users: [] diff --git a/src/test/resources/legacy/securityconfig_v6/migration/config.yml b/src/test/resources/legacy/securityconfig_v6/migration/config.yml deleted file mode 100644 index cd9b2398c9..0000000000 --- a/src/test/resources/legacy/securityconfig_v6/migration/config.yml +++ /dev/null @@ -1,210 +0,0 @@ -# This is the main OpenSearch Security configuration file where authentication -# and authorization is defined. -# -# You need to configure at least one authentication domain in the authc of this file. -# An authentication domain is responsible for extracting the user credentials from -# the request and for validating them against an authentication backend like Active Directory for example. -# -# If more than one authentication domain is configured the first one which succeeds wins. -# If all authentication domains fail then the request is unauthenticated. -# In this case an exception is thrown and/or the HTTP status is set to 401. -# -# After authentication authorization (authz) will be applied. There can be zero or more authorizers which collect -# the roles from a given backend for the authenticated user. -# -# Both, authc and auth can be enabled/disabled separately for REST and TRANSPORT layer. Default is true for both. -# http_enabled: true -# transport_enabled: true -# -# 5.x Migration: "enabled: true/false" will also be respected currently but only to provide backward compatibility. -# -# For HTTP it is possible to allow anonymous authentication. If that is the case then the HTTP authenticators try to -# find user credentials in the HTTP request. If credentials are found then the user gets regularly authenticated. -# If none can be found the user will be authenticated as an "anonymous" user. This user has always the username "opendistro_security_anonymous" -# and one role named "opendistro_security_anonymous_backendrole". -# If you enable anonymous authentication all HTTP authenticators will not challenge. -# -# -# Note: If you define more than one HTTP authenticators make sure to put non-challenging authenticators like "proxy" or "clientcert" -# first and the challenging one last. -# Because it's not possible to challenge a client with two different authentication methods (for example -# Kerberos and Basic) only one can have the challenge flag set to true. You can cope with this situation -# by using pre-authentication, e.g. sending a HTTP Basic authentication header in the request. -# -# Default value of the challenge flag is true. -# -# -# HTTP -# basic (challenging) -# proxy (not challenging, needs xff) -# kerberos (challenging) NOT FREE FOR COMMERCIAL -# clientcert (not challenging, needs https) -# jwt (not challenging) NOT FREE FOR COMMERCIAL -# host (not challenging) #DEPRECATED, will be removed in a future version. -# host based authentication is configurable in opendistro_security_roles_mapping - -# Authc -# internal -# noop -# ldap NOT FREE FOR COMMERCIAL USE - -# Authz -# ldap NOT FREE FOR COMMERCIAL USE -# noop - -opendistro_security: - dynamic: - http: - anonymous_auth_enabled: false - xff: - enabled: false - internalProxies: '192\.168\.0\.10|192\.168\.0\.11' # regex pattern - #internalProxies: '.*' # trust all internal proxies, regex pattern - remoteIpHeader: 'x-forwarded-for' - proxiesHeader: 'x-forwarded-by' - trustedProxies: '.*' # trust all external proxies, regex pattern - ###### see https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html for regex help - ###### more information about XFF https://en.wikipedia.org/wiki/X-Forwarded-For - ###### and here https://tools.ietf.org/html/rfc7239 - ###### and https://tomcat.apache.org/tomcat-8.0-doc/config/valve.html#Remote_IP_Valve - authc: - kerberos_auth_domain: - http_enabled: false - transport_enabled: false - order: 6 - http_authenticator: - type: kerberos # NOT FREE FOR COMMERCIAL USE - challenge: true - config: - # If true a lot of kerberos/security related debugging output will be logged to standard out - krb_debug: false - # If true then the realm will be stripped from the user name - strip_realm_from_principal: true - authentication_backend: - type: noop - basic_internal_auth_domain: - http_enabled: true - transport_enabled: true - order: 4 - http_authenticator: - type: basic - challenge: true - authentication_backend: - type: intern - proxy_auth_domain: - http_enabled: false - transport_enabled: false - order: 3 - http_authenticator: - type: proxy - challenge: false - config: - user_header: "x-proxy-user" - roles_header: "x-proxy-roles" - authentication_backend: - type: noop - jwt_auth_domain: - http_enabled: false - transport_enabled: false - order: 0 - http_authenticator: - type: jwt - challenge: false - config: - signing_key: "base64 encoded HMAC key or public RSA/ECDSA pem key" - jwt_header: "Authorization" - jwt_url_parameter: null - roles_key: null - subject_key: null - authentication_backend: - type: noop - clientcert_auth_domain: - http_enabled: false - transport_enabled: false - order: 2 - http_authenticator: - type: clientcert - config: - username_attribute: cn #optional, if omitted DN becomes username - challenge: false - authentication_backend: - type: noop - ldap: - http_enabled: false - transport_enabled: false - order: 5 - http_authenticator: - type: basic - challenge: false - authentication_backend: - # LDAP authentication backend (authenticate users against a LDAP or Active Directory) - type: ldap # NOT FREE FOR COMMERCIAL USE - config: - # enable ldaps - enable_ssl: false - # enable start tls, enable_ssl should be false - enable_start_tls: false - # send client certificate - enable_ssl_client_auth: false - # verify ldap hostname - verify_hostnames: true - hosts: - - localhost:8389 - bind_dn: null - password: null - userbase: 'ou=people,dc=example,dc=com' - # Filter to search for users (currently in the whole subtree beneath userbase) - # {0} is substituted with the username - usersearch: '(sAMAccountName={0})' - # Use this attribute from the user as username (if not set then DN is used) - username_attribute: null - authz: - roles_from_myldap: - http_enabled: false - transport_enabled: false - authorization_backend: - # LDAP authorization backend (gather roles from a LDAP or Active Directory, you have to configure the above LDAP authentication backend settings too) - type: ldap # NOT FREE FOR COMMERCIAL USE - config: - # enable ldaps - enable_ssl: false - # enable start tls, enable_ssl should be false - enable_start_tls: false - # send client certificate - enable_ssl_client_auth: false - # verify ldap hostname - verify_hostnames: true - hosts: - - localhost:8389 - bind_dn: null - password: null - rolebase: 'ou=groups,dc=example,dc=com' - # Filter to search for roles (currently in the whole subtree beneath rolebase) - # {0} is substituted with the DN of the user - # {1} is substituted with the username - # {2} is substituted with an attribute value from user's directory entry, of the authenticated user. Use userroleattribute to specify the name of the attribute - rolesearch: '(member={0})' - # Specify the name of the attribute which value should be substituted with {2} above - userroleattribute: null - # Roles as an attribute of the user entry - userrolename: disabled - #userrolename: memberOf - # The attribute in a role entry containing the name of that role, Default is "name". - # Can also be "dn" to use the full DN as rolename. - rolename: cn - # Resolve nested roles transitive (roles which are members of other roles and so on ...) - resolve_nested_roles: true - userbase: 'ou=people,dc=example,dc=com' - # Filter to search for users (currently in the whole subtree beneath userbase) - # {0} is substituted with the username - usersearch: '(uid={0})' - # Skip users matching a user name, a wildcard or a regex pattern - #skip_users: - # - 'cn=Michael Jackson,ou*people,o=TEST' - # - '/\S*/' - roles_from_another_ldap: - http_enabled: false - transport_enabled: false - authorization_backend: - type: ldap # NOT FREE FOR COMMERCIAL USE - #config goes here ... diff --git a/src/test/resources/legacy/securityconfig_v6/migration/internal_users.yml b/src/test/resources/legacy/securityconfig_v6/migration/internal_users.yml deleted file mode 100644 index c7d177787d..0000000000 --- a/src/test/resources/legacy/securityconfig_v6/migration/internal_users.yml +++ /dev/null @@ -1,45 +0,0 @@ -# This is the internal user database -# The hash value is a bcrypt hash and can be generated with plugin/tools/hash.sh - -#password is: admin -admin: - readonly: true - hash: $2a$12$VcCDgh2NDk07JGN0rjGbM.Ad41qVR/YFJcgHp0UGns5JDymv..TOG - roles: - - admin - attributes: - #no dots allowed in attribute names - attribute1: value1 - attribute2: value2 - attribute3: value3 - -#password is: logstash -logstash: - hash: $2a$12$u1ShR4l4uBS3Uv59Pa2y5.1uQuZBrZtmNfqB3iM/.jL0XoV9sghS2 - roles: - - logstash - -#password is: kibanaserver -kibanaserver: - readonly: true - hash: $2a$12$4AcgAt3xwOWadA5s5blL6ev39OXDNhmOesEoo33eZtrq2N0YrU3H. - -#password is: kibanaro -kibanaro: - hash: $2a$12$JJSXNfTowz7Uu5ttXfeYpeYE0arACvcwlPBStB1F.MI7f0U9Z4DGC - roles: - - kibanauser - - readall - -#password is: readall -readall: - hash: $2a$12$ae4ycwzwvLtZxwZ82RmiEunBbIPiAmGZduBAjKN0TXdwQFtCwARz2 - #password is: readall - roles: - - readall - -#password is: snapshotrestore -snapshotrestore: - hash: $2y$12$DpwmetHKwgYnorbgdvORCenv4NAK8cPUg8AI6pxLCuWf/ALc0.v7W - roles: - - snapshotrestore diff --git a/src/test/resources/legacy/securityconfig_v6/migration/internal_users2.yml b/src/test/resources/legacy/securityconfig_v6/migration/internal_users2.yml deleted file mode 100644 index 6ed4154482..0000000000 --- a/src/test/resources/legacy/securityconfig_v6/migration/internal_users2.yml +++ /dev/null @@ -1,46 +0,0 @@ -# This is the internal user database -# The hash value is a bcrypt hash and can be generated with plugin/tools/hash.sh - -#password is: admin -admin: - abc: xyz - readonly: true - hash: $2a$12$VcCDgh2NDk07JGN0rjGbM.Ad41qVR/YFJcgHp0UGns5JDymv..TOG - roles: - - admin - attributes: - #no dots allowed in attribute names - attribute1: value1 - attribute2: value2 - attribute3: value3 - -#password is: logstash -logstash: - hash: $2a$12$u1ShR4l4uBS3Uv59Pa2y5.1uQuZBrZtmNfqB3iM/.jL0XoV9sghS2 - roles: - - logstash - -#password is: kibanaserver -kibanaserver: - readonly: true - hash: $2a$12$4AcgAt3xwOWadA5s5blL6ev39OXDNhmOesEoo33eZtrq2N0YrU3H. - -#password is: kibanaro -kibanaro: - hash: $2a$12$JJSXNfTowz7Uu5ttXfeYpeYE0arACvcwlPBStB1F.MI7f0U9Z4DGC - roles: - - kibanauser - - readall - -#password is: readall -readall: - hash: $2a$12$ae4ycwzwvLtZxwZ82RmiEunBbIPiAmGZduBAjKN0TXdwQFtCwARz2 - #password is: readall - roles: - - readall - -#password is: snapshotrestore -snapshotrestore: - hash: $2y$12$DpwmetHKwgYnorbgdvORCenv4NAK8cPUg8AI6pxLCuWf/ALc0.v7W - roles: - - snapshotrestore diff --git a/src/test/resources/legacy/securityconfig_v6/migration/roles.yml b/src/test/resources/legacy/securityconfig_v6/migration/roles.yml deleted file mode 100644 index 52f9dd60a4..0000000000 --- a/src/test/resources/legacy/securityconfig_v6/migration/roles.yml +++ /dev/null @@ -1,177 +0,0 @@ -#: -# cluster: -# - '' -# indices: -# '': -# '': -# - '' -# _dls_: '' -# _fls_: -# - '' -# - '' - -# When a user make a request to OpenSearch then the following roles will be evaluated to see if the user has -# permissions for the request. A request is always associated with an action and is executed against and index (or alias) -# and a type. If a request is executed against all indices (or all types) then the asterix ('*') is needed. -# Every role a user has will be examined if it allows the action against an index (or type). At least one role must match -# for the request to be successful. If no role match then the request will be denied. Currently a match must happen within -# one single role - that means that permissions can not span multiple roles. - -# For , and simple wildcards and regular expressions are possible. -# A asterix (*) will match any character sequence (or an empty sequence) -# A question mark (?) will match any single character (but NOT empty character) -# Example: '*my*index' will match 'my_first_index' as well as 'myindex' but not 'myindex1' -# Example: '?kibana' will match '.kibana' but not 'kibana' - -# To use a full blown regex you have to pre- and apend a '/' to use regex instead of simple wildcards -# '//' -# Example: '/\S*/' will match any non whitespace characters - -# Important: -# Index, alias or type names can not contain dots (.) in the or expression. -# Reason is that we currently parse the config file into a opensearch settings object which cannot cope with dots in keys. -# Workaround: Just configure something like '?kibana' instead of '.kibana' or 'my?index' instead of 'my.index' - -# Allows everything, but no changes to opendistro_security configuration index -opendistro_security_all_access: - readonly: true - cluster: - - UNLIMITED - indices: - '*': - '*': - - UNLIMITED - tenants: - admin_tenant: RW - -# Read all, but no write permissions -opendistro_security_readall: - readonly: true - cluster: - - CLUSTER_COMPOSITE_OPS_RO - indices: - '*': - '*': - - READ - -# Read all and monitor, but no write permissions -opendistro_security_readall_and_monitor: - cluster: - - CLUSTER_MONITOR - - CLUSTER_COMPOSITE_OPS_RO - indices: - '*': - '*': - - READ - -# For users which use kibana, access to indices must be granted separately -opendistro_security_kibana_user: - readonly: true - cluster: - - INDICES_MONITOR - - CLUSTER_COMPOSITE_OPS - indices: - '?kibana': - '*': - - MANAGE - - INDEX - - READ - - DELETE - '?kibana-6': - '*': - - MANAGE - - INDEX - - READ - - DELETE - '?kibana_*': - '*': - - MANAGE - - INDEX - - READ - - DELETE - '?tasks': - '*': - - INDICES_ALL - '?management-beats': - '*': - - INDICES_ALL - '*': - '*': - - indices:data/read/field_caps* - - indices:admin/mappings/get* - - indices:admin/get - -# For the kibana server -opendistro_security_kibana_server: - readonly: true - cluster: - - CLUSTER_MONITOR - - CLUSTER_COMPOSITE_OPS - - indices:admin/template* - - indices:data/read/scroll* - indices: - '?kibana': - '*': - - INDICES_ALL - '?kibana-6': - '*': - - INDICES_ALL - '?kibana_*': - '*': - - INDICES_ALL - '?tasks': - '*': - - INDICES_ALL - '?management-beats*': - '*': - - INDICES_ALL - '*': - '*': - - "indices:admin/aliases*" - -# For logstash and beats -opendistro_security_logstash: - cluster: - - CLUSTER_MONITOR - - CLUSTER_COMPOSITE_OPS - - indices:admin/template/get - - indices:admin/template/put - indices: - 'logstash-*': - '*': - - CRUD - - CREATE_INDEX - '*beat*': - '*': - - CRUD - - CREATE_INDEX - -# Allows adding and modifying repositories and creating and restoring snapshots -opendistro_security_manage_snapshots: - cluster: - - MANAGE_SNAPSHOTS - indices: - '*': - '*': - - "indices:data/write/index" - - "indices:admin/create" - -# Allows each user to access own named index -opendistro_security_own_index: - cluster: - - CLUSTER_COMPOSITE_OPS - indices: - '${user_name}': - '*': - - INDICES_ALL - -### LEGACY ROLES, FOR COMPATIBILITY ONLY - -opendistro_security_readonly_and_monitor: - cluster: - - CLUSTER_MONITOR - - CLUSTER_COMPOSITE_OPS_RO - indices: - '*': - '*': - - READ diff --git a/src/test/resources/legacy/securityconfig_v6/migration/roles_mapping.yml b/src/test/resources/legacy/securityconfig_v6/migration/roles_mapping.yml deleted file mode 100644 index f470d45fd2..0000000000 --- a/src/test/resources/legacy/securityconfig_v6/migration/roles_mapping.yml +++ /dev/null @@ -1,31 +0,0 @@ -opendistro_security_all_access: - readonly: true - backendroles: - - admin - -opendistro_security_logstash: - backendroles: - - logstash - -opendistro_security_kibana_server: - readonly: true - users: - - kibanaserver - -opendistro_security_kibana_user: - backendroles: - - kibanauser - -opendistro_security_readall: - readonly: true - backendroles: - - readall - -opendistro_security_manage_snapshots: - readonly: true - backendroles: - - snapshotrestore - -opendistro_security_own_index: - users: - - '*' diff --git a/src/test/resources/legacy/securityconfig_v6/migration/whitelist.yml b/src/test/resources/legacy/securityconfig_v6/migration/whitelist.yml deleted file mode 100644 index 49af0d7ac7..0000000000 --- a/src/test/resources/legacy/securityconfig_v6/migration/whitelist.yml +++ /dev/null @@ -1,12 +0,0 @@ -#this name must be config -config: - enabled: false - requests: - /_cat/nodes: - - GET - /_cat/plugins: - - GET - /_cluster/health: - - GET - /_cluster/settings: - - GET diff --git a/src/test/resources/legacy/securityconfig_v6/nodes_dn.yml b/src/test/resources/legacy/securityconfig_v6/nodes_dn.yml deleted file mode 100644 index 9ebcdab102..0000000000 --- a/src/test/resources/legacy/securityconfig_v6/nodes_dn.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -cluster1: - nodes_dn: - - cn=popeye diff --git a/src/test/resources/legacy/securityconfig_v6/roles.yml b/src/test/resources/legacy/securityconfig_v6/roles.yml deleted file mode 100644 index 65f02a7106..0000000000 --- a/src/test/resources/legacy/securityconfig_v6/roles.yml +++ /dev/null @@ -1,182 +0,0 @@ -#: -# cluster: -# - '' -# indices: -# '': -# '': -# - '' -# _dls_: '' -# _fls_: -# - '' -# - '' - -# When a user make a request to OpenSearch then the following roles will be evaluated to see if the user has -# permissions for the request. A request is always associated with an action and is executed against and index (or alias) -# and a type. If a request is executed against all indices (or all types) then the asterix ('*') is needed. -# Every role a user has will be examined if it allows the action against an index (or type). At least one role must match -# for the request to be successful. If no role match then the request will be denied. Currently a match must happen within -# one single role - that means that permissions can not span multiple roles. - -# For , and simple wildcards and regular expressions are possible. -# A asterix (*) will match any character sequence (or an empty sequence) -# A question mark (?) will match any single character (but NOT empty character) -# Example: '*my*index' will match 'my_first_index' as well as 'myindex' but not 'myindex1' -# Example: '?kibana' will match '.kibana' but not 'kibana' - -# To use a full blown regex you have to pre- and apend a '/' to use regex instead of simple wildcards -# '//' -# Example: '/\S*/' will match any non whitespace characters - -# Important: -# Index, alias or type names can not contain dots (.) in the or expression. -# Reason is that we currently parse the config file into a opensearch settings object which cannot cope with dots in keys. -# Workaround: Just configure something like '?kibana' instead of '.kibana' or 'my?index' instead of 'my.index' - -# DLS (Document level security) -# https://opendistro.github.io/for-elasticsearch-docs/docs/security/document-level-security/ - -# Allows everything, but no changes to opendistro_security configuration index -opendistro_security_all_access: - readonly: true - cluster: - - UNLIMITED - indices: - '*': - '*': - - UNLIMITED - tenants: - admin_tenant: RW - -# Read all, but no write permissions -opendistro_security_readall: - readonly: true - cluster: - - CLUSTER_COMPOSITE_OPS_RO - indices: - '*': - '*': - - READ - -# Read all and monitor, but no write permissions -opendistro_security_readall_and_monitor: - cluster: - - CLUSTER_MONITOR - - CLUSTER_COMPOSITE_OPS_RO - indices: - '*': - '*': - - READ - -# For users which use kibana, access to indices must be granted separately -opendistro_security_kibana_user: - readonly: true - cluster: - - INDICES_MONITOR - - CLUSTER_COMPOSITE_OPS - indices: - '?kibana': - '*': - - MANAGE - - INDEX - - READ - - DELETE - '?kibana-6': - '*': - - MANAGE - - INDEX - - READ - - DELETE - '?kibana_*': - '*': - - MANAGE - - INDEX - - READ - - DELETE - '?tasks': - '*': - - INDICES_ALL - '?management-beats': - '*': - - INDICES_ALL - '*': - '*': - - indices:data/read/field_caps* - - indices:admin/mappings/get* - - indices:admin/get - -# For the kibana server -opendistro_security_kibana_server: - readonly: true - cluster: - - CLUSTER_MONITOR - - CLUSTER_COMPOSITE_OPS - - indices:admin/template* - - indices:data/read/scroll* - indices: - '?kibana': - '*': - - INDICES_ALL - '?kibana-6': - '*': - - INDICES_ALL - '?kibana_*': - '*': - - INDICES_ALL - '?tasks': - '*': - - INDICES_ALL - '?management-beats*': - '*': - - INDICES_ALL - '*': - '*': - - "indices:admin/aliases*" - -# For logstash and beats -opendistro_security_logstash: - cluster: - - CLUSTER_MONITOR - - CLUSTER_COMPOSITE_OPS - - indices:admin/template/get - - indices:admin/template/put - indices: - 'logstash-*': - '*': - - CRUD - - CREATE_INDEX - '*beat*': - '*': - - CRUD - - CREATE_INDEX - -# Allows adding and modifying repositories and creating and restoring snapshots -opendistro_security_manage_snapshots: - cluster: - - MANAGE_SNAPSHOTS - indices: - '*': - '*': - - "indices:data/write/index" - - "indices:admin/create" - -# Allows each user to access own named index -opendistro_security_own_index: - cluster: - - CLUSTER_COMPOSITE_OPS - indices: - '${user_name}': - '*': - - INDICES_ALL - - -### LEGACY ROLES, FOR COMPATIBILITY ONLY -### WILL BE REMOVED IN SG7, DO NOT USE ANYMORE - -opendistro_security_readonly_and_monitor: - cluster: - - CLUSTER_MONITOR - - CLUSTER_COMPOSITE_OPS_RO - indices: - '*': - '*': - - READ diff --git a/src/test/resources/legacy/securityconfig_v6/roles_mapping.yml b/src/test/resources/legacy/securityconfig_v6/roles_mapping.yml deleted file mode 100644 index 588ba13f6e..0000000000 --- a/src/test/resources/legacy/securityconfig_v6/roles_mapping.yml +++ /dev/null @@ -1,34 +0,0 @@ -# In this file users, backendroles and hosts can be mapped to Security roles. -# Permissions for Security roles are configured in opendistro_security_roles.yml - -opendistro_security_all_access: - readonly: true - backendroles: - - admin - -opendistro_security_logstash: - backendroles: - - logstash - -opendistro_security_kibana_server: - readonly: true - users: - - kibanaserver - -opendistro_security_kibana_user: - backendroles: - - kibanauser - -opendistro_security_readall: - readonly: true - backendroles: - - readall - -opendistro_security_manage_snapshots: - readonly: true - backendroles: - - snapshotrestore - -opendistro_security_own_index: - users: - - '*' diff --git a/src/test/resources/legacy/securityconfig_v6/whitelist.yml b/src/test/resources/legacy/securityconfig_v6/whitelist.yml deleted file mode 100644 index 49af0d7ac7..0000000000 --- a/src/test/resources/legacy/securityconfig_v6/whitelist.yml +++ /dev/null @@ -1,12 +0,0 @@ -#this name must be config -config: - enabled: false - requests: - /_cat/nodes: - - GET - /_cat/plugins: - - GET - /_cluster/health: - - GET - /_cluster/settings: - - GET From e997aed319865224a2e3009a6490d9470734d8ef Mon Sep 17 00:00:00 2001 From: Nils Bandener Date: Tue, 24 Sep 2024 10:03:01 +0200 Subject: [PATCH 4/9] Removed v6 configuration related code Signed-off-by: Nils Bandener --- .../LegacyConfigV6AutoConversionTest.java | 79 ------------------- 1 file changed, 79 deletions(-) delete mode 100644 src/integrationTest/java/org/opensearch/security/legacy/LegacyConfigV6AutoConversionTest.java diff --git a/src/integrationTest/java/org/opensearch/security/legacy/LegacyConfigV6AutoConversionTest.java b/src/integrationTest/java/org/opensearch/security/legacy/LegacyConfigV6AutoConversionTest.java deleted file mode 100644 index 35fd179019..0000000000 --- a/src/integrationTest/java/org/opensearch/security/legacy/LegacyConfigV6AutoConversionTest.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * 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. - * - */ -package org.opensearch.security.legacy; - -import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope; -import org.junit.ClassRule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.opensearch.test.framework.TestSecurityConfig; -import org.opensearch.test.framework.cluster.ClusterManager; -import org.opensearch.test.framework.cluster.LocalCluster; -import org.opensearch.test.framework.cluster.TestRestClient; - -@RunWith(com.carrotsearch.randomizedtesting.RandomizedRunner.class) -@ThreadLeakScope(ThreadLeakScope.Scope.NONE) -public class LegacyConfigV6AutoConversionTest { - static final TestSecurityConfig LEGACY_CONFIG = new TestSecurityConfig()// - .rawConfigurationDocumentYaml("config", """ - opendistro_security: - dynamic: - authc: - basic_internal_auth_domain: - http_enabled: true - order: 4 - http_authenticator: - type: basic - challenge: true - authentication_backend: - type: intern - """)// - .rawConfigurationDocumentYaml("internalusers", """ - admin: - readonly: true - hash: $2a$12$VcCDgh2NDk07JGN0rjGbM.Ad41qVR/YFJcgHp0UGns5JDymv..TOG - roles: - - admin - attributes: - attribute1: value1 - """)// - .rawConfigurationDocumentYaml("roles", """ - all_access: - readonly: true - cluster: - - UNLIMITED - indices: - '*': - '*': - - UNLIMITED - tenants: - admin_tenant: RW - """)// - .rawConfigurationDocumentYaml("rolesmapping", """ - all_access: - readonly: true - backendroles: - - admin - """); - - @ClassRule - public static LocalCluster cluster = new LocalCluster.Builder().clusterManager(ClusterManager.THREE_CLUSTER_MANAGERS) - .config(LEGACY_CONFIG) - .build(); - - @Test - public void checkAuthc() { - try (TestRestClient client = cluster.getRestClient("admin", "admin")) { - TestRestClient.HttpResponse response = client.get("_opendistro/_security/authinfo"); - System.out.println(response.getBody()); - } - } -} From 2239566bd0f8342f0bf7003f21f264869268e22e Mon Sep 17 00:00:00 2001 From: Nils Bandener Date: Wed, 25 Sep 2024 06:52:20 +0200 Subject: [PATCH 5/9] Fixes Signed-off-by: Nils Bandener --- .../ConfigurationLoaderSecurity7.java | 17 ----------------- .../impl/SecurityDynamicConfiguration.java | 2 +- .../opensearch/security/SecurityAdminTests.java | 15 --------------- src/test/resources/ldap/internal_users.yml | 3 +++ 4 files changed, 4 insertions(+), 33 deletions(-) diff --git a/src/main/java/org/opensearch/security/configuration/ConfigurationLoaderSecurity7.java b/src/main/java/org/opensearch/security/configuration/ConfigurationLoaderSecurity7.java index 2ac2bf8cdb..f1062cede3 100644 --- a/src/main/java/org/opensearch/security/configuration/ConfigurationLoaderSecurity7.java +++ b/src/main/java/org/opensearch/security/configuration/ConfigurationLoaderSecurity7.java @@ -300,23 +300,6 @@ private SecurityDynamicConfiguration toConfig(GetResponse singleGetResponse, log.debug("Load " + id + " with version " + configVersion); } - if (CType.ACTIONGROUPS.toLCString().equals(id)) { - try { - return SecurityDynamicConfiguration.fromJson( - jsonAsString, - CType.fromString(id), - configVersion, - seqNo, - primaryTerm, - acceptInvalid - ); - } catch (Exception e) { - if (log.isDebugEnabled()) { - log.debug("Unable to load " + id + " with version " + configVersion + " - Try loading legacy format ..."); - } - return SecurityDynamicConfiguration.fromJson(jsonAsString, CType.fromString(id), 0, seqNo, primaryTerm, acceptInvalid); - } - } return SecurityDynamicConfiguration.fromJson( jsonAsString, CType.fromString(id), diff --git a/src/main/java/org/opensearch/security/securityconf/impl/SecurityDynamicConfiguration.java b/src/main/java/org/opensearch/security/securityconf/impl/SecurityDynamicConfiguration.java index a56d8e8aa3..6abe37b542 100644 --- a/src/main/java/org/opensearch/security/securityconf/impl/SecurityDynamicConfiguration.java +++ b/src/main/java/org/opensearch/security/securityconf/impl/SecurityDynamicConfiguration.java @@ -127,7 +127,7 @@ public static SecurityDynamicConfiguration fromMap(Map ma public static void validate(SecurityDynamicConfiguration sdc, int version, CType ctype) throws IOException { if (version < 2) { - throw new IOException("Config version " + version + " is not supported"); + throw new IOException("Config version " + version + " is not supported; config type: " + ctype); } if (sdc.get_meta() == null) { diff --git a/src/test/java/org/opensearch/security/SecurityAdminTests.java b/src/test/java/org/opensearch/security/SecurityAdminTests.java index 44c2934469..0f2d5b3caa 100644 --- a/src/test/java/org/opensearch/security/SecurityAdminTests.java +++ b/src/test/java/org/opensearch/security/SecurityAdminTests.java @@ -598,21 +598,6 @@ public void testSecurityAdminValidateConfig() throws Exception { returnCode = SecurityAdmin.execute(argsAsList.toArray(new String[0])); Assert.assertNotEquals(0, returnCode); - argsAsList = new ArrayList<>(); - addDirectoryPath(argsAsList, TEST_RESOURCE_ABSOLUTE_PATH + "legacy/securityconfig_v6"); - argsAsList.add("-vc"); - - returnCode = SecurityAdmin.execute(argsAsList.toArray(new String[0])); - Assert.assertNotEquals(0, returnCode); - - argsAsList = new ArrayList<>(); - addDirectoryPath(argsAsList, TEST_RESOURCE_ABSOLUTE_PATH + "legacy/securityconfig_v6"); - argsAsList.add("-vc"); - argsAsList.add("6"); - - returnCode = SecurityAdmin.execute(argsAsList.toArray(new String[0])); - assertThat(returnCode, is(0)); - argsAsList = new ArrayList<>(); addDirectoryPath(argsAsList, TEST_RESOURCE_ABSOLUTE_PATH); argsAsList.add("-vc"); diff --git a/src/test/resources/ldap/internal_users.yml b/src/test/resources/ldap/internal_users.yml index ae3789c1c8..d07fe17947 100644 --- a/src/test/resources/ldap/internal_users.yml +++ b/src/test/resources/ldap/internal_users.yml @@ -1,3 +1,6 @@ +_meta: + type: "internalusers" + config_version: 2 admin: hash: $2a$12$VcCDgh2NDk07JGN0rjGbM.Ad41qVR/YFJcgHp0UGns5JDymv..TOG #password is: admin From dc50298e3e70804563f00ecd0bb8187b24f9c28d Mon Sep 17 00:00:00 2001 From: Nils Bandener Date: Wed, 25 Sep 2024 07:00:40 +0200 Subject: [PATCH 6/9] Removed v6 stuff from securityadmin Signed-off-by: Nils Bandener --- .../security/tools/SecurityAdmin.java | 211 +++++------------- .../security/SecurityAdminTests.java | 77 ------- 2 files changed, 62 insertions(+), 226 deletions(-) diff --git a/src/main/java/org/opensearch/security/tools/SecurityAdmin.java b/src/main/java/org/opensearch/security/tools/SecurityAdmin.java index f0755e2162..6080224c36 100644 --- a/src/main/java/org/opensearch/security/tools/SecurityAdmin.java +++ b/src/main/java/org/opensearch/security/tools/SecurityAdmin.java @@ -139,7 +139,6 @@ @SuppressWarnings("deprecation") public class SecurityAdmin { - private static final boolean CREATE_AS_LEGACY = Boolean.parseBoolean(System.getenv("OPENDISTRO_SECURITY_ADMIN_CREATE_AS_LEGACY")); private static final boolean ALLOW_MIXED = Boolean.parseBoolean(System.getenv("OPENDISTRO_SECURITY_ADMIN_ALLOW_MIXED_CLUSTER")); private static final String OPENDISTRO_SECURITY_TS_PASS = "OPENDISTRO_SECURITY_TS_PASS"; private static final String OPENDISTRO_SECURITY_KS_PASS = "OPENDISTRO_SECURITY_KS_PASS"; @@ -586,9 +585,7 @@ public static int execute(final String[] args) throws Exception { if (updateSettings != null) { Settings indexSettings = Settings.builder().put("index.number_of_replicas", updateSettings).build(); Response res = restHighLevelClient.getLowLevelClient() - .performRequest( - new Request("PUT", "/_plugins/_security/configupdate?config_types=" + Joiner.on(",").join(getTypes(true))) - ); + .performRequest(new Request("PUT", "/_plugins/_security/configupdate?config_types=" + Joiner.on(",").join(getTypes()))); if (res.getStatusLine().getStatusCode() != 200) { System.out.println("Unable to reload configuration because return code was " + res.getStatusLine()); @@ -609,9 +606,7 @@ public static int execute(final String[] args) throws Exception { if (reload) { Response res = restHighLevelClient.getLowLevelClient() - .performRequest( - new Request("PUT", "/_plugins/_security/configupdate?config_types=" + Joiner.on(",").join(getTypes(false))) - ); + .performRequest(new Request("PUT", "/_plugins/_security/configupdate?config_types=" + Joiner.on(",").join(getTypes()))); if (res.getStatusLine().getStatusCode() != 200) { System.out.println("Unable to reload configuration because return code was " + res.getStatusLine()); @@ -641,9 +636,7 @@ public static int execute(final String[] args) throws Exception { .put("index.auto_expand_replicas", replicaAutoExpand ? "0-all" : "false") .build(); Response res = restHighLevelClient.getLowLevelClient() - .performRequest( - new Request("PUT", "/_plugins/_security/configupdate?config_types=" + Joiner.on(",").join(getTypes(false))) - ); + .performRequest(new Request("PUT", "/_plugins/_security/configupdate?config_types=" + Joiner.on(",").join(getTypes()))); if (res.getStatusLine().getStatusCode() != 200) { System.out.println("Unable to reload configuration because return code was " + whoAmIRes.getStatusLine()); @@ -837,66 +830,30 @@ public static int execute(final String[] args) throws Exception { } } - final boolean createLegacyMode = !indexExists && CREATE_AS_LEGACY; - - if (createLegacyMode) { - System.out.println( - "We forcibly create the new index in legacy mode so that ES 6 config can be uploaded. To move to v7 configs youneed to migrate." - ); - } - - final boolean legacy = createLegacyMode - || (indexExists - && securityIndex.getMappings() != null - && securityIndex.getMappings().get(index) != null - && securityIndex.getMappings().get(index).getSourceAsMap().containsKey("security")); - - if (legacy) { - System.out.println("Legacy index '" + index + "' (ES 6) detected (or forced). You should migrate the configuration!"); - } - if (retrieve) { String date = DATE_FORMAT.format(new Date()); - boolean success = retrieveFile(restHighLevelClient, cd + "config_" + date + ".yml", index, "config", legacy); - success = retrieveFile(restHighLevelClient, cd + "roles_" + date + ".yml", index, "roles", legacy) && success; - success = retrieveFile(restHighLevelClient, cd + "roles_mapping_" + date + ".yml", index, "rolesmapping", legacy) - && success; - success = retrieveFile(restHighLevelClient, cd + "internal_users_" + date + ".yml", index, "internalusers", legacy) - && success; - success = retrieveFile(restHighLevelClient, cd + "action_groups_" + date + ".yml", index, "actiongroups", legacy) - && success; - success = retrieveFile(restHighLevelClient, cd + "audit_" + date + ".yml", index, "audit", legacy) && success; + boolean success = retrieveFile(restHighLevelClient, cd + "config_" + date + ".yml", index, "config"); + success = retrieveFile(restHighLevelClient, cd + "roles_" + date + ".yml", index, "roles") && success; + success = retrieveFile(restHighLevelClient, cd + "roles_mapping_" + date + ".yml", index, "rolesmapping") && success; + success = retrieveFile(restHighLevelClient, cd + "internal_users_" + date + ".yml", index, "internalusers") && success; + success = retrieveFile(restHighLevelClient, cd + "action_groups_" + date + ".yml", index, "actiongroups") && success; + success = retrieveFile(restHighLevelClient, cd + "audit_" + date + ".yml", index, "audit") && success; - if (!legacy) { - success = retrieveFile(restHighLevelClient, cd + "security_tenants_" + date + ".yml", index, "tenants", legacy) - && success; - } + success = retrieveFile(restHighLevelClient, cd + "security_tenants_" + date + ".yml", index, "tenants") && success; final boolean populateFileIfEmpty = true; - success = retrieveFile(restHighLevelClient, cd + "nodes_dn_" + date + ".yml", index, "nodesdn", legacy, populateFileIfEmpty) + success = retrieveFile(restHighLevelClient, cd + "nodes_dn_" + date + ".yml", index, "nodesdn", populateFileIfEmpty) + && success; + success = retrieveFile(restHighLevelClient, cd + "whitelist_" + date + ".yml", index, "whitelist", populateFileIfEmpty) + && success; + success = retrieveFile(restHighLevelClient, cd + "allowlist_" + date + ".yml", index, "allowlist", populateFileIfEmpty) && success; - success = retrieveFile( - restHighLevelClient, - cd + "whitelist_" + date + ".yml", - index, - "whitelist", - legacy, - populateFileIfEmpty - ) && success; - success = retrieveFile( - restHighLevelClient, - cd + "allowlist_" + date + ".yml", - index, - "allowlist", - legacy, - populateFileIfEmpty - ) && success; return (success ? 0 : -1); } if (backup != null) { - return backup(restHighLevelClient, index, new File(backup), legacy); + return backup(restHighLevelClient, index, new File(backup)); } boolean isCdAbs = new File(cd).isAbsolute(); @@ -919,7 +876,7 @@ public static int execute(final String[] args) throws Exception { return (-1); } - boolean success = uploadFile(restHighLevelClient, file, index, type, legacy, resolveEnvVars); + boolean success = uploadFile(restHighLevelClient, file, index, type, resolveEnvVars); if (!success) { System.out.println("ERR: cannot upload configuration, see errors above"); @@ -934,7 +891,7 @@ public static int execute(final String[] args) throws Exception { return (success ? 0 : -1); } - return upload(restHighLevelClient, index, cd, legacy, expectedNodeCount, resolveEnvVars); + return upload(restHighLevelClient, index, cd, expectedNodeCount, resolveEnvVars); } } @@ -1009,10 +966,9 @@ private static boolean uploadFile( final String filepath, final String index, final String _id, - final boolean legacy, boolean resolveEnvVars ) { - return uploadFile(restHighLevelClient, filepath, index, _id, legacy, resolveEnvVars, false); + return uploadFile(restHighLevelClient, filepath, index, _id, resolveEnvVars, false); } private static boolean uploadFile( @@ -1020,37 +976,22 @@ private static boolean uploadFile( final String filepath, final String index, final String _id, - final boolean legacy, boolean resolveEnvVars, final boolean populateEmptyIfMissing ) { String id = _id; - if (legacy) { - id = _id; - - try { - ConfigHelper.fromYamlFile(filepath, CType.fromString(_id), 2, 0, 0); - } catch (Exception e) { - System.out.println("ERR: Seems " + filepath + " is not in legacy format: " + e); - return false; - } - - } else { - try { - ConfigHelper.fromYamlFile(filepath, CType.fromString(_id), 2, 0, 0); - } catch (Exception e) { - System.out.println("ERR: Seems " + filepath + " is not in OpenSearch Security 7 format: " + e); - return false; - } + try { + ConfigHelper.fromYamlFile(filepath, CType.fromString(_id), 2, 0, 0); + } catch (Exception e) { + System.out.println("ERR: Seems " + filepath + " is not in OpenSearch Security 7 format: " + e); + return false; } - System.out.println("Will update '" + "/" + id + "' with " + filepath + " " + (legacy ? "(legacy mode)" : "")); + System.out.println("Will update '" + "/" + id + "' with " + filepath); - try ( - Reader reader = ConfigHelper.createFileOrStringReader(CType.fromString(_id), legacy ? 1 : 2, filepath, populateEmptyIfMissing) - ) { + try (Reader reader = ConfigHelper.createFileOrStringReader(CType.fromString(_id), 2, filepath, populateEmptyIfMissing)) { final String content = CharStreams.toString(reader); final String res = restHighLevelClient.index( new IndexRequest(index).id(id) @@ -1078,10 +1019,9 @@ private static boolean retrieveFile( final RestHighLevelClient restHighLevelClient, final String filepath, final String index, - final String _id, - final boolean legacy + final String _id ) { - return retrieveFile(restHighLevelClient, filepath, index, _id, legacy, false); + return retrieveFile(restHighLevelClient, filepath, index, _id, false); } private static boolean retrieveFile( @@ -1089,17 +1029,11 @@ private static boolean retrieveFile( final String filepath, final String index, final String _id, - final boolean legacy, final boolean populateFileIfEmpty ) { String id = _id; - if (legacy) { - id = _id; - - } - - System.out.println("Will retrieve '" + "/" + id + "' into " + filepath + " " + (legacy ? "(legacy mode)" : "")); + System.out.println("Will retrieve '" + "/" + id + "' into " + filepath); try (Writer writer = new FileWriter(filepath, StandardCharsets.UTF_8)) { final GetResponse response = restHighLevelClient.get( @@ -1111,7 +1045,7 @@ private static boolean retrieveFile( String yaml; if (isEmpty) { if (populateFileIfEmpty) { - yaml = ConfigHelper.createEmptySdcYaml(CType.fromString(_id), legacy ? 1 : 2); + yaml = ConfigHelper.createEmptySdcYaml(CType.fromString(_id), 2); } else { System.out.println(" FAIL: Configuration for '" + _id + "' failed because of empty source"); return false; @@ -1125,21 +1059,13 @@ private static boolean retrieveFile( } - if (legacy) { - try { - ConfigHelper.fromYamlString(yaml, CType.fromString(_id), 1, 0, 0); - } catch (Exception e) { - System.out.println("ERR: Seems " + _id + " from cluster is not in legacy format: " + e); - return false; - } - } else { - try { - ConfigHelper.fromYamlString(yaml, CType.fromString(_id), 2, 0, 0); - } catch (Exception e) { - System.out.println("ERR: Seems " + _id + " from cluster is not in 7 format: " + e); - return false; - } + try { + ConfigHelper.fromYamlString(yaml, CType.fromString(_id), 2, 0, 0); + } catch (Exception e) { + System.out.println("ERR: Seems " + _id + " from cluster is not in 7 format: " + e); + return false; } + } writer.write(yaml); @@ -1417,53 +1343,43 @@ private static int createConfigIndex(RestHighLevelClient restHighLevelClient, St } } - private static int backup(RestHighLevelClient tc, String index, File backupDir, boolean legacy) { + private static int backup(RestHighLevelClient tc, String index, File backupDir) { backupDir.mkdirs(); - boolean success = retrieveFile(tc, backupDir.getAbsolutePath() + "/config.yml", index, "config", legacy); - success = retrieveFile(tc, backupDir.getAbsolutePath() + "/roles.yml", index, "roles", legacy) && success; + boolean success = retrieveFile(tc, backupDir.getAbsolutePath() + "/config.yml", index, "config"); + success = retrieveFile(tc, backupDir.getAbsolutePath() + "/roles.yml", index, "roles") && success; - success = retrieveFile(tc, backupDir.getAbsolutePath() + "/roles_mapping.yml", index, "rolesmapping", legacy) && success; - success = retrieveFile(tc, backupDir.getAbsolutePath() + "/internal_users.yml", index, "internalusers", legacy) && success; - success = retrieveFile(tc, backupDir.getAbsolutePath() + "/action_groups.yml", index, "actiongroups", legacy) && success; + success = retrieveFile(tc, backupDir.getAbsolutePath() + "/roles_mapping.yml", index, "rolesmapping") && success; + success = retrieveFile(tc, backupDir.getAbsolutePath() + "/internal_users.yml", index, "internalusers") && success; + success = retrieveFile(tc, backupDir.getAbsolutePath() + "/action_groups.yml", index, "actiongroups") && success; - if (!legacy) { - success = retrieveFile(tc, backupDir.getAbsolutePath() + "/tenants.yml", index, "tenants", legacy) && success; - } - success = retrieveFile(tc, backupDir.getAbsolutePath() + "/nodes_dn.yml", index, "nodesdn", legacy, true) && success; - success = retrieveFile(tc, backupDir.getAbsolutePath() + "/whitelist.yml", index, "whitelist", legacy, true) && success; - success = retrieveFile(tc, backupDir.getAbsolutePath() + "/allowlist.yml", index, "allowlist", legacy, true) && success; - success = retrieveFile(tc, backupDir.getAbsolutePath() + "/audit.yml", index, "audit", legacy) && success; + success = retrieveFile(tc, backupDir.getAbsolutePath() + "/tenants.yml", index, "tenants") && success; + success = retrieveFile(tc, backupDir.getAbsolutePath() + "/nodes_dn.yml", index, "nodesdn", true) && success; + success = retrieveFile(tc, backupDir.getAbsolutePath() + "/whitelist.yml", index, "whitelist", true) && success; + success = retrieveFile(tc, backupDir.getAbsolutePath() + "/allowlist.yml", index, "allowlist", true) && success; + success = retrieveFile(tc, backupDir.getAbsolutePath() + "/audit.yml", index, "audit") && success; return success ? 0 : -1; } - private static int upload( - RestHighLevelClient tc, - String index, - String cd, - boolean legacy, - int expectedNodeCount, - boolean resolveEnvVars - ) throws IOException { - boolean success = uploadFile(tc, cd + "config.yml", index, "config", legacy, resolveEnvVars); - success = uploadFile(tc, cd + "roles.yml", index, "roles", legacy, resolveEnvVars) && success; - success = uploadFile(tc, cd + "roles_mapping.yml", index, "rolesmapping", legacy, resolveEnvVars) && success; + private static int upload(RestHighLevelClient tc, String index, String cd, int expectedNodeCount, boolean resolveEnvVars) + throws IOException { + boolean success = uploadFile(tc, cd + "config.yml", index, "config", resolveEnvVars); + success = uploadFile(tc, cd + "roles.yml", index, "roles", resolveEnvVars) && success; + success = uploadFile(tc, cd + "roles_mapping.yml", index, "rolesmapping", resolveEnvVars) && success; - success = uploadFile(tc, cd + "internal_users.yml", index, "internalusers", legacy, resolveEnvVars) && success; - success = uploadFile(tc, cd + "action_groups.yml", index, "actiongroups", legacy, resolveEnvVars) && success; + success = uploadFile(tc, cd + "internal_users.yml", index, "internalusers", resolveEnvVars) && success; + success = uploadFile(tc, cd + "action_groups.yml", index, "actiongroups", resolveEnvVars) && success; - if (!legacy) { - success = uploadFile(tc, cd + "tenants.yml", index, "tenants", legacy, resolveEnvVars) && success; - } + success = uploadFile(tc, cd + "tenants.yml", index, "tenants", resolveEnvVars) && success; - success = uploadFile(tc, cd + "nodes_dn.yml", index, "nodesdn", legacy, resolveEnvVars, true) && success; - success = uploadFile(tc, cd + "whitelist.yml", index, "whitelist", legacy, resolveEnvVars) && success; + success = uploadFile(tc, cd + "nodes_dn.yml", index, "nodesdn", resolveEnvVars, true) && success; + success = uploadFile(tc, cd + "whitelist.yml", index, "whitelist", resolveEnvVars) && success; if (new File(cd + "audit.yml").exists()) { - success = uploadFile(tc, cd + "audit.yml", index, "audit", legacy, resolveEnvVars) && success; + success = uploadFile(tc, cd + "audit.yml", index, "audit", resolveEnvVars) && success; } if (new File(cd + "allowlist.yml").exists()) { - success = uploadFile(tc, cd + "allowlist.yml", index, "allowlist", legacy, resolveEnvVars) && success; + success = uploadFile(tc, cd + "allowlist.yml", index, "allowlist", resolveEnvVars) && success; } if (!success) { @@ -1472,8 +1388,8 @@ private static int upload( } Response cur = tc.getLowLevelClient() - .performRequest(new Request("PUT", "/_plugins/_security/configupdate?config_types=" + Joiner.on(",").join(getTypes((legacy))))); - success = checkConfigUpdateResponse(cur, expectedNodeCount, getTypes(legacy).length) && success; + .performRequest(new Request("PUT", "/_plugins/_security/configupdate?config_types=" + Joiner.on(",").join(getTypes()))); + success = checkConfigUpdateResponse(cur, expectedNodeCount, getTypes().length) && success; System.out.println("Done with " + (success ? "success" : "failures")); return (success ? 0 : -1); @@ -1539,10 +1455,7 @@ private static boolean validateConfigFile(String file, CType cType, int versi } } - private static String[] getTypes(boolean legacy) { - if (legacy) { - return new String[] { "config", "roles", "rolesmapping", "internalusers", "actiongroups", "nodesdn", "audit" }; - } + private static String[] getTypes() { return CType.lcStringValues().toArray(new String[0]); } diff --git a/src/test/java/org/opensearch/security/SecurityAdminTests.java b/src/test/java/org/opensearch/security/SecurityAdminTests.java index 0f2d5b3caa..45c5c0e2a1 100644 --- a/src/test/java/org/opensearch/security/SecurityAdminTests.java +++ b/src/test/java/org/opensearch/security/SecurityAdminTests.java @@ -241,41 +241,6 @@ public void testSecurityAdminInvalidCert() throws Exception { assertThat((rh.executeGetRequest("_plugins/_security/health?pretty")).getStatusCode(), is(HttpStatus.SC_OK)); } - @Test - public void testSecurityAdminV6Update() throws Exception { - final Settings settings = Settings.builder() - .put("plugins.security.ssl.http.enabled", true) - .put("plugins.security.ssl.http.keystore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("node-0-keystore.jks")) - .put("plugins.security.ssl.http.truststore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("truststore.jks")) - .build(); - setup(Settings.EMPTY, null, settings, false); - - final String prefix = getResourceFolder() == null ? "" : getResourceFolder() + "/"; - - List argsAsList = new ArrayList<>(); - argsAsList.add("-ts"); - argsAsList.add( - Objects.requireNonNull(FileHelper.getAbsoluteFilePathFromClassPath(prefix + "truststore.jks")).toFile().getAbsolutePath() - ); - argsAsList.add("-ks"); - argsAsList.add( - Objects.requireNonNull(FileHelper.getAbsoluteFilePathFromClassPath(prefix + "kirk-keystore.jks")).toFile().getAbsolutePath() - ); - argsAsList.add("-p"); - argsAsList.add(String.valueOf(clusterInfo.httpPort)); - argsAsList.add("-cn"); - argsAsList.add(clusterInfo.clustername); - addDirectoryPath(argsAsList, new File("./legacy/securityconfig_v6").getAbsolutePath()); - argsAsList.add("-nhnv"); - - int returnCode = SecurityAdmin.execute(argsAsList.toArray(new String[0])); - Assert.assertNotEquals(0, returnCode); - - RestHelper rh = restHelper(); - - assertThat(rh.executeGetRequest("_opendistro/_security/health?pretty").getStatusCode(), is(HttpStatus.SC_SERVICE_UNAVAILABLE)); - } - @Test public void testSecurityAdminRegularUpdate() throws Exception { final Settings settings = Settings.builder() @@ -401,48 +366,6 @@ public void testSecurityAdminSingularV7Updates() throws Exception { assertNotContains(res, "*DOWN*"); } - @Test - public void testSecurityAdminSingularV6Updates() throws Exception { - final Settings settings = Settings.builder() - .put("plugins.security.ssl.http.enabled", true) - .put("plugins.security.ssl.http.keystore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("node-0-keystore.jks")) - .put("plugins.security.ssl.http.truststore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("truststore.jks")) - .build(); - setup(Settings.EMPTY, new DynamicSecurityConfig(), settings, true); - - final String prefix = getResourceFolder() == null ? "" : getResourceFolder() + "/"; - - List argsAsList = new ArrayList<>(); - argsAsList.add("-ts"); - argsAsList.add( - Objects.requireNonNull(FileHelper.getAbsoluteFilePathFromClassPath(prefix + "truststore.jks")).toFile().getAbsolutePath() - ); - argsAsList.add("-ks"); - argsAsList.add( - Objects.requireNonNull(FileHelper.getAbsoluteFilePathFromClassPath(prefix + "kirk-keystore.jks")).toFile().getAbsolutePath() - ); - argsAsList.add("-p"); - argsAsList.add(String.valueOf(clusterInfo.httpPort)); - argsAsList.add("-cn"); - argsAsList.add(clusterInfo.clustername); - argsAsList.add("-f"); - argsAsList.add(new File(TEST_RESOURCE_RELATIVE_PATH + "legacy/securityconfig_v6/config.yml").getAbsolutePath()); - argsAsList.add("-t"); - argsAsList.add("config"); - argsAsList.add("-nhnv"); - - int returnCode = SecurityAdmin.execute(argsAsList.toArray(new String[0])); - Assert.assertNotEquals(0, returnCode); - - RestHelper rh = restHelper(); - HttpResponse res; - - assertThat((res = rh.executeGetRequest("_opendistro/_security/health?pretty")).getStatusCode(), is(HttpStatus.SC_OK)); - assertContains(res, "*UP*"); - assertContains(res, "*strict*"); - assertNotContains(res, "*DOWN*"); - } - @Test public void testSecurityAdminInvalidYml() throws Exception { final Settings settings = Settings.builder() From 7f5bc7d5982f7fca4a90d932cdb016f5906dbe3d Mon Sep 17 00:00:00 2001 From: Nils Bandener Date: Wed, 25 Sep 2024 07:08:43 +0200 Subject: [PATCH 7/9] Removed unnecessary mockito stubbings Signed-off-by: Nils Bandener --- .../security/dlic/rest/validation/EndpointValidatorTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/test/java/org/opensearch/security/dlic/rest/validation/EndpointValidatorTest.java b/src/test/java/org/opensearch/security/dlic/rest/validation/EndpointValidatorTest.java index 19fba4caee..d87fb9ee77 100644 --- a/src/test/java/org/opensearch/security/dlic/rest/validation/EndpointValidatorTest.java +++ b/src/test/java/org/opensearch/security/dlic/rest/validation/EndpointValidatorTest.java @@ -353,7 +353,6 @@ public void regularUserCanNotChangeObjectWithRestAdminPermissionsForNewRoles() t restAdminPermissions().forEach(array::add); doReturn(CType.ROLES).when(configuration).getCType(); - when(configuration.getVersion()).thenReturn(2); when(configuration.getImplementingClass()).thenCallRealMethod(); when(configuration.exists("some_role")).thenReturn(false); when(restApiAdminPrivilegesEvaluator.containsRestApiAdminPermissions(any(Object.class))).thenCallRealMethod(); @@ -380,7 +379,6 @@ public void regularUserCanNotChangeObjectWithRestAdminPermissionsForExitingActio @Test public void regularUserCanNotChangeObjectWithRestAdminPermissionsForMewActionGroups() throws Exception { doReturn(CType.ACTIONGROUPS).when(configuration).getCType(); - when(configuration.getVersion()).thenReturn(2); when(configuration.getImplementingClass()).thenCallRealMethod(); when(configuration.exists("some_ag")).thenReturn(false); when(restApiAdminPrivilegesEvaluator.containsRestApiAdminPermissions(any(Object.class))).thenCallRealMethod(); From 407b8f60199cc3f02b61ad7f22bd4e98c114b211 Mon Sep 17 00:00:00 2001 From: Nils Bandener Date: Wed, 25 Sep 2024 07:25:12 +0200 Subject: [PATCH 8/9] Fix Signed-off-by: Nils Bandener --- .../securityconf/impl/SecurityDynamicConfiguration.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/opensearch/security/securityconf/impl/SecurityDynamicConfiguration.java b/src/main/java/org/opensearch/security/securityconf/impl/SecurityDynamicConfiguration.java index 6abe37b542..fb4d4afd02 100644 --- a/src/main/java/org/opensearch/security/securityconf/impl/SecurityDynamicConfiguration.java +++ b/src/main/java/org/opensearch/security/securityconf/impl/SecurityDynamicConfiguration.java @@ -67,7 +67,11 @@ public class SecurityDynamicConfiguration implements ToXContent { private int version = CURRENT_VERSION; public static SecurityDynamicConfiguration empty(CType ctype) { - return new SecurityDynamicConfiguration(ctype); + SecurityDynamicConfiguration result = new SecurityDynamicConfiguration(ctype); + result._meta = new Meta(); + result._meta.setType(ctype.toLCString()); + result._meta.setConfig_version(CURRENT_VERSION); + return result; } @JsonIgnore From 5ebe5007c4a1cf64b1ed1129d0c3b1c7d9cdf11b Mon Sep 17 00:00:00 2001 From: Nils Bandener Date: Wed, 2 Oct 2024 09:09:53 +0200 Subject: [PATCH 9/9] Rolled back unnecessary change Signed-off-by: Nils Bandener --- .../security/configuration/ConfigurationRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/opensearch/security/configuration/ConfigurationRepository.java b/src/main/java/org/opensearch/security/configuration/ConfigurationRepository.java index a546db131e..9d64732e2d 100644 --- a/src/main/java/org/opensearch/security/configuration/ConfigurationRepository.java +++ b/src/main/java/org/opensearch/security/configuration/ConfigurationRepository.java @@ -639,7 +639,7 @@ public ConfigurationMap getConfigurationsFromIndex( private ConfigurationMap validate(ConfigurationMap conf, int expectedSize) throws InvalidConfigException { - if (conf == null || conf.size() < expectedSize) { + if (conf == null || conf.size() != expectedSize) { throw new InvalidConfigException("Retrieved only partial configuration"); }