From a34bd741e3ec5dc3b3f04d25682cc5bd30539c0f Mon Sep 17 00:00:00 2001 From: GRIMSIM Date: Fri, 11 Jul 2025 11:02:54 +0200 Subject: [PATCH] Force SSO user registration due to a bug --- .../administration/SsoManagement.tsx | 21 ++++++++-- .../SsoAuthenticationSuccessHandler.kt | 14 ++++--- .../org/gameyfin/app/games/GameService.kt | 9 +++- .../org/gameyfin/app/users/UserEndpoint.kt | 7 ++-- .../org/gameyfin/app/users/UserService.kt | 41 +++++++++++-------- 5 files changed, 60 insertions(+), 32 deletions(-) diff --git a/app/src/main/frontend/components/administration/SsoManagement.tsx b/app/src/main/frontend/components/administration/SsoManagement.tsx index 78568f2..b77be72 100644 --- a/app/src/main/frontend/components/administration/SsoManagement.tsx +++ b/app/src/main/frontend/components/administration/SsoManagement.tsx @@ -3,8 +3,8 @@ import withConfigPage from "Frontend/components/administration/withConfigPage"; import * as Yup from 'yup'; import ConfigFormField from "Frontend/components/administration/ConfigFormField"; import Section from "Frontend/components/general/Section"; -import {addToast, Button} from "@heroui/react"; -import {MagicWand} from "@phosphor-icons/react"; +import {addToast, Button, Checkbox, CheckboxGroup, Tooltip} from "@heroui/react"; +import {MagicWand, Warning} from "@phosphor-icons/react"; function SsoManagementLayout({getConfig, formik, setSaveMessage}: any) { @@ -50,8 +50,21 @@ function SsoManagementLayout({getConfig, formik, setSaveMessage}: any) {
- + +
+ + Automatically create new users after registration + + + + +
+
+ {/*TODO: enable when the issues with unregistered SSO users are sorted + + + */} diff --git a/app/src/main/kotlin/org/gameyfin/app/core/security/SsoAuthenticationSuccessHandler.kt b/app/src/main/kotlin/org/gameyfin/app/core/security/SsoAuthenticationSuccessHandler.kt index 30784d4..15cb458 100644 --- a/app/src/main/kotlin/org/gameyfin/app/core/security/SsoAuthenticationSuccessHandler.kt +++ b/app/src/main/kotlin/org/gameyfin/app/core/security/SsoAuthenticationSuccessHandler.kt @@ -1,12 +1,12 @@ package org.gameyfin.app.core.security -import org.gameyfin.app.config.ConfigService -import org.gameyfin.app.users.UserService import jakarta.servlet.http.HttpServletRequest import jakarta.servlet.http.HttpServletResponse import org.gameyfin.app.config.ConfigProperties +import org.gameyfin.app.config.ConfigService import org.gameyfin.app.config.MatchUsersBy import org.gameyfin.app.users.RoleService +import org.gameyfin.app.users.UserService import org.gameyfin.app.users.entities.User import org.springframework.security.core.Authentication import org.springframework.security.oauth2.core.oidc.user.OidcUser @@ -42,11 +42,13 @@ class SsoAuthenticationSuccessHandler( // User could not be found in the database if (matchedUser == null) { + // TODO: User registration is currently forced, but this should be configurable. + // However, this causes conflict with user preferences and game entities (since both reference the user entity) // Check if new user registration is enabled - if (config.get(ConfigProperties.SSO.OIDC.AutoRegisterNewUsers) == false) { - response.sendRedirect("/") - return - } + //if (config.get(ConfigProperties.SSO.OIDC.AutoRegisterNewUsers) == false) { + // response.sendRedirect("/") + // return + // // Register as new user matchedUser = User(oidcUser) diff --git a/app/src/main/kotlin/org/gameyfin/app/games/GameService.kt b/app/src/main/kotlin/org/gameyfin/app/games/GameService.kt index c503f36..e496747 100644 --- a/app/src/main/kotlin/org/gameyfin/app/games/GameService.kt +++ b/app/src/main/kotlin/org/gameyfin/app/games/GameService.kt @@ -26,6 +26,7 @@ import org.gameyfin.pluginapi.gamemetadata.* import org.springframework.data.repository.findByIdOrNull import org.springframework.security.core.context.SecurityContextHolder import org.springframework.security.core.userdetails.UserDetails +import org.springframework.security.oauth2.core.oidc.user.OidcUser import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional import reactor.core.publisher.Flux @@ -125,8 +126,12 @@ class GameService( val existingGame = gameRepository.findByIdOrNull(gameUpdateDto.id) ?: throw IllegalArgumentException("Game with ID $gameUpdateDto.id not found") - val userDetails = SecurityContextHolder.getContext().authentication.principal as UserDetails - val user = userService.getByUsernameNonNull(userDetails.username) + val userDetails = SecurityContextHolder.getContext().authentication.principal + val user = when (userDetails) { + is UserDetails -> userService.getByUsernameNonNull(userDetails.username) + is OidcUser -> userService.getByUsernameNonNull(userDetails.preferredUsername) + else -> throw IllegalStateException("Unkown user type: ${userDetails::class.java.name}") + } // Update only non-null fields gameUpdateDto.title?.let { diff --git a/app/src/main/kotlin/org/gameyfin/app/users/UserEndpoint.kt b/app/src/main/kotlin/org/gameyfin/app/users/UserEndpoint.kt index 15853c0..7b0411e 100644 --- a/app/src/main/kotlin/org/gameyfin/app/users/UserEndpoint.kt +++ b/app/src/main/kotlin/org/gameyfin/app/users/UserEndpoint.kt @@ -1,12 +1,12 @@ package org.gameyfin.app.users import com.vaadin.hilla.Endpoint -import org.gameyfin.app.users.dto.UserUpdateDto -import org.gameyfin.app.users.enums.RoleAssignmentResult import jakarta.annotation.security.PermitAll import jakarta.annotation.security.RolesAllowed import org.gameyfin.app.core.Role import org.gameyfin.app.users.dto.UserInfoDto +import org.gameyfin.app.users.dto.UserUpdateDto +import org.gameyfin.app.users.enums.RoleAssignmentResult import org.springframework.security.core.Authentication import org.springframework.security.core.context.SecurityContextHolder @@ -23,8 +23,7 @@ class UserEndpoint( @PermitAll fun getUserInfo(): UserInfoDto { - val auth: Authentication = SecurityContextHolder.getContext().authentication - return userService.getUserInfo(auth) + return userService.getUserInfo() } @PermitAll 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 31a76c4..8ecc257 100644 --- a/app/src/main/kotlin/org/gameyfin/app/users/UserService.kt +++ b/app/src/main/kotlin/org/gameyfin/app/users/UserService.kt @@ -1,25 +1,20 @@ package org.gameyfin.app.users +import io.github.oshai.kotlinlogging.KotlinLogging +import org.gameyfin.app.config.ConfigProperties import org.gameyfin.app.config.ConfigService +import org.gameyfin.app.core.Role +import org.gameyfin.app.core.Utils +import org.gameyfin.app.core.events.* import org.gameyfin.app.games.entities.Image +import org.gameyfin.app.media.ImageService +import org.gameyfin.app.users.dto.UserInfoDto +import org.gameyfin.app.users.dto.UserRegistrationDto import org.gameyfin.app.users.dto.UserUpdateDto import org.gameyfin.app.users.emailconfirmation.EmailConfirmationService import org.gameyfin.app.users.enums.RoleAssignmentResult import org.gameyfin.app.users.persistence.UserRepository -import io.github.oshai.kotlinlogging.KotlinLogging -import org.gameyfin.app.config.ConfigProperties -import org.gameyfin.app.core.Role -import org.gameyfin.app.core.Utils -import org.gameyfin.app.core.events.AccountDeletedEvent -import org.gameyfin.app.core.events.AccountStatusChangedEvent -import org.gameyfin.app.core.events.EmailNeedsConfirmationEvent -import org.gameyfin.app.core.events.RegistrationAttemptWithExistingEmailEvent -import org.gameyfin.app.core.events.UserRegistrationWaitingForApprovalEvent -import org.gameyfin.app.media.ImageService -import org.gameyfin.app.users.dto.UserInfoDto -import org.gameyfin.app.users.dto.UserRegistrationDto import org.springframework.context.ApplicationEventPublisher -import org.springframework.security.core.Authentication import org.springframework.security.core.GrantedAuthority import org.springframework.security.core.authority.SimpleGrantedAuthority import org.springframework.security.core.context.SecurityContextHolder @@ -66,7 +61,8 @@ class UserService( fun existsByUsername(username: String): Boolean = userRepository.existsByUsername(username) fun existsByEmail(email: String): Boolean = userRepository.existsByEmail(email) - fun findByOidcProviderId(oidcProviderId: String): org.gameyfin.app.users.entities.User? = userRepository.findByOidcProviderId(oidcProviderId) + fun findByOidcProviderId(oidcProviderId: String): org.gameyfin.app.users.entities.User? = + userRepository.findByOidcProviderId(oidcProviderId) fun getAllUsers(): List { return userRepository.findAll().map { u -> toUserInfo(u) } @@ -84,7 +80,8 @@ class UserService( return userRepository.findByUsername(username) ?: throw UsernameNotFoundException("Unknown user '$username'") } - fun getUserInfo(auth: Authentication): UserInfoDto { + fun getUserInfo(): UserInfoDto { + val auth = SecurityContextHolder.getContext().authentication val principal = auth.principal if (principal is OidcUser) { @@ -99,6 +96,15 @@ class UserService( return toUserInfo(user) } + fun getCurrentUser(): org.gameyfin.app.users.entities.User { + val auth = SecurityContextHolder.getContext().authentication + if (auth.principal is OidcUser) { + return userRepository.findByOidcProviderId((auth.principal as OidcUser).subject) + ?: throw UsernameNotFoundException("OIDC user not found") + } + return getByUsernameNonNull(auth.name) + } + fun getAvatar(username: String): Image? { val user = getByUsernameNonNull(username) return user.avatar @@ -174,7 +180,10 @@ class UserService( } } - fun registerUserFromInvitation(registration: UserRegistrationDto, email: String): org.gameyfin.app.users.entities.User { + fun registerUserFromInvitation( + registration: UserRegistrationDto, + email: String + ): org.gameyfin.app.users.entities.User { val user = org.gameyfin.app.users.entities.User( username = registration.username, password = passwordEncoder.encode(registration.password),