Skip to content
This repository has been archived by the owner on Mar 28, 2023. It is now read-only.

Past bans #98

Merged
merged 12 commits into from
Mar 1, 2021
22 changes: 22 additions & 0 deletions migrations/v4.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
PRAGMA foreign_keys = OFF;

-- Remove FOREIGN KEY constraint from ip_bans
CREATE TABLE ip_bans_new(
ipid TEXT PRIMARY KEY,
ban_id INTEGER NOT NULL,
FOREIGN KEY (ban_id) REFERENCES bans(ban_id)
ON DELETE CASCADE
);
INSERT INTO ip_bans_new SELECT * FROM ip_bans;
DROP TABLE ip_bans;
ALTER TABLE ip_bans_new RENAME TO ip_bans;

-- Add unbanned column
ALTER TABLE bans ADD unbanned INTEGER DEFAULT 0;

PRAGMA foreign_key_check;
PRAGMA foreign_keys = ON;

VACUUM;

PRAGMA user_version = 4;
33 changes: 18 additions & 15 deletions server/commands/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -396,21 +396,24 @@ def ooc_cmd_baninfo(client, arg):
if lookup_type not in ('ban_id', 'ipid', 'hdid'):
raise ArgumentError('Incorrect lookup type.')

ban = database.find_ban(**{lookup_type: args[0]})
if ban is None:
bans = database.ban_history(**{lookup_type: args[0]})
if bans is None:
client.send_ooc('No ban found for this ID.')
else:
msg = f'Ban ID: {ban.ban_id}\n'
msg += 'Affected IPIDs: ' + ', '.join([str(ipid) for ipid in ban.ipids]) + '\n'
msg += 'Affected HDIDs: ' + ', '.join(ban.hdids) + '\n'
msg += f'Reason: "{ban.reason}"\n'
msg += f'Banned by: {ban.banned_by_name} ({ban.banned_by})\n'

ban_date = arrow.get(ban.ban_date)
msg += f'Banned on: {ban_date.format()} ({ban_date.humanize()})\n'
if ban.unban_date is not None:
unban_date = arrow.get(ban.unban_date)
msg += f'Unban date: {unban_date.format()} ({unban_date.humanize()})'
else:
msg += 'Unban date: N/A'
msg = f'Bans for {args[0]}'
for ban in bans:
msg += f'\nBan ID: {ban.ban_id}\n'
msg += 'Affected IPIDs: ' + ', '.join([str(ipid) for ipid in ban.ipids]) + '\n'
msg += 'Affected HDIDs: ' + ', '.join(ban.hdids) + '\n'
msg += f'Reason: "{ban.reason}"\n'
msg += 'Unbanned: {}\n'.format(bool(ban.unbanned))
msg += f'Banned by: {ban.banned_by_name} ({ban.banned_by})\n'

ban_date = arrow.get(ban.ban_date)
msg += f'Banned on: {ban_date.format()} ({ban_date.humanize()})\n'
if ban.unban_date is not None:
unban_date = arrow.get(ban.unban_date)
msg += f'Unban date: {unban_date.format()} ({unban_date.humanize()})'
else:
msg += 'Unban date: N/A'
client.send_ooc(msg)
31 changes: 26 additions & 5 deletions server/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ def migrate_json_to_v1(self):
logger.debug('Migration to v1 complete')

def migrate(self):
for version in [2, 3]:
for version in [2, 3, 4]:
self.migrate_to_version(version)

def migrate_to_version(self, version):
Expand Down Expand Up @@ -216,6 +216,7 @@ class Ban:
unban_date: datetime
banned_by: int
reason: str
unbanned: int

def __post_init__(self):
self.ban_date = arrow.get(self.ban_date).datetime
Expand Down Expand Up @@ -269,19 +270,39 @@ def find_ban(self, ipid=None, hdid=None, ban_id=None):
UNION SELECT ban_id FROM hdid_bans WHERE hdid = ?
UNION SELECT ban_id FROM bans WHERE ban_id = ?
)
JOIN bans USING (ban_id)
JOIN bans USING (ban_id) WHERE unbanned = 0
'''), (ipid, hdid, ban_id)).fetchone()
if ban is not None:
return Database.Ban(**ban)
else:
return None

def ban_history(self, ipid=None, hdid=None, ban_id=None):
"""Check if an IPID and/or HDID has been banned in the past."""
with self.db as conn:
bans = conn.execute(dedent('''
SELECT *
FROM (
SELECT ban_id FROM ip_bans WHERE ipid = ?
UNION SELECT ban_id FROM hdid_bans WHERE hdid = ?
UNION SELECT ban_id FROM bans WHERE ban_id = ?
)
JOIN bans USING (ban_id)
'''), (ipid, hdid, ban_id)).fetchall()
if bans is not None:
history = []
for ban in bans:
history.append(Database.Ban(**ban))
return history
else:
return None

def unban(self, ban_id):
"""Remove a ban entry."""
event_logger.info(f'Unbanning {ban_id}')
with self.db as conn:
unbans = conn.execute(dedent('''
DELETE FROM bans WHERE ban_id = ?
UPDATE bans SET unbanned = 1 WHERE ban_id = ?
'''), (ban_id,)).rowcount
return unbans > 0

Expand All @@ -298,7 +319,7 @@ def schedule_unbans(self):
with self.db as conn:
dated_bans = conn.execute(dedent('''
SELECT ban_id FROM bans
WHERE unban_date IS NOT NULL AND
WHERE unban_date IS NOT NULL AND unbanned = 0 AND
datetime(unban_date) < datetime(?, '+12 hours')
'''), (arrow.utcnow().datetime,)).fetchall()

Expand All @@ -308,7 +329,7 @@ def schedule_unbans(self):
def _schedule_unban(self, ban_id):
with self.db as conn:
ban = conn.execute(dedent('''
SELECT unban_date FROM bans WHERE ban_id = ?
SELECT unban_date FROM bans WHERE unbanned = 0 AND ban_id = ?
'''), (ban_id,)).fetchone()
time_to_unban = (arrow.get(ban['unban_date']) - arrow.utcnow()).total_seconds()

Expand Down