diff --git a/client/src/pages/Teacher/Dashboard/Dashboard.tsx b/client/src/pages/Teacher/Dashboard/Dashboard.tsx index 7c0bff5..2faacbe 100644 --- a/client/src/pages/Teacher/Dashboard/Dashboard.tsx +++ b/client/src/pages/Teacher/Dashboard/Dashboard.tsx @@ -357,8 +357,9 @@ const Dashboard: React.FC = () => { } } - - + if (!ApiService.isTeacher()) { + return (
); + } return ( diff --git a/client/src/pages/Teacher/EditorQuiz/EditorQuiz.tsx b/client/src/pages/Teacher/EditorQuiz/EditorQuiz.tsx index 15ceeb2..8313280 100644 --- a/client/src/pages/Teacher/EditorQuiz/EditorQuiz.tsx +++ b/client/src/pages/Teacher/EditorQuiz/EditorQuiz.tsx @@ -171,6 +171,10 @@ const QuizForm: React.FC = () => { navigator.clipboard.writeText(link); } + if (!ApiService.isTeacher()) { + return (
); + } + return (
diff --git a/client/src/services/ApiService.tsx b/client/src/services/ApiService.tsx index 55dccb7..90d8d20 100644 --- a/client/src/services/ApiService.tsx +++ b/client/src/services/ApiService.tsx @@ -76,6 +76,22 @@ class ApiService { return true; } + public isTeacher(): boolean { + const token = this.getToken() + + if (token == null) { + return false; + } + + let user = JSON.parse(atob(token.split('.')[1])) + + if (user.role == "teacher") { + return true; + } else { + return false; + } + } + public logout(): void { return localStorage.removeItem("jwt"); } diff --git a/server/auth/modules/passport-providers/oauth.js b/server/auth/modules/passport-providers/oauth.js index a38dc44..5ca2f39 100644 --- a/server/auth/modules/passport-providers/oauth.js +++ b/server/auth/modules/passport-providers/oauth.js @@ -1,4 +1,5 @@ var OAuth2Strategy = require('passport-oauth2') +const model = require("../../../models/users"); class PassportOAuth { register(app, passport,endpoint, name, provider) { @@ -17,6 +18,15 @@ class PassportOAuth { headers: { 'Authorization': `Bearer ${accessToken}` } }); const userInfo = await userInfoResponse.json(); + let role; + + if (userInfo.groups.includes(provider.OAUTH_ROLE_TEACHER_VALUE)) { + role = "teacher"; + } else if (userInfo.groups.includes(provider.OAUTH_ROLE_STUDENT_VALUE)) { + role = "student"; + } else { + role = "anonymous"; + } const user = { id: userInfo.sub, @@ -25,7 +35,8 @@ class PassportOAuth { groups: userInfo.groups ?? [], accessToken: accessToken, refreshToken: refreshToken, - expiresIn: params.expires_in + expiresIn: params.expires_in, + role: role }; // Store the tokens in the session diff --git a/server/auth/modules/passport-providers/oidc.js b/server/auth/modules/passport-providers/oidc.js index 7f7ffa2..7f2fa4e 100644 --- a/server/auth/modules/passport-providers/oidc.js +++ b/server/auth/modules/passport-providers/oidc.js @@ -1,4 +1,5 @@ var OpenIDConnectStrategy = require('passport-openidconnect') +const model = require("../../../models/users"); class PassportOpenIDConnect { @@ -54,6 +55,13 @@ class PassportOpenIDConnect { }, (req, res) => { if (req.user) { + if (req.user.groups.includes(provider.OAUTH_ROLE_TEACHER_VALUE)) { + model.register(req.user.email, "teacher"); + } else if (req.user.groups.includes(provider.OAUTH_ROLE_STUDENT_VALUE)) { + model.register(req.user.email, "student"); + } else { + model.register(req.user.email, "anonymous"); + } res.json(req.user) console.info(`L'utilisateur '${req.user.name}' vient de se connecter`) } else { diff --git a/server/config/auth.js b/server/config/auth.js index 6d2b425..ffb5927 100644 --- a/server/config/auth.js +++ b/server/config/auth.js @@ -151,15 +151,11 @@ class AuthConfig { if (providerConfig.type === 'oauth') { passportConfig[providerName] = { - type: providerConfig.type, - authorizationUrl: providerConfig.OAUTH_AUTHORIZATION_URL, - callbackUrl: providerConfig.OAUTH_CALLBACK_URL, + type: providerConfig.type }; } else if (providerConfig.type === 'oidc') { passportConfig[providerName] = { - type: providerConfig.type, - issuerUrl: providerConfig.OIDC_ISSUER_URL, - callbackUrl: providerConfig.OIDC_CALLBACK_URL + type: providerConfig.type }; } }); diff --git a/server/config/roles.json b/server/config/roles.json index 3195e49..8d0880e 100644 --- a/server/config/roles.json +++ b/server/config/roles.json @@ -14,7 +14,8 @@ "permissions": [ "crud_quiz", "crud_folders", - "crud_images" + "crud_images", + "participate_quiz" ] }, { diff --git a/server/middleware/rbac.js b/server/middleware/rbac.js index 2a32d86..f1628e7 100644 --- a/server/middleware/rbac.js +++ b/server/middleware/rbac.js @@ -1,12 +1,18 @@ 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'; + let userRole; + if (req.user) { + userRole = req.user.role; + } else if (req.session.passport.user) { + userRole = req.session.passport.user.role; + } else { + userRole = 'anonymous'; + } const userPermissions = Permissions.getPermissionsByRoleName(userRole); for (let permission of permissions) { diff --git a/server/models/users.js b/server/models/users.js index 829ac64..b3606a8 100644 --- a/server/models/users.js +++ b/server/models/users.js @@ -47,6 +47,37 @@ class Users { // TODO: verif if inserted properly... } + async register(email, role) { + await db.connect(); + const conn = db.getConnection(); + + const userCollection = conn.collection('users'); + + const existingUser = await userCollection.findOne({ email: email }); + + if (existingUser) { + await userCollection.updateOne( + { email: existingUser.email }, + { + $set: { 'role': role }, + $currentDate: { lastModified: true } + } + ); + } else { + const newUser = { + email: email, + role: role, + created_at: new Date() + }; + + await userCollection.insertOne(newUser); + + const folderTitle = 'Dossier par Défaut'; + const userId = newUser._id.toString(); + await Folders.create(folderTitle, userId); + } + } + async login(email, password) { await db.connect() const conn = db.getConnection(); diff --git a/server/socket/socket.js b/server/socket/socket.js index 90bb7fb..8d95720 100644 --- a/server/socket/socket.js +++ b/server/socket/socket.js @@ -1,11 +1,38 @@ const MAX_USERS_PER_ROOM = 60; const MAX_TOTAL_CONNECTIONS = 2000; +var passport = require('passport') +const Permissions = require("../models/permissions"); +const AppError = require("../middleware/AppError"); +const {UNAUTHORIZED_PERMISSION_MISSING} = require("../constants/errorCodes"); const setupWebsocket = (io) => { let totalConnections = 0; + function onlyForHandshake(middleware) { + return (req, res, next) => { + const isHandshake = req._query.sid === undefined; + if (isHandshake) { + middleware(req, res, next); + } else { + next(); + } + }; + } + + io.engine.use(onlyForHandshake(passport.session())); + io.engine.use( + onlyForHandshake((req, res, next) => { + if (req.user) { + next(); + } else { + res.writeHead(401); + res.end(); + } + }), + ); + io.on("connection", (socket) => { - // Get jwt and roles + const userRole = socket.request.user.role; if (totalConnections >= MAX_TOTAL_CONNECTIONS) { console.log("Connection limit reached. Disconnecting client."); socket.emit( @@ -25,10 +52,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'); - // } + const userPermissions = Permissions.getPermissionsByRoleName(userRole); + if (!userPermissions.includes("create_quiz")) { + socket.emit(UNAUTHORIZED_PERMISSION_MISSING.code, UNAUTHORIZED_PERMISSION_MISSING.message); + } if (sentRoomName) { const roomName = sentRoomName.toUpperCase(); if (!io.sockets.adapter.rooms.get(roomName)) { @@ -49,6 +76,10 @@ const setupWebsocket = (io) => { }); socket.on("join-room", ({ enteredRoomName, username }) => { + const userPermissions = Permissions.getPermissionsByRoleName(userRole); + if (!userPermissions.includes("participate_quiz")) { + socket.emit(UNAUTHORIZED_PERMISSION_MISSING.code, UNAUTHORIZED_PERMISSION_MISSING.message); + } if (io.sockets.adapter.rooms.has(enteredRoomName)) { const clientsInRoom = io.sockets.adapter.rooms.get(enteredRoomName).size;