mirror of
https://github.com/Suwayomi/Suwayomi-Server.git
synced 2026-07-04 11:24:35 -05:00
Basic JWT implementation (#1524)
* Basic JWT implementation * Move JWT to UI_LOGIN mode and bring back SIMPLE_LOGIN as before * Update server/src/main/kotlin/suwayomi/tachidesk/global/impl/util/Jwt.kt Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com> * Refresh: Update only access token Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com> * Implement JWT Audience * Store JWT key Generates the key on startup if not set * Handle invalid Base64 * Make JWT expiry configurable * Missing value parse * Update server/src/main/kotlin/suwayomi/tachidesk/global/impl/util/Jwt.kt Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com> * Simplify Duration parsing * JWT Protect Mutations * JWT Protect Queries and Subscriptions * JWT Protect v1 WebSockets * WebSockets allow sending token via protocol header * Also respect the `suwayomi-server-token` cookie * JWT reduce default token expiry * JWT Support cookie on WebSocket as well * Lint * Authenticate graphql subscription via connection_init payload * WebView: Prefer explicit token over cookie This hack was implemented because WebView sent `"null"` if no token was supplied, just don't send a bad token, then we can do this properly * WebView: Implement basic login dialog if no token supplied --------- Co-authored-by: Mitchell Syer <Syer10@users.noreply.github.com> Co-authored-by: schroda <50052685+schroda@users.noreply.github.com>
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
package suwayomi.tachidesk.graphql.mutations
|
||||
|
||||
import graphql.execution.DataFetcherResult
|
||||
import graphql.schema.DataFetchingEnvironment
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.withTimeout
|
||||
import org.jetbrains.exposed.sql.selectAll
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import suwayomi.tachidesk.graphql.asDataFetcherResult
|
||||
import suwayomi.tachidesk.graphql.server.getAttribute
|
||||
import suwayomi.tachidesk.graphql.types.ChapterType
|
||||
import suwayomi.tachidesk.graphql.types.DownloadStatus
|
||||
import suwayomi.tachidesk.manga.impl.Chapter
|
||||
@@ -13,7 +15,10 @@ import suwayomi.tachidesk.manga.impl.download.DownloadManager
|
||||
import suwayomi.tachidesk.manga.impl.download.model.DownloadUpdateType.DEQUEUED
|
||||
import suwayomi.tachidesk.manga.impl.download.model.Status
|
||||
import suwayomi.tachidesk.manga.model.table.ChapterTable
|
||||
import suwayomi.tachidesk.server.JavalinSetup.Attribute
|
||||
import suwayomi.tachidesk.server.JavalinSetup.future
|
||||
import suwayomi.tachidesk.server.JavalinSetup.getAttribute
|
||||
import suwayomi.tachidesk.server.user.requireUser
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
@@ -28,7 +33,11 @@ class DownloadMutation {
|
||||
val chapters: List<ChapterType>,
|
||||
)
|
||||
|
||||
fun deleteDownloadedChapters(input: DeleteDownloadedChaptersInput): DataFetcherResult<DeleteDownloadedChaptersPayload?> {
|
||||
fun deleteDownloadedChapters(
|
||||
dataFetchingEnvironment: DataFetchingEnvironment,
|
||||
input: DeleteDownloadedChaptersInput,
|
||||
): DataFetcherResult<DeleteDownloadedChaptersPayload?> {
|
||||
dataFetchingEnvironment.getAttribute(Attribute.TachideskUser).requireUser()
|
||||
val (clientMutationId, chapters) = input
|
||||
|
||||
return asDataFetcherResult {
|
||||
@@ -57,7 +66,11 @@ class DownloadMutation {
|
||||
val chapters: ChapterType,
|
||||
)
|
||||
|
||||
fun deleteDownloadedChapter(input: DeleteDownloadedChapterInput): DataFetcherResult<DeleteDownloadedChapterPayload?> {
|
||||
fun deleteDownloadedChapter(
|
||||
dataFetchingEnvironment: DataFetchingEnvironment,
|
||||
input: DeleteDownloadedChapterInput,
|
||||
): DataFetcherResult<DeleteDownloadedChapterPayload?> {
|
||||
dataFetchingEnvironment.getAttribute(Attribute.TachideskUser).requireUser()
|
||||
val (clientMutationId, chapter) = input
|
||||
|
||||
return asDataFetcherResult {
|
||||
@@ -84,8 +97,10 @@ class DownloadMutation {
|
||||
)
|
||||
|
||||
fun enqueueChapterDownloads(
|
||||
dataFetchingEnvironment: DataFetchingEnvironment,
|
||||
input: EnqueueChapterDownloadsInput,
|
||||
): CompletableFuture<DataFetcherResult<EnqueueChapterDownloadsPayload?>> {
|
||||
dataFetchingEnvironment.getAttribute(Attribute.TachideskUser).requireUser()
|
||||
val (clientMutationId, chapters) = input
|
||||
|
||||
return future {
|
||||
@@ -118,7 +133,11 @@ class DownloadMutation {
|
||||
val downloadStatus: DownloadStatus,
|
||||
)
|
||||
|
||||
fun enqueueChapterDownload(input: EnqueueChapterDownloadInput): CompletableFuture<DataFetcherResult<EnqueueChapterDownloadPayload?>> {
|
||||
fun enqueueChapterDownload(
|
||||
dataFetchingEnvironment: DataFetchingEnvironment,
|
||||
input: EnqueueChapterDownloadInput,
|
||||
): CompletableFuture<DataFetcherResult<EnqueueChapterDownloadPayload?>> {
|
||||
dataFetchingEnvironment.getAttribute(Attribute.TachideskUser).requireUser()
|
||||
val (clientMutationId, chapter) = input
|
||||
|
||||
return future {
|
||||
@@ -151,8 +170,10 @@ class DownloadMutation {
|
||||
)
|
||||
|
||||
fun dequeueChapterDownloads(
|
||||
dataFetchingEnvironment: DataFetchingEnvironment,
|
||||
input: DequeueChapterDownloadsInput,
|
||||
): CompletableFuture<DataFetcherResult<DequeueChapterDownloadsPayload?>> {
|
||||
dataFetchingEnvironment.getAttribute(Attribute.TachideskUser).requireUser()
|
||||
val (clientMutationId, chapters) = input
|
||||
|
||||
return future {
|
||||
@@ -187,7 +208,11 @@ class DownloadMutation {
|
||||
val downloadStatus: DownloadStatus,
|
||||
)
|
||||
|
||||
fun dequeueChapterDownload(input: DequeueChapterDownloadInput): CompletableFuture<DataFetcherResult<DequeueChapterDownloadPayload?>> {
|
||||
fun dequeueChapterDownload(
|
||||
dataFetchingEnvironment: DataFetchingEnvironment,
|
||||
input: DequeueChapterDownloadInput,
|
||||
): CompletableFuture<DataFetcherResult<DequeueChapterDownloadPayload?>> {
|
||||
dataFetchingEnvironment.getAttribute(Attribute.TachideskUser).requireUser()
|
||||
val (clientMutationId, chapter) = input
|
||||
|
||||
return future {
|
||||
@@ -221,9 +246,13 @@ class DownloadMutation {
|
||||
val downloadStatus: DownloadStatus,
|
||||
)
|
||||
|
||||
fun startDownloader(input: StartDownloaderInput): CompletableFuture<DataFetcherResult<StartDownloaderPayload?>> =
|
||||
fun startDownloader(
|
||||
dataFetchingEnvironment: DataFetchingEnvironment,
|
||||
input: StartDownloaderInput,
|
||||
): CompletableFuture<DataFetcherResult<StartDownloaderPayload?>> =
|
||||
future {
|
||||
asDataFetcherResult {
|
||||
dataFetchingEnvironment.getAttribute(Attribute.TachideskUser).requireUser()
|
||||
DownloadManager.start()
|
||||
|
||||
StartDownloaderPayload(
|
||||
@@ -249,9 +278,13 @@ class DownloadMutation {
|
||||
val downloadStatus: DownloadStatus,
|
||||
)
|
||||
|
||||
fun stopDownloader(input: StopDownloaderInput): CompletableFuture<DataFetcherResult<StopDownloaderPayload?>> =
|
||||
fun stopDownloader(
|
||||
dataFetchingEnvironment: DataFetchingEnvironment,
|
||||
input: StopDownloaderInput,
|
||||
): CompletableFuture<DataFetcherResult<StopDownloaderPayload?>> =
|
||||
future {
|
||||
asDataFetcherResult {
|
||||
dataFetchingEnvironment.getAttribute(Attribute.TachideskUser).requireUser()
|
||||
DownloadManager.stop()
|
||||
|
||||
StopDownloaderPayload(
|
||||
@@ -277,9 +310,13 @@ class DownloadMutation {
|
||||
val downloadStatus: DownloadStatus,
|
||||
)
|
||||
|
||||
fun clearDownloader(input: ClearDownloaderInput): CompletableFuture<DataFetcherResult<ClearDownloaderPayload?>> =
|
||||
fun clearDownloader(
|
||||
dataFetchingEnvironment: DataFetchingEnvironment,
|
||||
input: ClearDownloaderInput,
|
||||
): CompletableFuture<DataFetcherResult<ClearDownloaderPayload?>> =
|
||||
future {
|
||||
asDataFetcherResult {
|
||||
dataFetchingEnvironment.getAttribute(Attribute.TachideskUser).requireUser()
|
||||
DownloadManager.clear()
|
||||
|
||||
ClearDownloaderPayload(
|
||||
@@ -307,7 +344,11 @@ class DownloadMutation {
|
||||
val downloadStatus: DownloadStatus,
|
||||
)
|
||||
|
||||
fun reorderChapterDownload(input: ReorderChapterDownloadInput): CompletableFuture<DataFetcherResult<ReorderChapterDownloadPayload?>> {
|
||||
fun reorderChapterDownload(
|
||||
dataFetchingEnvironment: DataFetchingEnvironment,
|
||||
input: ReorderChapterDownloadInput,
|
||||
): CompletableFuture<DataFetcherResult<ReorderChapterDownloadPayload?>> {
|
||||
dataFetchingEnvironment.getAttribute(Attribute.TachideskUser).requireUser()
|
||||
val (clientMutationId, chapter, to) = input
|
||||
|
||||
return future {
|
||||
|
||||
Reference in New Issue
Block a user