mirror of
https://github.com/Suwayomi/Suwayomi-Server.git
synced 2026-07-15 00:34:12 -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:
@@ -26,7 +26,10 @@ import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass
|
||||
import suwayomi.tachidesk.manga.model.dataclass.ChapterDataClass
|
||||
import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass
|
||||
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 suwayomi.tachidesk.server.util.formParam
|
||||
import suwayomi.tachidesk.server.util.handler
|
||||
import suwayomi.tachidesk.server.util.pathParam
|
||||
@@ -49,6 +52,7 @@ object MangaController {
|
||||
}
|
||||
},
|
||||
behaviorOf = { ctx, mangaId, onlineFetch ->
|
||||
ctx.getAttribute(Attribute.TachideskUser).requireUser()
|
||||
ctx.future {
|
||||
future {
|
||||
Manga.getManga(mangaId, onlineFetch)
|
||||
@@ -73,6 +77,7 @@ object MangaController {
|
||||
}
|
||||
},
|
||||
behaviorOf = { ctx, mangaId, onlineFetch ->
|
||||
ctx.getAttribute(Attribute.TachideskUser).requireUser()
|
||||
ctx.future {
|
||||
future {
|
||||
Manga.getMangaFull(mangaId, onlineFetch)
|
||||
@@ -96,6 +101,7 @@ object MangaController {
|
||||
}
|
||||
},
|
||||
behaviorOf = { ctx, mangaId ->
|
||||
ctx.getAttribute(Attribute.TachideskUser).requireUser()
|
||||
ctx.future {
|
||||
future { Manga.getMangaThumbnail(mangaId) }
|
||||
.thenApply {
|
||||
@@ -123,6 +129,7 @@ object MangaController {
|
||||
}
|
||||
},
|
||||
behaviorOf = { ctx, mangaId ->
|
||||
ctx.getAttribute(Attribute.TachideskUser).requireUser()
|
||||
ctx.future {
|
||||
future { Library.addMangaToLibrary(mangaId) }
|
||||
.thenApply { ctx.status(HttpStatus.OK) }
|
||||
@@ -145,6 +152,7 @@ object MangaController {
|
||||
}
|
||||
},
|
||||
behaviorOf = { ctx, mangaId ->
|
||||
ctx.getAttribute(Attribute.TachideskUser).requireUser()
|
||||
ctx.future {
|
||||
future { Library.removeMangaFromLibrary(mangaId) }
|
||||
.thenApply { ctx.status(HttpStatus.OK) }
|
||||
@@ -167,6 +175,7 @@ object MangaController {
|
||||
}
|
||||
},
|
||||
behaviorOf = { ctx, mangaId ->
|
||||
ctx.getAttribute(Attribute.TachideskUser).requireUser()
|
||||
ctx.json(CategoryManga.getMangaCategories(mangaId))
|
||||
},
|
||||
withResults = {
|
||||
@@ -186,6 +195,7 @@ object MangaController {
|
||||
}
|
||||
},
|
||||
behaviorOf = { ctx, mangaId, categoryId ->
|
||||
ctx.getAttribute(Attribute.TachideskUser).requireUser()
|
||||
CategoryManga.addMangaToCategory(mangaId, categoryId)
|
||||
ctx.status(200)
|
||||
},
|
||||
@@ -206,6 +216,7 @@ object MangaController {
|
||||
}
|
||||
},
|
||||
behaviorOf = { ctx, mangaId, categoryId ->
|
||||
ctx.getAttribute(Attribute.TachideskUser).requireUser()
|
||||
CategoryManga.removeMangaFromCategory(mangaId, categoryId)
|
||||
ctx.status(200)
|
||||
},
|
||||
@@ -227,6 +238,7 @@ object MangaController {
|
||||
}
|
||||
},
|
||||
behaviorOf = { ctx, mangaId, key, value ->
|
||||
ctx.getAttribute(Attribute.TachideskUser).requireUser()
|
||||
Manga.modifyMangaMeta(mangaId, key, value)
|
||||
ctx.status(200)
|
||||
},
|
||||
@@ -252,6 +264,7 @@ object MangaController {
|
||||
}
|
||||
},
|
||||
behaviorOf = { ctx, mangaId, onlineFetch ->
|
||||
ctx.getAttribute(Attribute.TachideskUser).requireUser()
|
||||
ctx.future {
|
||||
future { Chapter.getChapterList(mangaId, onlineFetch) }
|
||||
.thenApply { ctx.json(it) }
|
||||
@@ -275,6 +288,7 @@ object MangaController {
|
||||
body<Chapter.MangaChapterBatchEditInput>()
|
||||
},
|
||||
behaviorOf = { ctx, mangaId ->
|
||||
ctx.getAttribute(Attribute.TachideskUser).requireUser()
|
||||
val input = json.decodeFromString<Chapter.MangaChapterBatchEditInput>(ctx.body())
|
||||
Chapter.modifyChapters(input, mangaId)
|
||||
},
|
||||
@@ -294,6 +308,7 @@ object MangaController {
|
||||
body<Chapter.ChapterBatchEditInput>()
|
||||
},
|
||||
behaviorOf = { ctx ->
|
||||
ctx.getAttribute(Attribute.TachideskUser).requireUser()
|
||||
val input = json.decodeFromString<Chapter.ChapterBatchEditInput>(ctx.body())
|
||||
Chapter.modifyChapters(
|
||||
Chapter.MangaChapterBatchEditInput(
|
||||
@@ -320,6 +335,7 @@ object MangaController {
|
||||
}
|
||||
},
|
||||
behaviorOf = { ctx, mangaId, chapterIndex ->
|
||||
ctx.getAttribute(Attribute.TachideskUser).requireUser()
|
||||
ctx.future {
|
||||
future {
|
||||
var chapter = getChapterDownloadReadyByIndex(chapterIndex, mangaId)
|
||||
@@ -368,6 +384,7 @@ object MangaController {
|
||||
}
|
||||
},
|
||||
behaviorOf = { ctx, mangaId, chapterIndex, read, bookmarked, markPrevRead, lastPageRead ->
|
||||
ctx.getAttribute(Attribute.TachideskUser).requireUser()
|
||||
val chapterId = Chapter.modifyChapter(mangaId, chapterIndex, read, bookmarked, markPrevRead, lastPageRead)
|
||||
|
||||
// Sync with KoreaderSync when progress is updated
|
||||
@@ -394,6 +411,7 @@ object MangaController {
|
||||
}
|
||||
},
|
||||
behaviorOf = { ctx, mangaId, chapterIndex ->
|
||||
ctx.getAttribute(Attribute.TachideskUser).requireUser()
|
||||
Chapter.deleteChapter(mangaId, chapterIndex)
|
||||
|
||||
ctx.status(200)
|
||||
@@ -418,6 +436,7 @@ object MangaController {
|
||||
}
|
||||
},
|
||||
behaviorOf = { ctx, mangaId, chapterIndex, key, value ->
|
||||
ctx.getAttribute(Attribute.TachideskUser).requireUser()
|
||||
Chapter.modifyChapterMeta(mangaId, chapterIndex, key, value)
|
||||
|
||||
ctx.status(200)
|
||||
@@ -445,6 +464,7 @@ object MangaController {
|
||||
}
|
||||
},
|
||||
behaviorOf = { ctx, mangaId, chapterIndex, index, updateProgress, format ->
|
||||
ctx.getAttribute(Attribute.TachideskUser).requireUser()
|
||||
ctx.future {
|
||||
future { Page.getPageImage(mangaId, chapterIndex, index, format, null) }
|
||||
.thenApply {
|
||||
@@ -480,6 +500,7 @@ object MangaController {
|
||||
}
|
||||
},
|
||||
behaviorOf = { ctx, chapterId, markAsRead ->
|
||||
ctx.getAttribute(Attribute.TachideskUser).requireUser()
|
||||
if (ctx.method() == HandlerType.HEAD) {
|
||||
ctx.future {
|
||||
future { ChapterDownloadHelper.getCbzMetadataForDownload(chapterId) }
|
||||
|
||||
Reference in New Issue
Block a user