diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/configuration/KomgaProperties.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/configuration/KomgaProperties.kt index 4f18b7c00..af733469b 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/configuration/KomgaProperties.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/configuration/KomgaProperties.kt @@ -4,6 +4,8 @@ import org.springframework.boot.context.properties.ConfigurationProperties import org.springframework.stereotype.Component import org.springframework.validation.annotation.Validated import javax.validation.constraints.Min +import javax.validation.constraints.NotBlank +import javax.validation.constraints.Positive @Component @ConfigurationProperties(prefix = "komga") @@ -25,4 +27,14 @@ class KomgaProperties { @Min(1) var analyzer: Int = 2 } + + var rememberMe = RememberMe() + + class RememberMe { + @NotBlank + var key: String? = null + + @Positive + var validity: Int = 1209600 // 2 weeks + } } diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/security/LoggingBasicAuthFilter.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/security/LoggingBasicAuthFilter.kt deleted file mode 100644 index 89cb97458..000000000 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/security/LoggingBasicAuthFilter.kt +++ /dev/null @@ -1,54 +0,0 @@ -package org.gotson.komga.infrastructure.security - -import mu.KotlinLogging -import org.springframework.security.authentication.AuthenticationManager -import org.springframework.security.authentication.BadCredentialsException -import org.springframework.security.core.Authentication -import org.springframework.security.core.AuthenticationException -import org.springframework.security.web.authentication.www.BasicAuthenticationFilter -import java.security.Principal -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse - -private val log = KotlinLogging.logger {} - -class LoggingBasicAuthFilter( - authenticationManager: AuthenticationManager -) : BasicAuthenticationFilter(authenticationManager) { - - override fun onUnsuccessfulAuthentication(request: HttpServletRequest, response: HttpServletResponse, failed: AuthenticationException) { - val cause = when { - failed is BadCredentialsException -> "Bad credentials" - !failed.message.isNullOrBlank() -> failed.message!! - else -> failed.toString() - } - - log.info { "Authentication failure: $cause, ${request.extractInfo()}" } - } - - override fun onSuccessfulAuthentication(request: HttpServletRequest, response: HttpServletResponse, authResult: Authentication) { - val user = when (val p = authResult.principal) { - is KomgaPrincipal -> p.user.email - is Principal -> p.name - else -> p.toString() - } - - log.info { "Authentication success for user: $user, ${request.extractInfo()}" } - } -} - -data class RequestInfo( - val ip: String, - val userAgent: String?, - val method: String, - val url: String, - val query: String? -) - -fun HttpServletRequest.extractInfo() = RequestInfo( - ip = getHeader("X-Real-IP") ?: getHeader("X-Forwarded-For") ?: remoteAddr, - userAgent = getHeader("User-Agent"), - method = method, - url = requestURL.toString(), - query = queryString -) diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/security/SecurityConfiguration.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/security/SecurityConfiguration.kt index e4c501218..e6767c4b5 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/security/SecurityConfiguration.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/security/SecurityConfiguration.kt @@ -1,5 +1,6 @@ package org.gotson.komga.infrastructure.security +import mu.KotlinLogging import org.gotson.komga.infrastructure.configuration.KomgaProperties import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest import org.springframework.boot.autoconfigure.security.servlet.PathRequest @@ -12,18 +13,17 @@ import org.springframework.security.config.annotation.web.builders.WebSecurity import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter import org.springframework.security.core.session.SessionRegistry -import org.springframework.security.core.session.SessionRegistryImpl -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder -import org.springframework.security.crypto.password.PasswordEncoder -import org.springframework.security.web.authentication.www.BasicAuthenticationFilter +import org.springframework.security.core.userdetails.UserDetailsService import org.springframework.web.cors.CorsConfiguration import org.springframework.web.cors.UrlBasedCorsConfigurationSource +private val logger = KotlinLogging.logger {} @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) class SecurityConfiguration( private val komgaProperties: KomgaProperties, + private val komgaUserDetailsLifecycle: UserDetailsService, private val sessionRegistry: SessionRegistry ) : WebSecurityConfigurerAdapter() { @@ -31,36 +31,45 @@ class SecurityConfiguration( // @formatter:off http - .addFilterAt(LoggingBasicAuthFilter(this.authenticationManager()), BasicAuthenticationFilter::class.java) - .cors() - .and() - .csrf().disable() + .cors() + .and() + .csrf().disable() - .authorizeRequests() - // restrict all actuator endpoints to ADMIN only - .requestMatchers(EndpointRequest.toAnyEndpoint()).hasRole("ADMIN") + .authorizeRequests() + // restrict all actuator endpoints to ADMIN only + .requestMatchers(EndpointRequest.toAnyEndpoint()).hasRole("ADMIN") - // restrict H2 console to ADMIN only - .requestMatchers(PathRequest.toH2Console()).hasRole("ADMIN") + // restrict H2 console to ADMIN only + .requestMatchers(PathRequest.toH2Console()).hasRole("ADMIN") - // all other endpoints are restricted to authenticated users - .antMatchers( - "/api/**", - "/opds/**" - ).hasRole("USER") + // all other endpoints are restricted to authenticated users + .antMatchers( + "/api/**", + "/opds/**" + ).hasRole("USER") - // authorize frames for H2 console - .and() - .headers().frameOptions().sameOrigin() + // authorize frames for H2 console + .and() + .headers().frameOptions().sameOrigin() - .and() - .httpBasic() + .and() + .httpBasic() - .and() - .sessionManagement() - .maximumSessions(10) - .sessionRegistry(sessionRegistry) + .and() + .sessionManagement() + .maximumSessions(10) + .sessionRegistry(sessionRegistry) + if(!komgaProperties.rememberMe.key.isNullOrBlank()) { + logger.info { "RememberMe is active, validity: ${komgaProperties.rememberMe.validity}s" } + + http + .rememberMe() + .key(komgaProperties.rememberMe.key) + .tokenValiditySeconds(komgaProperties.rememberMe.validity) + .alwaysRemember(true) + .userDetailsService(komgaUserDetailsLifecycle) + } // @formatter:on } diff --git a/komga/src/main/resources/application-dev.yml b/komga/src/main/resources/application-dev.yml index f392b0eee..32831a468 100644 --- a/komga/src/main/resources/application-dev.yml +++ b/komga/src/main/resources/application-dev.yml @@ -2,6 +2,9 @@ komga: threads: analyzer: 1 filesystem-scanner-force-directory-modified-time: false + remember-me: + key: changeMe! + validity: 2592000 # 1 month # libraries-scan-directory-exclusions: # - "#recycle" # - "@eaDir"