mirror of
https://github.com/gotson/komga.git
synced 2025-12-06 00:25:08 +01:00
fix(api): empty content when x-api-key is sent alongside session
Closes: #2099
This commit is contained in:
parent
bdca990e82
commit
5a5f8d701e
5 changed files with 22 additions and 8 deletions
|
|
@ -20,6 +20,8 @@ class Hasher {
|
|||
return computeHash(path.inputStream())
|
||||
}
|
||||
|
||||
fun computeHash(string: String): String = computeHash(string.byteInputStream())
|
||||
|
||||
fun computeHash(stream: InputStream): String {
|
||||
val hash = Algorithm.XXH3_128.Seeded(SEED.toLong()).createDigest()
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package org.gotson.komga.infrastructure.security
|
|||
import jakarta.servlet.Filter
|
||||
import org.gotson.komga.domain.model.UserRoles
|
||||
import org.gotson.komga.infrastructure.configuration.KomgaSettingsProvider
|
||||
import org.gotson.komga.infrastructure.hash.Hasher
|
||||
import org.gotson.komga.infrastructure.security.apikey.ApiKeyAuthenticationFilter
|
||||
import org.gotson.komga.infrastructure.security.apikey.ApiKeyAuthenticationProvider
|
||||
import org.gotson.komga.infrastructure.security.apikey.HeaderApiKeyAuthenticationConverter
|
||||
|
|
@ -34,6 +35,7 @@ import org.springframework.security.web.authentication.AnonymousAuthenticationFi
|
|||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler
|
||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource
|
||||
import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter
|
||||
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher
|
||||
|
||||
@Configuration
|
||||
|
|
@ -51,6 +53,7 @@ class SecurityConfiguration(
|
|||
private val opdsAuthenticationEntryPoint: OpdsAuthenticationEntryPoint,
|
||||
private val authenticationEventPublisher: AuthenticationEventPublisher,
|
||||
private val tokenEncoder: TokenEncoder,
|
||||
private val hasher: Hasher,
|
||||
clientRegistrationRepository: InMemoryClientRegistrationRepository?,
|
||||
) {
|
||||
private val oauth2Enabled = clientRegistrationRepository != null
|
||||
|
|
@ -158,7 +161,7 @@ class SecurityConfiguration(
|
|||
)
|
||||
}
|
||||
|
||||
http.addFilterBefore(restAuthenticationFilter(), AnonymousAuthenticationFilter::class.java)
|
||||
http.addFilterAfter(restAuthenticationFilter(), BasicAuthenticationFilter::class.java)
|
||||
|
||||
return http.build()
|
||||
}
|
||||
|
|
@ -239,19 +242,19 @@ class SecurityConfiguration(
|
|||
fun koboAuthenticationFilter(): Filter =
|
||||
ApiKeyAuthenticationFilter(
|
||||
apiKeyAuthenticationProvider(),
|
||||
UriRegexApiKeyAuthenticationConverter(Regex("""/kobo/([\w-]+)"""), tokenEncoder, userAgentWebAuthenticationDetailsSource),
|
||||
UriRegexApiKeyAuthenticationConverter(Regex("""/kobo/([\w-]+)"""), hasher, tokenEncoder, userAgentWebAuthenticationDetailsSource),
|
||||
)
|
||||
|
||||
fun kosyncAuthenticationFilter(): Filter =
|
||||
ApiKeyAuthenticationFilter(
|
||||
apiKeyAuthenticationProvider(),
|
||||
HeaderApiKeyAuthenticationConverter("X-Auth-User", tokenEncoder, userAgentWebAuthenticationDetailsSource),
|
||||
HeaderApiKeyAuthenticationConverter("X-Auth-User", hasher, tokenEncoder, userAgentWebAuthenticationDetailsSource),
|
||||
)
|
||||
|
||||
fun restAuthenticationFilter(): Filter =
|
||||
ApiKeyAuthenticationFilter(
|
||||
apiKeyAuthenticationProvider(),
|
||||
HeaderApiKeyAuthenticationConverter("X-API-Key", tokenEncoder, userAgentWebAuthenticationDetailsSource),
|
||||
HeaderApiKeyAuthenticationConverter("X-API-Key", hasher, tokenEncoder, userAgentWebAuthenticationDetailsSource),
|
||||
)
|
||||
|
||||
fun apiKeyAuthenticationProvider(): AuthenticationManager =
|
||||
|
|
|
|||
|
|
@ -55,6 +55,8 @@ class ApiKeyAuthenticationFilter(
|
|||
} catch (ex: AuthenticationException) {
|
||||
unsuccessfulAuthentication(request, response, ex)
|
||||
}
|
||||
|
||||
filterChain.doFilter(request, response)
|
||||
}
|
||||
|
||||
private fun unsuccessfulAuthentication(
|
||||
|
|
@ -78,7 +80,6 @@ class ApiKeyAuthenticationFilter(
|
|||
}
|
||||
securityContextHolderStrategy.context = context
|
||||
securityContextRepository.saveContext(context, request, response)
|
||||
filterChain.doFilter(request, response)
|
||||
}
|
||||
|
||||
private fun authenticationIsRequired(username: String): Boolean {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package org.gotson.komga.infrastructure.security.apikey
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest
|
||||
import org.gotson.komga.infrastructure.hash.Hasher
|
||||
import org.gotson.komga.infrastructure.security.TokenEncoder
|
||||
import org.springframework.security.authentication.AuthenticationDetailsSource
|
||||
import org.springframework.security.core.Authentication
|
||||
|
|
@ -11,11 +12,13 @@ import org.springframework.security.web.authentication.AuthenticationConverter
|
|||
* and convert it to an [ApiKeyAuthenticationToken]
|
||||
*
|
||||
* @property headerName the header name from which to retrieve the API key
|
||||
* @property hasher the hasher to use to encode the API key as username in the [Authentication] object
|
||||
* @property tokenEncoder the encoder to use to encode the API key in the [Authentication] object
|
||||
* @property authenticationDetailsSource the [AuthenticationDetailsSource] to enrich the [Authentication] details
|
||||
*/
|
||||
class HeaderApiKeyAuthenticationConverter(
|
||||
private val headerName: String,
|
||||
private val hasher: Hasher,
|
||||
private val tokenEncoder: TokenEncoder,
|
||||
private val authenticationDetailsSource: AuthenticationDetailsSource<HttpServletRequest, *>,
|
||||
) : AuthenticationConverter {
|
||||
|
|
@ -23,7 +26,8 @@ class HeaderApiKeyAuthenticationConverter(
|
|||
request
|
||||
.getHeader(headerName)
|
||||
?.let {
|
||||
val (maskedToken, hashedToken) = it.take(6) + "*".repeat(6) to tokenEncoder.encode(it)
|
||||
val maskedToken = hasher.computeHash(it)
|
||||
val hashedToken = tokenEncoder.encode(it)
|
||||
ApiKeyAuthenticationToken
|
||||
.unauthenticated(maskedToken, hashedToken)
|
||||
.apply { details = authenticationDetailsSource.buildDetails(request) }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package org.gotson.komga.infrastructure.security.apikey
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest
|
||||
import org.gotson.komga.infrastructure.hash.Hasher
|
||||
import org.gotson.komga.infrastructure.security.TokenEncoder
|
||||
import org.springframework.security.authentication.AuthenticationDetailsSource
|
||||
import org.springframework.security.core.Authentication
|
||||
|
|
@ -11,11 +12,13 @@ import org.springframework.security.web.authentication.AuthenticationConverter
|
|||
* request URI, and convert it to an [ApiKeyAuthenticationToken]
|
||||
*
|
||||
* @property tokenRegex the regex used to extract the API key
|
||||
* @property tokenEncoder the encoder to use to encode the API key in the [Authentication] object
|
||||
* @property hasher the hasher to use to encode the API key as username in the [Authentication] object
|
||||
* @property tokenEncoder the encoder to use to encode the API key as credentials in the [Authentication] object
|
||||
* @property authenticationDetailsSource the [AuthenticationDetailsSource] to enrich the [Authentication] details
|
||||
*/
|
||||
class UriRegexApiKeyAuthenticationConverter(
|
||||
private val tokenRegex: Regex,
|
||||
private val hasher: Hasher,
|
||||
private val tokenEncoder: TokenEncoder,
|
||||
private val authenticationDetailsSource: AuthenticationDetailsSource<HttpServletRequest, *>,
|
||||
) : AuthenticationConverter {
|
||||
|
|
@ -24,7 +27,8 @@ class UriRegexApiKeyAuthenticationConverter(
|
|||
?.let {
|
||||
tokenRegex.find(it)?.groupValues?.lastOrNull()
|
||||
}?.let {
|
||||
val (maskedToken, hashedToken) = it.take(6) + "*".repeat(6) to tokenEncoder.encode(it)
|
||||
val maskedToken = hasher.computeHash(it)
|
||||
val hashedToken = tokenEncoder.encode(it)
|
||||
ApiKeyAuthenticationToken
|
||||
.unauthenticated(maskedToken, hashedToken)
|
||||
.apply { details = authenticationDetailsSource.buildDetails(request) }
|
||||
|
|
|
|||
Loading…
Reference in a new issue