This was an old attempt at gracefully unloading channels that still had pending callbacks. Its implementation was always flawed, and the number of places where it was used is small enough to replace with straightforward checks for whether the channel has been unloaded after an asynchronous operation. Hopefully fixes the stuck 0 user channels issue.
592 lines
19 KiB
JavaScript
592 lines
19 KiB
JavaScript
const assert = require('assert');
|
|
const KickbanModule = require('../../lib/channel/kickban');
|
|
const database = require('../../lib/database');
|
|
const Promise = require('bluebird');
|
|
const testDB = require('../testutil/db').testDB;
|
|
|
|
database.init(testDB);
|
|
|
|
describe('KickbanModule', () => {
|
|
const channelName = `test_${Math.random().toString(31).substring(2)}`;
|
|
|
|
let mockChannel;
|
|
let mockUser;
|
|
let kickban;
|
|
|
|
beforeEach(() => {
|
|
mockChannel = {
|
|
name: channelName,
|
|
logger: {
|
|
log() { }
|
|
},
|
|
modules: {
|
|
permissions: {
|
|
canBan() {
|
|
return true;
|
|
}
|
|
}
|
|
},
|
|
users: []
|
|
};
|
|
|
|
mockUser = {
|
|
getName() {
|
|
return 'The_Admin';
|
|
},
|
|
|
|
getLowerName() {
|
|
return 'the_admin';
|
|
},
|
|
|
|
socket: {
|
|
emit(frame) {
|
|
if (frame === 'errorMsg') {
|
|
throw new Error(arguments[1].msg);
|
|
}
|
|
}
|
|
},
|
|
|
|
account: {
|
|
effectiveRank: 3
|
|
}
|
|
};
|
|
|
|
kickban = new KickbanModule(mockChannel);
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await database.getDB().runTransaction(async tx => {
|
|
await tx.table('channel_bans')
|
|
.where({ channel: channelName })
|
|
.del();
|
|
await tx.table('channel_ranks')
|
|
.where({ channel: channelName })
|
|
.del();
|
|
});
|
|
});
|
|
|
|
function patch(fn, after) {
|
|
let existing = kickban[fn];
|
|
kickban[fn] = async function () {
|
|
try {
|
|
await existing.apply(this, arguments)
|
|
} finally {
|
|
after();
|
|
}
|
|
};
|
|
}
|
|
|
|
describe('#handleCmdBan', () => {
|
|
it('inserts a valid ban', done => {
|
|
let kicked = false;
|
|
|
|
patch('banName', () => {
|
|
assert(kicked, 'Expected user to be kicked');
|
|
|
|
database.getDB().runTransaction(async tx => {
|
|
const ban = await tx.table('channel_bans')
|
|
.where({
|
|
channel: channelName,
|
|
name: 'test_user'
|
|
})
|
|
.first();
|
|
|
|
assert.strictEqual(ban.ip, '*');
|
|
assert.strictEqual(ban.reason, 'because reasons');
|
|
assert.strictEqual(ban.bannedby, mockUser.getName());
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
mockChannel.users = [{
|
|
getLowerName() {
|
|
return 'test_user';
|
|
},
|
|
|
|
kick(reason) {
|
|
assert.strictEqual(reason, "You're banned!");
|
|
kicked = true;
|
|
}
|
|
}];
|
|
|
|
kickban.handleCmdBan(
|
|
mockUser,
|
|
'/ban test_user because reasons',
|
|
{}
|
|
);
|
|
});
|
|
|
|
it('rejects if the user does not have ban permission', done => {
|
|
mockUser.socket.emit = (frame, obj) => {
|
|
if (frame === 'errorMsg') {
|
|
assert.strictEqual(
|
|
obj.msg,
|
|
'You do not have ban permissions on this channel'
|
|
);
|
|
|
|
done();
|
|
}
|
|
};
|
|
|
|
mockChannel.modules.permissions.canBan = () => false;
|
|
|
|
kickban.handleCmdBan(
|
|
mockUser,
|
|
'/ban test_user because reasons',
|
|
{}
|
|
);
|
|
});
|
|
|
|
it('rejects if the user tries to ban themselves', done => {
|
|
let costanza = false;
|
|
|
|
mockUser.socket.emit = (frame, obj) => {
|
|
if (frame === 'errorMsg') {
|
|
assert.strictEqual(
|
|
obj.msg,
|
|
'You cannot ban yourself'
|
|
);
|
|
|
|
if (!costanza) {
|
|
throw new Error('Expected costanza for banning self');
|
|
}
|
|
|
|
done();
|
|
} else if (frame === 'costanza') {
|
|
assert.strictEqual(
|
|
obj.msg,
|
|
"You can't ban yourself"
|
|
);
|
|
|
|
costanza = true;
|
|
}
|
|
};
|
|
|
|
kickban.handleCmdBan(
|
|
mockUser,
|
|
'/ban the_Admin because reasons',
|
|
{}
|
|
);
|
|
});
|
|
|
|
it('rejects if the user is ranked below the ban recipient', done => {
|
|
database.getDB().runTransaction(tx => {
|
|
return tx.table('channel_ranks')
|
|
.insert({
|
|
channel: channelName,
|
|
name: 'test_user',
|
|
rank: 5
|
|
});
|
|
}).then(() => {
|
|
mockUser.socket.emit = (frame, obj) => {
|
|
if (frame === 'errorMsg') {
|
|
assert.strictEqual(
|
|
obj.msg,
|
|
"You don't have permission to ban test_user"
|
|
);
|
|
|
|
done();
|
|
}
|
|
};
|
|
|
|
kickban.handleCmdBan(
|
|
mockUser,
|
|
'/ban test_user because reasons',
|
|
{}
|
|
);
|
|
});
|
|
});
|
|
|
|
it('rejects if the the ban recipient is already banned', done => {
|
|
database.getDB().runTransaction(tx => {
|
|
return tx.table('channel_bans')
|
|
.insert({
|
|
channel: channelName,
|
|
name: 'test_user',
|
|
ip: '*',
|
|
bannedby: 'somebody',
|
|
reason: 'I dunno'
|
|
});
|
|
}).then(() => {
|
|
mockUser.socket.emit = (frame, obj) => {
|
|
if (frame === 'errorMsg') {
|
|
assert.strictEqual(
|
|
obj.msg,
|
|
'test_user is already banned'
|
|
);
|
|
|
|
done();
|
|
}
|
|
};
|
|
|
|
kickban.handleCmdBan(
|
|
mockUser,
|
|
'/ban test_user because reasons',
|
|
{}
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('#handleCmdIPBan', () => {
|
|
beforeEach(async () => {
|
|
await database.getDB().runTransaction(async tx => {
|
|
await tx.table('aliases')
|
|
.insert([{
|
|
name: 'test_user',
|
|
ip: '1.2.3.4',
|
|
time: Date.now()
|
|
}]);
|
|
});
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await database.getDB().runTransaction(async tx => {
|
|
await tx.table('aliases')
|
|
.where({ name: 'test_user' })
|
|
.orWhere({ ip: '1.2.3.4' })
|
|
.del();
|
|
});
|
|
});
|
|
|
|
it('inserts a valid ban', done => {
|
|
let firstUserKicked = false;
|
|
let secondUserKicked = false;
|
|
|
|
patch('banAll', () => {
|
|
assert(firstUserKicked, 'Expected banned user to be kicked');
|
|
assert(
|
|
secondUserKicked,
|
|
'Expected user with banned IP to be kicked'
|
|
);
|
|
|
|
database.getDB().runTransaction(async tx => {
|
|
const nameBan = await tx.table('channel_bans')
|
|
.where({
|
|
channel: channelName,
|
|
name: 'test_user',
|
|
ip: '*'
|
|
})
|
|
.first();
|
|
|
|
assert.strictEqual(nameBan.reason, 'because reasons');
|
|
assert.strictEqual(nameBan.bannedby, mockUser.getName());
|
|
|
|
const ipBan = await tx.table('channel_bans')
|
|
.where({
|
|
channel: channelName,
|
|
ip: '1.2.3.4'
|
|
})
|
|
.first();
|
|
|
|
assert.strictEqual(ipBan.name, 'test_user');
|
|
assert.strictEqual(ipBan.reason, 'because reasons');
|
|
assert.strictEqual(ipBan.bannedby, mockUser.getName());
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
mockChannel.users = [{
|
|
getLowerName() {
|
|
return 'test_user';
|
|
},
|
|
|
|
realip: '1.2.3.4',
|
|
|
|
kick(reason) {
|
|
assert.strictEqual(reason, "You're banned!");
|
|
firstUserKicked = true;
|
|
}
|
|
}, {
|
|
getLowerName() {
|
|
return 'second_user_same_ip';
|
|
},
|
|
|
|
realip: '1.2.3.4',
|
|
|
|
kick(reason) {
|
|
assert.strictEqual(reason, "You're banned!");
|
|
secondUserKicked = true;
|
|
}
|
|
}];
|
|
|
|
kickban.handleCmdIPBan(
|
|
mockUser,
|
|
'/ipban test_user because reasons',
|
|
{}
|
|
);
|
|
});
|
|
|
|
it('inserts a valid range ban', done => {
|
|
patch('banIP', () => {
|
|
database.getDB().runTransaction(async tx => {
|
|
const ipBan = await tx.table('channel_bans')
|
|
.where({
|
|
channel: channelName,
|
|
ip: '1.2.3'
|
|
})
|
|
.first();
|
|
|
|
assert.strictEqual(ipBan.name, 'test_user');
|
|
assert.strictEqual(ipBan.reason, 'because reasons');
|
|
assert.strictEqual(ipBan.bannedby, mockUser.getName());
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
kickban.handleCmdIPBan(
|
|
mockUser,
|
|
'/ipban test_user range because reasons',
|
|
{}
|
|
);
|
|
});
|
|
|
|
it('inserts a valid wide-range ban', done => {
|
|
patch('banIP', () => {
|
|
database.getDB().runTransaction(async tx => {
|
|
const ipBan = await tx.table('channel_bans')
|
|
.where({
|
|
channel: channelName,
|
|
ip: '1.2'
|
|
})
|
|
.first();
|
|
|
|
assert.strictEqual(ipBan.name, 'test_user');
|
|
assert.strictEqual(ipBan.reason, 'because reasons');
|
|
assert.strictEqual(ipBan.bannedby, mockUser.getName());
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
kickban.handleCmdIPBan(
|
|
mockUser,
|
|
'/ipban test_user wrange because reasons',
|
|
{}
|
|
);
|
|
});
|
|
|
|
it('inserts a valid IPv6 ban', done => {
|
|
const longIP = require('../../lib/utilities').expandIPv6('::abcd');
|
|
|
|
patch('banAll', () => {
|
|
database.getDB().runTransaction(async tx => {
|
|
const ipBan = await tx.table('channel_bans')
|
|
.where({
|
|
channel: channelName,
|
|
ip: longIP
|
|
})
|
|
.first();
|
|
|
|
assert.strictEqual(ipBan.name, 'test_user');
|
|
assert.strictEqual(ipBan.reason, 'because reasons');
|
|
assert.strictEqual(ipBan.bannedby, mockUser.getName());
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
database.getDB().runTransaction(async tx => {
|
|
await tx.table('aliases')
|
|
.insert({
|
|
name: 'test_user',
|
|
ip: longIP,
|
|
time: Date.now()
|
|
});
|
|
}).then(() => {
|
|
kickban.handleCmdIPBan(
|
|
mockUser,
|
|
'/ipban test_user because reasons',
|
|
{}
|
|
);
|
|
});
|
|
});
|
|
|
|
it('rejects if the user does not have ban permission', done => {
|
|
mockUser.socket.emit = (frame, obj) => {
|
|
if (frame === 'errorMsg') {
|
|
assert.strictEqual(
|
|
obj.msg,
|
|
'You do not have ban permissions on this channel'
|
|
);
|
|
|
|
done();
|
|
}
|
|
};
|
|
|
|
mockChannel.modules.permissions.canBan = () => false;
|
|
|
|
kickban.handleCmdIPBan(
|
|
mockUser,
|
|
'/ipban test_user because reasons',
|
|
{}
|
|
);
|
|
});
|
|
|
|
it('rejects if the user tries to ban themselves', done => {
|
|
let costanza = false;
|
|
|
|
mockUser.socket.emit = (frame, obj) => {
|
|
if (frame === 'errorMsg') {
|
|
assert.strictEqual(
|
|
obj.msg,
|
|
'You cannot ban yourself'
|
|
);
|
|
|
|
if (!costanza) {
|
|
throw new Error('Expected costanza for banning self');
|
|
}
|
|
|
|
done();
|
|
} else if (frame === 'costanza') {
|
|
assert.strictEqual(
|
|
obj.msg,
|
|
"You can't ban yourself"
|
|
);
|
|
|
|
costanza = true;
|
|
}
|
|
};
|
|
|
|
kickban.handleCmdIPBan(
|
|
mockUser,
|
|
'/ipban the_Admin because reasons',
|
|
{}
|
|
);
|
|
});
|
|
|
|
it('rejects if the user is ranked below the ban recipient', done => {
|
|
database.getDB().runTransaction(tx => {
|
|
return tx.table('channel_ranks')
|
|
.insert({
|
|
channel: channelName,
|
|
name: 'test_user',
|
|
rank: 5
|
|
});
|
|
}).then(() => {
|
|
mockUser.socket.emit = (frame, obj) => {
|
|
if (frame === 'errorMsg') {
|
|
assert.strictEqual(
|
|
obj.msg,
|
|
"You don't have permission to ban IP " +
|
|
"09l.TFb.5To.HBB"
|
|
);
|
|
|
|
done();
|
|
}
|
|
};
|
|
|
|
kickban.handleCmdIPBan(
|
|
mockUser,
|
|
'/ipban test_user because reasons',
|
|
{}
|
|
);
|
|
});
|
|
});
|
|
|
|
it('rejects if the user is ranked below an alias of the ban recipient', done => {
|
|
database.getDB().runTransaction(async tx => {
|
|
await tx.table('channel_ranks')
|
|
.insert({
|
|
channel: channelName,
|
|
name: 'another_user',
|
|
rank: 5
|
|
});
|
|
await tx.table('aliases')
|
|
.insert({
|
|
name: 'another_user',
|
|
ip: '1.2.3.3', // different IP, same /24 range
|
|
time: Date.now()
|
|
});
|
|
}).then(() => {
|
|
mockUser.socket.emit = (frame, obj) => {
|
|
if (frame === 'errorMsg') {
|
|
assert.strictEqual(
|
|
obj.msg,
|
|
"You don't have permission to ban IP " +
|
|
"09l.TFb.5To.*"
|
|
);
|
|
|
|
done();
|
|
}
|
|
};
|
|
|
|
kickban.handleCmdIPBan(
|
|
mockUser,
|
|
'/ipban test_user range because reasons',
|
|
{}
|
|
);
|
|
});
|
|
});
|
|
|
|
it('rejects if the the ban recipient IP is already banned', done => {
|
|
database.getDB().runTransaction(tx => {
|
|
return tx.table('channel_bans')
|
|
.insert({
|
|
channel: channelName,
|
|
name: 'another_user',
|
|
ip: '1.2.3.4',
|
|
bannedby: 'somebody',
|
|
reason: 'I dunno'
|
|
});
|
|
}).then(() => {
|
|
mockUser.socket.emit = (frame, obj) => {
|
|
if (frame === 'errorMsg') {
|
|
assert.strictEqual(
|
|
obj.msg,
|
|
'09l.TFb.5To.HBB is already banned'
|
|
);
|
|
|
|
done();
|
|
}
|
|
};
|
|
|
|
kickban.handleCmdIPBan(
|
|
mockUser,
|
|
'/ipban test_user because reasons',
|
|
{}
|
|
);
|
|
});
|
|
});
|
|
|
|
it('still adds the IP ban even if the name is already banned', done => {
|
|
patch('banIP', () => {
|
|
database.getDB().runTransaction(async tx => {
|
|
const ipBan = await tx.table('channel_bans')
|
|
.where({
|
|
channel: channelName,
|
|
ip: '1.2.3.4'
|
|
})
|
|
.first();
|
|
|
|
assert.strictEqual(ipBan.name, 'test_user');
|
|
assert.strictEqual(ipBan.reason, 'because reasons');
|
|
assert.strictEqual(ipBan.bannedby, mockUser.getName());
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
database.getDB().runTransaction(tx => {
|
|
return tx.table('channel_bans')
|
|
.insert({
|
|
channel: channelName,
|
|
name: 'test_user',
|
|
ip: '*',
|
|
bannedby: 'somebody',
|
|
reason: 'I dunno'
|
|
});
|
|
}).then(() => {
|
|
kickban.handleCmdIPBan(
|
|
mockUser,
|
|
'/ipban test_user because reasons',
|
|
{}
|
|
);
|
|
});
|
|
});
|
|
});
|
|
});
|