Start implementation of game requests

This commit is contained in:
grimsi
2025-08-31 15:56:56 +02:00
parent d5b2eb039e
commit 6b81d3904c
25 changed files with 250 additions and 73 deletions
@@ -0,0 +1,10 @@
package org.gameyfin.app.core.plugins.dto
class ExternalProviderIdDto(
val pluginId: String,
val externalProviderId: String,
) {
override fun toString(): String {
return "$pluginId:$externalProviderId"
}
}
@@ -1,9 +1,14 @@
package org.gameyfin.app.core.security
import org.gameyfin.app.core.Role
import org.springframework.security.core.Authentication
import org.springframework.security.core.context.SecurityContextHolder
fun getCurrentAuth(): Authentication {
return SecurityContextHolder.getContext().authentication
}
fun isCurrentUserAdmin(): Boolean {
return SecurityContextHolder.getContext().authentication?.authorities?.any { it.authority == Role.Names.ADMIN || it.authority == Role.Names.SUPERADMIN }
return getCurrentAuth().authorities?.any { it.authority == Role.Names.ADMIN || it.authority == Role.Names.SUPERADMIN }
?: false
}
@@ -5,8 +5,12 @@ import com.vaadin.hilla.Endpoint
import jakarta.annotation.security.RolesAllowed
import org.gameyfin.app.core.Role
import org.gameyfin.app.core.annotations.DynamicPublicAccess
import org.gameyfin.app.core.plugins.dto.ExternalProviderIdDto
import org.gameyfin.app.core.security.isCurrentUserAdmin
import org.gameyfin.app.games.dto.*
import org.gameyfin.app.games.dto.GameDto
import org.gameyfin.app.games.dto.GameEvent
import org.gameyfin.app.games.dto.GameSearchResultDto
import org.gameyfin.app.games.dto.GameUpdateDto
import org.gameyfin.app.libraries.LibraryCoreService
import org.gameyfin.app.libraries.LibraryService
import reactor.core.publisher.Flux
@@ -45,7 +49,12 @@ class GameEndpoint(
}
@RolesAllowed(Role.Names.ADMIN)
fun matchManually(originalIds: Map<String, OriginalIdDto>, path: String, libraryId: Long, replaceGameId: Long?) {
fun matchManually(
originalIds: Map<String, ExternalProviderIdDto>,
path: String,
libraryId: Long,
replaceGameId: Long?
) {
val library = libraryService.getById(libraryId)
val game = gameService.matchManually(originalIds, Path.of(path), library, replaceGameId)
if (game != null) {
@@ -12,10 +12,12 @@ import org.gameyfin.app.core.alphaNumeric
import org.gameyfin.app.core.filesystem.FilesystemService
import org.gameyfin.app.core.filterValuesNotNull
import org.gameyfin.app.core.plugins.PluginService
import org.gameyfin.app.core.plugins.dto.ExternalProviderIdDto
import org.gameyfin.app.core.plugins.management.GameyfinPluginDescriptor
import org.gameyfin.app.core.plugins.management.GameyfinPluginManager
import org.gameyfin.app.core.plugins.management.PluginManagementEntry
import org.gameyfin.app.core.replaceRomanNumerals
import org.gameyfin.app.core.security.getCurrentAuth
import org.gameyfin.app.games.dto.*
import org.gameyfin.app.games.entities.*
import org.gameyfin.app.games.extensions.toDtos
@@ -25,7 +27,6 @@ import org.gameyfin.app.media.ImageService
import org.gameyfin.app.users.UserService
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
@@ -149,8 +150,7 @@ class GameService(
val existingGame = gameRepository.findByIdOrNull(gameUpdateDto.id)
?: throw IllegalArgumentException("Game with ID $gameUpdateDto.id not found")
val userDetails = SecurityContextHolder.getContext().authentication.principal
val user = when (userDetails) {
val user = when (val userDetails = getCurrentAuth().principal) {
is UserDetails -> userService.getByUsernameNonNull(userDetails.username)
is OidcUser -> userService.getByUsernameNonNull(userDetails.preferredUsername)
else -> throw IllegalStateException("Unkown user type: ${userDetails::class.java.name}")
@@ -260,12 +260,12 @@ class GameService(
val game = getById(game.id!!)
val originalIds: Map<String, OriginalIdDto> = game.metadata.originalIds
val originalIds: Map<String, ExternalProviderIdDto> = game.metadata.originalIds
.map { (provider, originalId) ->
val providerId = pluginManager.getExtensions(provider.pluginId).first()?.javaClass?.name ?: return null
val pluginId = provider.pluginId
val originalId = originalId
providerId to OriginalIdDto(pluginId, originalId)
providerId to ExternalProviderIdDto(pluginId, originalId)
}
.toMap()
@@ -504,12 +504,12 @@ class GameService(
sorted.mapNotNull { selector(it.second) }.firstOrNull { it.isNotEmpty() }
// Collect originalIds for this group
val originalIds: Map<String, OriginalIdDto> = group
val originalIds: Map<String, ExternalProviderIdDto> = group
.mapNotNull { (provider, metadata) ->
val providerId = provider.javaClass.name
val pluginId = providerToManagementEntry[provider]?.pluginId ?: return@mapNotNull null
val originalId = metadata.originalId
if (providerId != null) providerId to OriginalIdDto(pluginId, originalId) else null
if (providerId != null) providerId to ExternalProviderIdDto(pluginId, originalId) else null
}
.toMap()
@@ -567,7 +567,7 @@ class GameService(
}
fun matchManually(
originalIds: Map<String, OriginalIdDto>,
originalIds: Map<String, ExternalProviderIdDto>,
path: Path,
library: Library,
replaceGameId: Long? = null,
@@ -578,7 +578,7 @@ class GameService(
coroutineScope {
metadataPlugins.associateWith { plugin ->
async {
val originalId = originalIds[plugin.javaClass.name]?.originalId ?: return@async null
val originalId = originalIds[plugin.javaClass.name]?.externalProviderId ?: return@async null
try {
return@async plugin.fetchById(originalId)
} catch (e: Exception) {
@@ -1,5 +1,6 @@
package org.gameyfin.app.games.dto
import org.gameyfin.app.core.plugins.dto.ExternalProviderIdDto
import java.time.Instant
import java.util.*
@@ -11,18 +12,9 @@ class GameSearchResultDto(
val release: Instant?,
val publishers: Collection<String>?,
val developers: Collection<String>?,
val originalIds: Map<String, OriginalIdDto>
val originalIds: Map<String, ExternalProviderIdDto>
)
class OriginalIdDto(
val pluginId: String,
val originalId: String,
) {
override fun toString(): String {
return "$pluginId:$originalId"
}
}
class UrlWithSourceDto(
val url: String,
val pluginId: String
@@ -7,6 +7,7 @@ import org.gameyfin.app.core.Role
import org.gameyfin.app.core.Utils
import org.gameyfin.app.core.annotations.DynamicPublicAccess
import org.gameyfin.app.core.plugins.PluginService
import org.gameyfin.app.core.security.getCurrentAuth
import org.gameyfin.app.games.entities.Image
import org.gameyfin.app.games.entities.ImageType
import org.gameyfin.app.users.UserService
@@ -15,8 +16,6 @@ import org.springframework.core.io.InputStreamResource
import org.springframework.http.HttpHeaders
import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity
import org.springframework.security.core.Authentication
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.web.bind.annotation.*
import org.springframework.web.multipart.MultipartFile
@@ -61,7 +60,7 @@ class ImageEndpoint(
@PermitAll
@PostMapping("/avatar/upload")
fun uploadAvatar(@RequestParam("file") file: MultipartFile) {
val auth: Authentication = SecurityContextHolder.getContext().authentication
val auth = getCurrentAuth()
val image: Image = if (!userService.hasAvatar(auth.name)) {
imageService.createFile(ImageType.AVATAR, file.inputStream, file.contentType!!)
@@ -76,7 +75,7 @@ class ImageEndpoint(
@PermitAll
@PostMapping("/avatar/delete")
fun deleteAvatar() {
val auth: Authentication = SecurityContextHolder.getContext().authentication
val auth = getCurrentAuth()
userService.deleteAvatar(auth.name)
}
@@ -3,6 +3,7 @@ package org.gameyfin.app.messages
import io.github.oshai.kotlinlogging.KLogger
import io.github.oshai.kotlinlogging.KotlinLogging
import org.gameyfin.app.core.events.*
import org.gameyfin.app.core.security.getCurrentAuth
import org.gameyfin.app.messages.providers.AbstractMessageProvider
import org.gameyfin.app.messages.templates.MessageTemplateService
import org.gameyfin.app.messages.templates.MessageTemplates
@@ -10,8 +11,6 @@ import org.gameyfin.app.users.UserService
import org.springframework.context.ApplicationContext
import org.springframework.context.event.EventListener
import org.springframework.scheduling.annotation.Async
import org.springframework.security.core.Authentication
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.stereotype.Service
import java.util.*
@@ -61,7 +60,7 @@ class MessageService(
}
try {
val auth: Authentication = SecurityContextHolder.getContext().authentication
val auth = getCurrentAuth()
val user = userService.getByUsername(auth.name) ?: throw IllegalStateException("User not found")
val template = templateService.getMessageTemplate(templateKey)
sendNotification(user.email, "[Gameyfin] Test Notification", template, placeholders)
@@ -0,0 +1,42 @@
package org.gameyfin.app.requests
import jakarta.persistence.*
import org.gameyfin.app.requests.status.GameRequestStatus
import org.gameyfin.app.users.entities.User
import org.hibernate.annotations.CreationTimestamp
import org.hibernate.annotations.UpdateTimestamp
import java.time.Instant
typealias ExternalProviderIds = Map<String, String>
@Entity
class GameRequest(
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
var id: Long? = null,
@CreationTimestamp
@Column(nullable = false, updatable = false)
var createdAt: Instant? = null,
@UpdateTimestamp
@Column(nullable = false)
var updatedAt: Instant? = null,
@Column(nullable = false)
val title: String,
@Column(nullable = false)
val release: Instant,
@Column(nullable = false)
var status: GameRequestStatus,
@ManyToOne(fetch = FetchType.EAGER)
var requester: User? = null,
var voters: MutableList<User> = mutableListOf(),
@ElementCollection
val externalProviderIds: ExternalProviderIds
)
@@ -0,0 +1,5 @@
package org.gameyfin.app.requests
import org.springframework.data.jpa.repository.JpaRepository
interface GameRequestRepository : JpaRepository<GameRequest, Long>
@@ -0,0 +1,57 @@
package org.gameyfin.app.requests
import io.github.oshai.kotlinlogging.KotlinLogging
import org.gameyfin.app.core.security.getCurrentAuth
import org.gameyfin.app.games.dto.GameUserEvent
import org.gameyfin.app.requests.dto.GameRequestCreationDto
import org.gameyfin.app.requests.status.GameRequestStatus
import org.gameyfin.app.users.UserService
import org.springframework.stereotype.Service
import reactor.core.publisher.Flux
import reactor.core.publisher.Sinks
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.toJavaDuration
@Service
class GameRequestService(
private val gameRequestRepository: GameRequestRepository,
private val userService: UserService
) {
companion object {
private val log = KotlinLogging.logger {}
/* Websockets */
private val gameRequestEvents = Sinks.many().multicast().onBackpressureBuffer<GameUserEvent>(1024, false)
fun subscribe(): Flux<List<GameUserEvent>> {
log.debug { "New user subscription for gameRequestEvents" }
return gameRequestEvents.asFlux()
.buffer(100.milliseconds.toJavaDuration())
.doOnSubscribe {
log.debug { "Subscriber added to gameRequestEvents [${gameRequestEvents.currentSubscriberCount()}]" }
}
.doFinally {
log.debug { "Subscriber removed from gameRequestEvents with signal type $it [${gameRequestEvents.currentSubscriberCount()}]" }
}
}
fun emit(event: GameUserEvent) {
gameRequestEvents.tryEmitNext(event)
}
}
fun createRequest(gameRequest: GameRequestCreationDto) {
val currentUser = userService.getByUsername(getCurrentAuth().name)
val gameRequest = GameRequest(
title = gameRequest.title,
release = gameRequest.release,
status = GameRequestStatus.PENDING,
externalProviderIds = gameRequest.externalProviderIds,
requester = currentUser
)
gameRequestRepository.save(gameRequest)
}
}
@@ -0,0 +1,10 @@
package org.gameyfin.app.requests.dto
import java.time.Instant
class GameRequestCreationDto(
val title: String,
val release: Instant,
val externalProviderIds: Map<String, String>
)
@@ -0,0 +1,13 @@
package org.gameyfin.app.requests.dto
import org.gameyfin.app.requests.status.GameRequestStatus
import org.gameyfin.app.users.dto.UserInfoAdminDto
import java.time.Instant
class GameRequestDto(
val id: Long,
val title: String,
val release: Instant,
val status: GameRequestStatus,
val requester: UserInfoAdminDto
)
@@ -0,0 +1,9 @@
package org.gameyfin.app.requests.dto
sealed class GameRequestEvent {
abstract val type: String
data class Created(val game: GameRequestDto, override val type: String = "created") : GameRequestEvent()
data class Updated(val game: GameRequestDto, override val type: String = "updated") : GameRequestEvent()
data class Deleted(val gameRequestId: Long, override val type: String = "deleted") : GameRequestEvent()
}
@@ -0,0 +1,15 @@
package org.gameyfin.app.requests.extensions
import org.gameyfin.app.requests.GameRequest
import org.gameyfin.app.requests.dto.GameRequestDto
fun GameRequest.toDto(): GameRequestDto {
return GameRequestDto(
id = this.id!!,
title = this.title,
release = this.release,
externalProviderIds = this.externalProviderIds,
status = this.status,
requester = this.requester.toDto()
)
}
@@ -0,0 +1,8 @@
package org.gameyfin.app.requests.status
enum class GameRequestStatus {
PENDING,
APPROVED,
FULFILLED,
REJECTED
}
@@ -3,7 +3,7 @@ package org.gameyfin.app.setup
import com.vaadin.flow.server.auth.AnonymousAllowed
import com.vaadin.hilla.Endpoint
import com.vaadin.hilla.exception.EndpointException
import org.gameyfin.app.users.dto.UserInfoDto
import org.gameyfin.app.users.dto.UserInfoAdminDto
import org.gameyfin.app.users.dto.UserRegistrationDto
@Endpoint
@@ -16,7 +16,7 @@ class SetupEndpoint(
}
@AnonymousAllowed
fun registerSuperAdmin(superAdminRegistration: UserRegistrationDto): UserInfoDto {
fun registerSuperAdmin(superAdminRegistration: UserRegistrationDto): UserInfoAdminDto {
if (setupService.isSetupCompleted()) throw EndpointException("Setup already completed")
return setupService.createInitialAdminUser(superAdminRegistration)
}
@@ -1,9 +1,9 @@
package org.gameyfin.app.setup
import org.gameyfin.app.users.UserService
import org.gameyfin.app.core.Role
import org.gameyfin.app.users.RoleService
import org.gameyfin.app.users.dto.UserInfoDto
import org.gameyfin.app.users.UserService
import org.gameyfin.app.users.dto.UserInfoAdminDto
import org.gameyfin.app.users.dto.UserRegistrationDto
import org.gameyfin.app.users.entities.User
import org.springframework.stereotype.Service
@@ -26,7 +26,7 @@ class SetupService(
/**
* Creates the initial user with Super-Admin permissions
*/
fun createInitialAdminUser(registration: UserRegistrationDto): UserInfoDto {
fun createInitialAdminUser(registration: UserRegistrationDto): UserInfoAdminDto {
val superAdmin = User(
username = registration.username,
password = registration.password,
@@ -1,7 +1,7 @@
package org.gameyfin.app.users
import org.gameyfin.app.core.security.getCurrentAuth
import org.gameyfin.app.users.entities.User
import org.springframework.security.core.Authentication
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.core.session.SessionInformation
import org.springframework.security.core.session.SessionRegistry
@@ -11,14 +11,12 @@ import org.springframework.stereotype.Service
class SessionService(private val sessionRegistry: SessionRegistry) {
fun logoutAllSessions() {
val auth: Authentication? = SecurityContextHolder.getContext().authentication
if (auth != null) {
val sessions: List<SessionInformation> = sessionRegistry.getAllSessions(auth.principal, false)
for (sessionInfo in sessions) {
sessionInfo.expireNow()
}
SecurityContextHolder.clearContext()
val auth = getCurrentAuth()
val sessions: List<SessionInformation> = sessionRegistry.getAllSessions(auth.principal, false)
for (sessionInfo in sessions) {
sessionInfo.expireNow()
}
SecurityContextHolder.clearContext()
}
fun logoutAllSessions(user: User) {
@@ -5,11 +5,11 @@ 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.dto.UserInfoDto
import org.gameyfin.app.core.security.getCurrentAuth
import org.gameyfin.app.users.dto.UserInfoAdminDto
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
@Endpoint
@@ -18,15 +18,15 @@ class UserEndpoint(
private val roleService: RoleService
) {
@AnonymousAllowed
fun getUserInfo(): UserInfoDto? {
val auth = SecurityContextHolder.getContext().authentication
fun getUserInfo(): UserInfoAdminDto? {
val auth = getCurrentAuth()
if (!auth.isAuthenticated || auth.principal == "anonymousUser") return null
return userService.getUserInfo()
}
@PermitAll
fun updateUser(updates: UserUpdateDto) {
val auth: Authentication = SecurityContextHolder.getContext().authentication
val auth: Authentication = getCurrentAuth()
userService.updateUser(auth.name, updates)
}
@@ -36,7 +36,7 @@ class UserEndpoint(
}
@RolesAllowed(Role.Names.ADMIN)
fun getAllUsers(): List<UserInfoDto> {
fun getAllUsers(): List<UserInfoAdminDto> {
return userService.getAllUsers()
}
@@ -52,7 +52,7 @@ class UserEndpoint(
@PermitAll
fun deleteUser() {
val auth: Authentication = SecurityContextHolder.getContext().authentication
val auth: Authentication = getCurrentAuth()
userService.deleteUser(auth.name)
}
@@ -68,7 +68,7 @@ class UserEndpoint(
@RolesAllowed(Role.Names.ADMIN)
fun getRolesBelow(): List<String> {
val auth: Authentication = SecurityContextHolder.getContext().authentication
val auth: Authentication = getCurrentAuth()
return roleService.getRolesBelowAuth(auth).map { it.roleName }
}
@@ -6,9 +6,10 @@ 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.core.security.getCurrentAuth
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.UserInfoAdminDto
import org.gameyfin.app.users.dto.UserRegistrationDto
import org.gameyfin.app.users.dto.UserUpdateDto
import org.gameyfin.app.users.emailconfirmation.EmailConfirmationService
@@ -17,7 +18,6 @@ import org.gameyfin.app.users.persistence.UserRepository
import org.springframework.context.ApplicationEventPublisher
import org.springframework.security.core.GrantedAuthority
import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.core.userdetails.User
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.core.userdetails.UserDetailsService
@@ -77,7 +77,7 @@ class UserService(
fun findByOidcProviderId(oidcProviderId: String): org.gameyfin.app.users.entities.User? =
userRepository.findByOidcProviderId(oidcProviderId)
fun getAllUsers(): List<UserInfoDto> {
fun getAllUsers(): List<UserInfoAdminDto> {
return userRepository.findAll().map { u -> toUserInfo(u) }
}
@@ -93,8 +93,8 @@ class UserService(
return userRepository.findByUsername(username) ?: throw UsernameNotFoundException("Unknown user '$username'")
}
fun getUserInfo(): UserInfoDto {
val auth = SecurityContextHolder.getContext().authentication
fun getUserInfo(): UserInfoAdminDto {
val auth = getCurrentAuth()
val principal = auth.principal
if (principal is OidcUser) {
@@ -238,7 +238,7 @@ class UserService(
return RoleAssignmentResult.NO_ROLES_PROVIDED
}
val currentUser = SecurityContextHolder.getContext().authentication
val currentUser = getCurrentAuth()
val targetUser = getByUsernameNonNull(username)
if (!canManage(targetUser)) {
@@ -266,7 +266,7 @@ class UserService(
}
fun canManage(targetUser: org.gameyfin.app.users.entities.User): Boolean {
val currentUser = SecurityContextHolder.getContext().authentication
val currentUser = getCurrentAuth()
val currentUserLevel = roleService.getHighestRoleFromAuthorities(currentUser.authorities).powerLevel
val targetUserLevel = roleService.getHighestRole(targetUser.roles).powerLevel
return currentUserLevel > targetUserLevel
@@ -285,8 +285,8 @@ class UserService(
eventPublisher.publishEvent(AccountDeletedEvent(this, user, Utils.Companion.getBaseUrl()))
}
fun toUserInfo(user: org.gameyfin.app.users.entities.User): UserInfoDto {
return UserInfoDto(
fun toUserInfo(user: org.gameyfin.app.users.entities.User): UserInfoAdminDto {
return UserInfoAdminDto(
username = user.username,
email = user.email,
emailConfirmed = user.emailConfirmed,
@@ -2,7 +2,7 @@ package org.gameyfin.app.users.dto
import org.gameyfin.app.core.Role
data class UserInfoDto(
data class UserInfoAdminDto(
val username: String,
val managedBySso: Boolean,
val email: String,
@@ -0,0 +1,7 @@
package org.gameyfin.app.users.dto
data class UserInfoUserDto(
val username: String,
val hasAvatar: Boolean,
val avatarId: Long? = null,
)
@@ -1,11 +1,10 @@
package org.gameyfin.app.users.emailconfirmation
import com.vaadin.hilla.Endpoint
import org.gameyfin.app.users.UserService
import jakarta.annotation.security.PermitAll
import org.gameyfin.app.core.security.getCurrentAuth
import org.gameyfin.app.shared.token.TokenValidationResult
import org.springframework.security.core.Authentication
import org.springframework.security.core.context.SecurityContextHolder
import org.gameyfin.app.users.UserService
@Endpoint
class EmailConfirmationEndpoint(
@@ -20,7 +19,7 @@ class EmailConfirmationEndpoint(
@PermitAll
fun resendEmailConfirmation() {
val auth: Authentication = SecurityContextHolder.getContext().authentication
val auth = getCurrentAuth()
userService.getByUsername(auth.name)?.let {
emailConfirmationService.resendEmailConfirmation(it)
}
@@ -1,8 +1,8 @@
package org.gameyfin.app.users.preferences
import org.gameyfin.app.users.UserService
import io.github.oshai.kotlinlogging.KotlinLogging
import org.springframework.security.core.context.SecurityContextHolder
import org.gameyfin.app.core.security.getCurrentAuth
import org.gameyfin.app.users.UserService
import org.springframework.stereotype.Service
import java.io.Serializable
@@ -135,7 +135,7 @@ class UserPreferencesService(
}
private fun id(key: String): UserPreferenceKey {
val auth = SecurityContextHolder.getContext().authentication
val auth = getCurrentAuth()
val user = userService.getByUsernameNonNull(auth.name)
return UserPreferenceKey(key, user.id!!)
}
@@ -1,18 +1,17 @@
package org.gameyfin.app.users.registration
import org.gameyfin.app.core.Utils
import org.gameyfin.app.core.events.AccountStatusChangedEvent
import org.gameyfin.app.core.events.UserInvitationEvent
import org.gameyfin.app.core.security.getCurrentAuth
import org.gameyfin.app.shared.token.TokenDto
import org.gameyfin.app.shared.token.TokenRepository
import org.gameyfin.app.users.UserService
import org.gameyfin.app.core.Utils
import org.gameyfin.app.shared.token.TokenService
import org.gameyfin.app.shared.token.TokenType
import org.gameyfin.app.users.UserService
import org.gameyfin.app.users.dto.UserRegistrationDto
import org.gameyfin.app.users.enums.UserInvitationAcceptanceResult
import org.springframework.context.ApplicationEventPublisher
import org.springframework.security.core.Authentication
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.stereotype.Service
@Service
@@ -30,7 +29,7 @@ class InvitationService(
if (userService.existsByEmail(email))
throw IllegalStateException("User with email ${Utils.Companion.maskEmail(email)} is already registered")
val auth: Authentication = SecurityContextHolder.getContext().authentication
val auth = getCurrentAuth()
val user = userService.getByUsername(auth.name) ?: throw IllegalStateException("User not found")
val payload = mapOf(EMAIL_KEY to email)
val token = super.generateWithPayload(user, payload)
@@ -45,7 +44,8 @@ class InvitationService(
}
fun acceptInvitation(secret: String, registration: UserRegistrationDto): UserInvitationAcceptanceResult {
val invitationToken = super.get(secret, TokenType.Invitation) ?: return UserInvitationAcceptanceResult.TOKEN_INVALID
val invitationToken =
super.get(secret, TokenType.Invitation) ?: return UserInvitationAcceptanceResult.TOKEN_INVALID
val email = invitationToken.payload[EMAIL_KEY] ?: return UserInvitationAcceptanceResult.TOKEN_INVALID
if (invitationToken.expired) return UserInvitationAcceptanceResult.TOKEN_EXPIRED