Skip to content

Commit

Permalink
EDX-1565 - Cleans up websocket connection code
Browse files Browse the repository at this point in the history
  • Loading branch information
mightycox committed Jul 14, 2023
1 parent 001dd36 commit 4433508
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 9 deletions.
2 changes: 2 additions & 0 deletions backend/src/components/roles.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ const roles = {
Exchange: ['SECURE_EXCHANGE'],
//Help functions created in auth module: isValidPenTeamRoleUserToken, isValidPenTeamRoleUser
PenTeamRole: config.get('server:edx:teamRoles:pen'),
//Help functions created in auth module: isValidSchoolMoveUserToken, isValidSchoolMoveUser
School: ['SCHOOL_ADMIN']
},
Admin: {
//Help functions created in auth module: isValidGMPAdmin
Expand Down
13 changes: 10 additions & 3 deletions backend/src/routes/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const {
} = require('express-validator');

const isValidStaffUserWithRoles = auth.isValidUserWithRoles('GMP & UMP & PenRequestBatch & StudentSearch & StaffAdministration & NominalRoll & NominalRollReadOnly & GUMPAnalytics & PenRequestBatchAnalytics & Exchange', [...roles.User.GMP, ...roles.User.UMP, ...roles.User.PenRequestBatch, ...roles.User.StudentSearch, ...roles.User.StaffAdministration, ...roles.User.NominalRoll , ...roles.User.NominalRollReadOnly, ...roles.User.GUMPAnalytics, ...roles.User.PenRequestBatchAnalytics, ...roles.User.Exchange]);
const isValidWebSocketUserWithRoles = auth.isValidUserWithRoles('GMP & UMP & PenRequestBatch & Exchange & School', [...roles.User.GMP, ...roles.User.UMP, ...roles.User.PenRequestBatch, ...roles.User.Exchange, ...roles.User.School]);

const router = express.Router();

Expand Down Expand Up @@ -75,11 +76,13 @@ async function generateTokens(req, res) {
const isAuthorizedUser = isValidStaffUserWithRoles(req);
const isValidUsers = auth.isValidUsers(req);
const isValidAdminUsers = auth.isValidAdminUsers(req);
const isAuthorizedWebsocketUser = isValidWebSocketUserWithRoles(req);
const responseJson = {
jwtFrontend: req.user.jwtFrontend,
isAuthorizedUser: isAuthorizedUser,
...isValidUsers,
...isValidAdminUsers
...isValidAdminUsers,
isAuthorizedWebsocketUser: isAuthorizedWebsocketUser
};
return res.status(HttpStatus.OK).json(responseJson);
} else {
Expand Down Expand Up @@ -110,11 +113,13 @@ router.post('/refresh', [
const isAuthorizedUser = isValidStaffUserWithRoles(req);
const isValidUsers = auth.isValidUsers(req);
const isValidAdminUsers = auth.isValidAdminUsers(req);
const isAuthorizedWebsocketUser = isValidWebSocketUserWithRoles(req);
const responseJson = {
jwtFrontend: req.user.jwtFrontend,
isAuthorizedUser: isAuthorizedUser,
...isValidUsers,
...isValidAdminUsers
...isValidAdminUsers,
isAuthorizedWebsocketUser: isAuthorizedWebsocketUser
};
return res.status(HttpStatus.OK).json(responseJson);
}
Expand All @@ -127,12 +132,14 @@ router.get('/token', auth.refreshJWT, (req, res) => {
const isAuthorizedUser = isValidStaffUserWithRoles(req);
const isValidUsers = auth.isValidUsers(req);
const isValidAdminUsers = auth.isValidAdminUsers(req);
const isAuthorizedWebsocketUser = isValidWebSocketUserWithRoles(req);
if (req['user'] && req['user'].jwtFrontend && req['user'].refreshToken) {
const responseJson = {
jwtFrontend: req['user'].jwtFrontend,
isAuthorizedUser: isAuthorizedUser,
...isValidUsers,
...isValidAdminUsers
...isValidAdminUsers,
isAuthorizedWebsocketUser: isAuthorizedWebsocketUser
};
req.session.correlationID = uuidv4();
res.status(HttpStatus.OK).json(responseJson);
Expand Down
35 changes: 35 additions & 0 deletions backend/src/socket/web-socket.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
'use strict';
const logger = require('../components/logger');
let connectedClients = [];
const webSocket = {

Expand All @@ -10,7 +11,32 @@ const webSocket = {
init(app, server) {
require('express-ws')(app, server);
app.ws('/api/socket', (ws) => {
logger.debug('Connecting websocket client');

connectedClients.push(ws);
let isAlive = true;

// Send a ping message every 30 seconds
const pingInterval = setInterval(() => {
if (!isAlive) {
logger.debug('Ping test failed, closing client websocket connection');
cleanupWebSocket(ws, pingInterval);
return ws.terminate();
}

isAlive = false;
ws.ping();
}, 30000);

ws.on('pong', () => {
// The client responded to the ping message, so it's still alive
isAlive = true;
});

ws.on('close', () => {
logger.debug('Closing websocket client connection');
cleanupWebSocket(ws, pingInterval);
});
});
},
getWebSocketClients() {
Expand All @@ -23,4 +49,13 @@ const webSocket = {
return connectedClients; // returns only connected clients.
}
};

function cleanupWebSocket(ws, interval) {
clearInterval(interval);
const index = connectedClients.indexOf(ws);
if (index !== -1) {
connectedClients.splice(index, 1); // Remove the WebSocket connection from the array
}
}

module.exports = webSocket;
7 changes: 5 additions & 2 deletions frontend/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,15 @@ export default {
};
},
computed: {
...mapState(authStore, ['jwtToken', 'isAuthenticated', 'userInfo', 'isValidGMPUser', 'isValidUMPUser', 'isValidPenRequestBatchUser']),
...mapState(authStore, ['jwtToken', 'isAuthenticated', 'userInfo', 'isAuthorizedWebsocketUser']),
...mapState(appStore, ['pageTitle']),
},
watch: {
isAuthenticated() {
this.handleWebSocket();
},
isAuthorizedWebsocketUser() {
this.handleWebSocket();
}
},
beforeUnmount() {
Expand All @@ -69,7 +72,7 @@ export default {
authStore,
...mapActions(appStore, ['getConfig']),
handleWebSocket() {
if(this.isAuthenticated && (this.isValidPenRequestBatchUser || this.isValidGMPUser || this.isValidUMPUser)) {
if(this.isAuthenticated && this.isAuthorizedWebsocketUser) {
this.$webSocketsConnect();
} else {
this.$webSocketsDisconnect();
Expand Down
3 changes: 0 additions & 3 deletions frontend/src/services/web-socket-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ webSocketsService.install = function (Vue, options) {
ws = new WebSocket(options.url);

ws.onopen = () => {
console.log('connection opened');
// Restart reconnect interval
reconnectInterval = options.reconnectInterval || 1000;
};
Expand All @@ -30,7 +29,6 @@ webSocketsService.install = function (Vue, options) {
};

ws.onclose = (event) => {
console.log('connection closed');
if (event) {
// Event.code 1000 is our normal close event
if (event.code !== 1000) {
Expand Down Expand Up @@ -65,7 +63,6 @@ webSocketsService.install = function (Vue, options) {
Here we write our custom functions to not make a mess in one function
*/
function handleNotification (params) {
console.log('Received websocket event');
const noteStore = notificationsStore();
noteStore.setNotification(params.data);
}
Expand Down
14 changes: 13 additions & 1 deletion frontend/src/store/modules/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ export const authStore = defineStore('auth', {
isValidDistrictAdmin: localStorage.getItem('isValidDistrictAdmin') !== null,
isValidSchoolAdmin: localStorage.getItem('isValidSchoolAdmin') !== null,
isValidIndependentAuthorityAdmin: localStorage.getItem('isValidIndependentAuthorityAdmin') !== null,
isValidSchoolIndependentAdmin: localStorage.getItem('isValidSchoolIndependentAdmin') !== null
isValidSchoolIndependentAdmin: localStorage.getItem('isValidSchoolIndependentAdmin') !== null,
isAuthorizedWebsocketUser: localStorage.getItem('isAuthorizedWebsocketUser') !== null
}),
getters: {
acronymsGet: state => state.acronyms,
Expand Down Expand Up @@ -286,6 +287,16 @@ export const authStore = defineStore('auth', {
localStorage.removeItem('isValidIndependentAuthorityAdmin');
}
},
async setAuthorizedWebsocketUser(isAuthorizedWebsocketUser){
console.log(isAuthorizedWebsocketUser);
if (isAuthorizedWebsocketUser) {
this.isAuthorizedWebsocketUser = true;
localStorage.setItem('isAuthorizedWebsocketUser', 'true');
} else {
this.isAuthorizedWebsocketUser = false;
localStorage.removeItem(('isAuthorizedWebsocketUser'));
}
},
async setUserInfo(userInf) {
if (userInf) {
this.userInfo = userInf;
Expand Down Expand Up @@ -355,6 +366,7 @@ export const authStore = defineStore('auth', {
await this.setIsValidSchoolAdmin(response.isValidSchoolAdmin);
await this.setIsValidSchoolIndependentAdmin(response.isValidSchoolIndependentAdmin);
await this.setIsValidIndependentAuthorityAdmin(response.isValidIndependentAuthorityAdmin);
await this.setAuthorizedWebsocketUser(response.isAuthorizedWebsocketUser);

ApiService.setAuthHeader(response.jwtFrontend);
}
Expand Down

0 comments on commit 4433508

Please sign in to comment.