Add permissions

This commit is contained in:
fserres 2024-09-30 11:24:12 -04:00
parent cfee8a213a
commit 0a82574eb4
13 changed files with 135 additions and 27 deletions

27
server/config/roles.json Normal file
View file

@ -0,0 +1,27 @@
{
"roles": [
{
"name": "admin",
"permissions": [
"crud_quiz",
"crud_folders",
"crud_images",
"crud_user"
]
},
{
"name": "teacher",
"permissions": [
"crud_quiz",
"crud_folders",
"crud_images"
]
},
{
"name": "student",
"permissions": [
"participate_quiz"
]
}
]
}

View file

@ -7,6 +7,11 @@ exports.UNAUTHORIZED_INVALID_TOKEN = {
code: 401
}
exports.UNAUTHORIZED_PERMISSION_MISSING = {
message: 'Accès refusé. Vous n\'avez pas les permissions nécessaires.',
code: 403
}
exports.MISSING_REQUIRED_PARAMETER = {
message: 'Paramètre requis manquant.',
code: 400

View file

@ -9,13 +9,13 @@ class UsersController {
async register(req, res, next) {
try {
const { email, password } = req.body;
const { email, password, role } = req.body;
if (!email || !password) {
throw new AppError(MISSING_REQUIRED_PARAMETER);
}
await model.register(email, password);
await model.register(email, password, role);
emailer.registerConfirmation(email)
@ -43,11 +43,12 @@ class UsersController {
throw new AppError(LOGIN_CREDENTIALS_ERROR);
}
const token = jwt.create(user.email, user._id);
const token = jwt.create(user.email, user._id, user.role);
return res.status(200).json({
token: token,
id: user.email
id: user.email,
role: user.role
});
}

View file

@ -7,8 +7,8 @@ dotenv.config();
class Token {
create(email, userId) {
return jwt.sign({ email, userId }, process.env.JWT_SECRET);
create(email, userId, role) {
return jwt.sign({ email, userId, role }, process.env.JWT_SECRET);
}
authenticate(req, res, next) {

23
server/middleware/rbac.js Normal file
View file

@ -0,0 +1,23 @@
const Permissions = require('../models/permissions');
const AppError = require("./AppError");
const { UNAUTHORIZED_PERMISSION_MISSING} = require("../constants/errorCodes");
const jwt = require("jsonwebtoken");
class Rbac {
checkPermission = (...permissions) => {
return (req, res, next) => {
const userRole = req.user ? req.user.role : 'anonymous';
const userPermissions = Permissions.getPermissionsByRoleName(userRole);
for (let permission of permissions) {
if (!userPermissions.includes(permission)) {
return next(new AppError(UNAUTHORIZED_PERMISSION_MISSING));
}
}
return next();
};
};
}
module.exports = new Rbac;

View file

@ -0,0 +1,13 @@
const Role = require('../models/role');
class Permissions {
constructor() {
this.permissions = [];
}
getPermissionsByRoleName(roleName) {
const role = Role.getRoleByName(roleName);
return role ? role.permissions : [];
}
}
module.exports = new Permissions;

30
server/models/role.js Normal file
View file

@ -0,0 +1,30 @@
const roles = require('../config/roles.json');
class Role {
constructor() {
this.roles = roles.roles;
}
getRoleByName(name) {
return this.convertCrud(this.roles.find((role) => role.name === name));
}
getRoles() {
return this.roles;
}
convertCrud(role) {
for (let permission of role.permissions) {
if (permission.startsWith("crud")) {
let resourceName = permission.split("_");
role.permissions.push("create_" + resourceName[1]);
role.permissions.push("read_" + resourceName[1]);
role.permissions.push("update_" + resourceName[1]);
role.permissions.push("delete_" + resourceName[1]);
}
}
return role;
}
}
module.exports = new Role;

View file

@ -19,7 +19,7 @@ class Users {
return await bcrypt.compare(password, hash)
}
async register(email, password) {
async register(email, password, role) {
await db.connect()
const conn = db.getConnection();
@ -34,6 +34,7 @@ class Users {
const newUser = {
email: email,
password: await this.hashPassword(password),
role: role,
created_at: new Date()
};

View file

@ -3,16 +3,16 @@ const router = express.Router();
const jwt = require('../middleware/jwtToken.js');
const foldersController = require('../controllers/folders.js')
const rbac = require("../middleware/rbac");
router.post("/create", jwt.authenticate, foldersController.create);
router.get("/getUserFolders", jwt.authenticate, foldersController.getUserFolders);
router.get("/getFolderContent/:folderId", jwt.authenticate, foldersController.getFolderContent);
router.delete("/delete/:folderId", jwt.authenticate, foldersController.delete);
router.put("/rename", jwt.authenticate, foldersController.rename);
router.post("/create", jwt.authenticate, rbac.checkPermission('create_folders'), foldersController.create);
router.get("/getUserFolders", jwt.authenticate, rbac.checkPermission('read_folders'), foldersController.getUserFolders);
router.get("/getFolderContent/:folderId", jwt.authenticate, rbac.checkPermission('read_folders'), foldersController.getFolderContent);
router.delete("/delete/:folderId", jwt.authenticate, rbac.checkPermission('delete_folders'), foldersController.delete);
router.put("/rename", jwt.authenticate, rbac.checkPermission('update_folders'), foldersController.rename);
//router.post("/duplicate", jwt.authenticate, foldersController.duplicate);
router.post("/duplicate", jwt.authenticate, foldersController.duplicate);
router.post("/duplicate", jwt.authenticate, rbac.checkPermission('create_folders', 'read_folders'), foldersController.duplicate);
router.post("/copy/:folderId", jwt.authenticate, foldersController.copy);
router.post("/copy/:folderId", jwt.authenticate, rbac.checkPermission('create_folders', 'read_folders'), foldersController.copy);
module.exports = router;

View file

@ -6,10 +6,11 @@ const imagesController = require('../controllers/images.js')
// For getting the image out of the form data
const multer = require('multer');
const rbac = require("../middleware/rbac");
const storage = multer.memoryStorage();
const upload = multer({ storage: storage });
router.post("/upload", jwt.authenticate, upload.single('image'), imagesController.upload);
router.post("/upload", jwt.authenticate, rbac.checkPermission('create_images'), upload.single('image'), imagesController.upload);
router.get("/get/:id", imagesController.get);
module.exports = router;

View file

@ -3,17 +3,18 @@ const router = express.Router();
const jwt = require('../middleware/jwtToken.js');
const quizController = require('../controllers/quiz.js')
const rbac = require('../middleware/rbac.js');
router.post("/create", jwt.authenticate, quizController.create);
router.get("/get/:quizId", jwt.authenticate, quizController.get);
router.delete("/delete/:quizId", jwt.authenticate, quizController.delete);
router.put("/update", jwt.authenticate, quizController.update);
router.put("/move", jwt.authenticate, quizController.move);
router.post("/create", jwt.authenticate, rbac.checkPermission('create_quiz'), quizController.create);
router.get("/get/:quizId", jwt.authenticate, rbac.checkPermission('participate_quiz'), quizController.get);
router.delete("/delete/:quizId", jwt.authenticate, rbac.checkPermission('delete_quiz'), quizController.delete);
router.put("/update", jwt.authenticate, rbac.checkPermission('update_quiz'), quizController.update);
router.put("/move", jwt.authenticate, rbac.checkPermission('update_quiz'), quizController.move);
router.post("/duplicate", jwt.authenticate, quizController.duplicate);
router.post("/copy/:quizId", jwt.authenticate, quizController.copy);
router.put("/Share", jwt.authenticate, quizController.Share);
router.get("/getShare/:quizId", jwt.authenticate, quizController.getShare);
router.post("/receiveShare", jwt.authenticate, quizController.receiveShare);
router.post("/duplicate", jwt.authenticate, rbac.checkPermission('create_quiz', 'read_quiz'), quizController.duplicate);
router.post("/copy/:quizId", jwt.authenticate, rbac.checkPermission('create_quiz', 'read_quiz'), quizController.copy);
router.put("/Share", jwt.authenticate, rbac.checkPermission('create_quiz', 'read_quiz'), quizController.Share);
router.get("/getShare/:quizId", jwt.authenticate, rbac.checkPermission('create_quiz', 'read_quiz'), quizController.getShare);
router.post("/receiveShare", jwt.authenticate, rbac.checkPermission('create_quiz', 'read_quiz'), quizController.receiveShare);
module.exports = router;

View file

@ -3,11 +3,12 @@ const router = express.Router();
const jwt = require('../middleware/jwtToken.js');
const usersController = require('../controllers/users.js')
const rbac = require("../middleware/rbac");
router.post("/register", usersController.register);
router.post("/login", usersController.login);
router.post("/reset-password", usersController.resetPassword);
router.post("/change-password", jwt.authenticate, usersController.changePassword);
router.post("/delete-user", jwt.authenticate, usersController.delete);
router.post("/delete-user", jwt.authenticate, rbac.checkPermission('delete_user'), usersController.delete);
module.exports = router;

View file

@ -5,6 +5,7 @@ const setupWebsocket = (io) => {
let totalConnections = 0;
io.on("connection", (socket) => {
// Get jwt and roles
if (totalConnections >= MAX_TOTAL_CONNECTIONS) {
console.log("Connection limit reached. Disconnecting client.");
socket.emit(
@ -24,6 +25,10 @@ const setupWebsocket = (io) => {
);
socket.on("create-room", (sentRoomName) => {
// If roles authorize else send error message
// else {
// socket.emit('access_denied', 'You do not have permission to perform this action');
// }
if (sentRoomName) {
const roomName = sentRoomName.toUpperCase();
if (!io.sockets.adapter.rooms.get(roomName)) {