You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ja...@apache.org on 2019/04/15 19:19:11 UTC

[lucene-solr] branch master updated: SOLR-12371: Editing authorization config via REST API now works in standalone mode

This is an automated email from the ASF dual-hosted git repository.

janhoy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/lucene-solr.git


The following commit(s) were added to refs/heads/master by this push:
     new 9707bb6  SOLR-12371: Editing authorization config via REST API now works in standalone mode
9707bb6 is described below

commit 9707bb6fa91ea3062a404690aa7d041771f22746
Author: Jan Høydahl <ja...@apache.org>
AuthorDate: Mon Apr 15 21:09:30 2019 +0200

    SOLR-12371: Editing authorization config via REST API now works in standalone mode
---
 solr/CHANGES.txt                                   |  4 +-
 .../java/org/apache/solr/core/CoreContainer.java   | 20 ++++++--
 .../solr/security/BasicAuthStandaloneTest.java     | 59 ++++++++++++++--------
 3 files changed, 55 insertions(+), 28 deletions(-)

diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 4d5a53e..a41b034 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -193,10 +193,12 @@ Bug Fixes
 * SOLR-13336: add maxBooleanClauses (default to 1024) setting to solr.xml, reverting previous effective
   value of Integer.MAX_VALUE-1, to restrict risk of pathalogical query expansion.  (hossman)
 
-  * SOLR-13386: OverseerTaskQueue#remove should not throw an exception when no node exists after an exists 
+  * SOLR-13386: OverseerTaskQueue#remove should not throw an exception when no node exists after an exists
   check and the Overseer work loop should not allow free spinning the loop when it hits a KeeperException.
   (Mark Miller, Fernandez-Lobbe, Mike Drob)
 
+* SOLR-12371: Editing authorization config via REST API now works in standalone mode (janhoy)
+
 Improvements
 ----------------------
 
diff --git a/solr/core/src/java/org/apache/solr/core/CoreContainer.java b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
index a8e573a..966a4b8 100644
--- a/solr/core/src/java/org/apache/solr/core/CoreContainer.java
+++ b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
@@ -337,6 +337,7 @@ public class CoreContainer {
 
   private synchronized void initializeAuthorizationPlugin(Map<String, Object> authorizationConf) {
     authorizationConf = Utils.getDeepCopy(authorizationConf, 4);
+    int newVersion = readVersion(authorizationConf);
     //Initialize the Authorization module
     SecurityPluginHolder<AuthorizationPlugin> old = authorizationPlugin;
     SecurityPluginHolder<AuthorizationPlugin> authorizationPlugin = null;
@@ -345,11 +346,12 @@ public class CoreContainer {
       if (klas == null) {
         throw new SolrException(ErrorCode.SERVER_ERROR, "class is required for authorization plugin");
       }
-      if (old != null && old.getZnodeVersion() == readVersion(authorizationConf)) {
+      if (old != null && old.getZnodeVersion() == newVersion && newVersion > 0) {
+        log.debug("Authorization config not modified");
         return;
       }
       log.info("Initializing authorization plugin: " + klas);
-      authorizationPlugin = new SecurityPluginHolder<>(readVersion(authorizationConf),
+      authorizationPlugin = new SecurityPluginHolder<>(newVersion,
           getResourceLoader().newInstance(klas, AuthorizationPlugin.class));
 
       // Read and pass the authorization context to the plugin
@@ -369,6 +371,7 @@ public class CoreContainer {
 
   private void initializeAuditloggerPlugin(Map<String, Object> auditConf) {
     auditConf = Utils.getDeepCopy(auditConf, 4);
+    int newVersion = readVersion(auditConf);
     //Initialize the Auditlog module
     SecurityPluginHolder<AuditLoggerPlugin> old = auditloggerPlugin;
     SecurityPluginHolder<AuditLoggerPlugin> newAuditloggerPlugin = null;
@@ -377,11 +380,12 @@ public class CoreContainer {
       if (klas == null) {
         throw new SolrException(ErrorCode.SERVER_ERROR, "class is required for auditlogger plugin");
       }
-      if (old != null && old.getZnodeVersion() == readVersion(auditConf)) {
+      if (old != null && old.getZnodeVersion() == newVersion && newVersion > 0) {
+        log.debug("Auditlogger config not modified");
         return;
       }
       log.info("Initializing auditlogger plugin: " + klas);
-      newAuditloggerPlugin = new SecurityPluginHolder<>(readVersion(auditConf),
+      newAuditloggerPlugin = new SecurityPluginHolder<>(newVersion,
           getResourceLoader().newInstance(klas, AuditLoggerPlugin.class));
 
       newAuditloggerPlugin.plugin.init(auditConf);
@@ -401,6 +405,7 @@ public class CoreContainer {
   
   private synchronized void initializeAuthenticationPlugin(Map<String, Object> authenticationConfig) {
     authenticationConfig = Utils.getDeepCopy(authenticationConfig, 4);
+    int newVersion = readVersion(authenticationConfig);
     String pluginClassName = null;
     if (authenticationConfig != null) {
       if (authenticationConfig.containsKey("class")) {
@@ -422,10 +427,15 @@ public class CoreContainer {
     SecurityPluginHolder<AuthenticationPlugin> old = authenticationPlugin;
     SecurityPluginHolder<AuthenticationPlugin> authenticationPlugin = null;
 
+    if (old != null && old.getZnodeVersion() == newVersion && newVersion > 0) {
+      log.debug("Authentication config not modified");
+      return;
+    }
+
     // Initialize the plugin
     if (pluginClassName != null) {
       log.info("Initializing authentication plugin: " + pluginClassName);
-      authenticationPlugin = new SecurityPluginHolder<>(readVersion(authenticationConfig),
+      authenticationPlugin = new SecurityPluginHolder<>(newVersion,
           getResourceLoader().newInstance(pluginClassName,
               AuthenticationPlugin.class,
               null,
diff --git a/solr/core/src/test/org/apache/solr/security/BasicAuthStandaloneTest.java b/solr/core/src/test/org/apache/solr/security/BasicAuthStandaloneTest.java
index 381a1fb..0f3e140 100644
--- a/solr/core/src/test/org/apache/solr/security/BasicAuthStandaloneTest.java
+++ b/solr/core/src/test/org/apache/solr/security/BasicAuthStandaloneTest.java
@@ -16,11 +16,13 @@
  */
 package org.apache.solr.security;
 
+import java.io.IOException;
 import java.lang.invoke.MethodHandles;
 import java.nio.charset.Charset;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.Collections;
 import java.util.Properties;
 
 import org.apache.http.HttpResponse;
@@ -30,14 +32,10 @@ import org.apache.http.entity.ByteArrayEntity;
 import org.apache.http.message.AbstractHttpMessage;
 import org.apache.http.message.BasicHeader;
 import org.apache.solr.SolrTestCaseJ4;
-import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.embedded.JettySolrRunner;
 import org.apache.solr.client.solrj.impl.HttpClientUtil;
 import org.apache.solr.client.solrj.impl.HttpSolrClient;
-import org.apache.solr.client.solrj.request.GenericSolrRequest;
-import org.apache.solr.client.solrj.request.RequestWriter.StringPayloadContentWriter;
-import org.apache.solr.common.params.CommonParams;
-import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.solr.common.params.MapSolrParams;
 import org.apache.solr.common.util.Base64;
 import org.apache.solr.common.util.Utils;
 import org.apache.solr.handler.admin.SecurityConfHandler;
@@ -86,6 +84,7 @@ public class BasicAuthStandaloneTest extends SolrTestCaseJ4 {
   public void testBasicAuth() throws Exception {
 
     String authcPrefix = "/admin/authentication";
+    String authzPrefix = "/admin/authorization";
 
     HttpClient cl = null;
     HttpSolrClient httpSolrClient = null;
@@ -106,34 +105,35 @@ public class BasicAuthStandaloneTest extends SolrTestCaseJ4 {
           "'set-user': {'harry':'HarryIsCool'}\n" +
           "}";
 
-      GenericSolrRequest genericReq = new GenericSolrRequest(SolrRequest.METHOD.POST, authcPrefix, new ModifiableSolrParams());
-      genericReq.setContentWriter(new StringPayloadContentWriter(command, CommonParams.JSON_MIME));
-
-      HttpSolrClient finalHttpSolrClient = httpSolrClient;
-      HttpSolrClient.RemoteSolrException exp = expectThrows(HttpSolrClient.RemoteSolrException.class, () -> {
-        finalHttpSolrClient.request(genericReq);
-      });
-      assertEquals(401, exp.code());
+      doHttpPost(cl, baseUrl + authcPrefix, command, null, null, 401);
+      verifySecurityStatus(cl, baseUrl + authcPrefix, "authentication.enabled", "true", 20);
 
       command = "{\n" +
           "'set-user': {'harry':'HarryIsUberCool'}\n" +
           "}";
 
-      HttpPost httpPost = new HttpPost(baseUrl + authcPrefix);
-      setBasicAuthHeader(httpPost, "solr", "SolrRocks");
-      httpPost.setEntity(new ByteArrayEntity(command.getBytes(UTF_8)));
-      httpPost.addHeader("Content-Type", "application/json; charset=UTF-8");
-      verifySecurityStatus(cl, baseUrl + authcPrefix, "authentication.enabled", "true", 20);
-      HttpResponse r = cl.execute(httpPost);
-      int statusCode = r.getStatusLine().getStatusCode();
-      Utils.consumeFully(r.getEntity());
-      assertEquals("proper_cred sent, but access denied", 200, statusCode);
 
+      doHttpPost(cl, baseUrl + authcPrefix, command, "solr", "SolrRocks");
       verifySecurityStatus(cl, baseUrl + authcPrefix, "authentication/credentials/harry", NOT_NULL_PREDICATE, 20);
 
       // Read file from SOLR_HOME and verify that it contains our new user
       assertTrue(new String(Utils.toJSON(securityConfHandler.getSecurityConfig(false).getData()), 
           Charset.forName("UTF-8")).contains("harry"));
+
+      // Edit authorization
+      verifySecurityStatus(cl, baseUrl + authzPrefix, "authorization/permissions[1]/role", null, 20);
+      doHttpPost(cl, baseUrl + authzPrefix, "{'set-permission': {'name': 'update', 'role':'updaterole'}}", "solr", "SolrRocks");
+      command = "{\n" +
+          "'set-permission': {'name': 'read', 'role':'solr'}\n" +
+          "}";
+      doHttpPost(cl, baseUrl + authzPrefix, command, "solr", "SolrRocks");
+      try {
+        httpSolrClient.query("collection1", new MapSolrParams(Collections.singletonMap("q", "foo")));
+        fail("Should return a 401 response");
+      } catch (Exception e) {
+        // Test that the second doPost request to /security/authorization went through
+        verifySecurityStatus(cl, baseUrl + authzPrefix, "authorization/permissions[2]/role", "solr", 20);
+      }
     } finally {
       if (cl != null) {
         HttpClientUtil.close(cl);
@@ -142,6 +142,21 @@ public class BasicAuthStandaloneTest extends SolrTestCaseJ4 {
     }
   }
 
+  private void doHttpPost(HttpClient cl, String url, String jsonCommand, String basicUser, String basicPass) throws IOException {
+    doHttpPost(cl, url, jsonCommand, basicUser, basicPass, 200);
+  }
+
+  private void doHttpPost(HttpClient cl, String url, String jsonCommand, String basicUser, String basicPass, int expectStatusCode) throws IOException {
+    HttpPost httpPost = new HttpPost(url);
+    setBasicAuthHeader(httpPost, basicUser, basicPass);
+    httpPost.setEntity(new ByteArrayEntity(jsonCommand.replaceAll("'", "\"").getBytes(UTF_8)));
+    httpPost.addHeader("Content-Type", "application/json; charset=UTF-8");
+    HttpResponse r = cl.execute(httpPost);
+    int statusCode = r.getStatusLine().getStatusCode();
+    Utils.consumeFully(r.getEntity());
+    assertEquals("proper_cred sent, but access denied", expectStatusCode, statusCode);
+  }
+
   public static void setBasicAuthHeader(AbstractHttpMessage httpMsg, String user, String pwd) {
     String userPass = user + ":" + pwd;
     String encoded = Base64.byteArrayToBase64(userPass.getBytes(UTF_8));