You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by th...@apache.org on 2021/11/03 19:36:48 UTC

[lucene-solr] branch branch_8_11 updated: SOLR-15766: MultiAuthPlugin should send non-AJAX anonymous requests to the plugin that allows anonymous requests (#2601) (#2602)

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

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


The following commit(s) were added to refs/heads/branch_8_11 by this push:
     new a32727b  SOLR-15766: MultiAuthPlugin should send non-AJAX anonymous requests to the plugin that allows anonymous requests (#2601) (#2602)
a32727b is described below

commit a32727b8796b76113bcfc10aa68568e453fedf2b
Author: Timothy Potter <th...@gmail.com>
AuthorDate: Wed Nov 3 13:36:36 2021 -0600

    SOLR-15766: MultiAuthPlugin should send non-AJAX anonymous requests to the plugin that allows anonymous requests (#2601) (#2602)
---
 solr/CHANGES.txt                                   |  2 ++
 .../org/apache/solr/security/MultiAuthPlugin.java  | 28 ++++++++++-----
 .../solr/security/multi_auth_plugin_security.json  |  8 ++++-
 .../apache/solr/security/MultiAuthPluginTest.java  | 40 +++++++++++++++++-----
 4 files changed, 60 insertions(+), 18 deletions(-)

diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 037ebbb..2311785 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -46,6 +46,8 @@ Bug Fixes
 
 * SOLR-15676: Fix PeerSync failure due to RealTimeGetComponent returning duplicates. (Ramsey Haddad, Christine Poerschke, David Smiley)
 
+* SOLR-15766: MultiAuthPlugin should send non-AJAX anonymous requests to the plugin that allows anonymous requests (Timothy Potter, Eric Pugh)
+
 Build
 ---------------------
 
diff --git a/solr/core/src/java/org/apache/solr/security/MultiAuthPlugin.java b/solr/core/src/java/org/apache/solr/security/MultiAuthPlugin.java
index 3f782fa..da60eb9 100644
--- a/solr/core/src/java/org/apache/solr/security/MultiAuthPlugin.java
+++ b/solr/core/src/java/org/apache/solr/security/MultiAuthPlugin.java
@@ -59,6 +59,7 @@ public class MultiAuthPlugin extends AuthenticationPlugin implements ConfigEdita
 
   private final Map<String, AuthenticationPlugin> pluginMap = new LinkedHashMap<>();
   private final SolrResourceLoader loader;
+  private AuthenticationPlugin allowsUnknown = null; // the first of our plugins that allows anonymous requests
 
   // Get the loader from the CoreContainer so we can load the sub-plugins, such as the BasicAuthPlugin for Basic
   public MultiAuthPlugin(CoreContainer cc) {
@@ -145,6 +146,13 @@ public class MultiAuthPlugin extends AuthenticationPlugin implements ConfigEdita
     AuthenticationPlugin pluginForScheme = loader.newInstance(clazz, AuthenticationPlugin.class);
     pluginForScheme.init(schemeConfig);
     pluginMap.put(scheme.toLowerCase(Locale.ROOT), pluginForScheme);
+
+    if (allowsUnknown == null) {
+      if (!Boolean.parseBoolean(String.valueOf(schemeConfig.getOrDefault("blockUnknown", true)))) {
+        // plugin allows anonymous requests, so we'll send any non-AJAX requests without an authorization header to it
+        allowsUnknown = pluginForScheme;
+      }
+    }
   }
 
   @Override
@@ -165,21 +173,25 @@ public class MultiAuthPlugin extends AuthenticationPlugin implements ConfigEdita
     HttpServletResponse response = (HttpServletResponse) servletResponse;
 
     final String authHeader = request.getHeader(AUTHORIZATION_HEADER);
-
-    // if no Authorization header but is an AJAX request, forward to the default scheme so it can handle it
     if (authHeader == null) {
-      if (BasicAuthPlugin.isAjaxRequest(request)) {
-        // use the first scheme listed as the default
-        return pluginMap.values().iterator().next().doAuthenticate(request, response, filterChain);
+      // no Authorization header but if it's an AJAX request, forward to the default scheme so it can handle it
+      // otherwise, send to the first plugin that allows blockUnknown = false
+      final AuthenticationPlugin plugin = BasicAuthPlugin.isAjaxRequest(request) ? pluginMap.values().iterator().next() : allowsUnknown;
+      boolean result = false;
+      if (plugin != null) {
+        pluginInRequest.set(plugin);
+        result = plugin.doAuthenticate(request, response, filterChain);
+      } else {
+        response.sendError(ErrorCode.UNAUTHORIZED.code, "No Authorization header");
       }
-
-      throw new SolrException(ErrorCode.UNAUTHORIZED, "No Authorization header");
+      return result;
     }
 
     final String scheme = getSchemeFromAuthHeader(authHeader);
     final AuthenticationPlugin plugin = pluginMap.get(scheme);
     if (plugin == null) {
-      throw new SolrException(ErrorCode.SERVER_ERROR, "Authorization scheme '" + scheme + "' not supported!");
+      response.sendError(ErrorCode.UNAUTHORIZED.code, "Authorization scheme '" + scheme + "' not supported!");
+      return false;
     }
 
     pluginInRequest.set(plugin);
diff --git a/solr/core/src/test-files/solr/security/multi_auth_plugin_security.json b/solr/core/src/test-files/solr/security/multi_auth_plugin_security.json
index 0fd98fb..7fac044 100644
--- a/solr/core/src/test-files/solr/security/multi_auth_plugin_security.json
+++ b/solr/core/src/test-files/solr/security/multi_auth_plugin_security.json
@@ -3,7 +3,7 @@
     "class": "solr.MultiAuthPlugin",
     "schemes": [{
       "scheme": "basic",
-      "blockUnknown": false,
+      "blockUnknown": true,
       "class": "solr.BasicAuthPlugin",
       "credentials": {
         "admin": "orwp2Ghgj39lmnrZOTm7Qtre1VqHFDfwAEzr0ApbN3Y= Ju5osoAqOX8iafhWpPP01E5P+sg8tK8tHON7rCYZRRw="
@@ -33,6 +33,12 @@
     ],
     "permissions": [
       {
+        "name": "k8s-probe-0",
+        "role": null,
+        "collection": null,
+        "path": "/admin/info/system"
+      },
+      {
         "name": "read",
         "role": [
           "admin",
diff --git a/solr/core/src/test/org/apache/solr/security/MultiAuthPluginTest.java b/solr/core/src/test/org/apache/solr/security/MultiAuthPluginTest.java
index 4566185..a02630a 100644
--- a/solr/core/src/test/org/apache/solr/security/MultiAuthPluginTest.java
+++ b/solr/core/src/test/org/apache/solr/security/MultiAuthPluginTest.java
@@ -21,6 +21,7 @@ import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletRequestWrapper;
+import java.io.IOException;
 import java.io.Serializable;
 import java.nio.charset.StandardCharsets;
 import java.security.Principal;
@@ -30,7 +31,9 @@ import java.util.Objects;
 import java.util.function.Predicate;
 
 import org.apache.commons.io.FileUtils;
+import org.apache.http.HttpResponse;
 import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
 import org.apache.http.message.BasicHeader;
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.client.solrj.embedded.JettySolrRunner;
@@ -56,7 +59,7 @@ public class MultiAuthPluginTest extends SolrTestCaseJ4 {
 
   private static final String authcPrefix = "/admin/authentication";
   private static final String authzPrefix = "/admin/authorization";
-  
+
   final Predicate<Object> NULL_PREDICATE = Objects::isNull;
   SecurityConfHandlerLocalForTesting securityConfHandler;
   JettySolrRunner jetty;
@@ -104,8 +107,19 @@ public class MultiAuthPluginTest extends SolrTestCaseJ4 {
       verifySecurityStatus(cl, baseUrl + authcPrefix, "authentication/class", "solr.MultiAuthPlugin", 5, user, pass);
       verifySecurityStatus(cl, baseUrl + authzPrefix, "authorization/class", "solr.MultiAuthRuleBasedAuthorizationPlugin", 5, user, pass);
 
-      // For the multi-auth plugin, every command is wrapped with an object that identifies the "scheme"
+      // anonymous requests are blocked by all plugins
+      int statusCode = doHttpGetAnonymous(cl, baseUrl + "/admin/info/system");
+      assertEquals("anonymous get succeeded but should not have", 401, statusCode);
+      // update blockUnknown to allow anonymous for the basic plugin
       String command = "{\n" +
+          "'set-property': { 'basic': {'blockUnknown':false} }\n" +
+          "}";
+      doHttpPost(cl, baseUrl + authcPrefix, command, user, pass, 200);
+      statusCode = doHttpGetAnonymous(cl, baseUrl + "/admin/info/system");
+      assertEquals("anonymous get failed but should have succeeded", 200, statusCode);
+
+      // For the multi-auth plugin, every command is wrapped with an object that identifies the "scheme"
+      command = "{\n" +
           "'set-user': {'harry':'HarryIsCool'}\n" +
           "}";
       // no scheme identified!
@@ -141,25 +155,25 @@ public class MultiAuthPluginTest extends SolrTestCaseJ4 {
       verifySecurityStatus(cl, baseUrl + authzPrefix, "authorization/schemes[0]/user-role/harry", NOT_NULL_PREDICATE, 5, user, pass);
 
       // give the users role a custom permission
-      verifySecurityStatus(cl, baseUrl + authzPrefix, "authorization/permissions[5]", NULL_PREDICATE, 5, user, pass);
+      verifySecurityStatus(cl, baseUrl + authzPrefix, "authorization/permissions[6]", NULL_PREDICATE, 5, user, pass);
       command = "{\n" +
           "'set-permission': { 'name':'k8s-zk', 'role':'users', 'collection':null, 'path':'/admin/zookeeper/status' }\n" +
           "}";
       doHttpPost(cl, baseUrl + authzPrefix, command, user, pass, 200);
-      verifySecurityStatus(cl, baseUrl + authzPrefix, "authorization/permissions[5]/path", new ExpectedValuePredicate("/admin/zookeeper/status"), 5, user, pass);
+      verifySecurityStatus(cl, baseUrl + authzPrefix, "authorization/permissions[6]/path", new ExpectedValuePredicate("/admin/zookeeper/status"), 5, user, pass);
 
       command = "{\n" +
-          "'update-permission': { 'index':'6', 'name':'k8s-zk', 'role':'users', 'collection':null, 'path':'/admin/zookeeper/status2' }\n" +
+          "'update-permission': { 'index':'7', 'name':'k8s-zk', 'role':'users', 'collection':null, 'path':'/admin/zookeeper/status2' }\n" +
           "}";
       doHttpPost(cl, baseUrl + authzPrefix, command, user, pass, 200);
-      verifySecurityStatus(cl, baseUrl + authzPrefix, "authorization/permissions[5]/path", new ExpectedValuePredicate("/admin/zookeeper/status2"), 5, user, pass);
+      verifySecurityStatus(cl, baseUrl + authzPrefix, "authorization/permissions[6]/path", new ExpectedValuePredicate("/admin/zookeeper/status2"), 5, user, pass);
 
       // delete the permission
       command = "{\n" +
-          "'delete-permission': 6\n" +
+          "'delete-permission': 7\n" +
           "}";
       doHttpPost(cl, baseUrl + authzPrefix, command, user, pass, 200);
-      verifySecurityStatus(cl, baseUrl + authzPrefix, "authorization/permissions[5]", NULL_PREDICATE, 5, user, pass);
+      verifySecurityStatus(cl, baseUrl + authzPrefix, "authorization/permissions[6]", NULL_PREDICATE, 5, user, pass);
 
       // delete the user
       command = "{\n" +
@@ -186,6 +200,14 @@ public class MultiAuthPluginTest extends SolrTestCaseJ4 {
     }
   }
 
+  private int doHttpGetAnonymous(HttpClient cl, String url) throws IOException {
+    HttpGet httpPost = new HttpGet(url);
+    HttpResponse r = cl.execute(httpPost);
+    int statusCode = r.getStatusLine().getStatusCode();
+    Utils.consumeFully(r.getEntity());
+    return statusCode;
+  }
+
   private static final class MockPrincipal implements Principal, Serializable {
     @Override
     public String getName() {
@@ -203,7 +225,7 @@ public class MultiAuthPluginTest extends SolrTestCaseJ4 {
     @Override
     public boolean doAuthenticate(ServletRequest request, ServletResponse response, FilterChain filterChain) throws Exception {
       Principal principal = new MockPrincipal();
-      request = wrapWithPrincipal((HttpServletRequest)request, principal, "mock");
+      request = wrapWithPrincipal((HttpServletRequest) request, principal, "mock");
       filterChain.doFilter(request, response);
       return true;
     }