You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by ep...@apache.org on 2024/05/29 13:30:10 UTC
(solr) branch branch_9x updated: SOLR-17247: Fix bug - 'WWW-Authenticate' headers missing in MultiAuthPlugin (#2416)
This is an automated email from the ASF dual-hosted git repository.
epugh pushed a commit to branch branch_9x
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/branch_9x by this push:
new 4bd265d81f9 SOLR-17247: Fix bug - 'WWW-Authenticate' headers missing in MultiAuthPlugin (#2416)
4bd265d81f9 is described below
commit 4bd265d81f9442b55d86dbb19723cde7ef04cd0f
Author: Lamine <10...@users.noreply.github.com>
AuthorDate: Wed May 29 08:29:35 2024 -0500
SOLR-17247: Fix bug - 'WWW-Authenticate' headers missing in MultiAuthPlugin (#2416)
Co-authored-by: Lamine Idjeraoui <li...@apple.com>
Co-authored-by: Eric Pugh <ep...@opensourceconnections.com>
---
solr/CHANGES.txt | 2 +
.../org/apache/solr/security/MultiAuthPlugin.java | 26 +++++++++++++
.../solr/security/multi_auth_plugin_security.json | 6 +++
.../apache/solr/security/MultiAuthPluginTest.java | 43 ++++++++++++++++++++++
4 files changed, 77 insertions(+)
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index dbf5761b8e5..466092c741b 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -51,6 +51,8 @@ Bug Fixes
* SOLR-16659: Properly construct V2 base urls instead of replacing substring "/solr" with "/api" (Andrey Bozhko via Eric Pugh)
+* SOLR-17247: 'WWW-Authenticate' headers missing in MultiAuthPlugin (Lamine Idjeraoui via Eric Pugh)
+
Dependency Upgrades
---------------------
(No changes)
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 96cd2974c21..5027cec6377 100644
--- a/solr/core/src/java/org/apache/solr/security/MultiAuthPlugin.java
+++ b/solr/core/src/java/org/apache/solr/security/MultiAuthPlugin.java
@@ -17,6 +17,7 @@
package org.apache.solr.security;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
@@ -26,6 +27,7 @@ import java.util.Map;
import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.apache.http.HttpHeaders;
import org.apache.http.HttpRequest;
import org.apache.http.protocol.HttpContext;
import org.apache.lucene.util.ResourceLoader;
@@ -59,6 +61,8 @@ public class MultiAuthPlugin extends AuthenticationPlugin
private static final String UNKNOWN_SCHEME = "";
private final Map<String, AuthenticationPlugin> pluginMap = new LinkedHashMap<>();
+ private final Map<String, String> realms = new LinkedHashMap<>();
+ private final List<String> WWWAuthenticateHeaders = new ArrayList<>();
private final ResourceLoader loader;
// the first of our plugins that allows anonymous requests
private AuthenticationPlugin allowsUnknown = null;
@@ -141,6 +145,7 @@ public class MultiAuthPlugin extends AuthenticationPlugin
}
initPluginForScheme((Map<String, Object>) s);
}
+ initWWWAuthenticateHeaders();
}
protected void initPluginForScheme(Map<String, Object> schemeMap) {
@@ -158,6 +163,11 @@ public class MultiAuthPlugin extends AuthenticationPlugin
ErrorCode.SERVER_ERROR, "'class' is a required attribute: " + schemeMap);
}
+ String realm = (String) schemeConfig.remove("realm");
+ if (!StrUtils.isNullOrEmpty(realm)) {
+ realms.put(scheme, realm);
+ }
+
AuthenticationPlugin pluginForScheme = loader.newInstance(clazz, AuthenticationPlugin.class);
pluginForScheme.init(schemeConfig);
pluginMap.put(scheme.toLowerCase(Locale.ROOT), pluginForScheme);
@@ -171,6 +181,20 @@ public class MultiAuthPlugin extends AuthenticationPlugin
}
}
+ private void initWWWAuthenticateHeaders() {
+ for (String scheme : pluginMap.keySet()) {
+ String realm = realms.get(scheme);
+ String realmStr = realm == null ? "" : " realm=\"" + realm + "\"";
+ WWWAuthenticateHeaders.add(scheme + realmStr);
+ }
+ }
+
+ private void addWWWAuthenticateHeaders(HttpServletResponse response) {
+ for (String wwwAuthHeader : WWWAuthenticateHeaders) {
+ response.addHeader(HttpHeaders.WWW_AUTHENTICATE, wwwAuthHeader);
+ }
+ }
+
@Override
public void initializeMetrics(SolrMetricsContext parentContext, String scope) {
for (AuthenticationPlugin plugin : pluginMap.values()) {
@@ -202,6 +226,7 @@ public class MultiAuthPlugin extends AuthenticationPlugin
pluginInRequest.set(plugin);
result = plugin.doAuthenticate(request, response, filterChain);
} else {
+ addWWWAuthenticateHeaders(response);
response.sendError(ErrorCode.UNAUTHORIZED.code, "No Authorization header");
}
return result;
@@ -210,6 +235,7 @@ public class MultiAuthPlugin extends AuthenticationPlugin
final String scheme = getSchemeFromAuthHeader(authHeader);
final AuthenticationPlugin plugin = pluginMap.get(scheme);
if (plugin == null) {
+ addWWWAuthenticateHeaders(response);
response.sendError(
ErrorCode.UNAUTHORIZED.code, "Authorization scheme '" + scheme + "' not supported!");
return false;
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 7fac044e079..618a0ef789c 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,6 +3,7 @@
"class": "solr.MultiAuthPlugin",
"schemes": [{
"scheme": "basic",
+ "realm": "basicRealm",
"blockUnknown": true,
"class": "solr.BasicAuthPlugin",
"credentials": {
@@ -11,6 +12,11 @@
"forwardCredentials": false
},{
"scheme": "mock",
+ "realm": "mockRealm",
+ "class": "org.apache.solr.security.MultiAuthPluginTest$MockAuthPluginForTesting",
+ "blockUnknown": true
+ },{
+ "scheme": "mockNoRealm",
"class": "org.apache.solr.security.MultiAuthPluginTest$MockAuthPluginForTesting",
"blockUnknown": true
}]
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 1f3bd062121..7b6a821134d 100644
--- a/solr/core/src/test/org/apache/solr/security/MultiAuthPluginTest.java
+++ b/solr/core/src/test/org/apache/solr/security/MultiAuthPluginTest.java
@@ -28,13 +28,18 @@ import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.security.Principal;
+import java.util.Arrays;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.function.Predicate;
+import java.util.stream.Collectors;
import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.apache.http.Header;
+import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
@@ -105,6 +110,10 @@ public class MultiAuthPluginTest extends SolrTestCaseJ4 {
new SecurityConfHandler.SecurityConfig()
.setData(Utils.fromJSONString(multiAuthPluginSecurityJson)));
securityConfHandler.securityConfEdited();
+
+ // verify "WWW-Authenticate" headers are returned
+ verifyWWWAuthenticateHeaders(httpClient, baseUrl);
+
verifySecurityStatus(
httpClient,
baseUrl + authcPrefix,
@@ -269,6 +278,40 @@ public class MultiAuthPluginTest extends SolrTestCaseJ4 {
return statusCode;
}
+ private void verifyWWWAuthenticateHeaders(HttpClient httpClient, String baseUrl)
+ throws Exception {
+ HttpGet httpGet = new HttpGet(baseUrl + "/admin/info/system");
+ HttpResponse response = httpClient.execute(httpGet);
+ Header[] headers = response.getHeaders(HttpHeaders.WWW_AUTHENTICATE);
+ List<String> actualSchemes =
+ Arrays.stream(headers).map(Header::getValue).collect(Collectors.toList());
+
+ List<String> expectedSchemes = generateExpectedSchemes();
+ actualSchemes.sort(String.CASE_INSENSITIVE_ORDER);
+ expectedSchemes.sort(String.CASE_INSENSITIVE_ORDER);
+
+ assertEquals(
+ "The actual schemes and realms should match the expected ones exactly",
+ expectedSchemes.stream().map(s -> s.toLowerCase(Locale.ROOT)).collect(Collectors.toList()),
+ actualSchemes.stream().map(s -> s.toLowerCase(Locale.ROOT)).collect(Collectors.toList()));
+ }
+
+ @SuppressWarnings("unchecked")
+ private List<String> generateExpectedSchemes() {
+ Map<String, Object> data = securityConfHandler.getSecurityConfig(false).getData();
+ Map<String, Object> authentication = (Map<String, Object>) data.get("authentication");
+ List<Map<String, Object>> schemes = (List<Map<String, Object>>) authentication.get("schemes");
+
+ return schemes.stream()
+ .map(
+ schemeMap -> {
+ String scheme = (String) schemeMap.get("scheme");
+ String realm = (String) schemeMap.get("realm");
+ return realm != null ? scheme + " realm=\"" + realm + "\"" : scheme;
+ })
+ .collect(Collectors.toList());
+ }
+
private static final class MockPrincipal implements Principal, Serializable {
@Override
public String getName() {