diff --git a/public/styles/chat.css b/public/styles/chat.css index fcbf9cd..96afdc0 100644 --- a/public/styles/chat.css +++ b/public/styles/chat.css @@ -137,11 +137,6 @@ header { flex: 1; } - #chatInputContainer.authorized > div { - display: flex; - } - - /* Reversed visibility for unauthenticated */ #chatInputContainer > #unauthenticated { display: flex; justify-content: space-around; @@ -181,14 +176,19 @@ header { cursor: not-allowed; } - #chatInputContainer.authorized > #unauthenticated { + #chatInputContainer.authorized > #unauthenticated, + #chatInputContainer.banned > #unauthenticated { display: none; } #authenticated { - display: flex; + display: none; flex-direction: column; } + #chatInputContainer.authorized > #authenticated { + display: flex; + } + #chatUserAndInputContainer { display: flex; margin: 2px 10px; @@ -269,7 +269,8 @@ header { margin-right: 4px; } - #chatButtons > *.minimal { + #chatButtons > *.minimal, + button.minimal { margin-left: 0; color: #999; font-size:.9em; @@ -282,6 +283,31 @@ header { text-decoration: none; } + #banned { + display: none; + text-align: center; + flex-direction: column; + background: #06060699; + } + #banned a, + #banned a:visited { + color: #09f; + } + + #banned a:hover { + color: #3df; + } + + #chatInputContainer.banned > #banned { + display: flex; + } + + #banTitle { + padding-top: 4px; + font-size: 2em; + color: #e44; + } + input[type=number] { max-width: 50px; min-width: 30px; diff --git a/src/client/chat/chat.js b/src/client/chat/chat.js index 7c63483..a16a39c 100644 --- a/src/client/chat/chat.js +++ b/src/client/chat/chat.js @@ -46,51 +46,7 @@ domReady.then(() => { // Check for former authentication cookieData = Cookies.getJSON("user_data"); - - if ( - // If there is former data, check if it is not outdated. - cookieData - - // See if current date is later than origin date + expiration period - && Date.now() < cookieData.access_granted + cookieData.expires_in * 1000 - ) { - // Request a fresh token - var xhr = new XMLHttpRequest(); - xhr.onreadystatechange = function() { - if (this.readyState === 4 && this.status === 200) { - let response = JSON.parse(xhr.responseText); - - if (response.authorized && response.refreshed && response.tokenBody) { - response.tokenBody.id = cookieData.id; - response.tokenBody.username = cookieData.username; - response.tokenBody.discriminator = cookieData.discriminator; - response.tokenBody.avatar = cookieData.avatar; - let days = (response.tokenBody.expires_in / 62400) - 0.1; // seconds to days minus some slack - Cookies.set("user_data", response.tokenBody, { - expires: days, - path: "/", - domain: window.location.hostname, - secure: config.ssl - }); - cookieData = response.tokenBody; - } - - // Redundant check as non-authorized requests are returned as a 400 - if (response.authorized) { - onAuthorization(cookieData); - } - } - }; - xhr.open("POST", "/chat", true); - xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8"); - xhr.send( - JSON.stringify({ - "type": "refresh_token", - "id": cookieData.id, - "access_token": cookieData.access_token - }) - ); - } + checkAuthentication(); let lastMessageSent = Date.now(); let sendMessage = function(message) { @@ -189,24 +145,25 @@ domReady.then(() => { }, false); // Make log out button functional - let chatButtonLogOut = document.getElementById("buttonLogOut"); - chatButtonLogOut.addEventListener("click", function() { - if (confirm("Do you really wish to log out?")) { - Cookies.remove("user_data", - { - path: "/", - domain: window.location.hostname + for (let chatButtonLogOut of document.getElementsByClassName("buttonLogOut")) { + chatButtonLogOut.addEventListener("click", function() { + if (confirm("Do you really wish to log out?")) { + Cookies.remove("user_data", + { + path: "/", + domain: window.location.hostname + } + ); + + // Send to parent if applicable + if (inIframe()) { + window.top.postMessage(userState.AUTH_CHANGED, `${window.location.origin}/client`); } - ); - // Send to parent if applicable - if (inIframe()) { - window.top.postMessage(userState.AUTH_CHANGED, `${window.location.origin}/client`); + window.location.reload(true); } - - window.location.reload(true); - } - }, false); + }, false); + } }); let authWindow; @@ -224,9 +181,9 @@ function authenticationWindow() { window.addEventListener("message", receiveMessage, false); function receiveMessage(event) { if (event.data && event.data.success && event.origin === window.location.origin) { - onAuthorization(event.data.response); cookieData = Cookies.getJSON("user_data"); authWindow.close(); + checkAuthentication(); // Send to parent if applicable if (inIframe()) { @@ -240,3 +197,55 @@ function onAuthorization(data) { document.getElementById("userName").innerText = `${data.username}#${data.discriminator}`; document.getElementById("chatInputContainer").className = "authorized"; } + +function onBanned() { + document.getElementById("chatInputContainer").className = "banned"; +} + +function checkAuthentication() { + if ( + // If there is former data, check if it is not outdated. + cookieData + + // See if current date is later than origin date + expiration period + && Date.now() < cookieData.access_granted + cookieData.expires_in * 1000 + ) { + // Request a fresh token + var xhr = new XMLHttpRequest(); + xhr.onreadystatechange = function() { + if (this.readyState === 4 && this.status === 200) { + let response = JSON.parse(xhr.responseText); + + if (response.authorized && response.refreshed && response.tokenBody) { + response.tokenBody.id = cookieData.id; + response.tokenBody.username = cookieData.username; + response.tokenBody.discriminator = cookieData.discriminator; + response.tokenBody.avatar = cookieData.avatar; + let days = (response.tokenBody.expires_in / 62400) - 0.1; // seconds to days minus some slack + Cookies.set("user_data", response.tokenBody, { + expires: days, + path: "/", + domain: window.location.hostname, + secure: config.ssl + }); + cookieData = response.tokenBody; + } + + if (response.authorized) { + onAuthorization(cookieData); + } else if (response.banned) { + onBanned(); + } + } + }; + xhr.open("POST", "/chat", true); + xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8"); + xhr.send( + JSON.stringify({ + "type": "refresh_token", + "id": cookieData.id, + "access_token": cookieData.access_token + }) + ); + } +} diff --git a/src/server/chat/discord-manager.js b/src/server/chat/discord-manager.js index 41afd8b..aff1f74 100644 --- a/src/server/chat/discord-manager.js +++ b/src/server/chat/discord-manager.js @@ -49,6 +49,18 @@ const discordManager = function() { }, console.error); this.client.on("error", console.error, console.error); + + this.client.on("guildBanAdd", function(guild, user) { + log.info(`DISCORD: ${"Banned user".red} ${user.username}#${user.discriminator} (${user.id})`); + db.user.setBanState(true, user.id); + }, console.error); + + this.client.on("guildBanRemove", function(guild, user) { + log.info(`DISCORD: ${"Unbanned user".green} ${user.username}#${user.discriminator} (${user.id})`); + db.user.setBanState(false, user.id); + }, console.error); + + this.client.login(config.discord.botToken); return socketChat; @@ -151,21 +163,25 @@ const discordManager = function() { let response = { authorized: true, refreshed: true, + banned: false, tokenBody }; res.send(response); }, () => { - res.status(400).send({ authorized: false, refreshed: false }); + res.status(400).send({ authorized: false, refreshed: false, banned: false }); }); } else if (this.db.user.idIsAuthenticated(req.body.id, req.body.access_token)) { - res.send({ authorized: true, refreshed: false }); + res.send({ authorized: true, refreshed: false, banned: false }); + return; + } else if (this.db.user.idIsBanned(req.body.id)) { + res.send({ authorized: false, refreshed: false, banned: true }); return; } } - res.status(400).send({ authorized: false, refreshed: false }); + res.status(400).send({ authorized: false, refreshed: false, banned: false }); } }; }(); diff --git a/src/server/database/initializer.js b/src/server/database/initializer.js index be9e5a3..78a8aad 100644 --- a/src/server/database/initializer.js +++ b/src/server/database/initializer.js @@ -1,7 +1,7 @@ const log = require("../../log"); module.exports = function(db) { - let _schemaVersion = 3; + let _schemaVersion = 4; return { _users: db.prepare( @@ -15,6 +15,7 @@ module.exports = function(db) { timestamp_refresh_last INTEGER, time_refresh_expire INTEGER, scope TEXT, + is_banned INTEGER, stat_points_earned INTEGER, stat_rounds_entered INTEGER, stat_rounds_finished INTEGER, diff --git a/src/server/database/users.js b/src/server/database/users.js index edef580..4bcd8d4 100644 --- a/src/server/database/users.js +++ b/src/server/database/users.js @@ -6,12 +6,28 @@ module.exports = function(db, common) { return !!this._idExists.get(id); }, - _idIsAuthenticated: db.prepare("SELECT access_token FROM users WHERE id = ?"), + _idIsAuthenticated: db.prepare("SELECT access_token, is_banned FROM users WHERE id = ?"), idIsAuthenticated(id, access_token) { if (this.idExists(id)) { let row = this._idIsAuthenticated.get(id); - if (row && row.access_token == access_token) { + if ( + row + && row.access_token == access_token + && row.is_banned !== 1 + ) { + return true; + } + } + return false; + }, + + _idIsBanned: db.prepare("SELECT is_banned FROM users WHERE id = ?"), + + idIsBanned(id) { + if (this.idExists(id)) { + let row = this._idIsBanned.get(id); + if (row && row.is_banned === 1) { return true; } } @@ -113,6 +129,7 @@ module.exports = function(db, common) { timestamp_refresh_last, time_refresh_expire, scope, + is_banned, stat_points_earned, stat_rounds_entered, stat_rounds_finished, @@ -122,7 +139,7 @@ module.exports = function(db, common) { stat_marbles_not_finished, stat_unique_levels_played, timestamp_first_login - ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)` + ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)` ), insertNewUserEmbed(token_body, user_body, scope) { @@ -144,6 +161,7 @@ module.exports = function(db, common) { 0, 0, 0, + 0, Date.now() ]); }, @@ -155,6 +173,7 @@ module.exports = function(db, common) { username, discriminator, avatar, + is_banned, stat_points_earned, stat_rounds_entered, stat_rounds_finished, @@ -164,7 +183,7 @@ module.exports = function(db, common) { stat_marbles_not_finished, stat_unique_levels_played, timestamp_first_login - ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)` + ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)` ), insertNewUserDiscord(user) { @@ -181,10 +200,29 @@ module.exports = function(db, common) { 0, 0, 0, + 0, Date.now() ]); }, + // bans + _setBanState: db.prepare( + `UPDATE OR REPLACE users SET + is_banned = ? + WHERE + id = ?` + ), + + setBanState(banState, id) { + if (this.idExists(id)) { + banState = banState ? 1 : 0; // Convert to integer since SQLite3 does not support the boolean type. + this._setBanState.run([ + banState, + id + ]); + } + }, + // stat_points _incrementUserPoints: db.prepare( `UPDATE OR ABORT diff --git a/src/server/network/sockets.js b/src/server/network/sockets.js index 3e18652..0051676 100644 --- a/src/server/network/sockets.js +++ b/src/server/network/sockets.js @@ -87,23 +87,25 @@ const setupChat = function(db, chatWebhook) { return; } - let row = db.user.getUserDetailsById(message.id); - if (row && row.access_token == message.access_token) { - messages.parse(message.content, message.id, row.username); + if (db.user.idIsAuthenticated(message.id, message.access_token)) { + let row = db.user.getUserDetailsById(message.id); + if (row) { + messages.parse(message.content, message.id, row.username); - chatWebhook.send(message.content, { - username: row.username, - avatarURL: `https://cdn.discordapp.com/avatars/${message.id}/${row.avatar}.png`, - disableEveryone: true - }); + chatWebhook.send(message.content, { + username: row.username, + avatarURL: `https://cdn.discordapp.com/avatars/${message.id}/${row.avatar}.png`, + disableEveryone: true + }); - chatSocketManager.emit(JSON.stringify({ - username: row.username, - discriminator: row.discriminator, - content: message.content - })); - } else { - log.warn("User ID and access token mismatch!", row); + chatSocketManager.emit(JSON.stringify({ + username: row.username, + discriminator: row.discriminator, + content: message.content + })); + } else { + log.warn("User ID and access token mismatch!", row); + } } }); diff --git a/templates/chat.mustache b/templates/chat.mustache index fddd1ee..e078f9a 100644 --- a/templates/chat.mustache +++ b/templates/chat.mustache @@ -64,12 +64,17 @@
- + {{#invitelink}} join server{{/invitelink}}
 
+
+
You are banned
+

If you wish to appeal this decision, contact us.

+ +