diff --git a/backend/cli/index.js b/backend/cli/index.js index 8954d2e..085db4f 100644 --- a/backend/cli/index.js +++ b/backend/cli/index.js @@ -277,7 +277,8 @@ async function init() { username: "Colubrina", id: 0, bot: true, - email: "colubrina@troplo.com" + email: "colubrina@troplo.com", + banned: true }) await User.update( { diff --git a/backend/index.js b/backend/index.js index edc7f9d..e5d6d33 100644 --- a/backend/index.js +++ b/backend/index.js @@ -16,6 +16,7 @@ app.use(bodyParser.urlencoded({ extended: true })) app.use("/api/v1/user", require("./routes/user.js")) app.use("/api/v1/themes", require("./routes/theme.js")) app.use("/api/v1/communications", require("./routes/communications.js")) +app.use("/api/v1/friends", require("./routes/friends.js")) app.use("/api/v1/admin", require("./routes/admin.js")) app.use("/usercontent", require("./routes/usercontent.js")) app.use("/api/v1/usercontent", require("./routes/usercontent.js")) diff --git a/backend/lib/authorize.js b/backend/lib/authorize.js index 5b02972..9ef0f4f 100644 --- a/backend/lib/authorize.js +++ b/backend/lib/authorize.js @@ -19,6 +19,10 @@ module.exports = async function (req, res, next) { ] }) if (user) { + if (user.banned) { + res.status(401).json(Errors.banned) + return + } await user.update({ lastSeenAt: new Date().toISOString() }) diff --git a/backend/lib/authorize_socket.js b/backend/lib/authorize_socket.js index 34fb4a7..ce48ac9 100644 --- a/backend/lib/authorize_socket.js +++ b/backend/lib/authorize_socket.js @@ -30,18 +30,22 @@ module.exports = async function (socket, next) { raw: true }) if (user) { - socket.user = user - next() + if (user.banned) { + socket.user = { + id: null, + username: "Not Authenticated" + } + next() + } else { + socket.user = user + next() + } } } else { socket.user = { id: null, username: "Not Authenticated" } - socket.compassUser = { - id: null, - username: "BC-NOAUTH" - } next() } } else { @@ -49,10 +53,6 @@ module.exports = async function (socket, next) { id: null, username: "Not Authenticated" } - socket.compassUser = { - id: null, - username: "BC-NOAUTH" - } next() } } catch (error) { @@ -60,10 +60,6 @@ module.exports = async function (socket, next) { id: null, username: "Not Authenticated" } - socket.compassUser = { - id: null, - username: "BC-NOAUTH" - } next() } } diff --git a/backend/lib/errors.js b/backend/lib/errors.js index 4e1cc48..b18d78d 100644 --- a/backend/lib/errors.js +++ b/backend/lib/errors.js @@ -37,7 +37,8 @@ let Errors = { registrationsDisabled: [ "Registrations are currently disabled on this instance. Please try again later.", 400 - ] + ], + banned: ["You are banned from this instance.", 400] } function processErrors(errorName) { diff --git a/backend/migrations/20220323090313-create-sessions.js b/backend/migrations/20220323090313-create-sessions.js index 400114d..33fc61d 100644 --- a/backend/migrations/20220323090313-create-sessions.js +++ b/backend/migrations/20220323090313-create-sessions.js @@ -11,17 +11,11 @@ module.exports = { session: { type: Sequelize.STRING }, - compassUserId: { - type: Sequelize.BIGINT - }, - sussiId: { - type: Sequelize.STRING - }, other: { type: Sequelize.JSON }, - instance: { - type: Sequelize.STRING + expiredAt: { + type: Sequelize.DATE }, createdAt: { allowNull: false, diff --git a/backend/migrations/20220326054833-compassSessionMap.js b/backend/migrations/20220326054833-compassSessionMap.js deleted file mode 100644 index 379fc53..0000000 --- a/backend/migrations/20220326054833-compassSessionMap.js +++ /dev/null @@ -1,18 +0,0 @@ -"use strict" - -module.exports = { - up: async (queryInterface, Sequelize) => { - await queryInterface.addColumn("Sessions", "expiredAt", { - type: Sequelize.DATE - }) - }, - - down: async (queryInterface, Sequelize) => { - /** - * Add reverting commands here. - * - * Example: - * await queryInterface.dropTable('users'); - */ - } -} diff --git a/backend/migrations/20220729091059-isBanned.js b/backend/migrations/20220729091059-isBanned.js new file mode 100644 index 0000000..6506ca5 --- /dev/null +++ b/backend/migrations/20220729091059-isBanned.js @@ -0,0 +1,20 @@ +"use strict" + +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.addColumn("Users", "banned", { + type: Sequelize.BOOLEAN, + allowNull: false, + defaultValue: false + }) + }, + + async down(queryInterface, Sequelize) { + /** + * Add reverting commands here. + * + * Example: + * await queryInterface.dropTable('users'); + */ + } +} diff --git a/backend/models/sessions.js b/backend/models/sessions.js index 290cd39..d94e49e 100644 --- a/backend/models/sessions.js +++ b/backend/models/sessions.js @@ -14,10 +14,7 @@ module.exports = (sequelize, DataTypes) => { Session.init( { session: DataTypes.STRING, - compassUserId: DataTypes.BIGINT, - sussiId: DataTypes.STRING, other: DataTypes.JSON, - instance: DataTypes.STRING, userId: DataTypes.BIGINT, expiredAt: DataTypes.DATE }, diff --git a/backend/models/users.js b/backend/models/users.js index 9652da0..7ab4243 100644 --- a/backend/models/users.js +++ b/backend/models/users.js @@ -153,6 +153,11 @@ module.exports = (sequelize, DataTypes) => { lastSeenAt: { type: DataTypes.DATE, allowNull: true + }, + banned: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false } }, { diff --git a/backend/routes/admin.js b/backend/routes/admin.js index 8cb8b55..cc5d790 100644 --- a/backend/routes/admin.js +++ b/backend/routes/admin.js @@ -58,7 +58,7 @@ router.get("/metrics", auth, async (req, res, next) => { createdAt: createdAt }, attributes: { - exclude: ["totp", "compassSession", "password"] + exclude: ["totp", "password"] } }) const messages = await Message.findAll({ @@ -66,7 +66,7 @@ router.get("/metrics", auth, async (req, res, next) => { createdAt: createdAt }, attributes: { - exclude: ["totp", "compassSession", "password"] + exclude: ["totp", "password"] } }) @@ -137,7 +137,7 @@ router.get("/users", auth, async (req, res, next) => { } ], attributes: { - exclude: ["totp", "compassSession", "password"] + exclude: ["totp", "password"] } }) res.json(users) @@ -154,7 +154,7 @@ router.get("/themes", auth, async (req, res, next) => { model: User, as: "user", attributes: { - exclude: ["totp", "compassSession", "password"] + exclude: ["totp", "password"] } }, { diff --git a/backend/routes/communications.js b/backend/routes/communications.js index 50ef74c..f6ab2d4 100644 --- a/backend/routes/communications.js +++ b/backend/routes/communications.js @@ -47,6 +47,7 @@ const upload = multer({ const resolveEmbeds = require("../lib/resolveEmbeds.js") const paginate = require("jw-paginate") + async function createMessage(req, type, content, association, userId) { const io = req.app.get("io") const message = await Message.create({ @@ -133,6 +134,7 @@ async function createMessage(req, type, content, association, userId) { }) }) } + router.get("/", auth, async (req, res, next) => { try { let chats = await ChatAssociation.findAll({ @@ -748,31 +750,6 @@ router.post("/association/:id", auth, async (req, res, next) => { } }) -router.get("/friends", auth, async (req, res, next) => { - try { - let friends = await Friend.findAll({ - where: { - userId: req.user.id - }, - include: [ - { - model: User, - as: "user", - attributes: ["id", "username", "avatar", "createdAt", "updatedAt"] - }, - { - model: User, - as: "user2", - attributes: ["id", "username", "avatar", "createdAt", "updatedAt"] - } - ] - }) - res.json(friends) - } catch (err) { - next(err) - } -}) - router.get("/users", auth, async (req, res, next) => { try { const users = await User.findAll({ @@ -780,11 +757,9 @@ router.get("/users", auth, async (req, res, next) => { "id", "username", "name", - "avatar", "createdAt", "updatedAt", - "status", "admin" ] @@ -795,141 +770,6 @@ router.get("/users", auth, async (req, res, next) => { } }) -router.post("/friends", auth, async (req, res, next) => { - try { - const io = req.app.get("io") - let friendRes - try { - friendRes = req.body.friend.split(":") - } catch { - friendRes = req.body.friend - } - const user = await User.findOne({ - where: { - username: friendRes[0] || friendRes - } - }) - if (user) { - const friend = await Friend.findOne({ - where: { - userId: req.user.id, - friendId: user.id - } - }) - if (friend) { - throw Errors.friendAlreadyFriends - } else { - if (!user.privacy.communications.enabled) { - throw Errors.communicationsUserNotOptedIn - } else { - const newFriend = await Friend.create({ - userId: req.user.id, - friendId: user.id - }) - const remoteFriend = await Friend.create({ - userId: user.id, - friendId: req.user.id, - status: "pendingCanAccept" - }) - io.to(user.id).emit("friendUpdate", {}) - io.to(req.user.id).emit("friendUpdate", {}) - io.to(user.id).emit("friendRequest", { - ...remoteFriend.dataValues, - user: { - username: req.user.username, - discussionsImage: req.user.discussionsImage, - avatar: req.user.avatar, - id: req.user.id - } - }) - res.json(newFriend) - } - } - } else { - throw Errors.communicationsUserNotFound - } - } catch (err) { - next(err) - } -}) - -router.delete("/friends/:id", auth, async (req, res, next) => { - try { - const io = req.app.get("io") - const friend = await Friend.findOne({ - where: { - userId: req.user.id, - id: req.params.id - } - }) - if (friend) { - await friend.destroy() - await Friend.destroy({ - where: { - userId: friend.friendId, - friendId: req.user.id - } - }) - io.to(friend.friendId).emit("friendUpdate", {}) - io.to(req.user.id).emit("friendUpdate", {}) - res.sendStatus(204) - } else { - throw Errors.friendNotFound - } - } catch (err) { - next(err) - } -}) - -router.put("/friends/:id", auth, async (req, res, next) => { - try { - const io = req.app.get("io") - const friend = await Friend.findOne({ - where: { - id: req.params.id, - userId: req.user.id, - status: "pendingCanAccept" - } - }) - if (friend) { - await friend.update({ - status: "accepted" - }) - const remoteFriend = await Friend.findOne({ - where: { - userId: friend.friendId, - friendId: friend.userId - }, - include: [ - { - model: User, - as: "user", - attributes: ["id", "username", "createdAt", "updatedAt"] - }, - { - model: User, - as: "user2", - attributes: ["id", "username", "createdAt", "updatedAt"] - } - ] - }) - await remoteFriend.update({ - status: "accepted" - }) - io.to(friend.userId).emit("friendUpdate", {}) - io.to(remoteFriend.userId).emit("friendUpdate", {}) - io.to(remoteFriend.userId).emit("friendAccepted", { - ...remoteFriend.dataValues - }) - res.json(friend) - } else { - throw Errors.friendNotFound - } - } catch (err) { - next(err) - } -}) - router.get("/search", auth, async (req, res, next) => { try { const friends = await Friend.findAll({ @@ -988,7 +828,6 @@ router.get("/:id", auth, async (req, res, next) => { attributes: [ "username", "name", - "avatar", "id", "createdAt", @@ -1230,7 +1069,6 @@ router.get("/:id/search", auth, async (req, res, next) => { attributes: [ "username", "name", - "avatar", "id", "createdAt", @@ -1247,7 +1085,6 @@ router.get("/:id/search", auth, async (req, res, next) => { attributes: [ "username", "name", - "avatar", "id", "createdAt", @@ -1309,7 +1146,6 @@ router.delete("/association/:id", auth, async (req, res, next) => { attributes: [ "username", "name", - "avatar", "id", "createdAt", @@ -1337,7 +1173,6 @@ router.delete("/association/:id", auth, async (req, res, next) => { attributes: [ "username", "name", - "avatar", "id", "createdAt", @@ -1356,7 +1191,6 @@ router.delete("/association/:id", auth, async (req, res, next) => { attributes: [ "username", "name", - "avatar", "id", "createdAt", @@ -1371,7 +1205,6 @@ router.delete("/association/:id", auth, async (req, res, next) => { attributes: [ "username", "name", - "avatar", "id", "createdAt", diff --git a/backend/routes/friends.js b/backend/routes/friends.js new file mode 100644 index 0000000..9842cf2 --- /dev/null +++ b/backend/routes/friends.js @@ -0,0 +1,167 @@ +const auth = require("../lib/authorize") +const { Friend, User } = require("../models") +const Errors = require("../lib/errors") +const express = require("express") +const router = express.Router() + +router.get("/", auth, async (req, res, next) => { + try { + let friends = await Friend.findAll({ + where: { + userId: req.user.id + }, + include: [ + { + model: User, + as: "user", + attributes: ["id", "username", "avatar", "createdAt", "updatedAt"] + }, + { + model: User, + as: "user2", + attributes: ["id", "username", "avatar", "createdAt", "updatedAt"] + } + ] + }) + res.json(friends) + } catch (err) { + next(err) + } +}) + +router.post("/", auth, async (req, res, next) => { + try { + const io = req.app.get("io") + let friendRes + try { + friendRes = req.body.friend.split(":") + } catch { + friendRes = req.body.friend + } + const user = await User.findOne({ + where: { + username: friendRes[0] || friendRes + } + }) + if (user) { + const friend = await Friend.findOne({ + where: { + userId: req.user.id, + friendId: user.id + } + }) + if (friend) { + throw Errors.friendAlreadyFriends + } else { + if (!user.privacy.communications.enabled) { + throw Errors.communicationsUserNotOptedIn + } else { + const newFriend = await Friend.create({ + userId: req.user.id, + friendId: user.id + }) + const remoteFriend = await Friend.create({ + userId: user.id, + friendId: req.user.id, + status: "pendingCanAccept" + }) + io.to(user.id).emit("friendUpdate", {}) + io.to(req.user.id).emit("friendUpdate", {}) + io.to(user.id).emit("friendRequest", { + ...remoteFriend.dataValues, + user: { + username: req.user.username, + discussionsImage: req.user.discussionsImage, + avatar: req.user.avatar, + id: req.user.id + } + }) + res.json(newFriend) + } + } + } else { + throw Errors.communicationsUserNotFound + } + } catch (err) { + next(err) + } +}) + +router.delete("/:id", auth, async (req, res, next) => { + try { + const io = req.app.get("io") + const friend = await Friend.findOne({ + where: { + userId: req.user.id, + id: req.params.id + } + }) + if (friend) { + await friend.destroy() + await Friend.destroy({ + where: { + userId: friend.friendId, + friendId: req.user.id + } + }) + io.to(friend.friendId).emit("friendUpdate", {}) + io.to(req.user.id).emit("friendUpdate", {}) + res.sendStatus(204) + } else { + throw Errors.friendNotFound + } + } catch (err) { + next(err) + } +}) + +router.put("/:id", auth, async (req, res, next) => { + try { + const io = req.app.get("io") + const friend = await Friend.findOne({ + where: { + id: req.params.id, + userId: req.user.id, + status: "pendingCanAccept" + } + }) + if (friend) { + await friend.update({ + status: "accepted" + }) + const remoteFriend = await Friend.findOne({ + where: { + userId: friend.friendId, + friendId: friend.userId + }, + include: [ + { + model: User, + as: "user", + attributes: ["id", "username", "createdAt", "updatedAt"] + }, + { + model: User, + as: "user2", + attributes: ["id", "username", "createdAt", "updatedAt"] + } + ] + }) + await remoteFriend.update({ + status: "accepted" + }) + io.to(friend.userId).emit("friendUpdate", {}) + io.to(remoteFriend.userId).emit("friendUpdate", {}) + io.to(remoteFriend.userId).emit("friendAccepted", { + ...remoteFriend.dataValues + }) + res.json(friend) + } else { + throw Errors.friendNotFound + } + } catch (err) { + next(err) + } +}) + +module.exports = router diff --git a/backend/routes/user.js b/backend/routes/user.js index 5973878..7306653 100644 --- a/backend/routes/user.js +++ b/backend/routes/user.js @@ -68,12 +68,8 @@ router.post("/login", async (req, res, next) => { .catch(() => {}) const session = await Session.create({ userId: user.id, - instance: req.body.instance || "", session: "COLUBRINA-" + cryptoRandomString({ length: 128 }), - compassUserId: user.compassUserId, - sussiId: user.sussiId, expiredAt: new Date(new Date().getTime() + 1000 * 60 * 60 * 24 * 30), - compassSession: user.compassSession, other: { ip: req.header("x-real-ip") || req.ip, location: ip.country @@ -113,6 +109,7 @@ router.post("/login", async (req, res, next) => { } }) if (user) { + if (user.banned) throw Errors.banned if (await checkPassword(req.body.password, user.password)) { if (user.totpEnabled) { const verified = speakeasy.totp.verify({ @@ -160,12 +157,8 @@ router.post("/register", async (req, res, next) => { .catch(() => {}) const session = await Session.create({ userId: user.id, - instance: req.body.instance || "", session: "COLUBRINA-" + cryptoRandomString({ length: 128 }), - compassUserId: user.compassUserId, - sussiId: user.sussiId, expiredAt: new Date(new Date().getTime() + 1000 * 60 * 60 * 24 * 30), - compassSession: user.compassSession, other: { ip: req.header("x-real-ip") || req.ip, location: ip.country diff --git a/src/main.js b/src/main.js index 81491a1..d689135 100644 --- a/src/main.js +++ b/src/main.js @@ -9,10 +9,8 @@ import Toast from "vue-toastification" import "./assets/styles.css" import "vue-toastification/dist/index.css" import "./registerServiceWorker" -import VueSanitize from "vue-sanitize" import "@mdi/font/css/materialdesignicons.css" import "./plugins/dayjs" -import VueApollo from "./plugins/apollo" import SocketIO from "socket.io-client" import twemoji from "twemoji" import VueNativeNotification from "vue-native-notification" @@ -84,129 +82,6 @@ Vue.use({ } }) -Vue.use(VueSanitize, { - allowedTags: [ - "address", - "article", - "aside", - "footer", - "header", - "h1", - "h2", - "h3", - "h4", - "h5", - "h6", - "hgroup", - "main", - "nav", - "section", - "blockquote", - "dd", - "div", - "dl", - "dt", - "figcaption", - "figure", - "hr", - "li", - "main", - "ol", - "p", - "pre", - "ul", - "a", - "abbr", - "b", - "bdi", - "bdo", - "br", - "cite", - "code", - "data", - "dfn", - "em", - "i", - "kbd", - "mark", - "q", - "rb", - "rp", - "rt", - "rtc", - "ruby", - "s", - "samp", - "small", - "span", - "strong", - "sub", - "sup", - "time", - "u", - "var", - "wbr", - "caption", - "col", - "colgroup", - "table", - "tbody", - "td", - "tfoot", - "th", - "thead", - "tr", - "img" - ], - disallowedTagsMode: "discard", - allowedAttributes: { - a: ["href", "name", "target"], - img: [ - "src", - "srcset", - "alt", - "title", - "width", - "height", - "loading", - "style" - ], - tr: ["style"], - td: ["style"], - table: ["style", "border", "cellpadding", "cellspacing"] - }, - allowedStyles: { - "*": { - // Match HEX and RGB - "text-align": [/^left$/, /^right$/, /^center$/], - // Match any number with px, em, or % - "font-size": [/^\d+(?:px|em|%)$/], - "font-weight": [/^\d+$/], - "font-style": [/^\d+$/], - height: [/^\d+(?:px|em|%)$/], - width: [/^\d+(?:px|em|%)$/] - } - }, - // Lots of these won't come up by default because we don't allow them - selfClosing: [ - "img", - "br", - "hr", - "area", - "base", - "basefont", - "input", - "link", - "meta" - ], - // URL schemes we permit - allowedSchemes: ["http", "https", "ftp", "mailto", "tel"], - - allowedSchemesByTag: {}, - allowedSchemesAppliedToAttributes: ["href", "src", "cite"], - allowProtocolRelative: true, - enforceHtmlBoundary: false -}) Vue.use(require("vue-shortkey")) Vue.config.productionTip = false Vue.use(VueAxios, axios) @@ -220,6 +95,5 @@ new Vue({ router, store, vuetify, - VueApollo, render: (h) => h(App) }).$mount("#app") diff --git a/src/plugins/apollo.js b/src/plugins/apollo.js deleted file mode 100644 index 89e753f..0000000 --- a/src/plugins/apollo.js +++ /dev/null @@ -1,23 +0,0 @@ -import ApolloClient, { InMemoryCache } from "apollo-boost" -import Vue from "vue" - -const cache = new InMemoryCache() - -const apolloClient = new ApolloClient({ - uri: "/graphql", - headers: { - CompassAPIKey: localStorage.getItem("apiKey"), - compassInstance: localStorage.getItem("schoolInstance") - }, - cache -}) - -Object.defineProperties(Vue.prototype, { - $apollo: { - get() { - return apolloClient - } - } -}) - -export default apolloClient diff --git a/src/views/Admin/AdminThemes.vue b/src/views/Admin/AdminThemes.vue index 33dca45..7cd578a 100644 --- a/src/views/Admin/AdminThemes.vue +++ b/src/views/Admin/AdminThemes.vue @@ -44,7 +44,7 @@ export default { value: "name" }, { - text: "Compass User ID", + text: "User ID", value: "user.id" }, { diff --git a/src/views/Communications/CommunicationsChat.vue b/src/views/Communications/CommunicationsChat.vue index ce8dab7..4c330a3 100644 --- a/src/views/Communications/CommunicationsChat.vue +++ b/src/views/Communications/CommunicationsChat.vue @@ -300,29 +300,6 @@

- - -

BetterCompass

-

- {{ embed.compass.name }} -

-

- {{ embed.compass.displayName }} -

-
-