You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by th...@apache.org on 2021/11/03 19:25:04 UTC
[solr] branch main updated: SOLR-15766: MultiAuthPlugin should send
non-AJAX anonymous requests to the plugin that allows anonymous requests
(#394)
This is an automated email from the ASF dual-hosted git repository.
thelabdude 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 b3835dc SOLR-15766: MultiAuthPlugin should send non-AJAX anonymous requests to the plugin that allows anonymous requests (#394)
b3835dc is described below
commit b3835dca64388f158d58a7f743e82304e0931b87
Author: Timothy Potter <th...@gmail.com>
AuthorDate: Wed Nov 3 13:24:55 2021 -0600
SOLR-15766: MultiAuthPlugin should send non-AJAX anonymous requests to the plugin that allows anonymous requests (#394)
---
solr/CHANGES.txt | 2 ++
.../org/apache/solr/security/MultiAuthPlugin.java | 28 +++++++++++-----
.../solr/security/multi_auth_plugin_security.json | 8 ++++-
.../apache/solr/security/MultiAuthPluginTest.java | 38 +++++++++++++++++-----
4 files changed, 59 insertions(+), 17 deletions(-)
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index fe92035..aaa3db2 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -445,6 +445,8 @@ Bug Fixes
* SOLR-15722: Delete Replica does not delete the Per replica state (noble)
+* 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 2857005..8d99cfa 100644
--- a/solr/core/src/java/org/apache/solr/security/MultiAuthPlugin.java
+++ b/solr/core/src/java/org/apache/solr/security/MultiAuthPlugin.java
@@ -57,6 +57,7 @@ public class MultiAuthPlugin extends AuthenticationPlugin implements ConfigEdita
private final Map<String, AuthenticationPlugin> pluginMap = new LinkedHashMap<>();
private final ResourceLoader 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) {
@@ -143,6 +144,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
@@ -160,21 +168,25 @@ public class MultiAuthPlugin extends AuthenticationPlugin implements ConfigEdita
@Override
public boolean doAuthenticate(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws Exception {
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 177a0b4..0599113 100644
--- a/solr/core/src/test/org/apache/solr/security/MultiAuthPluginTest.java
+++ b/solr/core/src/test/org/apache/solr/security/MultiAuthPluginTest.java
@@ -19,6 +19,7 @@ package org.apache.solr.security;
import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.security.Principal;
@@ -28,7 +29,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;
@@ -54,7 +57,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;
@@ -102,8 +105,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!
@@ -139,25 +153,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" +
@@ -184,6 +198,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() {