This is an automated email from the ASF dual-hosted git repository.
malliaridis 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 f9f45472662 SOLR-17885: Fix authentication for new UI in browser
(#3704)
f9f45472662 is described below
commit f9f45472662c44644b797adab7a5131128c61fd2
Author: Christos Malliaridis <[email protected]>
AuthorDate: Wed Oct 1 01:06:33 2025 +0300
SOLR-17885: Fix authentication for new UI in browser (#3704)
* Add assets of new UI to excludePatterns
* Resolve issues with Basic auth on web and implement realm support
---
.../commonMain/composeResources/values/strings.xml | 1 +
.../solr/ui/components/auth/BasicAuthComponent.kt | 2 +-
.../integration/DefaultAuthenticationComponent.kt | 13 ++++---
.../start/integration/HttpStartStoreClient.kt | 41 +++++++++++++++++-----
.../kotlin/org/apache/solr/ui/domain/AuthMethod.kt | 4 ++-
.../kotlin/org/apache/solr/ui/domain/AuthOption.kt | 4 +--
.../org/apache/solr/ui/utils/HttpClientUtils.kt | 8 ++++-
.../apache/solr/ui/views/auth/BasicAuthContent.kt | 5 ++-
solr/webapp/web/WEB-INF/web.xml | 2 +-
9 files changed, 57 insertions(+), 23 deletions(-)
diff --git a/solr/ui/src/commonMain/composeResources/values/strings.xml
b/solr/ui/src/commonMain/composeResources/values/strings.xml
index 6b4a6841c74..42becfb8734 100644
--- a/solr/ui/src/commonMain/composeResources/values/strings.xml
+++ b/solr/ui/src/commonMain/composeResources/values/strings.xml
@@ -30,6 +30,7 @@
<!-- Descriptions (desc) -->
<string name="desc_to_get_started">To get started, please provide a Solr
host URL:</string>
<string name="desc_solr_instance_with_auth">The Solr instance %1$s has
enabled authentication.</string>
+ <string name="desc_sign_in_with_credentials">To authenticate you may use
your credentials for Basic authentication.</string>
<string name="desc_sign_in_with_credentials_to_realm">To authenticate you
may use your credentials for the realm %1$s.</string>
<!-- Errors (error) -->
diff --git
a/solr/ui/src/commonMain/kotlin/org/apache/solr/ui/components/auth/BasicAuthComponent.kt
b/solr/ui/src/commonMain/kotlin/org/apache/solr/ui/components/auth/BasicAuthComponent.kt
index c18c2e48920..c243ad8ccbb 100644
---
a/solr/ui/src/commonMain/kotlin/org/apache/solr/ui/components/auth/BasicAuthComponent.kt
+++
b/solr/ui/src/commonMain/kotlin/org/apache/solr/ui/components/auth/BasicAuthComponent.kt
@@ -52,7 +52,7 @@ interface BasicAuthComponent {
* does not distinguish input fields.
*/
data class Model(
- val realm: String = "",
+ val realm: String? = null,
val username: String = "",
val password: String = "",
val hasError: Boolean = false,
diff --git
a/solr/ui/src/commonMain/kotlin/org/apache/solr/ui/components/auth/integration/DefaultAuthenticationComponent.kt
b/solr/ui/src/commonMain/kotlin/org/apache/solr/ui/components/auth/integration/DefaultAuthenticationComponent.kt
index 4fb7493bd00..0aae2090309 100644
---
a/solr/ui/src/commonMain/kotlin/org/apache/solr/ui/components/auth/integration/DefaultAuthenticationComponent.kt
+++
b/solr/ui/src/commonMain/kotlin/org/apache/solr/ui/components/auth/integration/DefaultAuthenticationComponent.kt
@@ -100,12 +100,11 @@ class DefaultAuthenticationComponent(
}
init {
- lifecycle.doOnCreate {
- methods.forEach { method ->
- when (method) {
- is AuthMethod.BasicAuthMethod ->
- basicAuthNavigation.activate(configuration =
BasicAuthConfiguration(method))
- }
+ methods.forEach { method ->
+ when (method) {
+ is AuthMethod.BasicAuthMethod ->
+ basicAuthNavigation.activate(configuration =
BasicAuthConfiguration(method))
+ is AuthMethod.Unknown -> {} // TODO Handle unknown auth methods
}
}
}
@@ -120,9 +119,9 @@ class DefaultAuthenticationComponent(
OnAuthenticated(
option = AuthOption.BasicAuthOption(
url = url,
- method = output.method,
username = output.username,
password = output.password,
+ realm = output.method.realm,
),
),
)
diff --git
a/solr/ui/src/commonMain/kotlin/org/apache/solr/ui/components/start/integration/HttpStartStoreClient.kt
b/solr/ui/src/commonMain/kotlin/org/apache/solr/ui/components/start/integration/HttpStartStoreClient.kt
index dfed6e3c921..885fccefe2e 100644
---
a/solr/ui/src/commonMain/kotlin/org/apache/solr/ui/components/start/integration/HttpStartStoreClient.kt
+++
b/solr/ui/src/commonMain/kotlin/org/apache/solr/ui/components/start/integration/HttpStartStoreClient.kt
@@ -64,7 +64,7 @@ class HttpStartStoreClient(
methods = methods,
message =
if (methods.isEmpty()) {
- "Unauthorized response received with missing or
unsupported auth method."
+ "Unauthorized response received with missing auth
methods."
} else {
null
},
@@ -83,15 +83,38 @@ class HttpStartStoreClient(
* @param headers The headers to use for extracting the information.
*/
private fun getAuthMethodsFromHeader(headers: Headers): List<AuthMethod> {
- val authHeader = headers["Www-Authenticate"]
- val parts = authHeader?.split(" ", limit = 2)
- val scheme = parts?.firstOrNull()
+ // Note that on JVM headers.getAll() may return multiple values,
whereas in WebAssembly/JS
+ // it may merge multiple headers and separate them by comma
+ val authHeaders = headers.getAll("Www-Authenticate") ?: emptyList()
- // TODO Get realm from header value
+ return authHeaders
+ // Split by comma, as there is the chance that headers will be
merged and separated by
+ // comma (e.g. on web target)
+ .flatMap { header -> header.split(",") }
+ .map { authHeader ->
+ val (scheme, params) = parseWwwAuthenticate(authHeader)
- return when (scheme) {
- "Basic" -> listOf(AuthMethod.BasicAuthMethod(realm = "solr"))
- else -> emptyList()
- }
+ when (scheme.lowercase()) {
+ "basic", "xbasic" -> AuthMethod.BasicAuthMethod(realm =
params["realm"])
+ else -> AuthMethod.Unknown
+ }
+ }
+ }
+
+ private fun parseWwwAuthenticate(headerValue: String): Pair<String,
Map<String, String>> {
+ val parts = headerValue.split(" ", limit = 2)
+ val scheme = parts[0]
+
+ // The below mapping is not supporting commas or spaces in the
parameter values, nor
+ // parameters separated by commas (only spaces)
+ val params = parts.getOrNull(1)
+ ?.split(" ")
+ ?.map(String::trim)
+ ?.associate {
+ val (k, v) = it.split("=", limit = 2)
+ k to v.trim('"')
+ }
+ ?: emptyMap()
+ return scheme to params
}
}
diff --git
a/solr/ui/src/commonMain/kotlin/org/apache/solr/ui/domain/AuthMethod.kt
b/solr/ui/src/commonMain/kotlin/org/apache/solr/ui/domain/AuthMethod.kt
index c9b711b2d43..e50d0429290 100644
--- a/solr/ui/src/commonMain/kotlin/org/apache/solr/ui/domain/AuthMethod.kt
+++ b/solr/ui/src/commonMain/kotlin/org/apache/solr/ui/domain/AuthMethod.kt
@@ -35,5 +35,7 @@ sealed interface AuthMethod {
* @property realm The realm of the basic auth.
*/
@Serializable
- data class BasicAuthMethod(val realm: String = "") : AuthMethod
+ data class BasicAuthMethod(val realm: String? = null) : AuthMethod
+
+ data object Unknown : AuthMethod
}
diff --git
a/solr/ui/src/commonMain/kotlin/org/apache/solr/ui/domain/AuthOption.kt
b/solr/ui/src/commonMain/kotlin/org/apache/solr/ui/domain/AuthOption.kt
index 637db9579d9..9665d7aa19c 100644
--- a/solr/ui/src/commonMain/kotlin/org/apache/solr/ui/domain/AuthOption.kt
+++ b/solr/ui/src/commonMain/kotlin/org/apache/solr/ui/domain/AuthOption.kt
@@ -38,17 +38,17 @@ sealed interface AuthOption {
/**
* Authentication option for authenticating with basic auth (credentials).
*
- * @property method The basic auth method used for authentication (holds
metadata).
* @property url The URL of the instance that requires basic auth and the
credentials of this
* instance are for.
* @property username The username to use for further authenticated
requests.
* @property password The password to use for further authenticated
requests.
+ * @property realm The realm defined and used in the Basic auth method.
*/
@Serializable
data class BasicAuthOption(
- val method: AuthMethod.BasicAuthMethod,
val url: Url,
val username: String,
val password: String,
+ val realm: String? = null,
) : AuthOption
}
diff --git
a/solr/ui/src/commonMain/kotlin/org/apache/solr/ui/utils/HttpClientUtils.kt
b/solr/ui/src/commonMain/kotlin/org/apache/solr/ui/utils/HttpClientUtils.kt
index e8b409a9138..98e6d146ed3 100644
--- a/solr/ui/src/commonMain/kotlin/org/apache/solr/ui/utils/HttpClientUtils.kt
+++ b/solr/ui/src/commonMain/kotlin/org/apache/solr/ui/utils/HttpClientUtils.kt
@@ -57,19 +57,25 @@ fun getHttpClientWithAuthOption(option: AuthOption) = when
(option) {
is AuthOption.None -> getDefaultClient(option.url)
is AuthOption.BasicAuthOption -> getHttpClientWithCredentials(
url = option.url,
+ realm = option.realm,
username = option.username,
password = option.password,
)
}
fun getHttpClientWithCredentials(
- url: Url = Url("http://127.0.0.1:8983/"),
username: String,
password: String,
+ url: Url = Url("http://127.0.0.1:8983/"),
+ realm: String? = null,
) = getDefaultClient(url) {
install(Auth) {
basic {
credentials { BasicAuthCredentials(username, password) }
+ // Always include the credentials, because we are accessing
protected endpoints from a
+ // not protected asset (web-assembly app)
+ sendWithoutRequest { true }
+ this.realm = realm
}
}
}
diff --git
a/solr/ui/src/commonMain/kotlin/org/apache/solr/ui/views/auth/BasicAuthContent.kt
b/solr/ui/src/commonMain/kotlin/org/apache/solr/ui/views/auth/BasicAuthContent.kt
index c3a1b1a53c5..3cb87fbba3d 100644
---
a/solr/ui/src/commonMain/kotlin/org/apache/solr/ui/views/auth/BasicAuthContent.kt
+++
b/solr/ui/src/commonMain/kotlin/org/apache/solr/ui/views/auth/BasicAuthContent.kt
@@ -35,6 +35,7 @@ import org.apache.solr.ui.components.auth.BasicAuthComponent
import org.apache.solr.ui.generated.resources.Res
import org.apache.solr.ui.generated.resources.action_sign_in_with_credentials
import org.apache.solr.ui.generated.resources.authenticating
+import org.apache.solr.ui.generated.resources.desc_sign_in_with_credentials
import
org.apache.solr.ui.generated.resources.desc_sign_in_with_credentials_to_realm
import org.apache.solr.ui.generated.resources.label_password
import org.apache.solr.ui.generated.resources.label_username
@@ -62,7 +63,9 @@ fun BasicAuthContent(
val model by component.model.collectAsState()
Text(
- text =
stringResource(Res.string.desc_sign_in_with_credentials_to_realm, model.realm),
+ text = model.realm?.let {
+ stringResource(Res.string.desc_sign_in_with_credentials_to_realm,
it)
+ } ?: stringResource(Res.string.desc_sign_in_with_credentials),
style = MaterialTheme.typography.bodyMedium,
)
diff --git a/solr/webapp/web/WEB-INF/web.xml b/solr/webapp/web/WEB-INF/web.xml
index dfc0c0ce454..37a68b4692b 100644
--- a/solr/webapp/web/WEB-INF/web.xml
+++ b/solr/webapp/web/WEB-INF/web.xml
@@ -35,7 +35,7 @@
-->
<init-param>
<param-name>excludePatterns</param-name>
-
<param-value>/partials/.+,/libs/.+,/css/.+,/js/.+,/img/.+,/templates/.+</param-value>
+
<param-value>/partials/.+,/libs/.+,/css/.+,/js/.+,/img/.+,/templates/.+,/ui/.*</param-value>
</init-param>
</filter>