Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove support for v6 configuration #4546

Merged
merged 9 commits into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@
}
}

@SuppressWarnings("removal")
public static <T> T convertValue(JsonNode jsonNode, JavaType jt) throws IOException {

final SecurityManager sm = System.getSecurityManager();

Check warning on line 273 in src/main/java/org/opensearch/security/DefaultObjectMapper.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/DefaultObjectMapper.java#L273

Added line #L273 was not covered by tests

if (sm != null) {
sm.checkPermission(new SpecialPermission());

Check warning on line 276 in src/main/java/org/opensearch/security/DefaultObjectMapper.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/DefaultObjectMapper.java#L276

Added line #L276 was not covered by tests
}

try {
return AccessController.doPrivileged((PrivilegedExceptionAction<T>) () -> objectMapper.convertValue(jsonNode, jt));
} catch (final PrivilegedActionException e) {
throw (IOException) e.getCause();

Check warning on line 282 in src/main/java/org/opensearch/security/DefaultObjectMapper.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/DefaultObjectMapper.java#L280-L282

Added lines #L280 - L282 were not covered by tests
}
}

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 {
cwperks marked this conversation as resolved.
Show resolved Hide resolved
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;

Check warning on line 38 in src/main/java/org/opensearch/security/configuration/ConfigurationMap.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/configuration/ConfigurationMap.java#L38

Added line #L38 was not covered by tests
}

if (!config.getCType().equals(ctype)) {
throw new RuntimeException("Stored configuration does not match type: " + ctype + "; " + config);

Check warning on line 42 in src/main/java/org/opensearch/security/configuration/ConfigurationMap.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/opensearch/security/configuration/ConfigurationMap.java#L42

Added line #L42 was not covered by tests
}

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
Loading