From 739c883bf175a7e6cda80858f4b47bafa24a2fc6 Mon Sep 17 00:00:00 2001 From: GRIMSIM Date: Fri, 11 Jul 2025 14:05:00 +0200 Subject: [PATCH] Fix various issues with SSO users --- .../administration/SsoManagement.tsx | 2 +- app/src/main/frontend/views/MainLayout.tsx | 2 -- .../org/gameyfin/app/config/ConfigEndpoint.kt | 8 ++++---- .../gameyfin/app/core/logging/LogEndpoint.kt | 8 ++++---- .../app/core/plugins/PluginEndpoint.kt | 8 ++++---- .../gameyfin/app/libraries/LibraryEndpoint.kt | 12 ++++++------ .../org/gameyfin/app/users/UserService.kt | 19 ++++++++++++++++++- .../app/users/util/UserDetailsExtensions.kt | 4 ++++ 8 files changed, 41 insertions(+), 22 deletions(-) diff --git a/app/src/main/frontend/components/administration/SsoManagement.tsx b/app/src/main/frontend/components/administration/SsoManagement.tsx index b77be72..7db75e9 100644 --- a/app/src/main/frontend/components/administration/SsoManagement.tsx +++ b/app/src/main/frontend/components/administration/SsoManagement.tsx @@ -56,7 +56,7 @@ function SsoManagementLayout({getConfig, formik, setSaveMessage}: any) { Automatically create new users after registration - + diff --git a/app/src/main/frontend/views/MainLayout.tsx b/app/src/main/frontend/views/MainLayout.tsx index 8c02d61..5c9e8cd 100644 --- a/app/src/main/frontend/views/MainLayout.tsx +++ b/app/src/main/frontend/views/MainLayout.tsx @@ -13,7 +13,6 @@ import {UserPreferenceService} from "Frontend/util/user-preference-service"; import SearchBar from "Frontend/components/general/SearchBar"; import {useSnapshot} from "valtio/react"; import {gameState} from "Frontend/state/GameState"; -import {scanState} from "Frontend/state/ScanState"; import ScanProgressPopover from "Frontend/components/general/ScanProgressPopover"; import {isAdmin} from "Frontend/util/utils"; @@ -27,7 +26,6 @@ export default function MainLayout() { const isHomePage = location.pathname === "/"; const [isExploding, setIsExploding] = useState(false); const games = useSnapshot(gameState).games; - const scans = useSnapshot(scanState); useEffect(() => { let newTitle = `Gameyfin - ${routeMetadata?.title}`; diff --git a/app/src/main/kotlin/org/gameyfin/app/config/ConfigEndpoint.kt b/app/src/main/kotlin/org/gameyfin/app/config/ConfigEndpoint.kt index 0077b70..16d70c1 100644 --- a/app/src/main/kotlin/org/gameyfin/app/config/ConfigEndpoint.kt +++ b/app/src/main/kotlin/org/gameyfin/app/config/ConfigEndpoint.kt @@ -7,15 +7,15 @@ import jakarta.annotation.security.RolesAllowed import org.gameyfin.app.config.dto.ConfigEntryDto import org.gameyfin.app.config.dto.ConfigUpdateDto import org.gameyfin.app.core.Role +import org.gameyfin.app.users.UserService import org.gameyfin.app.users.util.isAdmin -import org.springframework.security.core.context.SecurityContextHolder -import org.springframework.security.core.userdetails.UserDetails import reactor.core.publisher.Flux @Endpoint @RolesAllowed(Role.Names.ADMIN) class ConfigEndpoint( - private val configService: ConfigService + private val configService: ConfigService, + private val userService: UserService, ) { companion object { val log = KotlinLogging.logger { } @@ -25,7 +25,7 @@ class ConfigEndpoint( @PermitAll fun subscribe(): Flux> { - val user = SecurityContextHolder.getContext().authentication.principal as UserDetails + val user = userService.getCurrentUser() return if (user.isAdmin()) ConfigService.subscribe() else Flux.empty() } diff --git a/app/src/main/kotlin/org/gameyfin/app/core/logging/LogEndpoint.kt b/app/src/main/kotlin/org/gameyfin/app/core/logging/LogEndpoint.kt index d455884..c161243 100644 --- a/app/src/main/kotlin/org/gameyfin/app/core/logging/LogEndpoint.kt +++ b/app/src/main/kotlin/org/gameyfin/app/core/logging/LogEndpoint.kt @@ -4,15 +4,15 @@ import com.vaadin.hilla.Endpoint import jakarta.annotation.security.PermitAll import jakarta.annotation.security.RolesAllowed import org.gameyfin.app.core.Role +import org.gameyfin.app.users.UserService import org.gameyfin.app.users.util.isAdmin -import org.springframework.security.core.context.SecurityContextHolder -import org.springframework.security.core.userdetails.UserDetails import reactor.core.publisher.Flux @Endpoint @RolesAllowed(Role.Names.ADMIN) class LogEndpoint( - private val logService: LogService + private val logService: LogService, + private val userService: UserService, ) { fun reloadLogConfig() { @@ -21,7 +21,7 @@ class LogEndpoint( @PermitAll fun getApplicationLogs(): Flux { - val user = SecurityContextHolder.getContext().authentication.principal as UserDetails + val user = userService.getCurrentUser() return if (user.isAdmin()) logService.streamLogs() else Flux.empty() } diff --git a/app/src/main/kotlin/org/gameyfin/app/core/plugins/PluginEndpoint.kt b/app/src/main/kotlin/org/gameyfin/app/core/plugins/PluginEndpoint.kt index 75df853..6f25573 100644 --- a/app/src/main/kotlin/org/gameyfin/app/core/plugins/PluginEndpoint.kt +++ b/app/src/main/kotlin/org/gameyfin/app/core/plugins/PluginEndpoint.kt @@ -5,21 +5,21 @@ import jakarta.annotation.security.PermitAll import jakarta.annotation.security.RolesAllowed import org.gameyfin.app.core.Role import org.gameyfin.app.core.plugins.dto.PluginUpdateDto +import org.gameyfin.app.users.UserService import org.gameyfin.app.users.util.isAdmin import org.gameyfin.pluginapi.core.config.PluginConfigValidationResult -import org.springframework.security.core.context.SecurityContextHolder -import org.springframework.security.core.userdetails.UserDetails import reactor.core.publisher.Flux @Endpoint @RolesAllowed(Role.Names.ADMIN) class PluginEndpoint( - private val pluginService: PluginService + private val pluginService: PluginService, + private val userService: UserService, ) { @PermitAll fun subscribe(): Flux> { - val user = SecurityContextHolder.getContext().authentication.principal as UserDetails + val user = userService.getCurrentUser() return if (user.isAdmin()) PluginService.subscribe() else Flux.empty() } diff --git a/app/src/main/kotlin/org/gameyfin/app/libraries/LibraryEndpoint.kt b/app/src/main/kotlin/org/gameyfin/app/libraries/LibraryEndpoint.kt index c48d3e1..677906c 100644 --- a/app/src/main/kotlin/org/gameyfin/app/libraries/LibraryEndpoint.kt +++ b/app/src/main/kotlin/org/gameyfin/app/libraries/LibraryEndpoint.kt @@ -1,23 +1,23 @@ package org.gameyfin.app.libraries import com.vaadin.hilla.Endpoint -import org.gameyfin.app.libraries.dto.LibraryDto -import org.gameyfin.app.libraries.dto.LibraryEvent import jakarta.annotation.security.PermitAll import jakarta.annotation.security.RolesAllowed import org.gameyfin.app.core.Role +import org.gameyfin.app.libraries.dto.LibraryDto +import org.gameyfin.app.libraries.dto.LibraryEvent import org.gameyfin.app.libraries.dto.LibraryScanProgress import org.gameyfin.app.libraries.dto.LibraryUpdateDto import org.gameyfin.app.libraries.enums.ScanType +import org.gameyfin.app.users.UserService import org.gameyfin.app.users.util.isAdmin -import org.springframework.security.core.context.SecurityContextHolder -import org.springframework.security.core.userdetails.UserDetails import reactor.core.publisher.Flux @Endpoint @PermitAll class LibraryEndpoint( - private val libraryService: LibraryService + private val libraryService: LibraryService, + private val userService: UserService, ) { fun subscribeToLibraryEvents(): Flux> { return LibraryService.subscribeToLibraryEvents() @@ -27,7 +27,7 @@ class LibraryEndpoint( fun subscribeToScanProgressEvents(): Flux> { - val user = SecurityContextHolder.getContext().authentication.principal as UserDetails + val user = userService.getCurrentUser() return if (user.isAdmin()) LibraryService.subscribeToScanProgressEvents() else Flux.empty() } diff --git a/app/src/main/kotlin/org/gameyfin/app/users/UserService.kt b/app/src/main/kotlin/org/gameyfin/app/users/UserService.kt index 8ecc257..ab67587 100644 --- a/app/src/main/kotlin/org/gameyfin/app/users/UserService.kt +++ b/app/src/main/kotlin/org/gameyfin/app/users/UserService.kt @@ -47,6 +47,19 @@ class UserService( override fun loadUserByUsername(username: String): UserDetails { val user = getByUsernameNonNull(username) + if (user.oidcProviderId != null && user.password == null) { + // If the user is an OIDC user, we return a UserDetails with no password + return User( + user.username, + "", // OIDC users do not have a password + user.enabled, + true, + true, + true, + toAuthorities(user.roles) + ) + } + return User( user.username, user.password, @@ -132,7 +145,11 @@ class UserService( } fun registerOrUpdateUser(user: org.gameyfin.app.users.entities.User): org.gameyfin.app.users.entities.User { - user.password = passwordEncoder.encode(user.password) + // OIDC users can have null passwords, so we only encode if a password is provided + if (user.password != null) { + user.password = passwordEncoder.encode(user.password) + } + return userRepository.save(user) } diff --git a/app/src/main/kotlin/org/gameyfin/app/users/util/UserDetailsExtensions.kt b/app/src/main/kotlin/org/gameyfin/app/users/util/UserDetailsExtensions.kt index 6cb6ea1..6f17d81 100644 --- a/app/src/main/kotlin/org/gameyfin/app/users/util/UserDetailsExtensions.kt +++ b/app/src/main/kotlin/org/gameyfin/app/users/util/UserDetailsExtensions.kt @@ -14,4 +14,8 @@ fun UserDetails.hasRole(role: Role): Boolean { fun UserDetails.isAdmin(): Boolean { return hasRole(Role.SUPERADMIN) || hasRole(Role.ADMIN) +} + +fun User.isAdmin(): Boolean { + return hasRole(Role.SUPERADMIN) || hasRole(Role.ADMIN) } \ No newline at end of file