From 2dbf3b97d57a14734d1e1e26b7f9c9abda94d58b Mon Sep 17 00:00:00 2001 From: rainbownapkin Date: Mon, 2 Dec 2024 20:33:18 -0500 Subject: [PATCH] Added more granular permissions. --- .../api/admin/permissionsController.js | 1 - .../api/channel/deleteController.js | 1 - src/controllers/api/channel/rankController.js | 1 - src/routers/api/adminRouter.js | 19 ++--- src/routers/api/channelRouter.js | 34 ++++---- .../channel/channelPermissionSchema.js | 30 +++++++ src/schemas/channel/channelSchema.js | 83 ++++++++++--------- src/schemas/flairSchema.js | 1 - src/schemas/permissionSchema.js | 26 +++++- src/validators/permissionsValidator.js | 56 ++++++++++++- www/js/channelSettings.js | 2 + 11 files changed, 179 insertions(+), 75 deletions(-) diff --git a/src/controllers/api/admin/permissionsController.js b/src/controllers/api/admin/permissionsController.js index 86a60f5..2442139 100644 --- a/src/controllers/api/admin/permissionsController.js +++ b/src/controllers/api/admin/permissionsController.js @@ -98,7 +98,6 @@ module.exports.post = async function(req, res){ res.send({errors: validResult.array()}) } }catch(err){ - console.log(err); return exceptionHandler(res, err); } } \ No newline at end of file diff --git a/src/controllers/api/channel/deleteController.js b/src/controllers/api/channel/deleteController.js index 6a0b5b4..3d96fbb 100644 --- a/src/controllers/api/channel/deleteController.js +++ b/src/controllers/api/channel/deleteController.js @@ -41,7 +41,6 @@ module.exports.post = async function(req, res){ res.send({errors: validResult.array()}) } }catch(err){ - console.log(err); exceptionHandler(res, err); } diff --git a/src/controllers/api/channel/rankController.js b/src/controllers/api/channel/rankController.js index 5f9eb9c..be7641b 100644 --- a/src/controllers/api/channel/rankController.js +++ b/src/controllers/api/channel/rankController.js @@ -107,7 +107,6 @@ module.exports.post = async function(req, res){ res.send({errors: validResult.array()}) } }catch(err){ - console.log(err); return exceptionHandler(res, err); } } \ No newline at end of file diff --git a/src/routers/api/adminRouter.js b/src/routers/api/adminRouter.js index 74a92a9..1a0e254 100644 --- a/src/routers/api/adminRouter.js +++ b/src/routers/api/adminRouter.js @@ -32,18 +32,15 @@ const banController = require("../../controllers/api/admin/banController"); //globals const router = Router(); -//Use authentication middleware -router.use(permissionSchema.reqPermCheck("adminAPI")); - //routing functions -router.get('/listUsers', listUsersController.get); -router.get('/listChannels', listChannelsController.get); -router.get('/permissions', permissionsController.get); -router.post('/permissions', checkExact([permissionsValidator.permissionsMap(), channelPermissionValidator.channelPermissionsMap()]), permissionsController.post); -router.post('/changeRank', accountValidator.user(), accountValidator.rank(), changeRankController.post); -router.get('/ban', banController.get); +router.get('/listUsers', permissionSchema.reqPermCheck("adminPanel"), listUsersController.get); +router.get('/listChannels', permissionSchema.reqPermCheck("adminPanel"), listChannelsController.get); +router.get('/permissions', permissionSchema.reqPermCheck("adminPanel"), permissionsController.get); +router.post('/permissions', permissionSchema.reqPermCheck("changePerms"), checkExact([permissionsValidator.permissionsMap(), channelPermissionValidator.channelPermissionsMap()]), permissionsController.post); +router.post('/changeRank', permissionSchema.reqPermCheck("changeRank"), accountValidator.user(), accountValidator.rank(), changeRankController.post); +router.get('/ban', permissionSchema.reqPermCheck("adminPanel"), banController.get); //Sometimes they're so simple you don't need to put your validators in their own special place :P -router.post('/ban', accountValidator.user(), body("permanent").isBoolean(), body("expirationDays").isInt(), banController.post); -router.delete('/ban', accountValidator.user(), banController.delete); +router.post('/ban', permissionSchema.reqPermCheck("banUser"), accountValidator.user(), body("permanent").isBoolean(), body("expirationDays").isInt(), banController.post); +router.delete('/ban', permissionSchema.reqPermCheck("banUser"), accountValidator.user(), banController.delete); module.exports = router; diff --git a/src/routers/api/channelRouter.js b/src/routers/api/channelRouter.js index edb10cb..88f85b9 100644 --- a/src/routers/api/channelRouter.js +++ b/src/routers/api/channelRouter.js @@ -15,7 +15,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see .*/ //npm imports -const { body } = require('express-validator'); +const { body, checkExact } = require('express-validator'); const { Router } = require('express'); //local imports @@ -37,31 +37,31 @@ const router = Router(); //user authentication middleware router.use("/register",permissionSchema.reqPermCheck("registerChannel")); -router.use("/settings", channelValidator.name('chanName'), channelModel.reqPermCheck("manageChannel")); -router.use("/permissions", channelValidator.name('chanName'), channelModel.reqPermCheck("manageChannel")); -router.use("/rank", channelValidator.name('chanName'), channelModel.reqPermCheck("manageChannel")); -router.use("/delete", channelValidator.name('chanName'), channelModel.reqPermCheck("deleteChannel")); -router.use("/ban", channelValidator.name('chanName'), channelModel.reqPermCheck("manageChannel")); +router.use("/settings", channelValidator.name('chanName')); +router.use("/permissions", channelValidator.name('chanName')); +router.use("/rank", channelValidator.name('chanName')); +router.use("/delete", channelValidator.name('chanName')); +router.use("/ban", channelValidator.name('chanName')); //routing functions //register router.post('/register', channelValidator.name(), channelValidator.description(), channelValidator.thumbnail(), registerController.post); //list -router.get('/list', listController.get); +router.get('/list', channelModel.reqPermCheck("manageChannel"), listController.get); //settings -router.get('/settings', settingsController.get); -router.post('/settings', channelValidator.settingsMap(), settingsController.post); +router.get('/settings', channelModel.reqPermCheck("manageChannel"), settingsController.get); +router.post('/settings', channelModel.reqPermCheck("changeSettings"), channelValidator.settingsMap(), settingsController.post); //permissions -router.get('/permissions', permissionsController.get); -router.post('/permissions', channelPermissionValidator.channelPermissionsMap(), permissionsController.post); +router.get('/permissions', channelModel.reqPermCheck("manageChannel"), permissionsController.get); +router.post('/permissions', channelModel.reqPermCheck("changePerms"), checkExact(channelPermissionValidator.channelPermissionsMap()), permissionsController.post); //rank -router.get('/rank', rankController.get); -router.post('/rank', accountValidator.user(), channelValidator.rank(), rankController.post); +router.get('/rank', channelModel.reqPermCheck("manageChannel"), rankController.get); +router.post('/rank', channelModel.reqPermCheck("changeRank"), accountValidator.user(), channelValidator.rank(), rankController.post); //delete -router.post('/delete', channelValidator.name('confirm'), deleteController.post); +router.post('/delete', channelModel.reqPermCheck("deleteChannel"), channelValidator.name('confirm'), deleteController.post); //ban -router.get('/ban', banController.get); -router.post('/ban', accountValidator.user(), body("banAlts").isBoolean(), body("expirationDays").isInt(), banController.post); -router.delete('/ban', accountValidator.user(), banController.delete); +router.get('/ban', channelModel.reqPermCheck("manageChannel"), banController.get); +router.post('/ban', channelModel.reqPermCheck("banUser"), accountValidator.user(), body("banAlts").isBoolean(), body("expirationDays").isInt(), banController.post); +router.delete('/ban', channelModel.reqPermCheck("banUser"), accountValidator.user(), banController.delete); module.exports = router; \ No newline at end of file diff --git a/src/schemas/channel/channelPermissionSchema.js b/src/schemas/channel/channelPermissionSchema.js index 4b15a6f..e65fd1b 100644 --- a/src/schemas/channel/channelPermissionSchema.js +++ b/src/schemas/channel/channelPermissionSchema.js @@ -28,6 +28,36 @@ const channelPermissionSchema = new mongoose.Schema({ default: "admin", required: true }, + changeRank: { + type: mongoose.SchemaTypes.String, + enum: rankEnum, + default: "admin", + required: true + }, + changePerms: { + type: mongoose.SchemaTypes.String, + enum: rankEnum, + default: "admin", + required: true + }, + changeSettings: { + type: mongoose.SchemaTypes.String, + enum: rankEnum, + default: "admin", + required: true + }, + kickUser: { + type: mongoose.SchemaTypes.String, + enum: rankEnum, + default: "admin", + required: true + }, + banUser: { + type: mongoose.SchemaTypes.String, + enum: rankEnum, + default: "admin", + required: true + }, deleteChannel: { type: mongoose.SchemaTypes.String, enum: rankEnum, diff --git a/src/schemas/channel/channelSchema.js b/src/schemas/channel/channelSchema.js index 244189b..8641e9f 100644 --- a/src/schemas/channel/channelSchema.js +++ b/src/schemas/channel/channelSchema.js @@ -84,55 +84,56 @@ channelSchema.pre('save', async function (next){ //Getting the affected user would be a million times easier elsewhere //But this ensures it happens every time channel rank gets changed no matter what - if(this.isModified('rankList')){ + if(this.isModified('rankList') && this.rankList != null){ //Get the rank list before it was modified (gross but works, find a better way if you dont like it :P) var chanDB = await module.exports.findOne({_id: this._id}); //Create empty variable for the found rank object var foundRank = null; - - //If we're removing one - if(chanDB.rankList.length > this.rankList.length){ - //Child/Parent is *WAY* to atomic family for my tastes :P - var top = chanDB; - var bottom = this; - }else{ - //otherwise reverse the loops - var top = this; - var bottom = chanDB; - } - - //Populate the top doc - await top.populate('rankList.user'); - - - top.rankList.forEach((topObj) => { - //Create empty variable for the matched rank - var matchedRank = null; - //For each rank in the old copy of the rank list - bottom.rankList.forEach((bottomObj) => { - if(topObj.user._id.toString() == bottomObj.user._id.toString()){ - matchedRank = bottomObj; - } - }); - - - if(matchedRank == null || matchedRank.rank != topObj.rank){ - foundRank = topObj; + if(chanDB != null){ + //If we're removing one + if(chanDB.rankList.length > this.rankList.length){ + //Child/Parent is *WAY* to atomic family for my tastes :P + var top = chanDB; + var bottom = this; + }else{ + //otherwise reverse the loops + var top = this; + var bottom = chanDB; } - }); + //Populate the top doc + await top.populate('rankList.user'); - //get relevant active channel - const activeChan = server.channelManager.activeChannels.get(this.name); - //if the channel is online - if(activeChan != null){ - //Get the relevant user connection - const userConn = activeChan.userList.get(foundRank.user.user); - //if the user is online - if(userConn != null){ - //kick the user - userConn.disconnect("Your channel rank has changed!"); + top.rankList.forEach((topObj) => { + //Create empty variable for the matched rank + var matchedRank = null; + //For each rank in the old copy of the rank list + bottom.rankList.forEach((bottomObj) => { + if(topObj.user._id.toString() == bottomObj.user._id.toString()){ + matchedRank = bottomObj; + } + }); + + + if(matchedRank == null || matchedRank.rank != topObj.rank){ + foundRank = topObj; + } + + }); + + //get relevant active channel + const activeChan = server.channelManager.activeChannels.get(this.name); + + //if the channel is online + if(activeChan != null){ + //Get the relevant user connection + const userConn = activeChan.userList.get(foundRank.user.user); + //if the user is online + if(userConn != null){ + //kick the user + userConn.disconnect("Your channel rank has changed!"); + } } } } diff --git a/src/schemas/flairSchema.js b/src/schemas/flairSchema.js index f8981ed..76fb0ac 100644 --- a/src/schemas/flairSchema.js +++ b/src/schemas/flairSchema.js @@ -58,7 +58,6 @@ flairSchema.statics.loadDefaults = async function(){ }else{ console.log("Error, null flair:"); } - console.log(err); } }); } diff --git a/src/schemas/permissionSchema.js b/src/schemas/permissionSchema.js index 95958ba..f5e59c7 100644 --- a/src/schemas/permissionSchema.js +++ b/src/schemas/permissionSchema.js @@ -31,7 +31,31 @@ const permissionSchema = new mongoose.Schema({ default: "admin", required: true }, - adminAPI: { + changeRank: { + type: mongoose.SchemaTypes.String, + enum: rankEnum, + default: "admin", + required: true + }, + changePerms: { + type: mongoose.SchemaTypes.String, + enum: rankEnum, + default: "admin", + required: true + }, + banUser: { + type: mongoose.SchemaTypes.String, + enum: rankEnum, + default: "admin", + required: true + }, + nukeUser: { + type: mongoose.SchemaTypes.String, + enum: rankEnum, + default: "admin", + required: true + }, + genPasswordReset: { type: mongoose.SchemaTypes.String, enum: rankEnum, default: "admin", diff --git a/src/validators/permissionsValidator.js b/src/validators/permissionsValidator.js index 24b8b8f..c2d609d 100644 --- a/src/validators/permissionsValidator.js +++ b/src/validators/permissionsValidator.js @@ -34,7 +34,31 @@ module.exports.permissionsValidator = { options: module.exports.isRank }, }, - 'permissionsMap.adminAPI': { + 'permissionsMap.changeRank': { + optional: true, + custom: { + options: module.exports.isRank + }, + }, + 'permissionsMap.changePerms': { + optional: true, + custom: { + options: module.exports.isRank + }, + }, + 'permissionsMap.banUser': { + optional: true, + custom: { + options: module.exports.isRank + }, + }, + 'permissionsMap.nukeUser': { + optional: true, + custom: { + options: module.exports.isRank + }, + }, + 'permissionsMap.genPasswordReset': { optional: true, custom: { options: module.exports.isRank @@ -57,6 +81,36 @@ module.exports.channelPermissionValidator = { options: module.exports.isRank }, }, + 'channelPermissionsMap.changeRank': { + optional: true, + custom: { + options: module.exports.isRank + }, + }, + 'channelPermissionsMap.changePerms': { + optional: true, + custom: { + options: module.exports.isRank + }, + }, + 'channelPermissionsMap.changeSettings': { + optional: true, + custom: { + options: module.exports.isRank + }, + }, + 'channelPermissionsMap.kickUser': { + optional: true, + custom: { + options: module.exports.isRank + }, + }, + 'channelPermissionsMap.banUser': { + optional: true, + custom: { + options: module.exports.isRank + }, + }, 'channelPermissionsMap.deleteChannel': { optional: true, custom: { diff --git a/www/js/channelSettings.js b/www/js/channelSettings.js index 228c318..8d862fc 100644 --- a/www/js/channelSettings.js +++ b/www/js/channelSettings.js @@ -275,6 +275,8 @@ class prefrenceList{ constructor(channel){ this.channel = channel; this.inputs = document.querySelectorAll(".channel-preference-list-item"); + + this.setupInput(); } setupInput(){