From 01cc758b071df3e2c89f3f1227bb649f63f0eb0a Mon Sep 17 00:00:00 2001 From: grimsi <9295182+grimsi@users.noreply.github.com> Date: Sun, 18 May 2025 09:52:16 +0200 Subject: [PATCH] Fix authentication for websocket based endpoints --- gameyfin/src/main/frontend/views/LoginView.tsx | 15 ++++++++++----- .../de/grimsi/gameyfin/config/ConfigEndpoint.kt | 13 +++++++++---- .../grimsi/gameyfin/core/logging/LogEndpoint.kt | 12 ++++++++---- .../gameyfin/users/util/UserDetailsExtensions.kt | 3 --- 4 files changed, 27 insertions(+), 16 deletions(-) diff --git a/gameyfin/src/main/frontend/views/LoginView.tsx b/gameyfin/src/main/frontend/views/LoginView.tsx index 4fcefa1..536f150 100644 --- a/gameyfin/src/main/frontend/views/LoginView.tsx +++ b/gameyfin/src/main/frontend/views/LoginView.tsx @@ -1,12 +1,12 @@ import {useAuth} from "Frontend/util/auth"; import {useEffect, useState} from "react"; import {Button, Card, CardBody, CardHeader, Link, useDisclosure} from "@heroui/react"; -import {useNavigate} from "react-router"; import {Form, Formik} from "formik"; import Input from "Frontend/components/general/input/Input"; import PasswordResetModal from "Frontend/components/general/modals/PasswordResetModal"; import SignUpModal from "Frontend/components/general/modals/SignUpModal"; import {RegistrationEndpoint} from "Frontend/generated/endpoints"; +import {useNavigate} from "react-router"; export default function LoginView() { const {state, login} = useAuth(); @@ -15,26 +15,31 @@ export default function LoginView() { const passwordResetModal = useDisclosure(); const signUpModal = useDisclosure(); - const [url, setUrl] = useState(); const [signUpAllowed, setSignUpAllowed] = useState(false); useEffect(() => { if (state.user) { - const path = url ? new URL(url, document.baseURI).pathname : '/' - navigate(path, {replace: true}); + redirectAfterLogin(); } else { RegistrationEndpoint.isSelfRegistrationAllowed().then(setSignUpAllowed); } }, [state.user]); async function tryLogin(values: any, formik: any) { - const {error} = await login(values.username, values.password); + const {defaultUrl, error, redirectUrl} = await login(values.username, values.password); if (error) { formik.setFieldError("username", " "); // Mark the field red, but don't show an error message formik.setFieldError("password", "Invalid username and/or password."); + } else { + redirectAfterLogin(redirectUrl, defaultUrl); } } + function redirectAfterLogin(redirectUrl?: string, defaultUrl?: string) { + const url = redirectUrl ?? defaultUrl ?? '/'; + navigate(url, {replace: true}); + } + return (
diff --git a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/config/ConfigEndpoint.kt b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/config/ConfigEndpoint.kt index 12bb07e..9e5c166 100644 --- a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/config/ConfigEndpoint.kt +++ b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/config/ConfigEndpoint.kt @@ -1,12 +1,14 @@ package de.grimsi.gameyfin.config -import com.vaadin.flow.server.auth.AnonymousAllowed import com.vaadin.hilla.Endpoint import de.grimsi.gameyfin.config.dto.ConfigEntryDto import de.grimsi.gameyfin.config.dto.ConfigUpdateDto import de.grimsi.gameyfin.core.Role +import de.grimsi.gameyfin.users.util.isAdmin import jakarta.annotation.security.PermitAll import jakarta.annotation.security.RolesAllowed +import org.springframework.security.core.context.SecurityContextHolder +import org.springframework.security.core.userdetails.UserDetails import reactor.core.publisher.Flux import reactor.core.publisher.Sinks @@ -23,9 +25,12 @@ class ConfigEndpoint( return config.getAll(null) } - // FIXME - @AnonymousAllowed - fun subscribe(): Flux = configUpdates.asFlux() + @PermitAll + fun subscribe(): Flux { + val user = SecurityContextHolder.getContext().authentication.principal as UserDetails + return if (user.isAdmin()) configUpdates.asFlux() + else Flux.empty() + } fun update(update: ConfigUpdateDto) { config.update(update.updates) diff --git a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/logging/LogEndpoint.kt b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/logging/LogEndpoint.kt index b0a31c6..ae52aeb 100644 --- a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/logging/LogEndpoint.kt +++ b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/logging/LogEndpoint.kt @@ -1,9 +1,12 @@ package de.grimsi.gameyfin.core.logging -import com.vaadin.flow.server.auth.AnonymousAllowed import com.vaadin.hilla.Endpoint import de.grimsi.gameyfin.core.Role +import de.grimsi.gameyfin.users.util.isAdmin +import jakarta.annotation.security.PermitAll import jakarta.annotation.security.RolesAllowed +import org.springframework.security.core.context.SecurityContextHolder +import org.springframework.security.core.userdetails.UserDetails import reactor.core.publisher.Flux @Endpoint @@ -16,9 +19,10 @@ class LogEndpoint( logService.configureFileLogging() } - // FIXME: see https://vaadin.com/forum/t/can-only-access-flux-endpoint-with-anonymousallowed/167117 - @AnonymousAllowed + @PermitAll fun getApplicationLogs(): Flux { - return logService.streamLogs() + val user = SecurityContextHolder.getContext().authentication.principal as UserDetails + return if (user.isAdmin()) logService.streamLogs() + else Flux.empty() } } \ No newline at end of file diff --git a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/users/util/UserDetailsExtensions.kt b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/users/util/UserDetailsExtensions.kt index c49ae52..1f82442 100644 --- a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/users/util/UserDetailsExtensions.kt +++ b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/users/util/UserDetailsExtensions.kt @@ -1,6 +1,3 @@ -@file:JvmName("Utils") -@file:JvmMultifileClass - package de.grimsi.gameyfin.users.util import de.grimsi.gameyfin.core.Role