refactor: SocketIO

- Check request data in middleware
- Authenticate each connection before allowing room access
- Allow site room access only to System Users, restrict Website User &
  Guests to their respective user rooms

Note: This doesn't check for roles / permissions
This commit is contained in:
Gavin D'souza 2022-11-14 17:06:25 +05:30
parent 5210ea593f
commit 4de9c39bb8
2 changed files with 38 additions and 32 deletions

View file

@ -122,8 +122,10 @@ def get_user_info():
from frappe.sessions import Session
session = Session(None, resume=True).get_session_data()
return {
"user": session.user,
"user_type": session.user_type,
}

View file

@ -14,47 +14,53 @@ const io = require("socket.io")(conf.socketio_port, {
},
});
// on socket connection
io.on("connection", function (socket) {
io.use((socket, next) => {
if (get_hostname(socket.request.headers.host) != get_hostname(socket.request.headers.origin)) {
next(new Error("Invalid origin"));
return;
}
if (!socket.request.headers.cookie) {
next(new Error("No cookie transmitted."));
return;
}
const sid = cookie.parse(socket.request.headers.cookie).sid;
if (!sid) {
const cookies = cookie.parse(socket.request.headers.cookie);
if (!cookies.sid) {
next(new Error("No sid transmitted."));
return;
}
socket.sid = cookies.sid;
socket.user = cookies.user_id;
socket.user = cookie.parse(socket.request.headers.cookie).user_id;
request
.get(get_url(socket, "/api/method/frappe.realtime.get_user_info"))
.type("form")
.query({
sid: socket.sid,
})
.then((res) => {
console.log(`User ${res.body.message.user} found`);
socket.user = res.body.message.user;
socket.user_type = res.body.message.user_type;
})
.catch((e) => {
next(new Error(`Unauthorized: ${e}`));
return;
});
let retries = 0;
let join_user_room = () => {
request
.get(get_url(socket, "/api/method/frappe.realtime.get_user_info"))
.type("form")
.query({
sid: sid,
})
.then((res) => {
const room = get_user_room(socket, res.body.message.user);
socket.join(room);
socket.join(get_site_room(socket));
})
.catch((e) => {
if (e.code === "ECONNREFUSED" && retries < 5) {
// retry after 1s
retries += 1;
return setTimeout(join_user_room, 1000);
}
log(`Unable to join user room. ${e}`);
});
};
next();
});
join_user_room();
// on socket connection
io.on("connection", function (socket) {
const room = get_user_room(socket);
socket.join(room);
if (socket.user == "System User") {
socket.join(get_site_room(socket));
}
socket.on("task_subscribe", function (task_id) {
var room = get_task_room(socket, task_id);
@ -75,7 +81,6 @@ io.on("connection", function (socket) {
socket.on("doc_subscribe", function (doctype, docname) {
can_subscribe_doc({
socket,
sid,
doctype,
docname,
callback: () => {
@ -93,7 +98,6 @@ io.on("connection", function (socket) {
socket.on("doc_open", function (doctype, docname) {
can_subscribe_doc({
socket,
sid,
doctype,
docname,
callback: () => {
@ -210,7 +214,7 @@ function get_typing_room(socket, doctype, docname) {
}
function get_user_room(socket, user) {
return get_site_name(socket) + ":user:" + user;
return get_site_name(socket) + ":user:" + user || socket.user;
}
function get_site_room(socket) {
@ -261,7 +265,7 @@ function can_subscribe_doc(args) {
.get(get_url(args.socket, "/api/method/frappe.realtime.can_subscribe_doc"))
.type("form")
.query({
sid: args.sid,
sid: args.socket.sid,
doctype: args.doctype,
docname: args.docname,
})