diff --git a/server/Auth.js b/server/Auth.js index 416289c0..d27d3844 100644 --- a/server/Auth.js +++ b/server/Auth.js @@ -109,7 +109,7 @@ class Auth { Logger.error('JWT Verify Token Failed', err) return resolve(null) } - var user = this.users.find(u => u.id === payload.userId && u.username === payload.username) + const user = this.users.find(u => u.id === payload.userId && u.username === payload.username) resolve(user || null) }) }) diff --git a/server/SocketAuthority.js b/server/SocketAuthority.js index 5488cf74..bf6ea6f7 100644 --- a/server/SocketAuthority.js +++ b/server/SocketAuthority.js @@ -31,9 +31,13 @@ class SocketAuthority { } // Emits event to all authorized clients - emitter(evt, data) { + // optional filter function to only send event to specific users + // TODO: validate that filter is actually a function + emitter(evt, data, filter = null) { for (const socketId in this.clients) { if (this.clients[socketId].user) { + if (filter && !filter(this.clients[socketId].user)) continue + this.clients[socketId].socket.emit(evt, data) } } diff --git a/server/controllers/LibraryController.js b/server/controllers/LibraryController.js index f23b6f9d..817f9709 100644 --- a/server/controllers/LibraryController.js +++ b/server/controllers/LibraryController.js @@ -13,7 +13,7 @@ class LibraryController { constructor() { } async create(req, res) { - var newLibraryPayload = { + const newLibraryPayload = { ...req.body } if (!newLibraryPayload.name || !newLibraryPayload.folders || !newLibraryPayload.folders.length) { @@ -26,9 +26,9 @@ class LibraryController { f.fullPath = Path.resolve(f.fullPath) return f }) - for (var folder of newLibraryPayload.folders) { + for (const folder of newLibraryPayload.folders) { try { - var direxists = await fs.pathExists(folder.fullPath) + const direxists = await fs.pathExists(folder.fullPath) if (!direxists) { // If folder does not exist try to make it and set file permissions/owner await fs.mkdir(folder.fullPath) await filePerms.setDefault(folder.fullPath) @@ -39,12 +39,16 @@ class LibraryController { } } - var library = new Library() + const library = new Library() newLibraryPayload.displayOrder = this.db.libraries.length + 1 library.setData(newLibraryPayload) await this.db.insertEntity('library', library) - // TODO: Only emit to users that have access - SocketAuthority.emitter('library_added', library.toJSON()) + + // Only emit to users with access to library + const userFilter = (user) => { + return user.checkCanAccessLibrary && user.checkCanAccessLibrary(library.id) + } + SocketAuthority.emitter('library_added', library.toJSON(), userFilter) // Add library watcher this.watcher.addLibrary(library) @@ -53,7 +57,7 @@ class LibraryController { } findAll(req, res) { - var librariesAccessible = req.user.librariesAccessible || [] + const librariesAccessible = req.user.librariesAccessible || [] if (librariesAccessible && librariesAccessible.length) { return res.json(this.db.libraries.filter(lib => librariesAccessible.includes(lib.id)).map(lib => lib.toJSON())) } @@ -75,12 +79,12 @@ class LibraryController { } async update(req, res) { - var library = req.library + const library = req.library // Validate new folder paths exist or can be created & resolve rel paths // returns 400 if a new folder fails to access if (req.body.folders) { - var newFolderPaths = [] + const newFolderPaths = [] req.body.folders = req.body.folders.map(f => { if (!f.id) { f.fullPath = Path.resolve(f.fullPath) @@ -88,11 +92,11 @@ class LibraryController { } return f }) - for (var path of newFolderPaths) { - var pathExists = await fs.pathExists(path) + for (const path of newFolderPaths) { + const pathExists = await fs.pathExists(path) if (!pathExists) { // Ensure dir will recursively create directories which might be preferred over mkdir - var success = await fs.ensureDir(path).then(() => true).catch((error) => { + const success = await fs.ensureDir(path).then(() => true).catch((error) => { Logger.error(`[LibraryController] Failed to ensure folder dir "${path}"`, error) return false }) @@ -105,7 +109,7 @@ class LibraryController { } } - var hasUpdates = library.update(req.body) + const hasUpdates = library.update(req.body) // TODO: Should check if this is an update to folder paths or name only if (hasUpdates) { // Update watcher @@ -115,7 +119,7 @@ class LibraryController { this.cronManager.updateLibraryScanCron(library) // Remove libraryItems no longer in library - var itemsToRemove = this.db.libraryItems.filter(li => li.libraryId === library.id && !library.checkFullPathInLibrary(li.path)) + const itemsToRemove = this.db.libraryItems.filter(li => li.libraryId === library.id && !library.checkFullPathInLibrary(li.path)) if (itemsToRemove.length) { Logger.info(`[Scanner] Updating library, removing ${itemsToRemove.length} items`) for (let i = 0; i < itemsToRemove.length; i++) { @@ -123,32 +127,37 @@ class LibraryController { } } await this.db.updateEntity('library', library) - SocketAuthority.emitter('library_updated', library.toJSON()) + + // Only emit to users with access to library + const userFilter = (user) => { + return user.checkCanAccessLibrary && user.checkCanAccessLibrary(library.id) + } + SocketAuthority.emitter('library_updated', library.toJSON(), userFilter) } return res.json(library.toJSON()) } async delete(req, res) { - var library = req.library + const library = req.library // Remove library watcher this.watcher.removeLibrary(library) // Remove collections for library - var collections = this.db.collections.filter(c => c.libraryId === library.id) + const collections = this.db.collections.filter(c => c.libraryId === library.id) for (const collection of collections) { Logger.info(`[Server] deleting collection "${collection.name}" for library "${library.name}"`) await this.db.removeEntity('collection', collection.id) } // Remove items in this library - var libraryItems = this.db.libraryItems.filter(li => li.libraryId === library.id) + const libraryItems = this.db.libraryItems.filter(li => li.libraryId === library.id) Logger.info(`[Server] deleting library "${library.name}" with ${libraryItems.length} items"`) for (let i = 0; i < libraryItems.length; i++) { await this.handleDeleteLibraryItem(libraryItems[i]) } - var libraryJson = library.toJSON() + const libraryJson = library.toJSON() await this.db.removeEntity('library', library.id) SocketAuthority.emitter('library_removed', libraryJson) return res.json(libraryJson) @@ -170,10 +179,10 @@ class LibraryController { minified: req.query.minified === '1', collapseseries: req.query.collapseseries === '1' } - var mediaIsBook = payload.mediaType === 'book' + const mediaIsBook = payload.mediaType === 'book' // Step 1 - Filter the retrieved library items - var filterSeries = null + let filterSeries = null if (payload.filterBy) { libraryItems = libraryHelpers.getFilteredLibraryItems(libraryItems, payload.filterBy, req.user, this.rssFeedManager.feedsArray) payload.total = libraryItems.length @@ -216,7 +225,7 @@ class LibraryController { if (payload.sortBy) { // old sort key TODO: should be mutated in dbMigration - var sortKey = payload.sortBy + let sortKey = payload.sortBy if (sortKey.startsWith('book.')) { sortKey = sortKey.replace('book.', 'media.metadata.') } @@ -246,7 +255,7 @@ class LibraryController { } // Sort series based on the sortBy attribute - var direction = payload.sortDesc ? 'desc' : 'asc' + const direction = payload.sortDesc ? 'desc' : 'asc' sortArray.push({ [direction]: (li) => { if (mediaIsBook && sortBySequence) { @@ -332,7 +341,7 @@ class LibraryController { } async removeLibraryItemsWithIssues(req, res) { - var libraryItemsWithIssues = req.libraryItems.filter(li => li.hasIssues) + const libraryItemsWithIssues = req.libraryItems.filter(li => li.hasIssues) if (!libraryItemsWithIssues.length) { Logger.warn(`[LibraryController] No library items have issues`) return res.sendStatus(200) @@ -349,8 +358,8 @@ class LibraryController { // api/libraries/:id/series async getAllSeriesForLibrary(req, res) { - var libraryItems = req.libraryItems - var payload = { + const libraryItems = req.libraryItems + const payload = { results: [], total: 0, limit: req.query.limit && !isNaN(req.query.limit) ? Number(req.query.limit) : 0, @@ -361,7 +370,7 @@ class LibraryController { minified: req.query.minified === '1' } - var series = libraryHelpers.getSeriesFromBooks(libraryItems, this.db.series, null, payload.filterBy, req.user, payload.minified) + let series = libraryHelpers.getSeriesFromBooks(libraryItems, this.db.series, null, payload.filterBy, req.user, payload.minified) const direction = payload.sortDesc ? 'desc' : 'asc' series = naturalSort(series).by([