diff --git a/app/src/main/frontend/views/GameRequestView.tsx b/app/src/main/frontend/views/GameRequestView.tsx index 03a784a..427229e 100644 --- a/app/src/main/frontend/views/GameRequestView.tsx +++ b/app/src/main/frontend/views/GameRequestView.tsx @@ -35,9 +35,11 @@ export default function GameRequestView() { const gameRequests = useSnapshot(gameRequestState).gameRequests; const [areGameRequestsEnabled, setAreGameRequestsEnabled] = useState(false); + const [areGuestsAllowedToRequestGames, setAreGuestsAllowedToRequestGames] = useState(false); useEffect(() => { ConfigEndpoint.areGameRequestsEnabled().then(setAreGameRequestsEnabled); + ConfigEndpoint.areGuestsAllowedToRequestGames().then(setAreGuestsAllowedToRequestGames); }, []); const [searchTerm, setSearchTerm] = useState(""); @@ -168,7 +170,7 @@ export default function GameRequestView() { color="primary" startContent={} onPress={requestGameModal.onOpen} - isDisabled={!areGameRequestsEnabled}> + isDisabled={!areGameRequestsEnabled || (!auth.state.user && !areGuestsAllowedToRequestGames)}> Request a Game diff --git a/app/src/main/frontend/views/MainLayout.tsx b/app/src/main/frontend/views/MainLayout.tsx index 4a00641..a8aa302 100644 --- a/app/src/main/frontend/views/MainLayout.tsx +++ b/app/src/main/frontend/views/MainLayout.tsx @@ -94,18 +94,16 @@ export default function MainLayout() { } - {auth.state.user && - - - - - - } + + + + + {isAdmin(auth) && 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 395c038..67b11ff 100644 --- a/app/src/main/kotlin/org/gameyfin/app/config/ConfigEndpoint.kt +++ b/app/src/main/kotlin/org/gameyfin/app/config/ConfigEndpoint.kt @@ -59,4 +59,9 @@ class ConfigEndpoint( @DynamicPublicAccess @AnonymousAllowed fun areGameRequestsEnabled(): Boolean = configService.get(ConfigProperties.Requests.Games.Enabled) == true + + @DynamicPublicAccess + @AnonymousAllowed + fun areGuestsAllowedToRequestGames(): Boolean = + configService.get(ConfigProperties.Requests.Games.AllowGuestsToRequestGames) == true } diff --git a/app/src/main/kotlin/org/gameyfin/app/requests/GameRequestService.kt b/app/src/main/kotlin/org/gameyfin/app/requests/GameRequestService.kt index c56ee14..3f81d65 100644 --- a/app/src/main/kotlin/org/gameyfin/app/requests/GameRequestService.kt +++ b/app/src/main/kotlin/org/gameyfin/app/requests/GameRequestService.kt @@ -91,7 +91,7 @@ class GameRequestService( // Check if guests are allowed to create requests if (config.get(ConfigProperties.Requests.Games.AllowGuestsToRequestGames) != true && currentUser == null) { - throw EndpointException("Only registered users can create game requests") + throw EndpointException("Only registered users can submit game requests") } // Check if user has too many open requests (0 means no limit per user) 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 b98b41b..c3d58a4 100644 --- a/app/src/main/kotlin/org/gameyfin/app/users/UserService.kt +++ b/app/src/main/kotlin/org/gameyfin/app/users/UserService.kt @@ -99,7 +99,9 @@ class UserService( if (principal is OidcUser) { val oidcUser = org.gameyfin.app.users.entities.User(principal) - val userInfoDto = oidcUser.toExtendedUserInfoDto() + val user = userRepository.findByOidcProviderId(oidcUser.oidcProviderId!!) + ?: throw UsernameNotFoundException("Unknown OIDC user with provider ID '${oidcUser.oidcProviderId}'") + val userInfoDto = user.toExtendedUserInfoDto() userInfoDto.roles = roleService.extractGrantedAuthorities(principal.authorities) .mapNotNull { Role.safeValueOf(it.authority) } return userInfoDto diff --git a/app/src/main/kotlin/org/gameyfin/app/users/entities/User.kt b/app/src/main/kotlin/org/gameyfin/app/users/entities/User.kt index f170bb0..9c5994f 100644 --- a/app/src/main/kotlin/org/gameyfin/app/users/entities/User.kt +++ b/app/src/main/kotlin/org/gameyfin/app/users/entities/User.kt @@ -1,14 +1,15 @@ package org.gameyfin.app.users.entities -import org.gameyfin.app.games.entities.Image import jakarta.persistence.* import org.gameyfin.app.core.Role import org.gameyfin.app.core.security.EncryptionConverter +import org.gameyfin.app.games.entities.Image import org.springframework.security.oauth2.core.oidc.user.OidcUser @Entity @Table(name = "users") +@EntityListeners(UserEntityListener::class) class User( @Id @GeneratedValue(strategy = GenerationType.AUTO) diff --git a/app/src/main/kotlin/org/gameyfin/app/users/entities/UserEntityListener.kt b/app/src/main/kotlin/org/gameyfin/app/users/entities/UserEntityListener.kt new file mode 100644 index 0000000..9779485 --- /dev/null +++ b/app/src/main/kotlin/org/gameyfin/app/users/entities/UserEntityListener.kt @@ -0,0 +1,25 @@ +package org.gameyfin.app.users.entities + +import jakarta.persistence.EntityManager +import jakarta.persistence.PreRemove +import org.gameyfin.app.requests.entities.GameRequest +import org.gameyfin.app.util.EntityManagerHolder + +class UserEntityListener { + + @PreRemove + fun preRemove(user: User) { + val entityManager: EntityManager = EntityManagerHolder.getEntityManager() + + // Remove user from all GameRequest voters and requester fields + val gameRequests = entityManager.createQuery( + "SELECT gr FROM GameRequest gr WHERE :user MEMBER OF gr.voters OR gr.requester = :user", + GameRequest::class.java + ).setParameter("user", user).resultList + for (gr in gameRequests) { + gr.voters.remove(user) + if (gr.requester == user) gr.requester = null + entityManager.merge(gr) + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/org/gameyfin/app/util/EntityManagerHolder.kt b/app/src/main/kotlin/org/gameyfin/app/util/EntityManagerHolder.kt new file mode 100644 index 0000000..08059b7 --- /dev/null +++ b/app/src/main/kotlin/org/gameyfin/app/util/EntityManagerHolder.kt @@ -0,0 +1,19 @@ +package org.gameyfin.app.util + +import jakarta.persistence.EntityManager +import org.springframework.context.ApplicationContext +import org.springframework.context.ApplicationContextAware +import org.springframework.stereotype.Component + +@Component +object EntityManagerHolder : ApplicationContextAware { + private var entityManager: EntityManager? = null + + override fun setApplicationContext(context: ApplicationContext) { + entityManager = context.getBean(EntityManager::class.java) + } + + fun getEntityManager(): EntityManager { + return entityManager ?: throw IllegalStateException("EntityManager not set") + } +} \ No newline at end of file