Skip to content

Commit

Permalink
Remove support for v6 configuration (opensearch-project#4546)
Browse files Browse the repository at this point in the history
Signed-off-by: Nils Bandener <nils.bandener@eliatra.com>
  • Loading branch information
nibix authored Oct 2, 2024
1 parent f315c4c commit 830b341
Show file tree
Hide file tree
Showing 86 changed files with 646 additions and 6,586 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -94,6 +97,13 @@ public class TestSecurityConfig {

private Map<String, ActionGroup> 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<String, String> rawConfigurationDocuments;

private String indexName = ".opendistro_security";

public TestSecurityConfig() {
Expand Down Expand Up @@ -212,6 +222,27 @@ public List<ActionGroup> 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;

Expand Down Expand Up @@ -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<String, String> 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<User> users) {
Expand All @@ -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<String, ? extends ToXContentObject> config) {
private void writeConfigToIndex(Client client, CType<?> configType, Map<String, ? extends ToXContentObject> config) {
try {
String json = configToJson(configType, config);

Expand All @@ -1008,11 +1048,23 @@ private void writeConfigToIndex(Client client, CType configType, Map<String, ? e
}
}

private void writeConfigToIndex(Client client, String documentId, String jsonString) {
try {
log.info("Writing raw security configuration into index {}:\n{}", documentId, jsonString);

BytesReference bytesReference = toByteReference(jsonString);
client.index(new IndexRequest(indexName).id(documentId).setRefreshPolicy(IMMEDIATE).source(documentId, bytesReference))
.actionGet();
} catch (Exception e) {
throw new RuntimeException("Error while initializing config for " + indexName, e);
}
}

private static BytesReference toByteReference(String string) throws UnsupportedEncodingException {
return BytesReference.fromByteBuffer(ByteBuffer.wrap(string.getBytes("utf-8")));
}

private void updateConfigInIndex(Client client, CType configType, Map<String, ? extends ToXContentObject> config) {
private void updateConfigInIndex(Client client, CType<?> configType, Map<String, ? extends ToXContentObject> config) {
try {
String json = configToJson(configType, config);
BytesReference bytesReference = toByteReference(json);
Expand All @@ -1025,7 +1077,7 @@ private void updateConfigInIndex(Client client, CType configType, Map<String, ?
}
}

private static String configToJson(CType configType, Map<String, ? extends ToXContentObject> config) throws IOException {
private static String configToJson(CType<?> configType, Map<String, ? extends ToXContentObject> config) throws IOException {
XContentBuilder builder = XContentFactory.jsonBuilder();

builder.startObject();
Expand All @@ -1043,11 +1095,11 @@ private static String configToJson(CType configType, Map<String, ? extends ToXCo
return builder.toString();
}

private void writeSingleEntryConfigToIndex(Client client, CType configType, ToXContentObject config) {
private void writeSingleEntryConfigToIndex(Client client, CType<?> 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();

Expand Down
16 changes: 16 additions & 0 deletions src/main/java/org/opensearch/security/DefaultObjectMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,22 @@ public static <T> T readValue(String string, JavaType jt) throws IOException {
}
}

@SuppressWarnings("removal")
public static <T> 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<T>) () -> objectMapper.convertValue(jsonNode, jt));
} catch (final PrivilegedActionException e) {
throw (IOException) e.getCause();
}
}

public static TypeFactory getTypeFactory() {
return objectMapper.getTypeFactory();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand All @@ -39,5 +34,5 @@ public interface ConfigurationChangeListener {
/**
* @param configuration not null updated configuration on that was subscribe current listener
*/
void onChange(Map<CType, SecurityDynamicConfiguration<?>> typeToConfig);
void onChange(ConfigurationMap typeToConfig);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -94,10 +92,10 @@ boolean isAuditConfigDocPresentInIndex() {
return isAuditConfigDocPresentInIndex.get();
}

Map<CType, SecurityDynamicConfiguration<?>> 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<CType, SecurityDynamicConfiguration<?>> rs = new HashMap<>(events.length);
ConfigurationMap.Builder result = new ConfigurationMap.Builder();
final boolean isDebugEnabled = log.isDebugEnabled();
loadAsync(events, new ConfigCallback() {

Expand All @@ -118,7 +116,8 @@ public void success(SecurityDynamicConfiguration<?> dConf) {
isAuditConfigDocPresentInIndex.set(true);
}

rs.put(dConf.getCType(), dConf);
result.with(dConf);

latch.countDown();
if (isDebugEnabled) {
log.debug(
Expand All @@ -142,7 +141,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
Expand All @@ -154,7 +153,7 @@ public void noData(String id) {
cType,
ConfigurationRepository.getDefaultConfigVersion()
);
rs.put(cType, empty);
result.with(empty);
latch.countDown();
return;
} catch (Exception e) {
Expand All @@ -172,7 +171,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) {
Expand Down Expand Up @@ -204,10 +203,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;
Expand Down Expand Up @@ -301,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),
Expand Down
Original file line number Diff line number Diff line change
@@ -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<CType<?>, SecurityDynamicConfiguration<?>> map;

private ConfigurationMap(ImmutableMap<CType<?>, SecurityDynamicConfiguration<?>> map) {
this.map = map;
}

public <T> SecurityDynamicConfiguration<T> get(CType<T> ctype) {
@SuppressWarnings("unchecked")
SecurityDynamicConfiguration<T> config = (SecurityDynamicConfiguration<T>) 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<CType<?>> keySet() {
return map.keySet();
}

public int size() {
return this.map.size();
}

public ImmutableMap<CType<?>, 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<CType<?>, SecurityDynamicConfiguration<?>> map = new ImmutableMap.Builder<>();

public Builder() {}

public <T> Builder with(SecurityDynamicConfiguration<T> 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());
}
}
}
Loading

0 comments on commit 830b341

Please sign in to comment.