Skip to content

Commit

Permalink
Merge pull request #15 from allgood/main
Browse files Browse the repository at this point in the history
Correções segurança de websocket
  • Loading branch information
w3nder authored Mar 12, 2024
2 parents 52c2226 + 781c2c3 commit 8935da2
Show file tree
Hide file tree
Showing 16 changed files with 113 additions and 34 deletions.
14 changes: 10 additions & 4 deletions backend/src/controllers/TicketController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,13 @@ export const store = async (req: Request, res: Response): Promise<Response> => {
});

const io = getIO();
io.to(ticket.status).emit("ticket", {
action: "update",
ticket
});
// send status to the specific queue channel
io.to(ticket.status)
.to(`queue-${ticket.queueId}-${ticket.status}`)
.emit("ticket", {
action: "update",
ticket
});

return res.status(200).json(ticket);
};
Expand Down Expand Up @@ -138,9 +141,12 @@ export const remove = async (
const ticket = await DeleteTicketService(ticketId);

const io = getIO();
// send delete message to queues of ticket's current status
io.to(ticket.status)
.to(ticketId)
.to("notification")
.to(`queue-${ticket.queueId}-${ticket.status}`)
.to(`queue-${ticket.queueId}-notification`)
.emit("ticket", {
action: "delete",
ticketId: +ticketId
Expand Down
11 changes: 7 additions & 4 deletions backend/src/helpers/SetTicketMessagesAsRead.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,13 @@ const SetTicketMessagesAsRead = async (ticket: Ticket): Promise<void> => {
}

const io = getIO();
io.to(ticket.status).to("notification").emit("ticket", {
action: "updateUnread",
ticketId: ticket.id
});
io.to(ticket.status)
.to("notification")
.to(`queue-${ticket.queueId}-notification`)
.emit("ticket", {
action: "updateUnread",
ticketId: ticket.id
});
};

export default SetTicketMessagesAsRead;
80 changes: 73 additions & 7 deletions backend/src/libs/socket.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { Server as SocketIO } from "socket.io";
import { Server } from "http";
import { verify } from "jsonwebtoken";
import AppError from "../errors/AppError";
import { logger } from "../utils/logger";
import authConfig from "../config/auth";
import User from "../models/User";
import Queue from "../models/Queue";
import Ticket from "../models/Ticket";

let io: SocketIO;

Expand All @@ -12,26 +17,87 @@ export const initIO = (httpServer: Server): SocketIO => {
}
});

io.on("connection", socket => {
io.on("connection", async socket => {
const { token } = socket.handshake.query;
let tokenData = null;
try {
tokenData = verify(token, authConfig.secret);
logger.debug(tokenData, "io-onConnection: tokenData");
} catch (error) {
logger.error(error, "Error decoding token");
socket.disconnect();
return io;
}

const userId = tokenData.id;

let user: User = null;
if (userId && userId !== "undefined" && userId !== "null") {
user = await User.findByPk(userId, { include: [Queue] });
}

logger.info("Client Connected");
socket.on("joinChatBox", (ticketId: string) => {
logger.info("A client joined a ticket channel");
socket.join(ticketId);
if (ticketId === "undefined") {
return;
}
Ticket.findByPk(ticketId).then(
ticket => {
// only admin and the current user of the ticket
// can join the message channel of it.
if (
ticket &&
(ticket.userId === user.id || user.profile === "admin")
) {
logger.debug(`User ${user.id} joined ticket ${ticketId} channel`);
socket.join(ticketId);
} else {
logger.info(
`Invalid attempt to join chanel of ticket ${ticketId} by user ${user.id}`
);
}
},
error => {
logger.error(error, `Error fetching ticket ${ticketId}`);
}
);
});

socket.on("joinNotification", () => {
logger.info("A client joined notification channel");
socket.join("notification");
if (user.profile === "admin") {
// admin can join all notifications
logger.debug(`Admin ${user.id} joined the notification channel.`);
socket.join("notification");
} else {
// normal users join notifications of the queues they participate
user.queues.forEach(queue => {
logger.debug(`User ${user.id} joined queue ${queue.id} channel.`);
socket.join(`queue-${queue.id}-notification`);
});
}
});

socket.on("joinTickets", (status: string) => {
logger.info(`A client joined to ${status} tickets channel.`);
socket.join(status);
if (user.profile === "admin") {
// only admin can join the notifications of a particular status
logger.debug(`Admin ${user.id} joined ${status} tickets channel.`);
socket.join(`${status}`);
} else {
// normal users can only receive messages of the queues they participate
user.queues.forEach(queue => {
logger.debug(
`User ${user.id} joined queue ${queue.id} ${status} tickets channel.`
);
socket.join(`queue-${queue.id}-${status}`);
});
}
});

socket.on("disconnect", () => {
logger.info("Client disconnected");
});

socket.emit("ready");
});
return io;
};
Expand Down
3 changes: 3 additions & 0 deletions backend/src/services/MessageServices/CreateMessageService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ const CreateMessageService = async ({
io.to(message.ticketId.toString())
.to(message.ticket.status)
.to("notification")
// send message to specific queues
.to(`queue-${message.ticket.queueId}-${message.ticket.status}`)
.to(`queue-${message.ticket.queueId}-notification`)
.emit("appMessage", {
action: "create",
message,
Expand Down
5 changes: 4 additions & 1 deletion backend/src/services/TicketServices/UpdateTicketService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ const UpdateTicketService = async ({
const io = getIO();

if (ticket.status !== oldStatus || ticket.user?.id !== oldUserId) {
io.to(oldStatus).emit("ticket", {
io.to(oldStatus).to(`queue-${ticket.queueId}-${oldStatus}`).emit("ticket", {
action: "delete",
ticketId: ticket.id
});
Expand All @@ -68,6 +68,9 @@ const UpdateTicketService = async ({
io.to(ticket.status)
.to("notification")
.to(ticketId.toString())
// send queue specific messages
.to(`queue-${ticket.queueId}-${ticket.status}`)
.to(`queue-${ticket.queueId}-notification`)
.emit("ticket", {
action: "update",
ticket
Expand Down
2 changes: 1 addition & 1 deletion backend/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
"forceConsistentCasingInFileNames": true,
}
}
8 changes: 3 additions & 5 deletions frontend/src/components/MessagesList/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useState, useEffect, useReducer, useRef } from "react";

import { isSameDay, parseISO, format } from "date-fns";
import openSocket from "socket.io-client";
import openSocket from "../../services/socket-io";
import clsx from "clsx";

import { green } from "@material-ui/core/colors";
Expand Down Expand Up @@ -379,11 +379,9 @@ const MessagesList = ({ ticketId, isGroup }) => {
}, [pageNumber, ticketId]);

useEffect(() => {
const socket = openSocket(`${process.env.REACT_APP_BACKEND_URL}`, {
transports: ["websocket"],
});
const socket = openSocket();

socket.on("connect", () => socket.emit("joinChatBox", ticketId));
socket.on("ready", () => socket.emit("joinChatBox", ticketId));

socket.on("appMessage", (data) => {
if (data.action === "create") {
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/NotificationsPopOver/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ const NotificationsPopOver = () => {
useEffect(() => {
const socket = openSocket();

socket.on("connect", () => socket.emit("joinNotification"));
socket.on("ready", () => socket.emit("joinNotification"));

socket.on("ticket", data => {
if (data.action === "updateUnread" || data.action === "delete") {
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/Ticket/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ const Ticket = () => {
useEffect(() => {
const socket = openSocket();

socket.on("connect", () => socket.emit("joinChatBox", ticketId));
socket.on("ready", () => socket.emit("joinChatBox", ticketId));

socket.on("ticket", (data) => {
if (data.action === "update") {
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/TicketsList/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ const TicketsList = (props) => {
const notBelongsToUserQueues = (ticket) =>
ticket.queueId && selectedQueueIds.indexOf(ticket.queueId) === -1;

socket.on("connect", () => {
socket.on("ready", () => {
if (status) {
socket.emit("joinTickets", status);
} else {
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/hooks/useSocket/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const useProvideSocket = () => {
}, []);

React.useMemo(() => {
socket.on("connect", () => {
socket.on("ready", () => {
console.log("connected");
});
}, [socket]);
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/Queues/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ const Queues = () => {
const token = JSON.parse(localStorage.getItem("token"));

const socket = openSocket(process.env.REACT_APP_BACKEND_URL, {query: {token}});
socket.on("connect", () => {
socket.on("ready", () => {
socket.emit("joinCompany")
});

Expand Down
4 changes: 2 additions & 2 deletions frontend/src/pages/Schedules/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useState, useEffect, useReducer, useCallback } from "react";
import { toast } from "react-toastify";
import openSocket from "socket.io-client";
import openSocket from "../../services/socket-io";

import { makeStyles } from "@material-ui/core/styles";
import Paper from "@material-ui/core/Paper";
Expand Down Expand Up @@ -140,7 +140,7 @@ const Schedules = () => {

useEffect(() => {
handleOpenScheduleModalFromContactId();
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
const socket = openSocket();

socket.on("user", (data) => {
if (data.action === "update" || data.action === "create") {
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/pages/Settings/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useState, useEffect } from "react";
import openSocket from "socket.io-client";
import openSocket from "../../services/socket-io";

import { makeStyles } from "@material-ui/core/styles";
import Paper from "@material-ui/core/Paper";
Expand Down Expand Up @@ -57,7 +57,7 @@ const Settings = () => {
}, []);

useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
const socket = openSocket();

socket.on("settings", data => {
if (data.action === "update") {
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/pages/Tags/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useState, useEffect, useReducer, useCallback } from "react";
import { toast } from "react-toastify";
import openSocket from "socket.io-client";
import openSocket from "../../services/socket-io";

import { makeStyles } from "@material-ui/core/styles";
import Paper from "@material-ui/core/Paper";
Expand Down Expand Up @@ -124,7 +124,7 @@ const Tags = () => {
}, [searchParam, pageNumber, fetchTags]);

useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
const socket = openSocket();

socket.on("user", (data) => {
if (data.action === "update" || data.action === "create") {
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/services/socket-io.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ function connectToSocket() {
const token = localStorage.getItem("token");
const socket = openSocket(getBackendUrl(), {
query: {
auth_token: JSON.parse(token),
token: JSON.parse(token),
},
});

Expand Down

0 comments on commit 8935da2

Please sign in to comment.