Added alt-detection. Just need to set up pre-delete for userModel function to remove refrences to itself on alt accounts
This commit is contained in:
parent
6e785dc211
commit
980c84afff
14 changed files with 346 additions and 12 deletions
|
|
@ -35,10 +35,10 @@ module.exports.post = async function(req, res){
|
|||
//if we found any related nuked bans
|
||||
if(nukedBans != null){
|
||||
//Shit our pants!
|
||||
return errorHandler(res, 'Cannon re-create banned account!', 'unauthorized');
|
||||
return errorHandler(res, 'Cannot get alts for non-existant user!');
|
||||
}
|
||||
|
||||
await userModel.register(user)
|
||||
await userModel.register(user, req.ip);
|
||||
return res.sendStatus(200);
|
||||
}else{
|
||||
res.status(400);
|
||||
|
|
|
|||
46
src/controllers/tooltip/altListController.js
Normal file
46
src/controllers/tooltip/altListController.js
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024 Rainbownapkin and the TTN Community
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.*/
|
||||
|
||||
//NPM Imports
|
||||
const {validationResult, matchedData} = require('express-validator');
|
||||
|
||||
//local imports
|
||||
const {userModel} = require('../../schemas/userSchema');
|
||||
const {exceptionHandler, errorHandler} = require('../../utils/loggerUtils');
|
||||
|
||||
//root index functions
|
||||
module.exports.get = async function(req, res){
|
||||
try{
|
||||
const validResult = validationResult(req);
|
||||
|
||||
if(validResult.isEmpty()){
|
||||
const data = matchedData(req);
|
||||
const userDB = await userModel.findOne({user: data.user});
|
||||
|
||||
if(userDB == null){
|
||||
return errorHandler(res, 'Cannot re-create banned account!', 'unauthorized');
|
||||
}
|
||||
|
||||
return res.render('partial/tooltip/altList', {alts: await userDB.getAltProfiles()});
|
||||
}else{
|
||||
res.status(400);
|
||||
return res.send({errors: validResult.array()})
|
||||
}
|
||||
|
||||
}catch(err){
|
||||
return exceptionHandler(res, err);
|
||||
}
|
||||
}
|
||||
32
src/routers/tooltipRouter.js
Normal file
32
src/routers/tooltipRouter.js
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024 Rainbownapkin and the TTN Community
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.*/
|
||||
|
||||
//npm imports
|
||||
const { Router } = require('express');
|
||||
|
||||
|
||||
//local imports
|
||||
const altListController = require("../controllers/tooltip/altListController");
|
||||
const permissionSchema = require("../schemas/permissionSchema");
|
||||
const accountValidator = require("../validators/accountValidator");
|
||||
|
||||
//globals
|
||||
const router = Router();
|
||||
|
||||
//routing functions
|
||||
router.get('/altList', accountValidator.user(), permissionSchema.reqPermCheck("adminPanel"), altListController.get);
|
||||
|
||||
module.exports = router;
|
||||
|
|
@ -31,6 +31,7 @@ const permissionModel = require('./permissionSchema');
|
|||
const emoteModel = require('./emoteSchema');
|
||||
//Utils
|
||||
const hashUtil = require('../utils/hashUtils');
|
||||
const { profile } = require('console');
|
||||
|
||||
|
||||
const userSchema = new mongoose.Schema({
|
||||
|
|
@ -135,6 +136,10 @@ const userSchema = new mongoose.Schema({
|
|||
required: true,
|
||||
default: new Date()
|
||||
}
|
||||
}],
|
||||
alts:[{
|
||||
type: mongoose.SchemaTypes.ObjectID,
|
||||
ref: "user"
|
||||
}]
|
||||
});
|
||||
|
||||
|
|
@ -183,18 +188,33 @@ userSchema.pre('save', async function (next){
|
|||
});
|
||||
|
||||
//statics
|
||||
userSchema.statics.register = async function(userObj){
|
||||
userSchema.statics.register = async function(userObj, ip){
|
||||
//Pull values from user object
|
||||
const {user, pass, passConfirm, email} = userObj;
|
||||
|
||||
//Check password confirmation matches
|
||||
if(pass == passConfirm){
|
||||
const userDB = await this.findOne({$or: email ? [{user}, {email}] : [{user}]});
|
||||
//Look for a user (case insensitive)
|
||||
var userDB = await this.findOne({user: new RegExp(user, 'i')});
|
||||
|
||||
//If we didn't find a user and we submitted an email
|
||||
if(userDB == null && email != null){
|
||||
//Check to make sure no one's used this email address
|
||||
userDB = await this.findOne({email});
|
||||
}
|
||||
|
||||
//If the user is found or someones trying to impersonate tokeboi
|
||||
if(userDB || user.toLowerCase() == "tokebot"){
|
||||
throw new Error("User name/email already taken!");
|
||||
}else{
|
||||
//Increment the user count, pulling the id to tattoo to the user
|
||||
const id = await statModel.incrementUserCount();
|
||||
|
||||
//Create user document in the database
|
||||
const newUser = await this.create({id, user, pass, email});
|
||||
|
||||
//Tattoo the hashed IP used to register to the new user
|
||||
await newUser.tattooIPRecord(ip);
|
||||
}
|
||||
}else{
|
||||
throw new Error("Confirmation password doesn't match!");
|
||||
|
|
@ -397,6 +417,23 @@ userSchema.methods.getProfile = function(){
|
|||
return profile;
|
||||
}
|
||||
|
||||
userSchema.methods.getAltProfiles = async function(){
|
||||
//Create an empty list to hold alt profiles
|
||||
const profileList = [];
|
||||
|
||||
//populate the users alt list
|
||||
await this.populate('alts');
|
||||
|
||||
//For every alt for the current user
|
||||
for(let alt of this.alts){
|
||||
//get the alts profile and push it to the profile list
|
||||
profileList.push(alt.getProfile());
|
||||
}
|
||||
|
||||
//return our generated profile list
|
||||
return profileList;
|
||||
}
|
||||
|
||||
userSchema.methods.setFlair = async function(flair){
|
||||
//Find flair by name
|
||||
const flairDB = await flairModel.findOne({name: flair});
|
||||
|
|
@ -471,6 +508,10 @@ userSchema.methods.tattooIPRecord = async function(ip){
|
|||
|
||||
//If there is no entry
|
||||
if(foundIndex == -1){
|
||||
//Pull the entire userlist
|
||||
//TODO: update query to only pull users with recentIPs, so we aren't looping through inactive users
|
||||
const users = await this.model().find({});
|
||||
|
||||
//create record object
|
||||
const record = {
|
||||
ipHash: ipHash,
|
||||
|
|
@ -478,6 +519,35 @@ userSchema.methods.tattooIPRecord = async function(ip){
|
|||
lastLog: new Date()
|
||||
};
|
||||
|
||||
//We should really start using for loops and stop acting like its 2008
|
||||
//Though to be quite honest this bit would be particularly brutal without them
|
||||
//For every user in the userlist
|
||||
for(let curUser of users){
|
||||
//Ensure we're not checking the user against itself
|
||||
if(curUser._id != this._id){
|
||||
//For every IP record in the current user
|
||||
for(let curRecord of curUser.recentIPs){
|
||||
//If it matches the current ipHash
|
||||
if(curRecord.ipHash == ipHash){
|
||||
//Check if we've already marked the user as an alt
|
||||
const foundAlt = this.alts.indexOf(curUser._id);
|
||||
|
||||
//If these accounts aren't already marked as alts
|
||||
if(foundAlt == -1){
|
||||
//Add found user to this users alt list
|
||||
this.alts.push(curUser._id);
|
||||
|
||||
//add this user to found users alt list
|
||||
curUser.alts.push(this._id);
|
||||
|
||||
//Save changes to the found user, this user will save at the end of the function
|
||||
await curUser.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Pop it into place
|
||||
this.recentIPs.push(record);
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ const channelRouter = require('./routers/channelRouter');
|
|||
const newChannelRouter = require('./routers/newChannelRouter');
|
||||
const panelRouter = require('./routers/panelRouter');
|
||||
const popupRouter = require('./routers/popupRouter');
|
||||
const tooltipRouter = require('./routers/tooltipRouter');
|
||||
const apiRouter = require('./routers/apiRouter');
|
||||
|
||||
//Define Config
|
||||
|
|
@ -98,6 +99,8 @@ app.use('/newChannel', newChannelRouter);
|
|||
app.use('/panel', panelRouter);
|
||||
//Popup
|
||||
app.use('/popup', popupRouter);
|
||||
//tooltip
|
||||
app.use('/tooltip', tooltipRouter);
|
||||
//Bot-Ready
|
||||
app.use('/api', apiRouter);
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ const { check, body, checkSchema, checkExact} = require('express-validator');
|
|||
const {isRank} = require('./permissionsValidator');
|
||||
|
||||
module.exports = {
|
||||
user: (field = 'user') => body(field).escape().trim().isLength({min: 1, max: 22}),
|
||||
user: (field = 'user') => check(field).escape().trim().isLength({min: 1, max: 22}),
|
||||
|
||||
//Password security requirements may change over time, therefore we should only validate against strongPassword() when creating new accounts
|
||||
//that way we don't break old ones upon change
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.-->
|
|||
</a>
|
||||
</td>
|
||||
<td id="admin-user-list-entry-name-<%- curUser.user %>" class="admin-list-entry-item not-first-col">
|
||||
<a href="/profile/<%- curUser.user %>" class="admin-list-entry-item">
|
||||
<a href="/profile/<%- curUser.user %>" class="admin-list-entry-item admin-user-list-name" id="admin-user-list-name-<%- curUser.user %>">
|
||||
<%- curUser.user %>
|
||||
</a>
|
||||
</td>
|
||||
|
|
|
|||
28
src/views/partial/tooltip/altList.ejs
Normal file
28
src/views/partial/tooltip/altList.ejs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<!--Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024 Rainbownapkin and the TTN Community
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.-->
|
||||
<% if(alts.length > 0){ %>
|
||||
<h3 class="tooltip">Known Alts:</h3>
|
||||
<% for(let alt in alts){%>
|
||||
<div class="seperator"></div>
|
||||
<p class="tooltip">User: <%- alts[alt].user %></p>
|
||||
<p class="tooltip">ID: <%- alts[alt].id %></p>
|
||||
<p class="tooltip">Signature: <%- alts[alt].signature %></p>
|
||||
<p class="tooltip">Toke Count: <%- alts[alt].tokeCount %></p>
|
||||
<p class="tooltip">Joined: <%- alts[alt].date.toLocaleString() %></p>
|
||||
<% } %>
|
||||
<% }else{ %>
|
||||
<p class="tooltip">No alts detected...</p>
|
||||
<% } %>
|
||||
Loading…
Add table
Add a link
Reference in a new issue