You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by ja...@apache.org on 2021/06/18 22:42:19 UTC
[solr] branch main updated: SOLR-15423 Redesign integration test,
with cluster in local scope variable per test (#182)
This is an automated email from the ASF dual-hosted git repository.
janhoy pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/main by this push:
new 3155565 SOLR-15423 Redesign integration test, with cluster in local scope variable per test (#182)
3155565 is described below
commit 3155565420ac84b11cf8507cef7c412a87b15006
Author: Jan Høydahl <ja...@users.noreply.github.com>
AuthorDate: Sat Jun 19 00:42:09 2021 +0200
SOLR-15423 Redesign integration test, with cluster in local scope variable per test (#182)
SOLR-15484
---
.../src/java/org/apache/solr/util/CryptoKeys.java | 11 +++
.../security/JWTAuthPluginIntegrationTest.java | 98 ++++++++++------------
.../apache/solr/security/JWTAuthPluginTest.java | 7 ++
3 files changed, 63 insertions(+), 53 deletions(-)
diff --git a/solr/core/src/java/org/apache/solr/util/CryptoKeys.java b/solr/core/src/java/org/apache/solr/util/CryptoKeys.java
index 406f36f..0a98b69 100644
--- a/solr/core/src/java/org/apache/solr/util/CryptoKeys.java
+++ b/solr/core/src/java/org/apache/solr/util/CryptoKeys.java
@@ -212,6 +212,17 @@ public final class CryptoKeys {
}
}
+ /**
+ * Given a file, will try to
+ * @param pemContents the raw string content of the PEM file
+ * @return the certificate content between BEGIN and END markers
+ */
+ public static String extractCertificateFromPem(String pemContents) {
+ int from = pemContents.indexOf("-----BEGIN CERTIFICATE-----");
+ int end = pemContents.lastIndexOf("-----END CERTIFICATE-----") + 25;
+ return pemContents.substring(from, end);
+ }
+
public static class RSAKeyPair {
private final String pubKeyStr;
private final PublicKey publicKey;
diff --git a/solr/core/src/test/org/apache/solr/security/JWTAuthPluginIntegrationTest.java b/solr/core/src/test/org/apache/solr/security/JWTAuthPluginIntegrationTest.java
index ba08e75..c0ae25f 100644
--- a/solr/core/src/test/org/apache/solr/security/JWTAuthPluginIntegrationTest.java
+++ b/solr/core/src/test/org/apache/solr/security/JWTAuthPluginIntegrationTest.java
@@ -32,6 +32,7 @@ import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.client.solrj.impl.HttpClientUtil;
+import org.apache.solr.cloud.MiniSolrCloudCluster;
import org.apache.solr.cloud.SolrCloudAuthTestCase;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.util.Base64;
@@ -86,18 +87,14 @@ import static java.nio.charset.StandardCharsets.UTF_8;
*/
@SolrTestCaseJ4.SuppressSSL
public class JWTAuthPluginIntegrationTest extends SolrCloudAuthTestCase {
- protected static final int NUM_SERVERS = 2;
- protected static final int NUM_SHARDS = 2;
- protected static final int REPLICATION_FACTOR = 1;
+ private final String COLLECTION = "jwtColl";
+
private static String mockOAuthToken;
private static Path pemFilePath;
private static Path wrongPemFilePath;
- private final String COLLECTION = "jwtColl";
private static String jwtStaticTestToken;
- private String baseUrl;
private static JsonWebSignature jws;
private static String jwtTokenWrongSignature;
-
private static MockOAuth2Server mockOAuth2Server;
@BeforeClass
@@ -130,14 +127,9 @@ public class JWTAuthPluginIntegrationTest extends SolrCloudAuthTestCase {
}
@Test
- public void extractCertificateFromPem() throws IOException {
- String cert = extractCertificateFromPem(pemFilePath);
- assertEquals(2, CryptoKeys.parseX509Certs(IOUtils.toInputStream(cert, StandardCharsets.UTF_8)).size());
- }
-
- @Test
public void mockOAuth2Server() throws Exception {
- configureClusterMockOauth(2, pemFilePath, 10000);
+ MiniSolrCloudCluster myCluster = configureClusterMockOauth(2, pemFilePath, 10000);
+ String baseUrl = myCluster.getRandomJetty(random()).getBaseUrl().toString();
// First attempt without token fails
Map<String, String> headers = getHeaders(baseUrl + "/admin/info/system", null);
@@ -146,18 +138,18 @@ public class JWTAuthPluginIntegrationTest extends SolrCloudAuthTestCase {
// Second attempt with token from Oauth mock server succeeds
headers = getHeaders(baseUrl + "/admin/info/system", mockOAuthToken);
assertEquals("200", headers.get("code"));
+ myCluster.shutdown();
}
@Test
public void mockOAuth2ServerWrongPEMInTruststore() throws Exception {
// JWTAuthPlugin throws SSLHandshakeException when fetching JWK, so this trips cluster init
assertThrows(Exception.class, () -> configureClusterMockOauth(2, wrongPemFilePath, 2000));
- // Auth is not setup
- assertNull(cluster.getJettySolrRunner(0).getCoreContainer().getAuthenticationPlugin());
}
public void testStaticJwtKeys() throws Exception {
- configureClusterStaticKeys("jwt_plugin_jwk_security.json");
+ MiniSolrCloudCluster myCluster = configureClusterStaticKeys("jwt_plugin_jwk_security.json");
+ String baseUrl = myCluster.getRandomJetty(random()).getBaseUrl().toString();
// No token fails
assertThrows(IOException.class, () -> get(baseUrl + "/admin/info/system", null));
@@ -172,12 +164,14 @@ public class JWTAuthPluginIntegrationTest extends SolrCloudAuthTestCase {
" \"redirect_uris\":[],\n" +
" \"authorizationEndpoint\":\"http://acmepaymentscorp/oauth/auz/authorize\",\n" +
" \"client_id\":\"solr-cluster\"}", authData);
+ myCluster.shutdown();
}
@Test
public void infoRequestValidateXSolrAuthHeadersBlockUnknownFalse() throws Exception {
// https://issues.apache.org/jira/browse/SOLR-14196
- configureClusterStaticKeys("jwt_plugin_jwk_security_blockUnknownFalse.json");
+ MiniSolrCloudCluster myCluster = configureClusterStaticKeys("jwt_plugin_jwk_security_blockUnknownFalse.json");
+ String baseUrl = myCluster.getRandomJetty(random()).getBaseUrl().toString();
Map<String, String> headers = getHeaders(baseUrl + "/admin/info/system", null);
assertEquals("Should have received 401 code", "401", headers.get("code"));
@@ -188,11 +182,13 @@ public class JWTAuthPluginIntegrationTest extends SolrCloudAuthTestCase {
" \"redirect_uris\":[],\n" +
" \"authorizationEndpoint\":\"http://acmepaymentscorp/oauth/auz/authorize\",\n" +
" \"client_id\":\"solr-cluster\"}", authData);
+ myCluster.shutdown();
}
@Test
public void testMetrics() throws Exception {
- configureClusterStaticKeys("jwt_plugin_jwk_security.json");
+ // Here we use the "global" class-level cluster variable, but not for the other tests
+ cluster = configureClusterStaticKeys("jwt_plugin_jwk_security.json");
boolean isUseV2Api = random().nextBoolean();
String authcPrefix = "/admin/authentication";
@@ -202,7 +198,7 @@ public class JWTAuthPluginIntegrationTest extends SolrCloudAuthTestCase {
String baseUrl = cluster.getRandomJetty(random()).getBaseUrl().toString();
CloseableHttpClient cl = HttpClientUtil.createClient(null);
- createCollection(COLLECTION);
+ createCollection(cluster, COLLECTION);
// Missing token
getAndFail(baseUrl + "/" + COLLECTION + "/query?q=*:*", null);
@@ -225,17 +221,8 @@ public class JWTAuthPluginIntegrationTest extends SolrCloudAuthTestCase {
// JWT parse error
getAndFail(baseUrl + "/" + COLLECTION + "/query?q=*:*", "foozzz");
assertAuthMetricsMinimums(11, 4, 4, 1, 1, 1);
-
- HttpClientUtil.close(cl);
- }
-
- @Test
- public void createCollectionUpdateAndQueryDistributed() throws Exception {
- configureClusterStaticKeys("jwt_plugin_jwk_security.json");
- // Admin request will use PKI inter-node auth from Overseer, and succeed
- createCollection(COLLECTION);
-
+ // Merged with createCollectionUpdateAndQueryDistributed()
// Now update three documents
assertAuthMetricsMinimums(1, 1, 0, 0, 0, 0);
assertPkiAuthMetricsMinimums(2, 2, 0, 0, 0, 0);
@@ -243,7 +230,7 @@ public class JWTAuthPluginIntegrationTest extends SolrCloudAuthTestCase {
assertEquals(Integer.valueOf(200), result.second());
assertAuthMetricsMinimums(4, 4, 0, 0, 0, 0);
assertPkiAuthMetricsMinimums(2, 2, 0, 0, 0, 0);
-
+
// First a non distributed query
result = get(baseUrl + "/" + COLLECTION + "/query?q=*:*&distrib=false", jwtStaticTestToken);
assertEquals(Integer.valueOf(200), result.second());
@@ -253,45 +240,56 @@ public class JWTAuthPluginIntegrationTest extends SolrCloudAuthTestCase {
result = get(baseUrl + "/" + COLLECTION + "/query?q=*:*", jwtStaticTestToken);
assertEquals(Integer.valueOf(200), result.second());
assertAuthMetricsMinimums(10, 10, 0, 0, 0, 0);
-
+
// Delete
assertEquals(200, get(baseUrl + "/admin/collections?action=DELETE&name=" + COLLECTION, jwtStaticTestToken).second().intValue());
assertAuthMetricsMinimums(11, 11, 0, 0, 0, 0);
assertPkiAuthMetricsMinimums(4, 4, 0, 0, 0, 0);
+
+ HttpClientUtil.close(cl);
}
+ /**
+ * Configure solr cluster with a security.json talking to MockOAuth2 server
+ * @param numNodes number of nodes in cluster
+ * @param pemFilePath path to PEM file for SSL cert to trust for OAuth2 server
+ * @param timeoutMs how long to wait until the new security.json is applied to the cluster
+ * @return an instance of the created cluster that the test can talk to
+ */
@SuppressWarnings("BusyWait")
- private void configureClusterMockOauth(int numNodes, Path pemFilePath, long timeoutMs) throws Exception {
- configureCluster(numNodes)// nodes
+ private MiniSolrCloudCluster configureClusterMockOauth(int numNodes, Path pemFilePath, long timeoutMs) throws Exception {
+ MiniSolrCloudCluster myCluster = configureCluster(numNodes)// nodes
.addConfig("conf1", TEST_PATH().resolve("configsets").resolve("cloud-minimal").resolve("conf"))
.withDefaultClusterProperty("useLegacyReplicaAssignment", "false")
- .configure();
+ .build();
String securityJson = createMockOAuthSecurityJson(pemFilePath);
- cluster.getZkClient().setData("/security.json", securityJson.getBytes(Charset.defaultCharset()), true);
+ myCluster.getZkClient().setData("/security.json", securityJson.getBytes(Charset.defaultCharset()), true);
RTimer timer = new RTimer();
do { // Wait timeoutMs time for the security.json change to take effect
Thread.sleep(200);
if (timer.getTime() > timeoutMs) {
+ myCluster.shutdown();
throw new Exception("Custom 'security.json' not applied in " + timeoutMs +"ms");
}
- } while (cluster.getJettySolrRunner(0).getCoreContainer().getAuthenticationPlugin() == null);
- baseUrl = cluster.getRandomJetty(random()).getBaseUrl().toString();
- cluster.waitForAllNodes(10);
+ } while (myCluster.getJettySolrRunner(0).getCoreContainer().getAuthenticationPlugin() == null);
+ myCluster.waitForAllNodes(10);
+ return myCluster;
}
/**
* Configure solr cluster with a security.json made for static keys
* @param securityJsonFilename file name of test json, relative to test-files/solr/security
+ * @return an instance of the created cluster that the test can talk to
*/
- private void configureClusterStaticKeys(String securityJsonFilename) throws Exception {
- configureCluster(NUM_SERVERS)// nodes
+ private MiniSolrCloudCluster configureClusterStaticKeys(String securityJsonFilename) throws Exception {
+ MiniSolrCloudCluster myCluster = configureCluster(2)// nodes
.withSecurityJson(TEST_PATH().resolve("security").resolve(securityJsonFilename))
.addConfig("conf1", TEST_PATH().resolve("configsets").resolve("cloud-minimal").resolve("conf"))
.withDefaultClusterProperty("useLegacyReplicaAssignment", "false")
- .configure();
- baseUrl = cluster.getRandomJetty(random()).getBaseUrl().toString();
+ .build();
- cluster.waitForAllNodes(10);
+ myCluster.waitForAllNodes(10);
+ return myCluster;
}
/**
@@ -383,9 +381,10 @@ public class JWTAuthPluginIntegrationTest extends SolrCloudAuthTestCase {
return new Pair<>(result, code);
}
- private void createCollection(String collectionName) throws IOException {
+ private void createCollection(MiniSolrCloudCluster myCluster, String collectionName) throws IOException {
+ String baseUrl = myCluster.getRandomJetty(random()).getBaseUrl().toString();
assertEquals(200, get(baseUrl + "/admin/collections?action=CREATE&name=" + collectionName + "&numShards=2", jwtStaticTestToken).second().intValue());
- cluster.waitForActiveCollection(collectionName, 2, 2);
+ myCluster.waitForActiveCollection(collectionName, 2, 2);
}
private void executeCommand(String url, HttpClient cl, String payload, JsonWebSignature jws)
@@ -429,7 +428,7 @@ public class JWTAuthPluginIntegrationTest extends SolrCloudAuthTestCase {
*/
private static String createMockOAuthSecurityJson(Path pemFilePath) throws IOException {
String wellKnown = mockOAuth2Server.wellKnownUrl("default").toString();
- String pemCert = extractCertificateFromPem(pemFilePath).replaceAll("\n", "\\\\n");
+ String pemCert = CryptoKeys.extractCertificateFromPem(Files.readString(pemFilePath)).replaceAll("\n", "\\\\n");
return "{\n" +
" \"authentication\" : {\n" +
" \"class\": \"solr.JWTAuthPlugin\",\n" +
@@ -440,13 +439,6 @@ public class JWTAuthPluginIntegrationTest extends SolrCloudAuthTestCase {
"}";
}
- private static String extractCertificateFromPem(Path pemFilePath) throws IOException {
- String raw = Files.readString(pemFilePath);
- int from = raw.indexOf("-----BEGIN CERTIFICATE-----");
- int end = raw.lastIndexOf("-----END CERTIFICATE-----") + 25;
- return raw.substring(from, end);
- }
-
/**
* Create and return a MockOAuth2Server with given SSL certificate
* @param p12CertPath path to a p12 certificate store
diff --git a/solr/core/src/test/org/apache/solr/security/JWTAuthPluginTest.java b/solr/core/src/test/org/apache/solr/security/JWTAuthPluginTest.java
index 67d11b1..f9054bc 100644
--- a/solr/core/src/test/org/apache/solr/security/JWTAuthPluginTest.java
+++ b/solr/core/src/test/org/apache/solr/security/JWTAuthPluginTest.java
@@ -549,4 +549,11 @@ public class JWTAuthPluginTest extends SolrTestCaseJ4 {
"-----END CERTIFICATE-----\n", StandardCharsets.UTF_8));
});
}
+
+ @Test
+ public void extractCertificate() throws IOException {
+ Path pemFilePath = SolrTestCaseJ4.TEST_PATH().resolve("security").resolve("jwt_plugin_idp_cert.pem");
+ String cert = CryptoKeys.extractCertificateFromPem(Files.readString(pemFilePath));
+ assertEquals(2, CryptoKeys.parseX509Certs(IOUtils.toInputStream(cert, StandardCharsets.UTF_8)).size());
+ }
}