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());
+  }
 }