Database now stores hashes of recently used IP's for all users. They are retained for seven days AFTER their LAST use.
This commit is contained in:
parent
6b0a73e1c8
commit
6e785dc211
|
|
@ -52,12 +52,7 @@ module.exports = class{
|
||||||
//if everything looks good, admit the connection to the channel
|
//if everything looks good, admit the connection to the channel
|
||||||
socket.join(socket.chan);
|
socket.join(socket.chan);
|
||||||
|
|
||||||
//Make sure the client receives important client-info before userlist
|
await userObj.handleConnection(userDB, chanDB, socket)
|
||||||
//await this.sendClientMetadata(userDB, socket);
|
|
||||||
await userObj.sendClientMetadata();
|
|
||||||
await userObj.sendSiteEmotes();
|
|
||||||
await userObj.sendChanEmotes(chanDB);
|
|
||||||
await userObj.sendPersonalEmotes(userDB);
|
|
||||||
|
|
||||||
//Send out the userlist
|
//Send out the userlist
|
||||||
this.broadcastUserList(socket.chan);
|
this.broadcastUserList(socket.chan);
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,19 @@ module.exports = class{
|
||||||
this.sockets = [socket.id];
|
this.sockets = [socket.id];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async handleConnection(userDB, chanDB, socket){
|
||||||
|
//send metadata to client
|
||||||
|
await this.sendClientMetadata();
|
||||||
|
|
||||||
|
//Send out emotes
|
||||||
|
await this.sendSiteEmotes();
|
||||||
|
await this.sendChanEmotes(chanDB);
|
||||||
|
await this.sendPersonalEmotes(userDB);
|
||||||
|
|
||||||
|
//Tattoo hashed IP address to user account for seven days
|
||||||
|
await userDB.tattooIPRecord(socket.handshake.address);
|
||||||
|
}
|
||||||
|
|
||||||
socketCrawl(cb){
|
socketCrawl(cb){
|
||||||
//Crawl through user's sockets (lol)
|
//Crawl through user's sockets (lol)
|
||||||
this.sockets.forEach((sockid) => {
|
this.sockets.forEach((sockid) => {
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,10 @@ GNU Affero General Public License for more details.
|
||||||
You should have received a copy of the GNU Affero General Public License
|
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/>.*/
|
along with this program. If not, see <https://www.gnu.org/licenses/>.*/
|
||||||
|
|
||||||
|
|
||||||
|
//Built-In Imports
|
||||||
|
const crypto = require('crypto');
|
||||||
|
|
||||||
//NPM Imports
|
//NPM Imports
|
||||||
const {mongoose} = require('mongoose');
|
const {mongoose} = require('mongoose');
|
||||||
|
|
||||||
|
|
@ -115,6 +119,22 @@ const userSchema = new mongoose.Schema({
|
||||||
enum: emoteModel.typeEnum,
|
enum: emoteModel.typeEnum,
|
||||||
default: emoteModel.typeEnum[0]
|
default: emoteModel.typeEnum[0]
|
||||||
}
|
}
|
||||||
|
}],
|
||||||
|
recentIPs: [{
|
||||||
|
ipHash: {
|
||||||
|
type: mongoose.SchemaTypes.String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
firstLog: {
|
||||||
|
type: mongoose.SchemaTypes.Date,
|
||||||
|
required: true,
|
||||||
|
default: new Date()
|
||||||
|
},
|
||||||
|
lastLog: {
|
||||||
|
type: mongoose.SchemaTypes.Date,
|
||||||
|
required: true,
|
||||||
|
default: new Date()
|
||||||
|
}
|
||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -143,7 +163,9 @@ userSchema.pre('save', async function (next){
|
||||||
this.flair = flairDB._id;
|
this.flair = flairDB._id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//If rank was changed
|
||||||
if(this.isModified("rank")){
|
if(this.isModified("rank")){
|
||||||
|
//force a full log-out
|
||||||
await this.killAllSessions("Your site-wide rank has changed. Sign-in required.");
|
await this.killAllSessions("Your site-wide rank has changed. Sign-in required.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -302,6 +324,30 @@ userSchema.statics.getUserList = async function(fullList = false){
|
||||||
return userList;
|
return userList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
userSchema.statics.processAgedIPRecords = async function(){
|
||||||
|
//Pull full userlist
|
||||||
|
const users = await this.find({});
|
||||||
|
|
||||||
|
//for every user
|
||||||
|
users.forEach((userDB) => {
|
||||||
|
//For every recent ip within the user
|
||||||
|
userDB.recentIPs.forEach((record, recordI) => {
|
||||||
|
//Check how long it's been since we've last seen the IP
|
||||||
|
const daysSinceLastUse = ((new Date() - record.lastLog) / (1000 * 60 * 60 * 24)).toFixed(1);
|
||||||
|
|
||||||
|
//If it's been more than a week
|
||||||
|
if(daysSinceLastUse >= 7){
|
||||||
|
//Splice out the IP record
|
||||||
|
userDB.recentIPs.splice(recordI, 1);
|
||||||
|
//No reason to wait on this since we're done with this user
|
||||||
|
userDB.save();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//methods
|
//methods
|
||||||
userSchema.methods.checkPass = function(pass){
|
userSchema.methods.checkPass = function(pass){
|
||||||
return hashUtil.comparePassword(pass, this.pass);
|
return hashUtil.comparePassword(pass, this.pass);
|
||||||
|
|
@ -328,9 +374,7 @@ userSchema.methods.getAuthenticatedSessions = async function(){
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
resolve(returnArr);
|
resolve(returnArr);
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -412,6 +456,49 @@ userSchema.methods.deleteEmote = async function(name){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
userSchema.methods.tattooIPRecord = async function(ip){
|
||||||
|
//Create hash
|
||||||
|
const hashObj = crypto.createHash('md5');
|
||||||
|
|
||||||
|
//add IP to the hash
|
||||||
|
hashObj.update(ip);
|
||||||
|
|
||||||
|
//Store the IP hash as a string
|
||||||
|
const ipHash = hashObj.digest('hex');
|
||||||
|
|
||||||
|
//Look for a pre-existing entry for this ipHash
|
||||||
|
const foundIndex = this.recentIPs.findIndex(checkHash);
|
||||||
|
|
||||||
|
//If there is no entry
|
||||||
|
if(foundIndex == -1){
|
||||||
|
//create record object
|
||||||
|
const record = {
|
||||||
|
ipHash: ipHash,
|
||||||
|
firstLog: new Date(),
|
||||||
|
lastLog: new Date()
|
||||||
|
};
|
||||||
|
|
||||||
|
//Pop it into place
|
||||||
|
this.recentIPs.push(record);
|
||||||
|
|
||||||
|
//Save the user doc
|
||||||
|
await this.save();
|
||||||
|
//Otherwise, if we already have a record for this IP
|
||||||
|
}else{
|
||||||
|
//Update the last logged date for the found record
|
||||||
|
this.recentIPs[foundIndex].lastLog = new Date();
|
||||||
|
|
||||||
|
//Save the user doc
|
||||||
|
await this.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Look for matching ip record
|
||||||
|
function checkHash(ipRecord){
|
||||||
|
//return matching records
|
||||||
|
return ipRecord.ipHash == ipHash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//note: if you gotta call this from a request authenticated by it's user, make sure to kill that session first!
|
//note: if you gotta call this from a request authenticated by it's user, make sure to kill that session first!
|
||||||
userSchema.methods.killAllSessions = async function(reason = "A full log-out from all devices was requested for your account."){
|
userSchema.methods.killAllSessions = async function(reason = "A full log-out from all devices was requested for your account."){
|
||||||
//get authenticated sessions
|
//get authenticated sessions
|
||||||
|
|
|
||||||
|
|
@ -18,15 +18,22 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.*/
|
||||||
const cron = require('node-cron');
|
const cron = require('node-cron');
|
||||||
|
|
||||||
//Local Imports
|
//Local Imports
|
||||||
|
const {userModel} = require('../schemas/userSchema');
|
||||||
const userBanModel = require('../schemas/userBanSchema');
|
const userBanModel = require('../schemas/userBanSchema');
|
||||||
const channelModel = require('../schemas/channel/channelSchema');
|
const channelModel = require('../schemas/channel/channelSchema');
|
||||||
|
|
||||||
module.exports.schedule = function(){
|
module.exports.schedule = function(){
|
||||||
|
//Process hashed IP Records that haven't been recorded in a week or more
|
||||||
|
cron.schedule('0 0 * * *', ()=>{userModel.processAgedIPRecords()},{scheduled: true, timezone: "UTC"});
|
||||||
//Process expired global bans every night at midnight
|
//Process expired global bans every night at midnight
|
||||||
cron.schedule('0 0 * * *', ()=>{userBanModel.processExpiredBans()},{scheduled: true, timezone: "UTC"});
|
cron.schedule('0 0 * * *', ()=>{userBanModel.processExpiredBans()},{scheduled: true, timezone: "UTC"});
|
||||||
|
//Process expired channel bans every night at midnight
|
||||||
|
cron.schedule('0 0 * * *', ()=>{channelModel.processExpiredBans()},{scheduled: true, timezone: "UTC"});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.kickoff = function(){
|
module.exports.kickoff = function(){
|
||||||
|
//Process Hashed IP Records that haven't been recorded in a week or more
|
||||||
|
userModel.processAgedIPRecords();
|
||||||
//Process expired global bans that may have expired since last restart
|
//Process expired global bans that may have expired since last restart
|
||||||
userBanModel.processExpiredBans()
|
userBanModel.processExpiredBans()
|
||||||
//Process expired channel bans that may haven ot expired since last restart
|
//Process expired channel bans that may haven ot expired since last restart
|
||||||
|
|
|
||||||
|
|
@ -48,9 +48,8 @@ module.exports.authenticateSession = async function(user, pass, req){
|
||||||
rank: userDB.rank
|
rank: userDB.rank
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Tattoo hashed IP address to user account for seven days
|
||||||
//userDB.activeSessions.push(sessionData);
|
userDB.tattooIPRecord(req.ip);
|
||||||
await userDB.save();
|
|
||||||
|
|
||||||
//return user
|
//return user
|
||||||
return userDB.user;
|
return userDB.user;
|
||||||
|
|
|
||||||
|
|
@ -165,7 +165,6 @@ class panelObj{
|
||||||
}
|
}
|
||||||
|
|
||||||
closer(){
|
closer(){
|
||||||
console.log('closer');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue