Skip to content

Conversation

@Syer10
Copy link
Collaborator

@Syer10 Syer10 commented Jul 29, 2023

Closes #298

  • User data is now separated to its own tables or a column USER is added based on the requirements

Todo:

  • Decide how to update user's manga, update all manga at once or separate them by user?
  • Save JWT token secret
  • Investigate token audience
  • Implement open registration
  • Implement Permissions
  • Source settings are currently global, maybe figure out how to separate sources per user? Or make it a permission
  • Test to make sure I didn't break anything
  • Require current password to change password
  • Consider what in server.conf can be user specific settings
  • Handle downloads per-user
  • Handle library update
  • Make downloads endpoint filter by in-library?
  • Add NSFW permission
  • Fix chapter backup import
  • Fix chapter url change data migration

@schroda
Copy link
Collaborator

schroda commented Aug 3, 2023

what has to be considered as well are user related settings that are currently in the server config

@schroda
Copy link
Collaborator

schroda commented Aug 3, 2023

Decide how to update user's manga, update all manga at once or separate them by user?

I'd say everything should be per user.

that should also include downloads.
or how should that be handled? what if two users try to download from the same source, wouldn't then the downloads of one user block the ones from the other?

I guess this would still be a problem even with separated downloads, unless every user uses their own source instance.
but also overall, if every user has a own source instance, couldn't that, in theory, lead to basically spamming sources?

everything that is currently stored on the file system would also have to be per user right? e.g. downloads and thumbnails
I guess it could also be kept global by only deleting files if no user requires them anymore, e.g. for downloads, if a user "deletes" a chapter of manga X and other users still have that chapter downloaded, nothing would get deleted.
but then how would you handle normal or cbz downloads which could be different per user

@Syer10
Copy link
Collaborator Author

Syer10 commented Aug 3, 2023

Library update could be per user, for this I think we would need a task that runs every 6 hours, grabs manga from users who's interval has passed(could be days for some users, 6 at the minimum), and then update them.

Downloader is more special, right now my idea is allow anyone to download, and the download queue will grab the user data based on the subscriber, this would allow unique user data to go to each user. This only works on gql, so the old websocket url won't have user data. Only admins would be able to delete files. Or we could make a userDeleted field in the chapter user data, and if all users who have that manga favorited have the userDeleted field true, then actually delete the file.

I think normal vs cbz downloads would be configured by the system administrator, not per user.

Everything on the filesystem doesn't need to be per user, since manga and chapter ids won't change between users, we dont need to separate anything.

Thats right, if we separate sources per user, rate-limits won't properly apply between http clients. We may have to make source settings global that only the admin can change hmm.

@schroda
Copy link
Collaborator

schroda commented Aug 5, 2023

Library update could be per user, for this I think we would need a task that runs every 6 hours, grabs manga from users who's interval has passed(could be days for some users, 6 at the minimum), and then update them.

I guess you would have to decrease the task to 1h since in case someone has it to update between every e.g. 6h and 12h then it would only get triggered every 12h

would this be one global updater or one per user?
in case it's one global updater - what about cases were one user has a huge library while another has a small one and gets "blocked" by the other user or would this be works as expected, and you just have to wait?
e.g. user A updates 50 mangas of source A and user B updates 10 mangas of source A, but they're at the end of the queue

same for the downloader I guess?

Or we could make a userDeleted field in the chapter user data, and if all users who have that manga favorited have the userDeleted field true, then actually delete the file.

I think that would be a better experience, since you don't have to manually clean up things that aren't needed anymore

We may have to make source settings global that only the admin can change hmm.

that are the preferences right? I think doing it like that would be the easiest way, but I guess there are also settings which would make sense to be per user instead of global
e.g. going by MangaDex:

  • "Data saver" would be something that has to be decided by the one that runs the server
  • "Alternative titles in description" could be per user?
  • "Default content rating" also per user?

@Syer10
Copy link
Collaborator Author

Syer10 commented Aug 5, 2023

There are technical limitations to source settings, since we need to have 1 source instance to preserve the rate-limit, we can't separate the sources per user, and since sources expect their settings to be in the source, we have to limit them to either be editable by everyone or admin only.

There are drawbacks to library update and downloader sharing, but its the best option we have. We could always sort manga update order according to the the ones that are used the most or updating the smaller libraries first. I think 6 hours is a fine interval, since we can always just bump the user to the next interval. If a user sets 24 hours, they will always get the update at the same time, or if a user selects 6 hours, they will always get updates.

@schroda
Copy link
Collaborator

schroda commented Aug 5, 2023

I think the limitations should be made quite clear in the documentation once that is in to prevent any wrong expectations

If the "global" update task runs every 6h then I think the possible update interval has to be changed to be only multiples of 6, since everything between them will be bumped to the next higher multiple

@Syer10
Copy link
Collaborator Author

Syer10 commented Aug 5, 2023

Hmm, right I was thinking in Tachiyomi library update terms, I forgot that users can set weird update times like every 7 hours. I think every hour would be fine then

Syer10 added 19 commits August 19, 2023 13:09
# Conflicts:
#	server/build.gradle.kts
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ChapterMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MangaMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/server/subscriptions/ApolloSubscriptionProtocolHandler.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/server/subscriptions/ApolloSubscriptionSessionState.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/controller/UpdateController.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Chapter.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Library.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/update/Updater.kt
# Conflicts:
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ChapterMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ExtensionMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/CategoryQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/ChapterQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/ExtensionQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MangaQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MetaQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/SourceQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/controller/MangaController.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/chapter/ChapterForDownload.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/Downloader.kt
# Conflicts:
#	server/src/main/kotlin/suwayomi/tachidesk/global/controller/GlobalMetaController.kt
#	server/src/main/kotlin/suwayomi/tachidesk/global/controller/SettingsController.kt
#	server/src/main/kotlin/suwayomi/tachidesk/global/impl/GlobalMeta.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/CategoryDataLoader.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/ChapterDataLoader.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/MangaDataLoader.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/MetaDataLoader.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/BackupMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/CategoryMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ChapterMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/DownloadMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ExtensionMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MangaMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MetaMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SettingsMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SourceMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/UpdateMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/BackupQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/CategoryQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/ChapterQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/ExtensionQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/InfoQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MangaQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MetaQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/SourceQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskGraphQLContextFactory.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskGraphQLSchema.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskGraphQLServer.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/types/SettingsType.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/controller/BackupController.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/controller/CategoryController.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/controller/DownloadController.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/controller/ExtensionController.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/controller/MangaController.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/controller/SourceController.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/controller/UpdateController.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Category.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/CategoryManga.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Chapter.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Library.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Manga.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/MangaList.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Search.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/ProtoBackupExport.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/ProtoBackupImport.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/chapter/ChapterForDownload.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/DownloadManager.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/update/Updater.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/CategoryTable.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/ChapterTable.kt
#	server/src/main/kotlin/suwayomi/tachidesk/server/JavalinSetup.kt
#	server/src/test/kotlin/suwayomi/tachidesk/manga/impl/CategoryMangaTest.kt
#	server/src/test/kotlin/suwayomi/tachidesk/manga/impl/MangaTest.kt
#	server/src/test/kotlin/suwayomi/tachidesk/manga/impl/SearchTest.kt
@Syer10 Syer10 changed the title User Accounts [WIP] User Accounts Jan 24, 2024
@Syer10
Copy link
Collaborator Author

Syer10 commented Dec 14, 2024

Its a very low priority feature since none of the devs need it. If I have time I may continue working on it, I am also open to others contributing and completing any of the TODOs.

@L00maca
Copy link

L00maca commented Dec 14, 2024

Took a look at the source, Kotolin is not something I can help with unfortunately :/

# Conflicts:
#	.github/workflows/build_pull_request.yml
#	.github/workflows/build_push.yml
#	.github/workflows/publish.yml
#	gradle/libs.versions.toml
#	server/src/main/kotlin/suwayomi/tachidesk/global/controller/SettingsController.kt
#	server/src/main/kotlin/suwayomi/tachidesk/global/impl/GlobalMeta.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/CategoryDataLoader.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/ChapterDataLoader.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/MangaDataLoader.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/MetaDataLoader.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/BackupMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/CategoryMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ChapterMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/DownloadMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ExtensionMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/InfoMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MangaMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MetaMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SettingsMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SourceMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/UpdateMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/BackupQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/CategoryQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/ChapterQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/ExtensionQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/InfoQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MangaQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MetaQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/SettingsQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/SourceQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/UpdateQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskGraphQLContextFactory.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskGraphQLServer.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/types/MangaType.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/types/SettingsType.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/controller/BackupController.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/controller/CategoryController.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/controller/DownloadController.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/controller/ExtensionController.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/controller/MangaController.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/controller/SourceController.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/controller/UpdateController.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Category.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/CategoryManga.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Chapter.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Library.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Manga.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/MangaList.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/models/ChapterImpl.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/ProtoBackupExport.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/ProtoBackupImport.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/chapter/ChapterForDownload.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/DownloadManager.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/Downloader.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/update/Updater.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/CategoryTable.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/ChapterTable.kt
#	server/src/main/kotlin/suwayomi/tachidesk/server/JavalinSetup.kt
#	server/src/test/kotlin/suwayomi/tachidesk/manga/controller/UpdateControllerTest.kt
#	server/src/test/kotlin/suwayomi/tachidesk/test/TestUtils.kt
@GonzaCass
Copy link

Hi @Syer10,

I came across your work on the User Accounts feature branch and noticed it's marked as WIP. Since some time has passed, I wanted to ask:

  1. Is there any updated version or roadmap for this feature that I could reference?
  2. Are there specific outstanding tasks where community help would be valuable?.
  3. Would you be open to collaboration if I wanted to continue development based on your existing work?

I'm particularly interested in helping bring this feature to completion and would appreciate any guidance on:

  • Current state of the branch
  • Known challenges or blockers
  • Architectural decisions I should be aware of

Thanks for your work on this.

@Syer10
Copy link
Collaborator Author

Syer10 commented May 14, 2025

@ov3rsp1der I would love some contributions on this!

To answer your questions:

  • 1 and 2. Everything in the Todo block in the PR message still needs to be done, I've also littered quite a few Todo's around the codebase of things that need to be fixed or added for user support.

  • 3: Yes! I personally don't have a use for this feature so its in the backburner for me. I would defenitly be open to collaborate with people so that this can be implemented, I've done a lot of the groundwork needed for how I would like this feature to work on the server-side, just needs more implementating

  • For the branch I'll take a look at merging master into this within the next few days so you have a more recent base to work on. Already done, is now recent enough to work on

  • I can't think of any blockers at the top of my head, most of it is ready to be implemented, some design choices need to be taken for different features, but that can be done later once we have more of the groundwork done. If you run into a issue with a place requiring a userId and you don't have one, feel free to default to 0 and add a // todo: User Accounts

For Archeticual decisions, it should hopefully be pretty recognizable in the code. There will only be GQL endpoints for user handling(as the REST API is deprecated). We use the .getAttribute(Attribute.TachideskUser).requireUser() with the context or dataFetchingEnviornment to get the user, a similar system will be needed for permissions. We join tables to to get data + user data wherever we can. The admin/default user is id 1, and thats the only user when not in Multi-User mode. There is probably more, feel free to talk to me about this in the Discord whenever you start looking at the code.

# Conflicts:
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/CategoryMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/UpdateMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/InfoQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/UpdateQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskGraphQLServer.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/controller/MangaController.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Category.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/CategoryManga.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Chapter.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/ProtoBackupImport.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/chapter/ChapterForDownload.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/Downloader.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/Track.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/ChapterTable.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/MangaTable.kt
#	server/src/main/kotlin/suwayomi/tachidesk/server/JavalinSetup.kt
@GonzaCass
Copy link

Thank you! let's see what I can do

@zeedif
Copy link
Contributor

zeedif commented Aug 26, 2025

With the recent merge of the basic JWT login, I've gotten interested in helping out with multi-user support. Before diving in, I'd like to discuss how this would interact with the existing authentication modes.

For example, once an instance has multiple users, how would one revert back to a 'no-auth' mode, or switch to a single-user basic auth mode? I'm guessing the idea might be that each user gets their own /library and the decision to protect it is handled on a per-user basis? Or maybe the server would be toggled between a single-user and multi-user mode entirely?

@Syer10
Copy link
Collaborator Author

Syer10 commented Aug 27, 2025

Each user does get thier own library, the default user(user id 1) is the only user by default and is what will be used when multiuser is disabled.

Enabling multiuser would allow access to the server by other users. All data will be stored in the same way regardless.

If multiuser is disabled, all data will be kept but only the default user will be accessible

# Conflicts:
#	gradle/libs.versions.toml
#	server/build.gradle.kts
#	server/src/main/kotlin/suwayomi/tachidesk/global/controller/GlobalMetaController.kt
#	server/src/main/kotlin/suwayomi/tachidesk/global/impl/GlobalMeta.kt
#	server/src/main/kotlin/suwayomi/tachidesk/global/impl/util/Jwt.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/TrackDataLoader.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/BackupMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/CategoryMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ChapterMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/DownloadMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/ExtensionMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/InfoMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MangaMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/MetaMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SettingsMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SourceMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/TrackMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/UpdateMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/UserMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/BackupQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/CategoryQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/ChapterQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/ExtensionQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/InfoQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MangaQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MetaQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/SettingsQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/TrackQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/UpdateQuery.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskGraphQLContextFactory.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/subscriptions/DownloadSubscription.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/subscriptions/InfoSubscription.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/subscriptions/UpdateSubscription.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/controller/BackupController.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/controller/CategoryController.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/controller/MangaController.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/controller/SourceController.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/controller/TrackController.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/controller/UpdateController.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Category.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/CategoryManga.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Chapter.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/ChapterDownloadHelper.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Manga.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Source.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/ProtoBackupExport.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/ProtoBackupImport.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/chapter/ChapterForDownload.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/Track.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/DeletableTracker.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/Tracker.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/anilist/Anilist.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/anilist/AnilistInterceptor.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/bangumi/Bangumi.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/kitsu/Kitsu.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/kitsu/KitsuInterceptor.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/mangaupdates/MangaUpdates.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/myanimelist/MyAnimeList.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/myanimelist/MyAnimeListInterceptor.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/TrackRecordTable.kt
#	server/src/main/kotlin/suwayomi/tachidesk/opds/controller/OpdsV1Controller.kt
#	server/src/main/kotlin/suwayomi/tachidesk/opds/impl/Opds.kt
#	server/src/main/kotlin/suwayomi/tachidesk/server/JavalinSetup.kt
#	server/src/main/kotlin/suwayomi/tachidesk/server/ServerConfig.kt
#	server/src/main/kotlin/suwayomi/tachidesk/server/user/UserType.kt
#	server/src/main/resources/server-reference.conf
@Syer10
Copy link
Collaborator Author

Syer10 commented Sep 1, 2025

I have merged the branch with the latest server.

@zeedif
Copy link
Contributor

zeedif commented Sep 1, 2025

Each user does get thier own library, the default user(user id 1) is the only user by default and is what will be used when multiuser is disabled.

Enabling multiuser would allow access to the server by other users. All data will be stored in the same way regardless.

If multiuser is disabled, all data will be kept but only the default user will be accessible

I understand the point about starting with or reverting to the traditional single-user mode. However, it presents a challenge when you consider that users can be deleted and that at least one administrator role must exist.

This leads to scenarios where, for instance, in a two-user setup where both are admins (e.g., ID 1 and ID 2), the second admin could revoke the admin role from the default user (ID 1). In that case, how would the system revert to single-user mode?

I suppose the best approach would be one of two options: either the administrator who makes the change to single-user mode becomes the sole active account, or the administrator can select which account will become the active one in single-user mode.

@Syer10
Copy link
Collaborator Author

Syer10 commented Sep 1, 2025

The way I am thinking of is that there is the admin(id 1), and they can manage permissions for all other users. Manage permissions could also be a permission, but no permissions are can actually be taken away by user 1, since they have all of them.

@zeedif
Copy link
Contributor

zeedif commented Sep 1, 2025

I'm concerned that tying a permanent, irrevocable admin role to user ID 1 isn't a secure strategy due to potential security risks.

While I know Suwayomi is self-hosted and mostly used on LANs, there's always a chance an instance could be exposed online. If the credentials for user ID 1 were compromised, there would be no way for a second administrator to revoke their permissions.

It's a basic and common practice in any multi-user system for administrators to be able to manage each other's permissions to handle such security threats. I can't think of any examples of systems that use the model you're proposing.

@Syer10
Copy link
Collaborator Author

Syer10 commented Sep 1, 2025

The closest application that sorta models the way I am thinking of taking Suwayomi is Plex. There is 1 central user that manages the app, and everyone else is given access and is secondary.

@zeedif
Copy link
Contributor

zeedif commented Sep 2, 2025

Hmm, I'm not very familiar with Plex. I tried it briefly but switched to Jellyfin precisely because it wasn't possible for two users to administer the same server.

That said, one concern I have is how OIDC support would be added later under this strategy. I suppose it could be handled like Kavita does, where OIDC accounts are not the same as the accounts generated from within Suwayomi itself.

Perhaps it would be a good idea to do some kind of poll to see how the community would prefer to handle multiple users? I might not be the only one who is not entirely comfortable with the vision of making it similar to Plex's single-admin management.

@dhvcc
Copy link

dhvcc commented Sep 2, 2025

Hmm, I'm not very familiar with Plex. I tried it briefly but switched to Jellyfin precisely because it wasn't possible for two users to administer the same server.

That said, one concern I have is how OIDC support would be added later under this strategy. I suppose it could be handled like Kavita does, where OIDC accounts are not the same as the accounts generated from within Suwayomi itself.

Perhaps it would be a good idea to do some kind of poll to see how the community would prefer to handle multiple users? I might not be the only one who is not entirely comfortable with the vision of making it similar to Plex's single-admin management.

I agree with @Syer10
I think having 1 root admin access and an ability to escalate other user's permission to admin (or add more permissions, whatever) is pretty normal. This way you can have 2 admins, while id=1 account will be the one to be left if you switch off the multi tenancy. AWS also has root account and administrator ones alongside (ref)

I think if you expose root admin credentials it's on you really

# Conflicts:
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SettingsMutation.kt
#	server/src/main/kotlin/suwayomi/tachidesk/graphql/types/SettingsType.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/ProtoBackupExport.kt
#	server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/models/BackupServerSettings.kt
#	server/src/main/kotlin/suwayomi/tachidesk/server/ServerConfig.kt
#	server/src/main/resources/server-reference.conf
@cpiber
Copy link
Contributor

cpiber commented Sep 11, 2025

If the credentials for user ID 1 were compromised, there would be no way for a second administrator to revoke their permissions.

Maybe I'm not seeing it, but why not just change the password? As long as an admin can reset or set the password, that's effectively as good as disabling the account and creating a new one. If we allow changing user names additionally (since everything is tied to IDs anyways), there's no functional difference at all.
There would need to be a mechanism to revoke refresh tokens of course to terminate the compromised session, but I don't see a need to revoke the entire user. Easiest would be to just nuke the JWT secret and restart the server, that effectively terminates all sessions immediately.

@zeedif
Copy link
Contributor

zeedif commented Sep 11, 2025

Regarding dhvcc's point about the AWS root account model, I agree that's a good example. My main concern was a Plex-like, single-admin strategy, which I personally find too restrictive as it prevents delegating full administrative permissions. The AWS approach addresses my primary worry, as it fits Suwayomi's current structure well and would avoid significant refactoring. However, my question is how OIDC integration would work. We would need to consider whether to link external accounts to standard users—who could then be promoted in their role—while keeping the root user separate, or to simply ignore role customizations for any OIDC account that matches the root user.

@Syer10
Copy link
Collaborator Author

Syer10 commented Sep 12, 2025

I do not have any actual expierence with OIDC, but my vision would be that admin roles would be copied over to Suwayomi where possible, but the root user would never be able to have thier roles or permissions changed.

I think if they want thier accounts to be controlled by OIDC, that they should leave the root user unattached to an OIDC account.

OIDC users would be a regular user, maybe with a OIDC table row that references the specific user its attached to, or just a column on the user table. Depending on which is easiest and makes the most sense to implement.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature Request] Add Users Accounts on web login

7 participants