From 2472b86284946d3c9cb03de930db297a1d4bf5ea Mon Sep 17 00:00:00 2001 From: advplyr Date: Sun, 11 Aug 2024 16:07:29 -0500 Subject: [PATCH] Update:Express middleware sets req.user to new data model, openid permissions functions moved to new data model --- server/Auth.js | 7 +- server/Server.js | 11 +- server/controllers/AuthorController.js | 14 +- server/controllers/BackupController.js | 4 +- server/controllers/CacheController.js | 4 +- server/controllers/CollectionController.js | 14 +- .../CustomMetadataProviderController.js | 4 +- server/controllers/EmailController.js | 8 +- server/controllers/FileSystemController.js | 8 +- server/controllers/LibraryController.js | 58 ++--- server/controllers/LibraryItemController.js | 219 ++++++++++++------ server/controllers/MeController.js | 67 +++--- server/controllers/MiscController.js | 61 +++-- server/controllers/NotificationController.js | 5 +- server/controllers/PlaylistController.js | 15 +- server/controllers/PodcastController.js | 45 ++-- server/controllers/RSSFeedController.js | 17 +- server/controllers/SearchController.js | 3 +- server/controllers/SeriesController.js | 17 +- server/controllers/SessionController.js | 33 ++- server/controllers/ShareController.js | 13 +- server/controllers/ToolsController.js | 19 +- server/controllers/UserController.js | 34 ++- server/managers/ApiCacheManager.js | 2 +- server/managers/PlaybackSessionManager.js | 8 +- server/models/User.js | 105 ++++++++- server/objects/settings/ServerSettings.js | 2 +- server/objects/user/User.js | 105 --------- test/server/managers/ApiCacheManager.test.js | 2 +- 29 files changed, 474 insertions(+), 430 deletions(-) diff --git a/server/Auth.js b/server/Auth.js index ec229c0a..3e61477b 100644 --- a/server/Auth.js +++ b/server/Auth.js @@ -152,6 +152,8 @@ class Auth { /** * Finds an existing user by OpenID subject identifier, or by email/username based on server settings, * or creates a new user if configured to do so. + * + * @returns {import('./models/User')|null} */ async findOrCreateUser(userinfo) { let user = await Database.userModel.getUserByOpenIDSub(userinfo.sub) @@ -307,9 +309,8 @@ class Auth { const absPermissions = userinfo[absPermissionsClaim] if (!absPermissions) throw new Error(`Advanced permissions claim ${absPermissionsClaim} not found in userinfo`) - if (user.updatePermissionsFromExternalJSON(absPermissions)) { + if (await user.updatePermissionsFromExternalJSON(absPermissions)) { Logger.info(`[Auth] openid callback: Updating advanced perms for user "${user.username}" using "${JSON.stringify(absPermissions)}"`) - await Database.userModel.updateFromOld(user) } } @@ -921,7 +922,7 @@ class Auth { async userChangePassword(req, res) { let { password, newPassword } = req.body newPassword = newPassword || '' - const matchingUser = req.userNew + const matchingUser = req.user // Only root can have an empty password if (matchingUser.type !== 'root' && !newPassword) { diff --git a/server/Server.js b/server/Server.js index 61ad7ab1..f1cfc7f4 100644 --- a/server/Server.js +++ b/server/Server.js @@ -91,8 +91,6 @@ class Server { /** * Middleware to check if the current request is authenticated - * req.user is set if authenticated to the OLD user object - * req.userNew is set if authenticated to the NEW user object * * @param {import('express').Request} req * @param {import('express').Response} res @@ -100,14 +98,7 @@ class Server { */ authMiddleware(req, res, next) { // ask passportjs if the current request is authenticated - this.auth.isAuthenticated(req, res, () => { - if (req.user) { - // TODO: req.userNew to become req.user - req.userNew = req.user - req.user = Database.userModel.getOldUser(req.user) - } - next() - }) + this.auth.isAuthenticated(req, res, next) } cancelLibraryScan(libraryId) { diff --git a/server/controllers/AuthorController.js b/server/controllers/AuthorController.js index 74bf3bcc..2e762bb5 100644 --- a/server/controllers/AuthorController.js +++ b/server/controllers/AuthorController.js @@ -24,7 +24,7 @@ class AuthorController { // Used on author landing page to include library items and items grouped in series if (include.includes('items')) { - authorJson.libraryItems = await Database.libraryItemModel.getForAuthor(req.author, req.userNew) + authorJson.libraryItems = await Database.libraryItemModel.getForAuthor(req.author, req.user) if (include.includes('series')) { const seriesMap = {} @@ -222,8 +222,8 @@ class AuthorController { * @param {import('express').Response} res */ async uploadImage(req, res) { - if (!req.userNew.canUpload) { - Logger.warn(`User "${req.userNew.username}" attempted to upload an image without permission`) + if (!req.user.canUpload) { + Logger.warn(`User "${req.user.username}" attempted to upload an image without permission`) return res.sendStatus(403) } if (!req.body.url) { @@ -362,11 +362,11 @@ class AuthorController { const author = await Database.authorModel.getOldById(req.params.id) if (!author) return res.sendStatus(404) - if (req.method == 'DELETE' && !req.userNew.canDelete) { - Logger.warn(`[AuthorController] User "${req.userNew.username}" attempted to delete without permission`) + if (req.method == 'DELETE' && !req.user.canDelete) { + Logger.warn(`[AuthorController] User "${req.user.username}" attempted to delete without permission`) return res.sendStatus(403) - } else if ((req.method == 'PATCH' || req.method == 'POST') && !req.userNew.canUpdate) { - Logger.warn(`[AuthorController] User "${req.userNew.username}" attempted to update without permission`) + } else if ((req.method == 'PATCH' || req.method == 'POST') && !req.user.canUpdate) { + Logger.warn(`[AuthorController] User "${req.user.username}" attempted to update without permission`) return res.sendStatus(403) } diff --git a/server/controllers/BackupController.js b/server/controllers/BackupController.js index 86c4f4e7..99a3bf44 100644 --- a/server/controllers/BackupController.js +++ b/server/controllers/BackupController.js @@ -113,8 +113,8 @@ class BackupController { } middleware(req, res, next) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`[BackupController] Non-admin user "${req.userNew.username}" attempting to access backups`) + if (!req.user.isAdminOrUp) { + Logger.error(`[BackupController] Non-admin user "${req.user.username}" attempting to access backups`) return res.sendStatus(403) } diff --git a/server/controllers/CacheController.js b/server/controllers/CacheController.js index 3a06d203..85f248e6 100644 --- a/server/controllers/CacheController.js +++ b/server/controllers/CacheController.js @@ -5,7 +5,7 @@ class CacheController { // POST: api/cache/purge async purgeCache(req, res) { - if (!req.userNew.isAdminOrUp) { + if (!req.user.isAdminOrUp) { return res.sendStatus(403) } await CacheManager.purgeAll() @@ -14,7 +14,7 @@ class CacheController { // POST: api/cache/items/purge async purgeItemsCache(req, res) { - if (!req.userNew.isAdminOrUp) { + if (!req.user.isAdminOrUp) { return res.sendStatus(403) } await CacheManager.purgeItems() diff --git a/server/controllers/CollectionController.js b/server/controllers/CollectionController.js index 6657918c..d559f3ee 100644 --- a/server/controllers/CollectionController.js +++ b/server/controllers/CollectionController.js @@ -16,7 +16,7 @@ class CollectionController { */ async create(req, res) { const newCollection = new Collection() - req.body.userId = req.userNew.id + req.body.userId = req.user.id if (!newCollection.setData(req.body)) { return res.status(400).send('Invalid collection data') } @@ -50,7 +50,7 @@ class CollectionController { } async findAll(req, res) { - const collectionsExpanded = await Database.collectionModel.getOldCollectionsJsonExpanded(req.userNew) + const collectionsExpanded = await Database.collectionModel.getOldCollectionsJsonExpanded(req.user) res.json({ collections: collectionsExpanded }) @@ -59,7 +59,7 @@ class CollectionController { async findOne(req, res) { const includeEntities = (req.query.include || '').split(',') - const collectionExpanded = await req.collection.getOldJsonExpanded(req.userNew, includeEntities) + const collectionExpanded = await req.collection.getOldJsonExpanded(req.user, includeEntities) if (!collectionExpanded) { // This may happen if the user is restricted from all books return res.sendStatus(404) @@ -334,11 +334,11 @@ class CollectionController { req.collection = collection } - if (req.method == 'DELETE' && !req.userNew.canDelete) { - Logger.warn(`[CollectionController] User "${req.userNew.username}" attempted to delete without permission`) + if (req.method == 'DELETE' && !req.user.canDelete) { + Logger.warn(`[CollectionController] User "${req.user.username}" attempted to delete without permission`) return res.sendStatus(403) - } else if ((req.method == 'PATCH' || req.method == 'POST') && !req.userNew.canUpdate) { - Logger.warn(`[CollectionController] User "${req.userNew.username}" attempted to update without permission`) + } else if ((req.method == 'PATCH' || req.method == 'POST') && !req.user.canUpdate) { + Logger.warn(`[CollectionController] User "${req.user.username}" attempted to update without permission`) return res.sendStatus(403) } diff --git a/server/controllers/CustomMetadataProviderController.js b/server/controllers/CustomMetadataProviderController.js index fd31b5f4..8af20cee 100644 --- a/server/controllers/CustomMetadataProviderController.js +++ b/server/controllers/CustomMetadataProviderController.js @@ -101,8 +101,8 @@ class CustomMetadataProviderController { * @param {import('express').NextFunction} next */ async middleware(req, res, next) { - if (!req.userNew.isAdminOrUp) { - Logger.warn(`[CustomMetadataProviderController] Non-admin user "${req.userNew.username}" attempted access route "${req.path}"`) + if (!req.user.isAdminOrUp) { + Logger.warn(`[CustomMetadataProviderController] Non-admin user "${req.user.username}" attempted access route "${req.path}"`) return res.sendStatus(403) } diff --git a/server/controllers/EmailController.js b/server/controllers/EmailController.js index 42acbefd..69f4276d 100644 --- a/server/controllers/EmailController.js +++ b/server/controllers/EmailController.js @@ -59,7 +59,7 @@ class EmailController { * @param {import('express').Response} res */ async sendEBookToDevice(req, res) { - Logger.debug(`[EmailController] Send ebook to device requested by user "${req.userNew.username}" for libraryItemId=${req.body.libraryItemId}, deviceName=${req.body.deviceName}`) + Logger.debug(`[EmailController] Send ebook to device requested by user "${req.user.username}" for libraryItemId=${req.body.libraryItemId}, deviceName=${req.body.deviceName}`) const device = Database.emailSettings.getEReaderDevice(req.body.deviceName) if (!device) { @@ -67,7 +67,7 @@ class EmailController { } // Check user has access to device - if (!Database.emailSettings.checkUserCanAccessDevice(device, req.userNew)) { + if (!Database.emailSettings.checkUserCanAccessDevice(device, req.user)) { return res.sendStatus(403) } @@ -77,7 +77,7 @@ class EmailController { } // Check user has access to library item - if (!req.userNew.checkCanAccessLibraryItem(libraryItem)) { + if (!req.user.checkCanAccessLibraryItem(libraryItem)) { return res.sendStatus(403) } @@ -90,7 +90,7 @@ class EmailController { } adminMiddleware(req, res, next) { - if (!req.userNew.isAdminOrUp) { + if (!req.user.isAdminOrUp) { return res.sendStatus(404) } diff --git a/server/controllers/FileSystemController.js b/server/controllers/FileSystemController.js index 367215b2..2104ec25 100644 --- a/server/controllers/FileSystemController.js +++ b/server/controllers/FileSystemController.js @@ -13,8 +13,8 @@ class FileSystemController { * @param {import('express').Response} res */ async getPaths(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`[FileSystemController] Non-admin user "${req.userNew.username}" attempting to get filesystem paths`) + if (!req.user.isAdminOrUp) { + Logger.error(`[FileSystemController] Non-admin user "${req.user.username}" attempting to get filesystem paths`) return res.sendStatus(403) } @@ -69,8 +69,8 @@ class FileSystemController { // POST: api/filesystem/pathexists async checkPathExists(req, res) { - if (!req.userNew.canUpload) { - Logger.error(`[FileSystemController] Non-admin user "${req.userNew.username}" attempting to check path exists`) + if (!req.user.canUpload) { + Logger.error(`[FileSystemController] Non-admin user "${req.user.username}" attempting to check path exists`) return res.sendStatus(403) } diff --git a/server/controllers/LibraryController.js b/server/controllers/LibraryController.js index bd0882a7..57bec9f2 100644 --- a/server/controllers/LibraryController.js +++ b/server/controllers/LibraryController.js @@ -83,7 +83,7 @@ class LibraryController { async findAll(req, res) { const libraries = await Database.libraryModel.getAllOldLibraries() - const librariesAccessible = req.userNew.permissions?.librariesAccessible || [] + const librariesAccessible = req.user.permissions?.librariesAccessible || [] if (librariesAccessible.length) { return res.json({ libraries: libraries.filter((lib) => librariesAccessible.includes(lib.id)).map((lib) => lib.toJSON()) @@ -110,7 +110,7 @@ class LibraryController { return res.json({ filterdata, issues: filterdata.numIssues, - numUserPlaylists: await Database.playlistModel.getNumPlaylistsForUserAndLibrary(req.userNew.id, req.library.id), + numUserPlaylists: await Database.playlistModel.getNumPlaylistsForUserAndLibrary(req.user.id, req.library.id), customMetadataProviders, library: req.library }) @@ -327,9 +327,9 @@ class LibraryController { const filterByValue = filterByGroup ? libraryFilters.decode(payload.filterBy.replace(`${filterByGroup}.`, '')) : null if (filterByGroup === 'series' && filterByValue !== 'no-series' && payload.collapseseries) { const seriesId = libraryFilters.decode(payload.filterBy.split('.')[1]) - payload.results = await libraryHelpers.handleCollapseSubseries(payload, seriesId, req.userNew, req.library) + payload.results = await libraryHelpers.handleCollapseSubseries(payload, seriesId, req.user, req.library) } else { - const { libraryItems, count } = await Database.libraryItemModel.getByFilterAndSort(req.library, req.userNew, payload) + const { libraryItems, count } = await Database.libraryItemModel.getByFilterAndSort(req.library, req.user, payload) payload.results = libraryItems payload.total = count } @@ -420,7 +420,7 @@ class LibraryController { } const offset = payload.page * payload.limit - const { series, count } = await seriesFilters.getFilteredSeries(req.library, req.userNew, payload.filterBy, payload.sortBy, payload.sortDesc, include, payload.limit, offset) + const { series, count } = await seriesFilters.getFilteredSeries(req.library, req.user, payload.filterBy, payload.sortBy, payload.sortDesc, include, payload.limit, offset) payload.total = count payload.results = series @@ -447,11 +447,11 @@ class LibraryController { if (!series) return res.sendStatus(404) const oldSeries = series.getOldSeries() - const libraryItemsInSeries = await libraryItemsBookFilters.getLibraryItemsForSeries(oldSeries, req.userNew) + const libraryItemsInSeries = await libraryItemsBookFilters.getLibraryItemsForSeries(oldSeries, req.user) const seriesJson = oldSeries.toJSON() if (include.includes('progress')) { - const libraryItemsFinished = libraryItemsInSeries.filter((li) => !!req.userNew.getMediaProgress(li.media.id)?.isFinished) + const libraryItemsFinished = libraryItemsInSeries.filter((li) => !!req.user.getMediaProgress(li.media.id)?.isFinished) seriesJson.progress = { libraryItemIds: libraryItemsInSeries.map((li) => li.id), libraryItemIdsFinished: libraryItemsFinished.map((li) => li.id), @@ -492,7 +492,7 @@ class LibraryController { } // TODO: Create paginated queries - let collections = await Database.collectionModel.getOldCollectionsJsonExpanded(req.userNew, req.library.id, include) + let collections = await Database.collectionModel.getOldCollectionsJsonExpanded(req.user, req.library.id, include) payload.total = collections.length @@ -512,7 +512,7 @@ class LibraryController { * @param {*} res */ async getUserPlaylistsForLibrary(req, res) { - let playlistsForUser = await Database.playlistModel.getOldPlaylistsForUserAndLibrary(req.userNew.id, req.library.id) + let playlistsForUser = await Database.playlistModel.getOldPlaylistsForUserAndLibrary(req.user.id, req.library.id) const payload = { results: [], @@ -552,7 +552,7 @@ class LibraryController { .split(',') .map((v) => v.trim().toLowerCase()) .filter((v) => !!v) - const shelves = await Database.libraryItemModel.getPersonalizedShelves(req.library, req.userNew, include, limitPerShelf) + const shelves = await Database.libraryItemModel.getPersonalizedShelves(req.library, req.user, include, limitPerShelf) res.json(shelves) } @@ -563,8 +563,8 @@ class LibraryController { * @param {import('express').Response} res */ async reorder(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`[LibraryController] Non-admin user "${req.userNew}" attempted to reorder libraries`) + if (!req.user.isAdminOrUp) { + Logger.error(`[LibraryController] Non-admin user "${req.user}" attempted to reorder libraries`) return res.sendStatus(403) } const libraries = await Database.libraryModel.getAllOldLibraries() @@ -609,7 +609,7 @@ class LibraryController { const limit = req.query.limit && !isNaN(req.query.limit) ? Number(req.query.limit) : 12 const query = asciiOnlyToLowerCase(req.query.q.trim()) - const matches = await libraryItemFilters.search(req.userNew, req.library, query, limit) + const matches = await libraryItemFilters.search(req.user, req.library, query, limit) res.json(matches) } @@ -662,7 +662,7 @@ class LibraryController { * @param {import('express').Response} res */ async getAuthors(req, res) { - const { bookWhere, replacements } = libraryItemsBookFilters.getUserPermissionBookWhereQuery(req.userNew) + const { bookWhere, replacements } = libraryItemsBookFilters.getUserPermissionBookWhereQuery(req.user) const authors = await Database.authorModel.findAll({ where: { libraryId: req.library.id @@ -672,7 +672,7 @@ class LibraryController { model: Database.bookModel, attributes: ['id', 'tags', 'explicit'], where: bookWhere, - required: !req.userNew.isAdminOrUp, // Only show authors with 0 books for admin users or up + required: !req.user.isAdminOrUp, // Only show authors with 0 books for admin users or up through: { attributes: [] } @@ -746,8 +746,8 @@ class LibraryController { * @param {*} res */ async updateNarrator(req, res) { - if (!req.userNew.canUpdate) { - Logger.error(`[LibraryController] Unauthorized user "${req.userNew.username}" attempted to update narrator`) + if (!req.user.canUpdate) { + Logger.error(`[LibraryController] Unauthorized user "${req.user.username}" attempted to update narrator`) return res.sendStatus(403) } @@ -796,8 +796,8 @@ class LibraryController { * @param {*} res */ async removeNarrator(req, res) { - if (!req.userNew.canUpdate) { - Logger.error(`[LibraryController] Unauthorized user "${req.userNew.username}" attempted to remove narrator`) + if (!req.user.canUpdate) { + Logger.error(`[LibraryController] Unauthorized user "${req.user.username}" attempted to remove narrator`) return res.sendStatus(403) } @@ -839,8 +839,8 @@ class LibraryController { * @param {import('express').Response} res */ async matchAll(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`[LibraryController] Non-root user "${req.userNew.username}" attempted to match library items`) + if (!req.user.isAdminOrUp) { + Logger.error(`[LibraryController] Non-root user "${req.user.username}" attempted to match library items`) return res.sendStatus(403) } Scanner.matchLibraryItems(req.library) @@ -856,8 +856,8 @@ class LibraryController { * @param {import('express').Response} res */ async scan(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`[LibraryController] Non-admin user "${req.userNew.username}" attempted to scan library`) + if (!req.user.isAdminOrUp) { + Logger.error(`[LibraryController] Non-admin user "${req.user.username}" attempted to scan library`) return res.sendStatus(403) } res.sendStatus(200) @@ -887,7 +887,7 @@ class LibraryController { } const offset = payload.page * payload.limit - payload.episodes = await libraryItemsPodcastFilters.getRecentEpisodes(req.userNew, req.library, payload.limit, offset) + payload.episodes = await libraryItemsPodcastFilters.getRecentEpisodes(req.user, req.library, payload.limit, offset) res.json(payload) } @@ -898,7 +898,7 @@ class LibraryController { * @param {import('express').Response} res */ async getOPMLFile(req, res) { - const userPermissionPodcastWhere = libraryItemsPodcastFilters.getUserPermissionPodcastWhereQuery(req.userNew) + const userPermissionPodcastWhere = libraryItemsPodcastFilters.getUserPermissionPodcastWhereQuery(req.user) const podcasts = await Database.podcastModel.findAll({ attributes: ['id', 'feedURL', 'title', 'description', 'itunesPageURL', 'language'], where: userPermissionPodcastWhere.podcastWhere, @@ -924,8 +924,8 @@ class LibraryController { * @param {import('express').Response} res */ async removeAllMetadataFiles(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`[LibraryController] Non-admin user "${req.userNew.username}" attempted to remove all metadata files`) + if (!req.user.isAdminOrUp) { + Logger.error(`[LibraryController] Non-admin user "${req.user.username}" attempted to remove all metadata files`) return res.sendStatus(403) } @@ -974,8 +974,8 @@ class LibraryController { * @param {import('express').NextFunction} next */ async middleware(req, res, next) { - if (!req.userNew.checkCanAccessLibrary(req.params.id)) { - Logger.warn(`[LibraryController] Library ${req.params.id} not accessible to user ${req.userNew.username}`) + if (!req.user.checkCanAccessLibrary(req.params.id)) { + Logger.warn(`[LibraryController] Library ${req.params.id} not accessible to user ${req.user.username}`) return res.sendStatus(403) } diff --git a/server/controllers/LibraryItemController.js b/server/controllers/LibraryItemController.js index dbe47f93..7967c801 100644 --- a/server/controllers/LibraryItemController.js +++ b/server/controllers/LibraryItemController.js @@ -18,8 +18,7 @@ const ShareManager = require('../managers/ShareManager') /** * @typedef RequestUserObjects - * @property {import('../models/User')} userNew - * @property {import('../objects/user/User')} user + * @property {import('../models/User')} user * * @typedef {Request & RequestUserObjects} RequestWithUser */ @@ -33,8 +32,8 @@ class LibraryItemController { * ?include=progress,rssfeed,downloads,share * ?expanded=1 * - * @param {import('express').Request} req - * @param {import('express').Response} res + * @param {RequestWithUser} req + * @param {Response} res */ async findOne(req, res) { const includeEntities = (req.query.include || '').split(',') @@ -44,7 +43,7 @@ class LibraryItemController { // Include users media progress if (includeEntities.includes('progress')) { var episodeId = req.query.episode || null - item.userMediaProgress = req.userNew.getOldMediaProgress(item.id, episodeId) + item.userMediaProgress = req.user.getOldMediaProgress(item.id, episodeId) } if (includeEntities.includes('rssfeed')) { @@ -52,7 +51,7 @@ class LibraryItemController { item.rssFeed = feedData?.toJSONMinified() || null } - if (item.mediaType === 'book' && req.userNew.isAdminOrUp && includeEntities.includes('share')) { + if (item.mediaType === 'book' && req.user.isAdminOrUp && includeEntities.includes('share')) { item.mediaItemShare = ShareManager.findByMediaItemId(item.media.id) } @@ -69,6 +68,11 @@ class LibraryItemController { res.json(req.libraryItem) } + /** + * + * @param {RequestWithUser} req + * @param {Response} res + */ async update(req, res) { var libraryItem = req.libraryItem // Item has cover and update is removing cover so purge it from cache @@ -91,8 +95,8 @@ class LibraryItemController { * Optional query params: * ?hard=1 * - * @param {import('express').Request} req - * @param {import('express').Response} res + * @param {RequestWithUser} req + * @param {Response} res */ async delete(req, res) { const hardDelete = req.query.hard == 1 // Delete from file system @@ -114,12 +118,12 @@ class LibraryItemController { * GET: /api/items/:id/download * Download library item. Zip file if multiple files. * - * @param {import('express').Request} req - * @param {import('express').Response} res + * @param {RequestWithUser} req + * @param {Response} res */ download(req, res) { - if (!req.userNew.canDownload) { - Logger.warn(`User "${req.userNew.username}" attempted to download without permission`) + if (!req.user.canDownload) { + Logger.warn(`User "${req.user.username}" attempted to download without permission`) return res.sendStatus(403) } const libraryItemPath = req.libraryItem.path @@ -132,12 +136,12 @@ class LibraryItemController { if (audioMimeType) { res.setHeader('Content-Type', audioMimeType) } - Logger.info(`[LibraryItemController] User "${req.userNew.username}" requested download for item "${itemTitle}" at "${libraryItemPath}"`) + Logger.info(`[LibraryItemController] User "${req.user.username}" requested download for item "${itemTitle}" at "${libraryItemPath}"`) res.download(libraryItemPath, req.libraryItem.relPath) return } - Logger.info(`[LibraryItemController] User "${req.userNew.username}" requested download for item "${itemTitle}" at "${libraryItemPath}"`) + Logger.info(`[LibraryItemController] User "${req.user.username}" requested download for item "${itemTitle}" at "${libraryItemPath}"`) const filename = `${itemTitle}.zip` zipHelpers.zipDirectoryPipe(libraryItemPath, filename, res) } @@ -146,8 +150,8 @@ class LibraryItemController { * PATCH: /items/:id/media * Update media for a library item. Will create new authors & series when necessary * - * @param {import('express').Request} req - * @param {import('express').Response} res + * @param {RequestWithUser} req + * @param {Response} res */ async updateMedia(req, res) { const libraryItem = req.libraryItem @@ -207,10 +211,16 @@ class LibraryItemController { }) } - // POST: api/items/:id/cover + /** + * POST: /api/items/:id/cover + * + * @param {RequestWithUser} req + * @param {Response} res + * @param {boolean} [updateAndReturnJson=true] + */ async uploadCover(req, res, updateAndReturnJson = true) { - if (!req.userNew.canUpload) { - Logger.warn(`User "${req.userNew.username}" attempted to upload a cover without permission`) + if (!req.user.canUpload) { + Logger.warn(`User "${req.user.username}" attempted to upload a cover without permission`) return res.sendStatus(403) } @@ -243,7 +253,12 @@ class LibraryItemController { } } - // PATCH: api/items/:id/cover + /** + * PATCH: /api/items/:id/cover + * + * @param {RequestWithUser} req + * @param {Response} res + */ async updateCover(req, res) { const libraryItem = req.libraryItem if (!req.body.cover) { @@ -264,7 +279,12 @@ class LibraryItemController { }) } - // DELETE: api/items/:id/cover + /** + * DELETE: /api/items/:id/cover + * + * @param {RequestWithUser} req + * @param {Response} res + */ async removeCover(req, res) { var libraryItem = req.libraryItem @@ -279,10 +299,10 @@ class LibraryItemController { } /** - * GET: api/items/:id/cover + * GET: /api/items/:id/cover * - * @param {import('express').Request} req - * @param {import('express').Response} res + * @param {RequestWithUser} req + * @param {Response} res */ async getCover(req, res) { const { @@ -308,7 +328,7 @@ class LibraryItemController { } // Check if user can access this library item - if (!req.userNew.checkCanAccessLibraryItem(libraryItem)) { + if (!req.user.checkCanAccessLibraryItem(libraryItem)) { return res.sendStatus(403) } @@ -377,7 +397,12 @@ class LibraryItemController { this.playbackSessionManager.startSessionRequest(req, res, episodeId) } - // PATCH: api/items/:id/tracks + /** + * PATCH: /api/items/:id/tracks + * + * @param {RequestWithUser} req + * @param {Response} res + */ async updateTracks(req, res) { var libraryItem = req.libraryItem var orderedFileData = req.body.orderedFileData @@ -391,7 +416,12 @@ class LibraryItemController { res.json(libraryItem.toJSON()) } - // POST api/items/:id/match + /** + * POST /api/items/:id/match + * + * @param {RequestWithUser} req + * @param {Response} res + */ async match(req, res) { var libraryItem = req.libraryItem @@ -406,12 +436,12 @@ class LibraryItemController { * Optional query params: * ?hard=1 * - * @param {import('express').Request} req - * @param {import('express').Response} res + * @param {RequestWithUser} req + * @param {Response} res */ async batchDelete(req, res) { - if (!req.userNew.canDelete) { - Logger.warn(`[LibraryItemController] User "${req.userNew.username}" attempted to delete without permission`) + if (!req.user.canDelete) { + Logger.warn(`[LibraryItemController] User "${req.user.username}" attempted to delete without permission`) return res.sendStatus(403) } const hardDelete = req.query.hard == 1 // Delete files from filesystem @@ -447,7 +477,12 @@ class LibraryItemController { res.sendStatus(200) } - // POST: api/items/batch/update + /** + * POST: /api/items/batch/update + * + * @param {RequestWithUser} req + * @param {Response} res + */ async batchUpdate(req, res) { const updatePayloads = req.body if (!updatePayloads?.length) { @@ -493,7 +528,12 @@ class LibraryItemController { }) } - // POST: api/items/batch/get + /** + * POST: /api/items/batch/get + * + * @param {RequestWithUser} req + * @param {Response} res + */ async batchGet(req, res) { const libraryItemIds = req.body.libraryItemIds || [] if (!libraryItemIds.length) { @@ -507,10 +547,15 @@ class LibraryItemController { }) } - // POST: api/items/batch/quickmatch + /** + * POST: /api/items/batch/quickmatch + * + * @param {RequestWithUser} req + * @param {Response} res + */ async batchQuickMatch(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.warn(`Non-admin user "${req.userNew.username}" other than admin attempted to batch quick match library items`) + if (!req.user.isAdminOrUp) { + Logger.warn(`Non-admin user "${req.user.username}" other than admin attempted to batch quick match library items`) return res.sendStatus(403) } @@ -545,13 +590,18 @@ class LibraryItemController { updates: itemsUpdated, unmatched: itemsUnmatched } - SocketAuthority.clientEmitter(req.userNew.id, 'batch_quickmatch_complete', result) + SocketAuthority.clientEmitter(req.user.id, 'batch_quickmatch_complete', result) } - // POST: api/items/batch/scan + /** + * POST: /api/items/batch/scan + * + * @param {RequestWithUser} req + * @param {Response} res + */ async batchScan(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.warn(`Non-admin user "${req.userNew.username}" other than admin attempted to batch scan library items`) + if (!req.user.isAdminOrUp) { + Logger.warn(`Non-admin user "${req.user.username}" other than admin attempted to batch scan library items`) return res.sendStatus(403) } @@ -583,10 +633,15 @@ class LibraryItemController { await Database.resetLibraryIssuesFilterData(libraryId) } - // POST: api/items/:id/scan + /** + * POST: /api/items/:id/scan + * + * @param {RequestWithUser} req + * @param {Response} res + */ async scan(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`[LibraryItemController] Non-admin user "${req.userNew.username}" attempted to scan library item`) + if (!req.user.isAdminOrUp) { + Logger.error(`[LibraryItemController] Non-admin user "${req.user.username}" attempted to scan library item`) return res.sendStatus(403) } @@ -602,9 +657,15 @@ class LibraryItemController { }) } + /** + * GET: /api/items/:id/metadata-object + * + * @param {RequestWithUser} req + * @param {Response} res + */ getMetadataObject(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`[LibraryItemController] Non-admin user "${req.userNew.username}" attempted to get metadata object`) + if (!req.user.isAdminOrUp) { + Logger.error(`[LibraryItemController] Non-admin user "${req.user.username}" attempted to get metadata object`) return res.sendStatus(403) } @@ -616,10 +677,15 @@ class LibraryItemController { res.json(this.audioMetadataManager.getMetadataObjectForApi(req.libraryItem)) } - // POST: api/items/:id/chapters + /** + * POST: /api/items/:id/chapters + * + * @param {RequestWithUser} req + * @param {Response} res + */ async updateMediaChapters(req, res) { - if (!req.userNew.canUpdate) { - Logger.error(`[LibraryItemController] User "${req.userNew.username}" attempted to update chapters with invalid permissions`) + if (!req.user.canUpdate) { + Logger.error(`[LibraryItemController] User "${req.user.username}" attempted to update chapters with invalid permissions`) return res.sendStatus(403) } @@ -647,15 +713,15 @@ class LibraryItemController { } /** - * GET api/items/:id/ffprobe/:fileid + * GET: /api/items/:id/ffprobe/:fileid * FFProbe JSON result from audio file * - * @param {express.Request} req - * @param {express.Response} res + * @param {RequestWithUser} req + * @param {Response} res */ async getFFprobeData(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`[LibraryItemController] Non-admin user "${req.userNew.username}" attempted to get ffprobe data`) + if (!req.user.isAdminOrUp) { + Logger.error(`[LibraryItemController] Non-admin user "${req.user.username}" attempted to get ffprobe data`) return res.sendStatus(403) } if (req.libraryFile.fileType !== 'audio') { @@ -676,8 +742,8 @@ class LibraryItemController { /** * GET api/items/:id/file/:fileid * - * @param {express.Request} req - * @param {express.Response} res + * @param {RequestWithUser} req + * @param {Response} res */ async getLibraryFile(req, res) { const libraryFile = req.libraryFile @@ -699,13 +765,13 @@ class LibraryItemController { /** * DELETE api/items/:id/file/:fileid * - * @param {express.Request} req - * @param {express.Response} res + * @param {RequestWithUser} req + * @param {Response} res */ async deleteLibraryFile(req, res) { const libraryFile = req.libraryFile - Logger.info(`[LibraryItemController] User "${req.userNew.username}" requested file delete at "${libraryFile.metadata.path}"`) + Logger.info(`[LibraryItemController] User "${req.user.username}" requested file delete at "${libraryFile.metadata.path}"`) await fs.remove(libraryFile.metadata.path).catch((error) => { Logger.error(`[LibraryItemController] Failed to delete library file at "${libraryFile.metadata.path}"`, error) @@ -727,18 +793,19 @@ class LibraryItemController { /** * GET api/items/:id/file/:fileid/download * Same as GET api/items/:id/file/:fileid but allows logging and restricting downloads - * @param {express.Request} req - * @param {express.Response} res + * + * @param {RequestWithUser} req + * @param {Response} res */ async downloadLibraryFile(req, res) { const libraryFile = req.libraryFile - if (!req.userNew.canDownload) { - Logger.error(`[LibraryItemController] User "${req.userNew.username}" without download permission attempted to download file "${libraryFile.metadata.path}"`) + if (!req.user.canDownload) { + Logger.error(`[LibraryItemController] User "${req.user.username}" without download permission attempted to download file "${libraryFile.metadata.path}"`) return res.sendStatus(403) } - Logger.info(`[LibraryItemController] User "${req.userNew.username}" requested download for item "${req.libraryItem.media.metadata.title}" file at "${libraryFile.metadata.path}"`) + Logger.info(`[LibraryItemController] User "${req.user.username}" requested download for item "${req.libraryItem.media.metadata.title}" file at "${libraryFile.metadata.path}"`) if (global.XAccel) { const encodedURI = encodeUriPath(global.XAccel + libraryFile.metadata.path) @@ -761,8 +828,8 @@ class LibraryItemController { * fileid is only required when reading a supplementary ebook * when no fileid is passed in the primary ebook will be returned * - * @param {express.Request} req - * @param {express.Response} res + * @param {RequestWithUser} req + * @param {Response} res */ async getEBookFile(req, res) { let ebookFile = null @@ -782,7 +849,7 @@ class LibraryItemController { } const ebookFilePath = ebookFile.metadata.path - Logger.info(`[LibraryItemController] User "${req.userNew.username}" requested download for item "${req.libraryItem.media.metadata.title}" ebook at "${ebookFilePath}"`) + Logger.info(`[LibraryItemController] User "${req.user.username}" requested download for item "${req.libraryItem.media.metadata.title}" ebook at "${ebookFilePath}"`) if (global.XAccel) { const encodedURI = encodeUriPath(global.XAccel + ebookFilePath) @@ -799,8 +866,8 @@ class LibraryItemController { * if an ebook file is the primary ebook, then it will be changed to supplementary * if an ebook file is supplementary, then it will be changed to primary * - * @param {express.Request} req - * @param {express.Response} res + * @param {RequestWithUser} req + * @param {Response} res */ async updateEbookFileStatus(req, res) { const ebookLibraryFile = req.libraryItem.libraryFiles.find((lf) => lf.ino === req.params.fileid) @@ -826,16 +893,16 @@ class LibraryItemController { /** * - * @param {import('express').Request} req - * @param {import('express').Response} res - * @param {import('express').NextFunction} next + * @param {RequestWithUser} req + * @param {Response} res + * @param {NextFunction} next */ async middleware(req, res, next) { req.libraryItem = await Database.libraryItemModel.getOldById(req.params.id) if (!req.libraryItem?.media) return res.sendStatus(404) // Check user can access this library item - if (!req.userNew.checkCanAccessLibraryItem(req.libraryItem)) { + if (!req.user.checkCanAccessLibraryItem(req.libraryItem)) { return res.sendStatus(403) } @@ -850,11 +917,11 @@ class LibraryItemController { if (req.path.includes('/play')) { // allow POST requests using /play and /play/:episodeId - } else if (req.method == 'DELETE' && !req.userNew.canDelete) { - Logger.warn(`[LibraryItemController] User "${req.userNew.username}" attempted to delete without permission`) + } else if (req.method == 'DELETE' && !req.user.canDelete) { + Logger.warn(`[LibraryItemController] User "${req.user.username}" attempted to delete without permission`) return res.sendStatus(403) - } else if ((req.method == 'PATCH' || req.method == 'POST') && !req.userNew.canUpdate) { - Logger.warn(`[LibraryItemController] User "${req.userNew.username}" attempted to update without permission`) + } else if ((req.method == 'PATCH' || req.method == 'POST') && !req.user.canUpdate) { + Logger.warn(`[LibraryItemController] User "${req.user.username}" attempted to update without permission`) return res.sendStatus(403) } diff --git a/server/controllers/MeController.js b/server/controllers/MeController.js index 1b883a30..905c728e 100644 --- a/server/controllers/MeController.js +++ b/server/controllers/MeController.js @@ -8,8 +8,7 @@ const userStats = require('../utils/queries/userStats') /** * @typedef RequestUserObjects - * @property {import('../models/User')} userNew - * @property {import('../objects/user/User')} user + * @property {import('../models/User')} user * * @typedef {Request & RequestUserObjects} RequestWithUser */ @@ -24,7 +23,7 @@ class MeController { * @param {Response} res */ getCurrentUser(req, res) { - res.json(req.userNew.toOldJSONForBrowser()) + res.json(req.user.toOldJSONForBrowser()) } /** @@ -36,7 +35,7 @@ class MeController { * @param {Response} res */ async getListeningSessions(req, res) { - const listeningSessions = await this.getUserListeningSessionsHelper(req.userNew.id) + const listeningSessions = await this.getUserListeningSessionsHelper(req.user.id) const itemsPerPage = toNumber(req.query.itemsPerPage, 10) || 10 const page = toNumber(req.query.page, 0) @@ -73,7 +72,7 @@ class MeController { } const mediaItemId = episode?.id || libraryItem.mediaId - let listeningSessions = await this.getUserItemListeningSessionsHelper(req.userNew.id, mediaItemId) + let listeningSessions = await this.getUserItemListeningSessionsHelper(req.user.id, mediaItemId) const itemsPerPage = toNumber(req.query.itemsPerPage, 10) || 10 const page = toNumber(req.query.page, 0) @@ -101,7 +100,7 @@ class MeController { * @param {Response} res */ async getListeningStats(req, res) { - const listeningStats = await this.getUserListeningStatsHelpers(req.userNew.id) + const listeningStats = await this.getUserListeningStatsHelpers(req.user.id) res.json(listeningStats) } @@ -112,7 +111,7 @@ class MeController { * @param {Response} res */ async getMediaProgress(req, res) { - const mediaProgress = req.userNew.getOldMediaProgress(req.params.id, req.params.episodeId || null) + const mediaProgress = req.user.getOldMediaProgress(req.params.id, req.params.episodeId || null) if (!mediaProgress) { return res.sendStatus(404) } @@ -127,9 +126,9 @@ class MeController { */ async removeMediaProgress(req, res) { await Database.mediaProgressModel.removeById(req.params.id) - req.userNew.mediaProgresses = req.userNew.mediaProgresses.filter((mp) => mp.id !== req.params.id) + req.user.mediaProgresses = req.user.mediaProgresses.filter((mp) => mp.id !== req.params.id) - SocketAuthority.clientEmitter(req.userNew.id, 'user_updated', req.userNew.toOldJSONForBrowser()) + SocketAuthority.clientEmitter(req.user.id, 'user_updated', req.user.toOldJSONForBrowser()) res.sendStatus(200) } @@ -146,12 +145,12 @@ class MeController { libraryItemId: req.params.libraryItemId, episodeId: req.params.episodeId } - const mediaProgressResponse = await req.userNew.createUpdateMediaProgressFromPayload(progressUpdatePayload) + const mediaProgressResponse = await req.user.createUpdateMediaProgressFromPayload(progressUpdatePayload) if (mediaProgressResponse.error) { return res.status(mediaProgressResponse.statusCode || 400).send(mediaProgressResponse.error) } - SocketAuthority.clientEmitter(req.userNew.id, 'user_updated', req.userNew.toOldJSONForBrowser()) + SocketAuthority.clientEmitter(req.user.id, 'user_updated', req.user.toOldJSONForBrowser()) res.sendStatus(200) } @@ -170,7 +169,7 @@ class MeController { let hasUpdated = false for (const itemProgress of itemProgressPayloads) { - const mediaProgressResponse = await req.userNew.createUpdateMediaProgressFromPayload(itemProgress) + const mediaProgressResponse = await req.user.createUpdateMediaProgressFromPayload(itemProgress) if (mediaProgressResponse.error) { Logger.error(`[MeController] batchUpdateMediaProgress: ${mediaProgressResponse.error}`) continue @@ -180,7 +179,7 @@ class MeController { } if (hasUpdated) { - SocketAuthority.clientEmitter(req.userNew.id, 'user_updated', req.userNew.toOldJSONForBrowser()) + SocketAuthority.clientEmitter(req.user.id, 'user_updated', req.user.toOldJSONForBrowser()) } res.sendStatus(200) @@ -205,8 +204,8 @@ class MeController { return res.status(400).send('Invalid title') } - const bookmark = await req.userNew.createBookmark(req.params.id, time, title) - SocketAuthority.clientEmitter(req.userNew.id, 'user_updated', req.userNew.toOldJSONForBrowser()) + const bookmark = await req.user.createBookmark(req.params.id, time, title) + SocketAuthority.clientEmitter(req.user.id, 'user_updated', req.user.toOldJSONForBrowser()) res.json(bookmark) } @@ -229,13 +228,13 @@ class MeController { return res.status(400).send('Invalid title') } - const bookmark = await req.userNew.updateBookmark(req.params.id, time, title) + const bookmark = await req.user.updateBookmark(req.params.id, time, title) if (!bookmark) { Logger.error(`[MeController] updateBookmark not found for library item id "${req.params.id}" and time "${time}"`) return res.sendStatus(404) } - SocketAuthority.clientEmitter(req.userNew.id, 'user_updated', req.userNew.toOldJSONForBrowser()) + SocketAuthority.clientEmitter(req.user.id, 'user_updated', req.user.toOldJSONForBrowser()) res.json(bookmark) } @@ -253,14 +252,14 @@ class MeController { return res.status(400).send('Invalid time') } - if (!req.userNew.findBookmark(req.params.id, time)) { + if (!req.user.findBookmark(req.params.id, time)) { Logger.error(`[MeController] removeBookmark not found`) return res.sendStatus(404) } - await req.userNew.removeBookmark(req.params.id, time) + await req.user.removeBookmark(req.params.id, time) - SocketAuthority.clientEmitter(req.userNew.id, 'user_updated', req.userNew.toOldJSONForBrowser()) + SocketAuthority.clientEmitter(req.user.id, 'user_updated', req.user.toOldJSONForBrowser()) res.sendStatus(200) } @@ -275,8 +274,8 @@ class MeController { * @param {Response} res */ updatePassword(req, res) { - if (req.userNew.isGuest) { - Logger.error(`[MeController] Guest user "${req.userNew.username}" attempted to change password`) + if (req.user.isGuest) { + Logger.error(`[MeController] Guest user "${req.user.username}" attempted to change password`) return res.sendStatus(500) } this.auth.userChangePassword(req, res) @@ -294,7 +293,7 @@ class MeController { async getAllLibraryItemsInProgress(req, res) { const limit = !isNaN(req.query.limit) ? Number(req.query.limit) || 25 : 25 - const mediaProgressesInProgress = req.userNew.mediaProgresses.filter((mp) => !mp.isFinished && (mp.currentTime > 0 || mp.ebookProgress > 0)) + const mediaProgressesInProgress = req.user.mediaProgresses.filter((mp) => !mp.isFinished && (mp.currentTime > 0 || mp.ebookProgress > 0)) const libraryItemsIds = [...new Set(mediaProgressesInProgress.map((mp) => mp.extraData?.libraryItemId).filter((id) => id))] const libraryItems = await Database.libraryItemModel.getAllOldLibraryItems({ id: libraryItemsIds }) @@ -344,11 +343,11 @@ class MeController { return res.sendStatus(404) } - const hasUpdated = await req.userNew.addSeriesToHideFromContinueListening(req.params.id) + const hasUpdated = await req.user.addSeriesToHideFromContinueListening(req.params.id) if (hasUpdated) { - SocketAuthority.clientEmitter(req.userNew.id, 'user_updated', req.userNew.toOldJSONForBrowser()) + SocketAuthority.clientEmitter(req.user.id, 'user_updated', req.user.toOldJSONForBrowser()) } - res.json(req.userNew.toOldJSONForBrowser()) + res.json(req.user.toOldJSONForBrowser()) } /** @@ -363,11 +362,11 @@ class MeController { return res.sendStatus(404) } - const hasUpdated = await req.userNew.removeSeriesFromHideFromContinueListening(req.params.id) + const hasUpdated = await req.user.removeSeriesFromHideFromContinueListening(req.params.id) if (hasUpdated) { - SocketAuthority.clientEmitter(req.userNew.id, 'user_updated', req.userNew.toOldJSONForBrowser()) + SocketAuthority.clientEmitter(req.user.id, 'user_updated', req.user.toOldJSONForBrowser()) } - res.json(req.userNew.toOldJSONForBrowser()) + res.json(req.user.toOldJSONForBrowser()) } /** @@ -377,22 +376,22 @@ class MeController { * @param {Response} res */ async removeItemFromContinueListening(req, res) { - const mediaProgress = req.userNew.mediaProgresses.find((mp) => mp.id === req.params.id) + const mediaProgress = req.user.mediaProgresses.find((mp) => mp.id === req.params.id) if (!mediaProgress) { return res.sendStatus(404) } // Already hidden if (mediaProgress.hideFromContinueListening) { - return res.json(req.userNew.toOldJSONForBrowser()) + return res.json(req.user.toOldJSONForBrowser()) } mediaProgress.hideFromContinueListening = true await mediaProgress.save() - SocketAuthority.clientEmitter(req.userNew.id, 'user_updated', req.userNew.toOldJSONForBrowser()) + SocketAuthority.clientEmitter(req.user.id, 'user_updated', req.user.toOldJSONForBrowser()) - res.json(req.userNew.toOldJSONForBrowser()) + res.json(req.user.toOldJSONForBrowser()) } /** @@ -407,7 +406,7 @@ class MeController { Logger.error(`[MeController] Invalid year "${year}"`) return res.status(400).send('Invalid year') } - const data = await userStats.getStatsForYear(req.userNew.id, year) + const data = await userStats.getStatsForYear(req.user.id, year) res.json(data) } } diff --git a/server/controllers/MiscController.js b/server/controllers/MiscController.js index de660e28..7093ab1f 100644 --- a/server/controllers/MiscController.js +++ b/server/controllers/MiscController.js @@ -16,8 +16,7 @@ const adminStats = require('../utils/queries/adminStats') /** * @typedef RequestUserObjects - * @property {import('../models/User')} userNew - * @property {import('../objects/user/User')} user + * @property {import('../models/User')} user * * @typedef {Request & RequestUserObjects} RequestWithUser */ @@ -33,8 +32,8 @@ class MiscController { * @param {Response} res */ async handleUpload(req, res) { - if (!req.userNew.canUpload) { - Logger.warn(`User "${req.userNew.username}" attempted to upload without permission`) + if (!req.user.canUpload) { + Logger.warn(`User "${req.user.username}" attempted to upload without permission`) return res.sendStatus(403) } if (!req.files) { @@ -118,8 +117,8 @@ class MiscController { * @param {Response} res */ async updateServerSettings(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`User "${req.userNew.username}" other than admin attempting to update server settings`) + if (!req.user.isAdminOrUp) { + Logger.error(`User "${req.user.username}" other than admin attempting to update server settings`) return res.sendStatus(403) } const settingsUpdate = req.body @@ -149,8 +148,8 @@ class MiscController { * @param {Response} res */ async updateSortingPrefixes(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`User "${req.userNew.username}" other than admin attempting to update server sorting prefixes`) + if (!req.user.isAdminOrUp) { + Logger.error(`User "${req.user.username}" other than admin attempting to update server sorting prefixes`) return res.sendStatus(403) } let sortingPrefixes = req.body.sortingPrefixes @@ -249,7 +248,7 @@ class MiscController { * @param {Response} res */ async authorize(req, res) { - const userResponse = await this.auth.getUserLoginResponsePayload(req.userNew) + const userResponse = await this.auth.getUserLoginResponsePayload(req.user) res.json(userResponse) } @@ -261,8 +260,8 @@ class MiscController { * @param {Response} res */ async getAllTags(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`[MiscController] Non-admin user "${req.userNew.username}" attempted to getAllTags`) + if (!req.user.isAdminOrUp) { + Logger.error(`[MiscController] Non-admin user "${req.user.username}" attempted to getAllTags`) return res.sendStatus(403) } @@ -305,8 +304,8 @@ class MiscController { * @param {Response} res */ async renameTag(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`[MiscController] Non-admin user "${req.userNew.username}" attempted to renameTag`) + if (!req.user.isAdminOrUp) { + Logger.error(`[MiscController] Non-admin user "${req.user.username}" attempted to renameTag`) return res.sendStatus(403) } @@ -360,8 +359,8 @@ class MiscController { * @param {Response} res */ async deleteTag(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`[MiscController] Non-admin user "${req.userNew.username}" attempted to deleteTag`) + if (!req.user.isAdminOrUp) { + Logger.error(`[MiscController] Non-admin user "${req.user.username}" attempted to deleteTag`) return res.sendStatus(403) } @@ -400,8 +399,8 @@ class MiscController { * @param {Response} res */ async getAllGenres(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`[MiscController] Non-admin user "${req.userNew.username}" attempted to getAllGenres`) + if (!req.user.isAdminOrUp) { + Logger.error(`[MiscController] Non-admin user "${req.user.username}" attempted to getAllGenres`) return res.sendStatus(403) } const genres = [] @@ -443,8 +442,8 @@ class MiscController { * @param {Response} res */ async renameGenre(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`[MiscController] Non-admin user "${req.userNew.username}" attempted to renameGenre`) + if (!req.user.isAdminOrUp) { + Logger.error(`[MiscController] Non-admin user "${req.user.username}" attempted to renameGenre`) return res.sendStatus(403) } @@ -498,8 +497,8 @@ class MiscController { * @param {Response} res */ async deleteGenre(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`[MiscController] Non-admin user "${req.userNew.username}" attempted to deleteGenre`) + if (!req.user.isAdminOrUp) { + Logger.error(`[MiscController] Non-admin user "${req.user.username}" attempted to deleteGenre`) return res.sendStatus(403) } @@ -543,8 +542,8 @@ class MiscController { * @param {Response} res */ updateWatchedPath(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`[MiscController] Non-admin user "${req.userNew.username}" attempted to updateWatchedPath`) + if (!req.user.isAdminOrUp) { + Logger.error(`[MiscController] Non-admin user "${req.user.username}" attempted to updateWatchedPath`) return res.sendStatus(403) } @@ -601,8 +600,8 @@ class MiscController { * @param {Response} res */ getAuthSettings(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`[MiscController] Non-admin user "${req.userNew.username}" attempted to get auth settings`) + if (!req.user.isAdminOrUp) { + Logger.error(`[MiscController] Non-admin user "${req.user.username}" attempted to get auth settings`) return res.sendStatus(403) } return res.json(Database.serverSettings.authenticationSettings) @@ -616,8 +615,8 @@ class MiscController { * @param {Response} res */ async updateAuthSettings(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`[MiscController] Non-admin user "${req.userNew.username}" attempted to update auth settings`) + if (!req.user.isAdminOrUp) { + Logger.error(`[MiscController] Non-admin user "${req.user.username}" attempted to update auth settings`) return res.sendStatus(403) } @@ -721,8 +720,8 @@ class MiscController { * @param {Response} res */ async getAdminStatsForYear(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`[MiscController] Non-admin user "${req.userNew.username}" attempted to get admin stats for year`) + if (!req.user.isAdminOrUp) { + Logger.error(`[MiscController] Non-admin user "${req.user.username}" attempted to get admin stats for year`) return res.sendStatus(403) } const year = Number(req.params.year) @@ -742,8 +741,8 @@ class MiscController { * @param {Response} res */ async getLoggerData(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`[MiscController] Non-admin user "${req.userNew.username}" attempted to get logger data`) + if (!req.user.isAdminOrUp) { + Logger.error(`[MiscController] Non-admin user "${req.user.username}" attempted to get logger data`) return res.sendStatus(403) } diff --git a/server/controllers/NotificationController.js b/server/controllers/NotificationController.js index fb1c0fe1..ff9fff27 100644 --- a/server/controllers/NotificationController.js +++ b/server/controllers/NotificationController.js @@ -4,8 +4,7 @@ const { version } = require('../../package.json') /** * @typedef RequestUserObjects - * @property {import('../models/User')} userNew - * @property {import('../objects/user/User')} user + * @property {import('../models/User')} user * * @typedef {Request & RequestUserObjects} RequestWithUser */ @@ -135,7 +134,7 @@ class NotificationController { * @param {NextFunction} next */ middleware(req, res, next) { - if (!req.userNew.isAdminOrUp) { + if (!req.user.isAdminOrUp) { return res.sendStatus(403) } diff --git a/server/controllers/PlaylistController.js b/server/controllers/PlaylistController.js index 9428bca0..476db122 100644 --- a/server/controllers/PlaylistController.js +++ b/server/controllers/PlaylistController.js @@ -7,8 +7,7 @@ const Playlist = require('../objects/Playlist') /** * @typedef RequestUserObjects - * @property {import('../models/User')} userNew - * @property {import('../objects/user/User')} user + * @property {import('../models/User')} user * * @typedef {Request & RequestUserObjects} RequestWithUser */ @@ -25,7 +24,7 @@ class PlaylistController { */ async create(req, res) { const oldPlaylist = new Playlist() - req.body.userId = req.userNew.id + req.body.userId = req.user.id const success = oldPlaylist.setData(req.body) if (!success) { return res.status(400).send('Invalid playlist request data') @@ -75,7 +74,7 @@ class PlaylistController { async findAllForUser(req, res) { const playlistsForUser = await Database.playlistModel.findAll({ where: { - userId: req.userNew.id + userId: req.user.id } }) const playlists = [] @@ -415,7 +414,7 @@ class PlaylistController { return res.status(404).send('Collection not found') } // Expand collection to get library items - const collectionExpanded = await collection.getOldJsonExpanded(req.userNew) + const collectionExpanded = await collection.getOldJsonExpanded(req.user) if (!collectionExpanded) { // This can happen if the user has no access to all items in collection return res.status(404).send('Collection not found') @@ -428,7 +427,7 @@ class PlaylistController { const oldPlaylist = new Playlist() oldPlaylist.setData({ - userId: req.userNew.id, + userId: req.user.id, libraryId: collection.libraryId, name: collection.name, description: collection.description || null @@ -467,8 +466,8 @@ class PlaylistController { if (!playlist) { return res.status(404).send('Playlist not found') } - if (playlist.userId !== req.userNew.id) { - Logger.warn(`[PlaylistController] Playlist ${req.params.id} requested by user ${req.userNew.id} that is not the owner`) + if (playlist.userId !== req.user.id) { + Logger.warn(`[PlaylistController] Playlist ${req.params.id} requested by user ${req.user.id} that is not the owner`) return res.sendStatus(403) } req.playlist = playlist diff --git a/server/controllers/PodcastController.js b/server/controllers/PodcastController.js index a7346546..032f372e 100644 --- a/server/controllers/PodcastController.js +++ b/server/controllers/PodcastController.js @@ -16,8 +16,7 @@ const LibraryItem = require('../objects/LibraryItem') /** * @typedef RequestUserObjects - * @property {import('../models/User')} userNew - * @property {import('../objects/user/User')} user + * @property {import('../models/User')} user * * @typedef {Request & RequestUserObjects} RequestWithUser */ @@ -33,8 +32,8 @@ class PodcastController { * @param {Response} res */ async create(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`[PodcastController] Non-admin user "${req.userNew.username}" attempted to create podcast`) + if (!req.user.isAdminOrUp) { + Logger.error(`[PodcastController] Non-admin user "${req.user.username}" attempted to create podcast`) return res.sendStatus(403) } const payload = req.body @@ -134,8 +133,8 @@ class PodcastController { * @param {Response} res */ async getPodcastFeed(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`[PodcastController] Non-admin user "${req.userNew.username}" attempted to get podcast feed`) + if (!req.user.isAdminOrUp) { + Logger.error(`[PodcastController] Non-admin user "${req.user.username}" attempted to get podcast feed`) return res.sendStatus(403) } @@ -160,8 +159,8 @@ class PodcastController { * @param {Response} res */ async getFeedsFromOPMLText(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`[PodcastController] Non-admin user "${req.userNew.username}" attempted to get feeds from opml`) + if (!req.user.isAdminOrUp) { + Logger.error(`[PodcastController] Non-admin user "${req.user.username}" attempted to get feeds from opml`) return res.sendStatus(403) } @@ -183,8 +182,8 @@ class PodcastController { * @param {Response} res */ async bulkCreatePodcastsFromOpmlFeedUrls(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`[PodcastController] Non-admin user "${req.userNew.username}" attempted to bulk create podcasts`) + if (!req.user.isAdminOrUp) { + Logger.error(`[PodcastController] Non-admin user "${req.user.username}" attempted to bulk create podcasts`) return res.sendStatus(403) } @@ -218,8 +217,8 @@ class PodcastController { * @param {Response} res */ async checkNewEpisodes(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`[PodcastController] Non-admin user "${req.userNew.username}" attempted to check/download episodes`) + if (!req.user.isAdminOrUp) { + Logger.error(`[PodcastController] Non-admin user "${req.user.username}" attempted to check/download episodes`) return res.sendStatus(403) } @@ -246,8 +245,8 @@ class PodcastController { * @param {Response} res */ clearEpisodeDownloadQueue(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`[PodcastController] Non-admin user "${req.userNew.username}" attempting to clear download queue`) + if (!req.user.isAdminOrUp) { + Logger.error(`[PodcastController] Non-admin user "${req.user.username}" attempting to clear download queue`) return res.sendStatus(403) } this.podcastManager.clearDownloadQueue(req.params.id) @@ -297,8 +296,8 @@ class PodcastController { * @param {Response} res */ async downloadEpisodes(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`[PodcastController] Non-admin user "${req.userNew.username}" attempted to download episodes`) + if (!req.user.isAdminOrUp) { + Logger.error(`[PodcastController] Non-admin user "${req.user.username}" attempted to download episodes`) return res.sendStatus(403) } const libraryItem = req.libraryItem @@ -320,8 +319,8 @@ class PodcastController { * @param {Response} res */ async quickMatchEpisodes(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`[PodcastController] Non-admin user "${req.userNew.username}" attempted to download episodes`) + if (!req.user.isAdminOrUp) { + Logger.error(`[PodcastController] Non-admin user "${req.user.username}" attempted to download episodes`) return res.sendStatus(403) } @@ -469,15 +468,15 @@ class PodcastController { } // Check user can access this library item - if (!req.userNew.checkCanAccessLibraryItem(item)) { + if (!req.user.checkCanAccessLibraryItem(item)) { return res.sendStatus(403) } - if (req.method == 'DELETE' && !req.userNew.canDelete) { - Logger.warn(`[PodcastController] User "${req.userNew.username}" attempted to delete without permission`) + if (req.method == 'DELETE' && !req.user.canDelete) { + Logger.warn(`[PodcastController] User "${req.user.username}" attempted to delete without permission`) return res.sendStatus(403) - } else if ((req.method == 'PATCH' || req.method == 'POST') && !req.userNew.canUpdate) { - Logger.warn(`[PodcastController] User "${req.userNew.username}" attempted to update without permission`) + } else if ((req.method == 'PATCH' || req.method == 'POST') && !req.user.canUpdate) { + Logger.warn(`[PodcastController] User "${req.user.username}" attempted to update without permission`) return res.sendStatus(403) } diff --git a/server/controllers/RSSFeedController.js b/server/controllers/RSSFeedController.js index ac79764f..4b243c63 100644 --- a/server/controllers/RSSFeedController.js +++ b/server/controllers/RSSFeedController.js @@ -5,8 +5,7 @@ const libraryItemsBookFilters = require('../utils/queries/libraryItemsBookFilter /** * @typedef RequestUserObjects - * @property {import('../models/User')} userNew - * @property {import('../objects/user/User')} user + * @property {import('../models/User')} user * * @typedef {Request & RequestUserObjects} RequestWithUser */ @@ -45,8 +44,8 @@ class RSSFeedController { if (!item) return res.sendStatus(404) // Check user can access this library item - if (!req.userNew.checkCanAccessLibraryItem(item)) { - Logger.error(`[RSSFeedController] User "${req.userNew.username}" attempted to open an RSS feed for item "${item.media.metadata.title}" that they don\'t have access to`) + if (!req.user.checkCanAccessLibraryItem(item)) { + Logger.error(`[RSSFeedController] User "${req.user.username}" attempted to open an RSS feed for item "${item.media.metadata.title}" that they don\'t have access to`) return res.sendStatus(403) } @@ -68,7 +67,7 @@ class RSSFeedController { return res.status(400).send('Slug already in use') } - const feed = await this.rssFeedManager.openFeedForItem(req.userNew.id, item, req.body) + const feed = await this.rssFeedManager.openFeedForItem(req.user.id, item, req.body) res.json({ feed: feed.toJSONMinified() }) @@ -109,7 +108,7 @@ class RSSFeedController { return res.status(400).send('Collection has no audio tracks') } - const feed = await this.rssFeedManager.openFeedForCollection(req.userNew.id, collectionExpanded, req.body) + const feed = await this.rssFeedManager.openFeedForCollection(req.user.id, collectionExpanded, req.body) res.json({ feed: feed.toJSONMinified() }) @@ -152,7 +151,7 @@ class RSSFeedController { return res.status(400).send('Series has no audio tracks') } - const feed = await this.rssFeedManager.openFeedForSeries(req.userNew.id, seriesJson, req.body) + const feed = await this.rssFeedManager.openFeedForSeries(req.user.id, seriesJson, req.body) res.json({ feed: feed.toJSONMinified() }) @@ -177,9 +176,9 @@ class RSSFeedController { * @param {NextFunction} next */ middleware(req, res, next) { - if (!req.userNew.isAdminOrUp) { + if (!req.user.isAdminOrUp) { // Only admins can manage rss feeds - Logger.error(`[RSSFeedController] Non-admin user "${req.userNew.username}" attempted to make a request to an RSS feed route`) + Logger.error(`[RSSFeedController] Non-admin user "${req.user.username}" attempted to make a request to an RSS feed route`) return res.sendStatus(403) } diff --git a/server/controllers/SearchController.js b/server/controllers/SearchController.js index 7317faf4..a9fee2ab 100644 --- a/server/controllers/SearchController.js +++ b/server/controllers/SearchController.js @@ -9,8 +9,7 @@ const { isValidASIN } = require('../utils') /** * @typedef RequestUserObjects - * @property {import('../models/User')} userNew - * @property {import('../objects/user/User')} user + * @property {import('../models/User')} user * * @typedef {Request & RequestUserObjects} RequestWithUser */ diff --git a/server/controllers/SeriesController.js b/server/controllers/SeriesController.js index a08af1e3..5d063129 100644 --- a/server/controllers/SeriesController.js +++ b/server/controllers/SeriesController.js @@ -6,8 +6,7 @@ const libraryItemsBookFilters = require('../utils/queries/libraryItemsBookFilter /** * @typedef RequestUserObjects - * @property {import('../models/User')} userNew - * @property {import('../objects/user/User')} user + * @property {import('../models/User')} user * * @typedef {Request & RequestUserObjects} RequestWithUser */ @@ -37,7 +36,7 @@ class SeriesController { if (include.includes('progress')) { const libraryItemsInSeries = req.libraryItemsInSeries const libraryItemsFinished = libraryItemsInSeries.filter((li) => { - return req.userNew.getMediaProgress(li.media.id)?.isFinished + return req.user.getMediaProgress(li.media.id)?.isFinished }) seriesJson.progress = { libraryItemIds: libraryItemsInSeries.map((li) => li.id), @@ -81,17 +80,17 @@ class SeriesController { /** * Filter out any library items not accessible to user */ - const libraryItems = await libraryItemsBookFilters.getLibraryItemsForSeries(series, req.userNew) + const libraryItems = await libraryItemsBookFilters.getLibraryItemsForSeries(series, req.user) if (!libraryItems.length) { - Logger.warn(`[SeriesController] User "${req.userNew.username}" attempted to access series "${series.id}" with no accessible books`) + Logger.warn(`[SeriesController] User "${req.user.username}" attempted to access series "${series.id}" with no accessible books`) return res.sendStatus(404) } - if (req.method == 'DELETE' && !req.userNew.canDelete) { - Logger.warn(`[SeriesController] User "${req.userNew.username}" attempted to delete without permission`) + if (req.method == 'DELETE' && !req.user.canDelete) { + Logger.warn(`[SeriesController] User "${req.user.username}" attempted to delete without permission`) return res.sendStatus(403) - } else if ((req.method == 'PATCH' || req.method == 'POST') && !req.userNew.canUpdate) { - Logger.warn(`[SeriesController] User "${req.userNew.username}" attempted to update without permission`) + } else if ((req.method == 'PATCH' || req.method == 'POST') && !req.user.canUpdate) { + Logger.warn(`[SeriesController] User "${req.user.username}" attempted to update without permission`) return res.sendStatus(403) } diff --git a/server/controllers/SessionController.js b/server/controllers/SessionController.js index 882528c9..011aa950 100644 --- a/server/controllers/SessionController.js +++ b/server/controllers/SessionController.js @@ -7,8 +7,7 @@ const ShareManager = require('../managers/ShareManager') /** * @typedef RequestUserObjects - * @property {import('../models/User')} userNew - * @property {import('../objects/user/User')} user + * @property {import('../models/User')} user * * @typedef {Request & RequestUserObjects} RequestWithUser */ @@ -25,8 +24,8 @@ class SessionController { * @param {Response} res */ async getAllWithUserData(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`[SessionController] getAllWithUserData: Non-admin user "${req.userNew.username}" requested all session data`) + if (!req.user.isAdminOrUp) { + Logger.error(`[SessionController] getAllWithUserData: Non-admin user "${req.user.username}" requested all session data`) return res.sendStatus(404) } // Validate "user" query @@ -120,8 +119,8 @@ class SessionController { * @param {Response} res */ async getOpenSessions(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`[SessionController] getOpenSessions: Non-admin user "${req.userNew.username}" requested open session data`) + if (!req.user.isAdminOrUp) { + Logger.error(`[SessionController] getOpenSessions: Non-admin user "${req.user.username}" requested open session data`) return res.sendStatus(404) } @@ -164,7 +163,7 @@ class SessionController { * @param {Response} res */ sync(req, res) { - this.playbackSessionManager.syncSessionRequest(req.userNew, req.playbackSession, req.body, res) + this.playbackSessionManager.syncSessionRequest(req.user, req.playbackSession, req.body, res) } /** @@ -178,7 +177,7 @@ class SessionController { close(req, res) { let syncData = req.body if (syncData && !Object.keys(syncData).length) syncData = null - this.playbackSessionManager.closeSessionRequest(req.userNew, req.playbackSession, syncData, res) + this.playbackSessionManager.closeSessionRequest(req.user, req.playbackSession, syncData, res) } /** @@ -211,8 +210,8 @@ class SessionController { * @param {Response} res */ async batchDelete(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`[SessionController] Non-admin user "${req.userNew.username}" attempted to batch delete sessions`) + if (!req.user.isAdminOrUp) { + Logger.error(`[SessionController] Non-admin user "${req.user.username}" attempted to batch delete sessions`) return res.sendStatus(403) } // Validate session ids @@ -235,7 +234,7 @@ class SessionController { id: req.body.sessions } }) - Logger.info(`[SessionController] ${sessionsRemoved} playback sessions removed by "${req.userNew.username}"`) + Logger.info(`[SessionController] ${sessionsRemoved} playback sessions removed by "${req.user.username}"`) res.sendStatus(200) } catch (error) { Logger.error(`[SessionController] Failed to remove playback sessions`, error) @@ -277,8 +276,8 @@ class SessionController { var playbackSession = this.playbackSessionManager.getSession(req.params.id) if (!playbackSession) return res.sendStatus(404) - if (playbackSession.userId !== req.userNew.id) { - Logger.error(`[SessionController] User "${req.userNew.username}" attempting to access session belonging to another user "${req.params.id}"`) + if (playbackSession.userId !== req.user.id) { + Logger.error(`[SessionController] User "${req.user.username}" attempting to access session belonging to another user "${req.params.id}"`) return res.sendStatus(404) } @@ -299,11 +298,11 @@ class SessionController { return res.sendStatus(404) } - if (req.method == 'DELETE' && !req.userNew.canDelete) { - Logger.warn(`[SessionController] User "${req.userNew.username}" attempted to delete without permission`) + if (req.method == 'DELETE' && !req.user.canDelete) { + Logger.warn(`[SessionController] User "${req.user.username}" attempted to delete without permission`) return res.sendStatus(403) - } else if ((req.method == 'PATCH' || req.method == 'POST') && !req.userNew.canUpdate) { - Logger.warn(`[SessionController] User "${req.userNew.username}" attempted to update without permission`) + } else if ((req.method == 'PATCH' || req.method == 'POST') && !req.user.canUpdate) { + Logger.warn(`[SessionController] User "${req.user.username}" attempted to update without permission`) return res.sendStatus(403) } diff --git a/server/controllers/ShareController.js b/server/controllers/ShareController.js index 08225b60..374acef2 100644 --- a/server/controllers/ShareController.js +++ b/server/controllers/ShareController.js @@ -13,8 +13,7 @@ const ShareManager = require('../managers/ShareManager') /** * @typedef RequestUserObjects - * @property {import('../models/User')} userNew - * @property {import('../objects/user/User')} user + * @property {import('../models/User')} user * * @typedef {Request & RequestUserObjects} RequestWithUser */ @@ -255,8 +254,8 @@ class ShareController { * @param {Response} res */ async createMediaItemShare(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`[ShareController] Non-admin user "${req.userNew.username}" attempted to create item share`) + if (!req.user.isAdminOrUp) { + Logger.error(`[ShareController] Non-admin user "${req.user.username}" attempted to create item share`) return res.sendStatus(403) } @@ -299,7 +298,7 @@ class ShareController { expiresAt: expiresAt || null, mediaItemId, mediaItemType, - userId: req.userNew.id + userId: req.user.id }) ShareManager.openMediaItemShare(mediaItemShare) @@ -319,8 +318,8 @@ class ShareController { * @param {Response} res */ async deleteMediaItemShare(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`[ShareController] Non-admin user "${req.userNew.username}" attempted to delete item share`) + if (!req.user.isAdminOrUp) { + Logger.error(`[ShareController] Non-admin user "${req.user.username}" attempted to delete item share`) return res.sendStatus(403) } diff --git a/server/controllers/ToolsController.js b/server/controllers/ToolsController.js index 54c55948..f3062c17 100644 --- a/server/controllers/ToolsController.js +++ b/server/controllers/ToolsController.js @@ -4,8 +4,7 @@ const Database = require('../Database') /** * @typedef RequestUserObjects - * @property {import('../models/User')} userNew - * @property {import('../objects/user/User')} user + * @property {import('../models/User')} user * * @typedef {Request & RequestUserObjects} RequestWithUser */ @@ -39,7 +38,7 @@ class ToolsController { } const options = req.query || {} - this.abMergeManager.startAudiobookMerge(req.userNew.id, req.libraryItem, options) + this.abMergeManager.startAudiobookMerge(req.user.id, req.libraryItem, options) res.sendStatus(200) } @@ -86,7 +85,7 @@ class ToolsController { forceEmbedChapters: req.query.forceEmbedChapters === '1', backup: req.query.backup === '1' } - this.audioMetadataManager.updateMetadataForItem(req.userNew.id, req.libraryItem, options) + this.audioMetadataManager.updateMetadataForItem(req.user.id, req.libraryItem, options) res.sendStatus(200) } @@ -114,8 +113,8 @@ class ToolsController { } // Check user can access this library item - if (!req.userNew.checkCanAccessLibraryItem(libraryItem)) { - Logger.error(`[ToolsController] Batch embed metadata library item (${libraryItemId}) not accessible to user "${req.userNew.username}"`) + if (!req.user.checkCanAccessLibraryItem(libraryItem)) { + Logger.error(`[ToolsController] Batch embed metadata library item (${libraryItemId}) not accessible to user "${req.user.username}"`) return res.sendStatus(403) } @@ -136,7 +135,7 @@ class ToolsController { forceEmbedChapters: req.query.forceEmbedChapters === '1', backup: req.query.backup === '1' } - this.audioMetadataManager.handleBatchEmbed(req.userNew.id, libraryItems, options) + this.audioMetadataManager.handleBatchEmbed(req.user.id, libraryItems, options) res.sendStatus(200) } @@ -147,8 +146,8 @@ class ToolsController { * @param {NextFunction} next */ async middleware(req, res, next) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`[LibraryItemController] Non-root user "${req.userNew.username}" attempted to access tools route`) + if (!req.user.isAdminOrUp) { + Logger.error(`[LibraryItemController] Non-root user "${req.user.username}" attempted to access tools route`) return res.sendStatus(403) } @@ -157,7 +156,7 @@ class ToolsController { if (!item?.media) return res.sendStatus(404) // Check user can access this library item - if (!req.userNew.checkCanAccessLibraryItem(item)) { + if (!req.user.checkCanAccessLibraryItem(item)) { return res.sendStatus(403) } diff --git a/server/controllers/UserController.js b/server/controllers/UserController.js index 2916de1b..777bddb8 100644 --- a/server/controllers/UserController.js +++ b/server/controllers/UserController.js @@ -10,14 +10,12 @@ const { toNumber } = require('../utils/index') /** * @typedef RequestUserObjects - * @property {import('../models/User')} userNew - * @property {import('../objects/user/User')} user + * @property {import('../models/User')} user * * @typedef {Request & RequestUserObjects} RequestWithUser * * @typedef UserControllerRequestProps - * @property {import('../models/User')} userNew - * @property {import('../objects/user/User')} user - User that made the request + * @property {import('../models/User')} user - User that made the request * @property {import('../objects/user/User')} [reqUser] - User for req param id * * @typedef {Request & UserControllerRequestProps} UserControllerRequest @@ -32,8 +30,8 @@ class UserController { * @param {Response} res */ async findAll(req, res) { - if (!req.userNew.isAdminOrUp) return res.sendStatus(403) - const hideRootToken = !req.userNew.isRoot + if (!req.user.isAdminOrUp) return res.sendStatus(403) + const hideRootToken = !req.user.isRoot const includes = (req.query.include || '').split(',').map((i) => i.trim()) @@ -62,8 +60,8 @@ class UserController { * @param {Response} res */ async findOne(req, res) { - if (!req.userNew.isAdminOrUp) { - Logger.error(`Non-admin user "${req.userNew.username}" attempted to get user`) + if (!req.user.isAdminOrUp) { + Logger.error(`Non-admin user "${req.user.username}" attempted to get user`) return res.sendStatus(403) } @@ -102,7 +100,7 @@ class UserController { return oldMediaProgress }) - const userJson = req.reqUser.toJSONForBrowser(!req.userNew.isRoot) + const userJson = req.reqUser.toJSONForBrowser(!req.user.isRoot) userJson.mediaProgress = oldMediaProgresses @@ -155,8 +153,8 @@ class UserController { async update(req, res) { const user = req.reqUser - if (user.type === 'root' && !req.userNew.isRoot) { - Logger.error(`[UserController] Admin user "${req.userNew.username}" attempted to update root user`) + if (user.type === 'root' && !req.user.isRoot) { + Logger.error(`[UserController] Admin user "${req.user.username}" attempted to update root user`) return res.sendStatus(403) } @@ -184,7 +182,7 @@ class UserController { Logger.info(`[UserController] User ${user.username} was generated a new api token`) } await Database.updateUser(user) - SocketAuthority.clientEmitter(req.userNew.id, 'user_updated', user.toJSONForBrowser()) + SocketAuthority.clientEmitter(req.user.id, 'user_updated', user.toJSONForBrowser()) } res.json({ @@ -205,8 +203,8 @@ class UserController { Logger.error('[UserController] Attempt to delete root user. Root user cannot be deleted') return res.sendStatus(400) } - if (req.userNew.id === req.params.id) { - Logger.error(`[UserController] User ${req.userNew.username} is attempting to delete self`) + if (req.user.id === req.params.id) { + Logger.error(`[UserController] User ${req.user.username} is attempting to delete self`) return res.sendStatus(400) } const user = req.reqUser @@ -241,7 +239,7 @@ class UserController { Logger.debug(`[UserController] Unlinking user "${req.reqUser.username}" from OpenID with sub "${req.reqUser.authOpenIDSub}"`) req.reqUser.authOpenIDSub = null if (await Database.userModel.updateFromOld(req.reqUser)) { - SocketAuthority.clientEmitter(req.userNew.id, 'user_updated', req.reqUser.toJSONForBrowser()) + SocketAuthority.clientEmitter(req.user.id, 'user_updated', req.reqUser.toJSONForBrowser()) res.sendStatus(200) } else { res.sendStatus(500) @@ -296,7 +294,7 @@ class UserController { * @param {Response} res */ async getOnlineUsers(req, res) { - if (!req.userNew.isAdminOrUp) { + if (!req.user.isAdminOrUp) { return res.sendStatus(403) } @@ -313,9 +311,9 @@ class UserController { * @param {NextFunction} next */ async middleware(req, res, next) { - if (!req.userNew.isAdminOrUp && req.userNew.id !== req.params.id) { + if (!req.user.isAdminOrUp && req.user.id !== req.params.id) { return res.sendStatus(403) - } else if ((req.method == 'PATCH' || req.method == 'POST' || req.method == 'DELETE') && !req.userNew.isAdminOrUp) { + } else if ((req.method == 'PATCH' || req.method == 'POST' || req.method == 'DELETE') && !req.user.isAdminOrUp) { return res.sendStatus(403) } diff --git a/server/managers/ApiCacheManager.js b/server/managers/ApiCacheManager.js index 3b425cb1..35009447 100644 --- a/server/managers/ApiCacheManager.js +++ b/server/managers/ApiCacheManager.js @@ -42,7 +42,7 @@ class ApiCacheManager { Logger.debug(`[ApiCacheManager] Skipping cache for random sort`) return next() } - const key = { user: req.userNew.username, url: req.url } + const key = { user: req.user.username, url: req.url } const stringifiedKey = JSON.stringify(key) Logger.debug(`[ApiCacheManager] count: ${this.cache.size} size: ${this.cache.calculatedSize}`) const cached = this.cache.get(stringifiedKey) diff --git a/server/managers/PlaybackSessionManager.js b/server/managers/PlaybackSessionManager.js index 81372ef1..cafd6ff4 100644 --- a/server/managers/PlaybackSessionManager.js +++ b/server/managers/PlaybackSessionManager.js @@ -48,7 +48,7 @@ class PlaybackSessionManager { const ip = requestIp.getClientIp(req) const deviceInfo = new DeviceInfo() - deviceInfo.setData(ip, ua, clientDeviceInfo, serverVersion, req.userNew?.id) + deviceInfo.setData(ip, ua, clientDeviceInfo, serverVersion, req.user?.id) if (clientDeviceInfo?.deviceId) { const existingDevice = await Database.getDeviceByDeviceId(clientDeviceInfo.deviceId) @@ -75,7 +75,7 @@ class PlaybackSessionManager { const deviceInfo = await this.getDeviceInfo(req, req.body?.deviceInfo) Logger.debug(`[PlaybackSessionManager] startSessionRequest for device ${deviceInfo.deviceDescription}`) const { libraryItem, body: options } = req - const session = await this.startSession(req.userNew, deviceInfo, libraryItem, episodeId, options) + const session = await this.startSession(req.user, deviceInfo, libraryItem, episodeId, options) res.json(session.toJSONForClient(libraryItem)) } @@ -96,7 +96,7 @@ class PlaybackSessionManager { async syncLocalSessionsRequest(req, res) { const deviceInfo = await this.getDeviceInfo(req, req.body?.deviceInfo) - const user = req.userNew + const user = req.user const sessions = req.body.sessions || [] const syncResults = [] @@ -239,7 +239,7 @@ class PlaybackSessionManager { async syncLocalSessionRequest(req, res) { const deviceInfo = await this.getDeviceInfo(req, req.body?.deviceInfo) const sessionJson = req.body - const result = await this.syncLocalSession(req.userNew, sessionJson, deviceInfo) + const result = await this.syncLocalSession(req.user, sessionJson, deviceInfo) if (result.error) { res.status(500).send(result.error) } else { diff --git a/server/models/User.js b/server/models/User.js index ce1a419b..9bd8caa8 100644 --- a/server/models/User.js +++ b/server/models/User.js @@ -2,7 +2,6 @@ const uuidv4 = require('uuid').v4 const sequelize = require('sequelize') const Logger = require('../Logger') const oldUser = require('../objects/user/User') -const AudioBookmark = require('../objects/user/AudioBookmark') const SocketAuthority = require('../SocketAuthority') const { isNullOrNaN } = require('../utils') @@ -52,6 +51,47 @@ class User extends Model { this.mediaProgresses } + /** + * List of expected permission properties from the client + * Only used for OpenID + */ + static permissionMapping = { + canDownload: 'download', + canUpload: 'upload', + canDelete: 'delete', + canUpdate: 'update', + canAccessExplicitContent: 'accessExplicitContent', + canAccessAllLibraries: 'accessAllLibraries', + canAccessAllTags: 'accessAllTags', + tagsAreDenylist: 'selectedTagsNotAccessible', + // Direct mapping for array-based permissions + allowedLibraries: 'librariesAccessible', + allowedTags: 'itemTagsSelected' + } + + /** + * Get a sample to show how a JSON for updatePermissionsFromExternalJSON should look like + * Only used for OpenID + * + * @returns {string} JSON string + */ + static getSampleAbsPermissions() { + // Start with a template object where all permissions are false for simplicity + const samplePermissions = Object.keys(User.permissionMapping).reduce((acc, key) => { + // For array-based permissions, provide a sample array + if (key === 'allowedLibraries') { + acc[key] = [`5406ba8a-16e1-451d-96d7-4931b0a0d966`, `918fd848-7c1d-4a02-818a-847435a879ca`] + } else if (key === 'allowedTags') { + acc[key] = [`ExampleTag`, `AnotherTag`, `ThirdTag`] + } else { + acc[key] = false + } + return acc + }, {}) + + return JSON.stringify(samplePermissions, null, 2) // Pretty print the JSON + } + /** * * @param {string} type @@ -818,6 +858,69 @@ class User extends Model { await this.save() return true } + + /** + * Update user permissions from external JSON + * + * @param {Object} absPermissions JSON containing user permissions + * @returns {Promise} true if updates were made + */ + async updatePermissionsFromExternalJSON(absPermissions) { + if (!this.permissions) this.permissions = {} + let hasUpdates = false + + // Map the boolean permissions from absPermissions + Object.keys(absPermissions).forEach((absKey) => { + const userPermKey = User.permissionMapping[absKey] + if (!userPermKey) { + throw new Error(`Unexpected permission property: ${absKey}`) + } + + if (!['librariesAccessible', 'itemTagsSelected'].includes(userPermKey)) { + if (this.permissions[userPermKey] !== !!absPermissions[absKey]) { + this.permissions[userPermKey] = !!absPermissions[absKey] + hasUpdates = true + } + } + }) + + // Handle allowedLibraries + const librariesAccessible = this.permissions.librariesAccessible || [] + if (this.permissions.accessAllLibraries) { + if (librariesAccessible.length) { + this.permissions.librariesAccessible = [] + hasUpdates = true + } + } else if (absPermissions.allowedLibraries?.length && absPermissions.allowedLibraries.join(',') !== librariesAccessible.join(',')) { + if (absPermissions.allowedLibraries.some((lid) => typeof lid !== 'string')) { + throw new Error('Invalid permission property "allowedLibraries", expecting array of strings') + } + this.permissions.librariesAccessible = absPermissions.allowedLibraries + hasUpdates = true + } + + // Handle allowedTags + const itemTagsSelected = this.permissions.itemTagsSelected || [] + if (this.permissions.accessAllTags) { + if (itemTagsSelected.length) { + this.permissions.itemTagsSelected = [] + hasUpdates = true + } + } else if (absPermissions.allowedTags?.length && absPermissions.allowedTags.join(',') !== itemTagsSelected.join(',')) { + if (absPermissions.allowedTags.some((tag) => typeof tag !== 'string')) { + throw new Error('Invalid permission property "allowedTags", expecting array of strings') + } + this.permissions.itemTagsSelected = absPermissions.allowedTags + hasUpdates = true + } + + if (hasUpdates) { + this.changed('permissions', true) + await this.save() + } + + return hasUpdates + } } module.exports = User diff --git a/server/objects/settings/ServerSettings.js b/server/objects/settings/ServerSettings.js index 6d070dcc..8ecb8ff0 100644 --- a/server/objects/settings/ServerSettings.js +++ b/server/objects/settings/ServerSettings.js @@ -2,7 +2,7 @@ const Path = require('path') const packageJson = require('../../../package.json') const { BookshelfView } = require('../../utils/constants') const Logger = require('../../Logger') -const User = require('../user/User') +const User = require('../../models/User') class ServerSettings { constructor(settings) { diff --git a/server/objects/user/User.js b/server/objects/user/User.js index 7608ca1b..f98451b0 100644 --- a/server/objects/user/User.js +++ b/server/objects/user/User.js @@ -1,4 +1,3 @@ -const Logger = require('../../Logger') const AudioBookmark = require('./AudioBookmark') const MediaProgress = require('./MediaProgress') @@ -268,109 +267,5 @@ class User { } return hasUpdates } - - // List of expected permission properties from the client - static permissionMapping = { - canDownload: 'download', - canUpload: 'upload', - canDelete: 'delete', - canUpdate: 'update', - canAccessExplicitContent: 'accessExplicitContent', - canAccessAllLibraries: 'accessAllLibraries', - canAccessAllTags: 'accessAllTags', - tagsAreDenylist: 'selectedTagsNotAccessible', - // Direct mapping for array-based permissions - allowedLibraries: 'librariesAccessible', - allowedTags: 'itemTagsSelected' - } - - /** - * Update user permissions from external JSON - * - * @param {Object} absPermissions JSON containing user permissions - * @returns {boolean} true if updates were made - */ - updatePermissionsFromExternalJSON(absPermissions) { - let hasUpdates = false - let updatedUserPermissions = {} - - // Initialize all permissions to false first - Object.keys(User.permissionMapping).forEach((mappingKey) => { - const userPermKey = User.permissionMapping[mappingKey] - if (typeof this.permissions[userPermKey] === 'boolean') { - updatedUserPermissions[userPermKey] = false // Default to false for boolean permissions - } - }) - - // Map the boolean permissions from absPermissions - Object.keys(absPermissions).forEach((absKey) => { - const userPermKey = User.permissionMapping[absKey] - if (!userPermKey) { - throw new Error(`Unexpected permission property: ${absKey}`) - } - - if (updatedUserPermissions[userPermKey] !== undefined) { - updatedUserPermissions[userPermKey] = !!absPermissions[absKey] - } - }) - - // Update user permissions if changes were made - if (JSON.stringify(this.permissions) !== JSON.stringify(updatedUserPermissions)) { - this.permissions = updatedUserPermissions - hasUpdates = true - } - - // Handle allowedLibraries - if (this.permissions.accessAllLibraries) { - if (this.librariesAccessible.length) { - this.librariesAccessible = [] - hasUpdates = true - } - } else if (absPermissions.allowedLibraries?.length && absPermissions.allowedLibraries.join(',') !== this.librariesAccessible.join(',')) { - if (absPermissions.allowedLibraries.some((lid) => typeof lid !== 'string')) { - throw new Error('Invalid permission property "allowedLibraries", expecting array of strings') - } - this.librariesAccessible = absPermissions.allowedLibraries - hasUpdates = true - } - - // Handle allowedTags - if (this.permissions.accessAllTags) { - if (this.itemTagsSelected.length) { - this.itemTagsSelected = [] - hasUpdates = true - } - } else if (absPermissions.allowedTags?.length && absPermissions.allowedTags.join(',') !== this.itemTagsSelected.join(',')) { - if (absPermissions.allowedTags.some((tag) => typeof tag !== 'string')) { - throw new Error('Invalid permission property "allowedTags", expecting array of strings') - } - this.itemTagsSelected = absPermissions.allowedTags - hasUpdates = true - } - - return hasUpdates - } - - /** - * Get a sample to show how a JSON for updatePermissionsFromExternalJSON should look like - * - * @returns {string} JSON string - */ - static getSampleAbsPermissions() { - // Start with a template object where all permissions are false for simplicity - const samplePermissions = Object.keys(User.permissionMapping).reduce((acc, key) => { - // For array-based permissions, provide a sample array - if (key === 'allowedLibraries') { - acc[key] = [`5406ba8a-16e1-451d-96d7-4931b0a0d966`, `918fd848-7c1d-4a02-818a-847435a879ca`] - } else if (key === 'allowedTags') { - acc[key] = [`ExampleTag`, `AnotherTag`, `ThirdTag`] - } else { - acc[key] = false - } - return acc - }, {}) - - return JSON.stringify(samplePermissions, null, 2) // Pretty print the JSON - } } module.exports = User diff --git a/test/server/managers/ApiCacheManager.test.js b/test/server/managers/ApiCacheManager.test.js index 4185f45b..19bbeecf 100644 --- a/test/server/managers/ApiCacheManager.test.js +++ b/test/server/managers/ApiCacheManager.test.js @@ -12,7 +12,7 @@ describe('ApiCacheManager', () => { beforeEach(() => { cache = { get: sinon.stub(), set: sinon.spy() } - req = { user: { username: 'testUser' }, userNew: { username: 'testUser' }, url: '/test-url', query: {} } + req = { user: { username: 'testUser' }, url: '/test-url', query: {} } res = { send: sinon.spy(), getHeaders: sinon.stub(), statusCode: 200, status: sinon.spy(), set: sinon.spy() } next = sinon.spy() })