From 8ec4bd4279971576de641666c9ecf13c2f74321a Mon Sep 17 00:00:00 2001 From: advplyr Date: Wed, 31 Aug 2022 15:46:10 -0500 Subject: [PATCH] Fix:User permissions for collection API routes and UI #951 --- client/components/cards/LazyBookCard.vue | 8 +-- .../components/cards/LazyCollectionCard.vue | 5 +- .../components/modals/EditCollectionModal.vue | 5 +- .../tables/collection/BookTableRow.vue | 17 +++++- client/pages/collection/_id.vue | 10 +++- client/pages/item/_id/index.vue | 2 +- server/controllers/CollectionController.js | 57 +++++++++---------- server/routers/ApiRouter.js | 16 +++--- 8 files changed, 70 insertions(+), 50 deletions(-) diff --git a/client/components/cards/LazyBookCard.vue b/client/components/cards/LazyBookCard.vue index c715074b..7a2bc369 100644 --- a/client/components/cards/LazyBookCard.vue +++ b/client/components/cards/LazyBookCard.vue @@ -386,14 +386,14 @@ export default { { func: 'toggleFinished', text: `Mark as ${this.itemIsFinished ? 'Not Finished' : 'Finished'}` - }, - { - func: 'openCollections', - text: 'Add to Collection' } ] } if (this.userCanUpdate) { + items.push({ + func: 'openCollections', + text: 'Add to Collection' + }) items.push({ func: 'showEditModalFiles', text: 'Files' diff --git a/client/components/cards/LazyCollectionCard.vue b/client/components/cards/LazyCollectionCard.vue index ffcd8510..686890b4 100644 --- a/client/components/cards/LazyCollectionCard.vue +++ b/client/components/cards/LazyCollectionCard.vue @@ -4,7 +4,7 @@
-
+
edit
@@ -69,6 +69,9 @@ export default { isAlternativeBookshelfView() { const constants = this.$constants || this.$nuxt.$constants return this.bookshelfView == constants.BookshelfView.TITLES + }, + userCanUpdate() { + return this.store.getters['user/getUserCanUpdate'] } }, methods: { diff --git a/client/components/modals/EditCollectionModal.vue b/client/components/modals/EditCollectionModal.vue index 69cea658..1e9eeb3a 100644 --- a/client/components/modals/EditCollectionModal.vue +++ b/client/components/modals/EditCollectionModal.vue @@ -20,7 +20,7 @@
- Remove + Remove
Save
@@ -85,6 +85,9 @@ export default { }, books() { return this.collection.books || [] + }, + userCanDelete() { + return this.$store.getters['user/getUserCanDelete'] } }, methods: { diff --git a/client/components/tables/collection/BookTableRow.vue b/client/components/tables/collection/BookTableRow.vue index c4203741..e212aa2c 100644 --- a/client/components/tables/collection/BookTableRow.vue +++ b/client/components/tables/collection/BookTableRow.vue @@ -27,15 +27,15 @@ radio_button_unchecked
--> -
+
-
+
-
+
@@ -71,6 +71,11 @@ export default { } }, computed: { + translateDistance() { + if (!this.userCanUpdate && !this.userCanDelete) return 'translate-x-0' + else if (!this.userCanUpdate || !this.userCanDelete) return '-translate-x-12' + return '-translate-x-24' + }, media() { return this.book.media || {} }, @@ -113,6 +118,12 @@ export default { coverWidth() { if (this.bookCoverAspectRatio === 1) return this.coverSize * 1.6 return this.coverSize + }, + userCanUpdate() { + return this.$store.getters['user/getUserCanUpdate'] + }, + userCanDelete() { + return this.$store.getters['user/getUserCanDelete'] } }, methods: { diff --git a/client/pages/collection/_id.vue b/client/pages/collection/_id.vue index d55cb049..31bddca0 100644 --- a/client/pages/collection/_id.vue +++ b/client/pages/collection/_id.vue @@ -19,9 +19,9 @@ {{ streaming ? 'Streaming' : 'Play' }} - + - +
@@ -92,6 +92,12 @@ export default { }, showPlayButton() { return this.playableBooks.length + }, + userCanUpdate() { + return this.$store.getters['user/getUserCanUpdate'] + }, + userCanDelete() { + return this.$store.getters['user/getUserCanDelete'] } }, methods: { diff --git a/client/pages/item/_id/index.vue b/client/pages/item/_id/index.vue index fe4e6f01..03c52696 100644 --- a/client/pages/item/_id/index.vue +++ b/client/pages/item/_id/index.vue @@ -150,7 +150,7 @@ - + diff --git a/server/controllers/CollectionController.js b/server/controllers/CollectionController.js index 5d14530a..d3ae4917 100644 --- a/server/controllers/CollectionController.js +++ b/server/controllers/CollectionController.js @@ -24,18 +24,11 @@ class CollectionController { } findOne(req, res) { - var collection = this.db.collections.find(c => c.id === req.params.id) - if (!collection) { - return res.status(404).send('Collection not found') - } - res.json(collection.toJSONExpanded(this.db.libraryItems)) + res.json(req.collection.toJSONExpanded(this.db.libraryItems)) } async update(req, res) { - var collection = this.db.collections.find(c => c.id === req.params.id) - if (!collection) { - return res.status(404).send('Collection not found') - } + const collection = req.collection var wasUpdated = collection.update(req.body) var jsonExpanded = collection.toJSONExpanded(this.db.libraryItems) if (wasUpdated) { @@ -46,10 +39,7 @@ class CollectionController { } async delete(req, res) { - var collection = this.db.collections.find(c => c.id === req.params.id) - if (!collection) { - return res.status(404).send('Collection not found') - } + const collection = req.collection var jsonExpanded = collection.toJSONExpanded(this.db.libraryItems) await this.db.removeEntity('collection', collection.id) this.emitter('collection_removed', jsonExpanded) @@ -57,10 +47,7 @@ class CollectionController { } async addBook(req, res) { - var collection = this.db.collections.find(c => c.id === req.params.id) - if (!collection) { - return res.status(404).send('Collection not found') - } + const collection = req.collection var libraryItem = this.db.libraryItems.find(li => li.id === req.body.id) if (!libraryItem) { return res.status(500).send('Book not found') @@ -80,11 +67,7 @@ class CollectionController { // DELETE: api/collections/:id/book/:bookId async removeBook(req, res) { - var collection = this.db.collections.find(c => c.id === req.params.id) - if (!collection) { - return res.status(404).send('Collection not found') - } - + const collection = req.collection if (collection.books.includes(req.params.bookId)) { collection.removeBook(req.params.bookId) var jsonExpanded = collection.toJSONExpanded(this.db.libraryItems) @@ -96,10 +79,7 @@ class CollectionController { // POST: api/collections/:id/batch/add async addBatch(req, res) { - var collection = this.db.collections.find(c => c.id === req.params.id) - if (!collection) { - return res.status(404).send('Collection not found') - } + const collection = req.collection if (!req.body.books || !req.body.books.length) { return res.status(500).send('Invalid request body') } @@ -120,10 +100,7 @@ class CollectionController { // POST: api/collections/:id/batch/remove async removeBatch(req, res) { - var collection = this.db.collections.find(c => c.id === req.params.id) - if (!collection) { - return res.status(404).send('Collection not found') - } + const collection = req.collection if (!req.body.books || !req.body.books.length) { return res.status(500).send('Invalid request body') } @@ -141,5 +118,25 @@ class CollectionController { } res.json(collection.toJSONExpanded(this.db.libraryItems)) } + + middleware(req, res, next) { + if (req.params.id) { + var collection = this.db.collections.find(c => c.id === req.params.id) + if (!collection) { + return res.status(404).send('Collection not found') + } + req.collection = collection + } + + if (req.method == 'DELETE' && !req.user.canDelete) { + Logger.warn(`[CollectionController] User attempted to delete without permission`, req.user.username) + return res.sendStatus(403) + } else if ((req.method == 'PATCH' || req.method == 'POST') && !req.user.canUpdate) { + Logger.warn('[CollectionController] User attempted to update without permission', req.user.username) + return res.sendStatus(403) + } + + next() + } } module.exports = new CollectionController() \ No newline at end of file diff --git a/server/routers/ApiRouter.js b/server/routers/ApiRouter.js index 2ac09cef..2ad782f0 100644 --- a/server/routers/ApiRouter.js +++ b/server/routers/ApiRouter.js @@ -116,16 +116,16 @@ class ApiRouter { // // Collection Routes // - this.router.post('/collections', CollectionController.create.bind(this)) + this.router.post('/collections', CollectionController.middleware.bind(this), CollectionController.create.bind(this)) this.router.get('/collections', CollectionController.findAll.bind(this)) - this.router.get('/collections/:id', CollectionController.findOne.bind(this)) - this.router.patch('/collections/:id', CollectionController.update.bind(this)) - this.router.delete('/collections/:id', CollectionController.delete.bind(this)) + this.router.get('/collections/:id', CollectionController.middleware.bind(this), CollectionController.findOne.bind(this)) + this.router.patch('/collections/:id', CollectionController.middleware.bind(this), CollectionController.update.bind(this)) + this.router.delete('/collections/:id', CollectionController.middleware.bind(this), CollectionController.delete.bind(this)) - this.router.post('/collections/:id/book', CollectionController.addBook.bind(this)) - this.router.delete('/collections/:id/book/:bookId', CollectionController.removeBook.bind(this)) - this.router.post('/collections/:id/batch/add', CollectionController.addBatch.bind(this)) - this.router.post('/collections/:id/batch/remove', CollectionController.removeBatch.bind(this)) + this.router.post('/collections/:id/book', CollectionController.middleware.bind(this), CollectionController.addBook.bind(this)) + this.router.delete('/collections/:id/book/:bookId', CollectionController.middleware.bind(this), CollectionController.removeBook.bind(this)) + this.router.post('/collections/:id/batch/add', CollectionController.middleware.bind(this), CollectionController.addBatch.bind(this)) + this.router.post('/collections/:id/batch/remove', CollectionController.middleware.bind(this), CollectionController.removeBatch.bind(this)) // // Current User Routes (Me)