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

REST API tests refactoring (Part 2) #4252

Merged
merged 1 commit into from
May 1, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
@@ -0,0 +1,79 @@
/*
willyborankin marked this conversation as resolved.
Show resolved Hide resolved
* 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.api;

import com.fasterxml.jackson.databind.JsonNode;
import org.junit.Test;

import org.opensearch.security.dlic.rest.api.Endpoint;
import org.opensearch.test.framework.cluster.TestRestClient;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.opensearch.security.dlic.rest.api.RestApiAdminPrivilegesEvaluator.CERTS_INFO_ACTION;
import static org.opensearch.security.support.ConfigConstants.SECURITY_RESTAPI_ADMIN_ENABLED;

public class SslCertsRestApiIntegrationTest extends AbstractApiIntegrationTest {

final static String REST_API_ADMIN_SSL_INFO = "rest-api-admin-ssl-info";

static {
clusterSettings.put(SECURITY_RESTAPI_ADMIN_ENABLED, true);
reta marked this conversation as resolved.
Show resolved Hide resolved
testSecurityConfig.withRestAdminUser(REST_ADMIN_USER, allRestAdminPermissions())
.withRestAdminUser(REST_API_ADMIN_SSL_INFO, restAdminPermission(Endpoint.SSL, CERTS_INFO_ACTION));
}

protected String sslCertsPath() {
return super.apiPath("ssl", "certs");
}

@Test
public void certsInfoForbiddenForRegularUser() throws Exception {
withUser(NEW_USER, client -> forbidden(() -> client.get(sslCertsPath())));
}

@Test
public void certsInfoForbiddenForAdminUser() throws Exception {
withUser(NEW_USER, client -> forbidden(() -> client.get(sslCertsPath())));
}

@Test
public void certsInfoAvailableForTlsAdmin() throws Exception {
withUser(ADMIN_USER_NAME, localCluster.getAdminCertificate(), this::verifySSLCertsInfo);
}

@Test
public void certsInfoAvailableForRestAdmin() throws Exception {
withUser(REST_ADMIN_USER, this::verifySSLCertsInfo);
withUser(REST_API_ADMIN_SSL_INFO, this::verifySSLCertsInfo);
}

private void verifySSLCertsInfo(final TestRestClient client) throws Exception {
final var response = ok(() -> client.get(sslCertsPath()));

final var body = response.bodyAsJsonNode();
willyborankin marked this conversation as resolved.
Show resolved Hide resolved
assertThat(response.getBody(), body.has("http_certificates_list"));
assertThat(response.getBody(), body.get("http_certificates_list").isArray());
verifyCertsJson(body.get("http_certificates_list").get(0));
assertThat(response.getBody(), body.has("transport_certificates_list"));
assertThat(response.getBody(), body.get("transport_certificates_list").isArray());
verifyCertsJson(body.get("transport_certificates_list").get(0));
}

private void verifyCertsJson(final JsonNode jsonNode) {
assertThat(jsonNode.toPrettyString(), jsonNode.has("issuer_dn"));
assertThat(jsonNode.toPrettyString(), jsonNode.has("subject_dn"));
assertThat(jsonNode.toPrettyString(), jsonNode.get("subject_dn").asText().matches(".*node-\\d.example.com+"));
assertThat(jsonNode.toPrettyString(), jsonNode.get("san").asText().matches(".*node-\\d.example.com.*"));
assertThat(jsonNode.toPrettyString(), jsonNode.has("not_before"));
assertThat(jsonNode.toPrettyString(), jsonNode.has("not_after"));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,13 @@ public class TestCertificates {

private static final Logger log = LogManager.getLogger(TestCertificates.class);

public static final Integer MAX_NUMBER_OF_NODE_CERTIFICATES = 3;
public static final Integer DEFAULT_NUMBER_OF_NODE_CERTIFICATES = 3;

public static final String CA_SUBJECT = "DC=com,DC=example,O=Example Com Inc.,OU=Example Com Inc. Root CA,CN=Example Com Inc. Root CA";

public static final String LDAP_SUBJECT = "DC=de,L=test,O=node,OU=node,CN=ldap.example.com";
public static final String NODE_SUBJECT_PATTERN = "DC=de,L=test,O=node,OU=node,CN=node-%d.example.com";

private static final String CA_SUBJECT = "DC=com,DC=example,O=Example Com Inc.,OU=Example Com Inc. Root CA,CN=Example Com Inc. Root CA";
private static final String ADMIN_DN = "CN=kirk,OU=client,O=client,L=test,C=de";
private static final int CERTIFICATE_VALIDITY_DAYS = 365;
private static final String CERTIFICATE_FILE_EXT = ".cert";
Expand All @@ -66,13 +70,18 @@ public class TestCertificates {
private final CertificateData adminCertificate;
private final List<CertificateData> nodeCertificates;

private final int numberOfNodes;

private final CertificateData ldapCertificate;

public TestCertificates() {
this(DEFAULT_NUMBER_OF_NODE_CERTIFICATES);
}

public TestCertificates(final int numberOfNodes) {
this.caCertificate = createCaCertificate();
this.nodeCertificates = IntStream.range(0, MAX_NUMBER_OF_NODE_CERTIFICATES)
.mapToObj(this::createNodeCertificate)
.collect(Collectors.toList());
this.numberOfNodes = numberOfNodes;
this.nodeCertificates = IntStream.range(0, this.numberOfNodes).mapToObj(this::createNodeCertificate).collect(Collectors.toList());
this.ldapCertificate = createLdapCertificate();
this.adminCertificate = createAdminCertificate(ADMIN_DN);
log.info("Test certificates successfully generated");
Expand Down Expand Up @@ -109,7 +118,7 @@ public CertificateData getRootCertificateData() {

/**
* Certificate for Open Search node. The certificate is derived from root certificate, returned by method {@link #getRootCertificate()}
* @param node is a node index. It has to be less than {@link #MAX_NUMBER_OF_NODE_CERTIFICATES}
* @param node is a node index. It has to be less than {@link #DEFAULT_NUMBER_OF_NODE_CERTIFICATES}
* @return file which contains certificate in PEM format, defined by <a href="https://www.rfc-editor.org/rfc/rfc1421.txt">RFC 1421</a>
*/
public File getNodeCertificate(int node) {
Expand All @@ -123,18 +132,18 @@ public CertificateData getNodeCertificateData(int node) {
}

private void isCorrectNodeNumber(int node) {
if (node >= MAX_NUMBER_OF_NODE_CERTIFICATES) {
if (node >= numberOfNodes) {
String message = String.format(
"Cannot get certificate for node %d, number of created certificates for nodes is %d",
node,
MAX_NUMBER_OF_NODE_CERTIFICATES
numberOfNodes
);
throw new RuntimeException(message);
}
}

private CertificateData createNodeCertificate(Integer node) {
String subject = String.format("DC=de,L=test,O=node,OU=node,CN=node-%d.example.com", node);
final var subject = String.format(NODE_SUBJECT_PATTERN, node);
String domain = String.format("node-%d.example.com", node);
CertificateMetadata metadata = CertificateMetadata.basicMetadata(subject, CERTIFICATE_VALIDITY_DAYS)
.withKeyUsage(false, DIGITAL_SIGNATURE, NON_REPUDIATION, KEY_ENCIPHERMENT, CLIENT_AUTH, SERVER_AUTH)
Expand All @@ -150,8 +159,7 @@ public CertificateData issueUserCertificate(String organizationUnit, String user
}

private CertificateData createLdapCertificate() {
String subject = "DC=de,L=test,O=node,OU=node,CN=ldap.example.com";
CertificateMetadata metadata = CertificateMetadata.basicMetadata(subject, CERTIFICATE_VALIDITY_DAYS)
CertificateMetadata metadata = CertificateMetadata.basicMetadata(LDAP_SUBJECT, CERTIFICATE_VALIDITY_DAYS)
.withKeyUsage(false, DIGITAL_SIGNATURE, NON_REPUDIATION, KEY_ENCIPHERMENT, CLIENT_AUTH, SERVER_AUTH)
.withSubjectAlternativeName(null, List.of("localhost"), "127.0.0.1");
return CertificatesIssuerFactory.rsaBaseCertificateIssuer().issueSignedCertificate(metadata, caCertificate);
Expand All @@ -164,7 +172,7 @@ public CertificateData getLdapCertificateData() {
/**
* It returns private key associated with node certificate returned by method {@link #getNodeCertificate(int)}
*
* @param node is a node index. It has to be less than {@link #MAX_NUMBER_OF_NODE_CERTIFICATES}
* @param node is a node index. It has to be less than {@link #DEFAULT_NUMBER_OF_NODE_CERTIFICATES}
* @param privateKeyPassword is a password used to encode private key, can be <code>null</code> to retrieve unencrypted key.
* @return file which contains private key encoded in PEM format, defined
* by <a href="https://www.rfc-editor.org/rfc/rfc1421.txt">RFC 1421</a>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,6 @@ public void before() {
}

for (Map.Entry<String, LocalCluster> entry : remotes.entrySet()) {
@SuppressWarnings("resource")
DarshitChanpura marked this conversation as resolved.
Show resolved Hide resolved
InetSocketAddress transportAddress = entry.getValue().localOpenSearchCluster.clusterManagerNode().getTransportAddress();
String key = "cluster.remote." + entry.getKey() + ".seeds";
String value = transportAddress.getHostString() + ":" + transportAddress.getPort();
Expand Down Expand Up @@ -509,7 +508,7 @@ public Builder defaultConfigurationInitDirectory(String defaultConfigurationInit
public LocalCluster build() {
try {
if (testCertificates == null) {
testCertificates = new TestCertificates();
testCertificates = new TestCertificates(clusterManager.getNodes());
}
clusterName += "_" + num.incrementAndGet();
Settings settings = nodeOverrideSettingsBuilder.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ public class LocalOpenSearchCluster {

private File snapshotDir;

private int nodeCounter = 0;

public LocalOpenSearchCluster(
String clusterName,
ClusterManager clusterManager,
Expand Down Expand Up @@ -163,7 +165,6 @@ public void start() throws Exception {
this.initialClusterManagerHosts = toHostList(clusterManagerPorts);

started = true;

CompletableFuture<Void> clusterManagerNodeFuture = startNodes(
clusterManager.getClusterManagerNodeSettings(),
clusterManagerNodeTransportPorts,
Expand Down Expand Up @@ -195,7 +196,6 @@ public void start() throws Exception {
log.info("Startup finished. Waiting for GREEN");

waitForCluster(ClusterHealthStatus.GREEN, TimeValue.timeValueSeconds(10), nodes.size());

log.info("Started: {}", this);

}
Expand Down Expand Up @@ -303,10 +303,10 @@ private CompletableFuture<Void> startNodes(
List<CompletableFuture<StartStage>> futures = new ArrayList<>();

for (NodeSettings nodeSettings : nodeSettingList) {
Node node = new Node(nodeSettings, transportPortIterator.next(), httpPortIterator.next());
Node node = new Node(nodeCounter, nodeSettings, transportPortIterator.next(), httpPortIterator.next());
futures.add(node.start());
nodeCounter += 1;
}

return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
}

Expand Down Expand Up @@ -386,8 +386,10 @@ public class Node implements OpenSearchClientProvider {
private PluginAwareNode node;
private boolean running = false;
private boolean portCollision = false;
private final int nodeNumber;

Node(NodeSettings nodeSettings, int transportPort, int httpPort) {
Node(int nodeNumber, NodeSettings nodeSettings, int transportPort, int httpPort) {
this.nodeNumber = nodeNumber;
this.nodeName = createNextNodeName(requireNonNull(nodeSettings, "Node settings are required."));
this.nodeSettings = nodeSettings;
this.nodeHomeDir = new File(clusterHomeDir, nodeName);
Expand Down Expand Up @@ -517,7 +519,7 @@ private Settings getOpenSearchSettings() {

if (nodeSettingsSupplier != null) {
// TODO node number
return Settings.builder().put(settings).put(nodeSettingsSupplier.get(0)).build();
return Settings.builder().put(settings).put(nodeSettingsSupplier.get(nodeNumber)).build();
}
return settings;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,6 @@ public class OpenSearchSecuritySSLPlugin extends Plugin implements SystemIndexPl
protected final SSLConfig SSLConfig;
protected volatile ThreadPool threadPool;

// public OpenSearchSecuritySSLPlugin(final Settings settings, final Path configPath) {
// this(settings, configPath, false);
// }

@SuppressWarnings("removal")
protected OpenSearchSecuritySSLPlugin(final Settings settings, final Path configPath, boolean disabled) {

Expand Down
22 changes: 11 additions & 11 deletions src/main/java/org/opensearch/security/ssl/SecurityKeyStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,25 @@

public interface SecurityKeyStore {

public SSLEngine createHTTPSSLEngine() throws SSLException;
SSLEngine createHTTPSSLEngine() throws SSLException;

public SSLEngine createServerTransportSSLEngine() throws SSLException;
SSLEngine createServerTransportSSLEngine() throws SSLException;

public SSLEngine createClientTransportSSLEngine(String peerHost, int peerPort) throws SSLException;
SSLEngine createClientTransportSSLEngine(String peerHost, int peerPort) throws SSLException;

public String getHTTPProviderName();
String getHTTPProviderName();

public String getTransportServerProviderName();
String getTransportServerProviderName();

public String getTransportClientProviderName();
String getTransportClientProviderName();

public String getSubjectAlternativeNames(X509Certificate cert);
String getSubjectAlternativeNames(X509Certificate cert);

public void initHttpSSLConfig();
void initHttpSSLConfig();

public void initTransportSSLConfig();
void initTransportSSLConfig();

public X509Certificate[] getTransportCerts();
X509Certificate[] getTransportCerts();

public X509Certificate[] getHttpCerts();
X509Certificate[] getHttpCerts();
}
Loading
Loading