Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion server/src/QRServer/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,14 +197,20 @@ def __init__(self):
config=self,
name='discord.bot.channel_user_notifications.id',
cli_args=[],
description='Discord Channel ID for user notifications such as registration, claiming of account or bans',
description='Discord Channel ID for user notifications such as registration or claiming of account',

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

claiming accounts or claiming an account

default_value='')
self.discord_bot_max_aliases = ConfigKey(
config=self,
name='discord.bot.max_aliases',
cli_args=[],
description='Maximum number of aliases per user',
default_value=1)
self.discord_bot_channel_ban_notifications_id = ConfigKey(
config=self,
name='discord.bot.channel_ban_notifications.id',
cli_args=[],
description='Discord Channel ID for sending notifications about user bans/unbans',
default_value='')

def get_key(self, name: str):
by_name = self.keys_by_name()
Expand Down
84 changes: 77 additions & 7 deletions server/src/QRServer/db/connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ async def close(self):
async def get_user(self, user_id: str) -> DbUser | None:
c = await self.conn.cursor()
await c.execute(
"select id, username, password, created_at, discord_user_id from users where id = ?", (
"select id, username, password, created_at, discord_user_id,"
" is_banned, banned_at, banned_by_dc_id, ban_reason from users where id = ?", (
user_id,
))
row = await c.fetchone()
Expand All @@ -48,12 +49,17 @@ async def get_user(self, user_id: str) -> DbUser | None:
password=row[2],
created_at=row[3],
discord_user_id=row[4],
is_banned=bool(row[5]),
banned_at=datetime.fromtimestamp(row[6], tz=timezone.utc) if row[6] else None,
banned_by_dc_id=row[7],
ban_reason=row[8],
)

async def get_user_by_username(self, username) -> DbUser | None:
c = await self.conn.cursor()
await c.execute(
"select id, username, password, created_at, discord_user_id from users where username = ?", (
"select id, username, password, created_at, discord_user_id,"
" is_banned, banned_at, banned_by_dc_id, ban_reason from users where username = ?", (
username,
))
row = await c.fetchone()
Expand All @@ -65,12 +71,17 @@ async def get_user_by_username(self, username) -> DbUser | None:
password=row[2],
created_at=row[3],
discord_user_id=row[4],
is_banned=bool(row[5]),
banned_at=datetime.fromtimestamp(row[6], tz=timezone.utc) if row[6] else None,
banned_by_dc_id=row[7],
ban_reason=row[8],
)

async def get_users_by_discord_id(self, discord_user_id: str) -> list[DbUser]:
c = await self.conn.cursor()
await c.execute(
"select id, username, password, created_at, discord_user_id from users where discord_user_id = ?", (
"select id, username, password, created_at, discord_user_id,"
"is_banned, banned_at, banned_by_dc_id, ban_reason from users where discord_user_id = ?", (
discord_user_id,
))
rows = await c.fetchall()
Expand All @@ -85,7 +96,11 @@ async def get_users_by_discord_id(self, discord_user_id: str) -> list[DbUser]:
username=row[1],
password=row[2],
created_at=row[3],
discord_user_id=row[4]
discord_user_id=row[4],
is_banned=bool(row[5]),
banned_at=datetime.fromtimestamp(row[6], tz=timezone.utc) if row[6] else None,
banned_by_dc_id=row[7],
ban_reason=row[8],
)
)
return result
Expand Down Expand Up @@ -123,8 +138,10 @@ async def authenticate_user(self, username: str, password: bytes | None, auto_cr
)
await self.conn.commit()

await c.execute("select id, username, password, created_at, discord_user_id from users where username = ?",
(username,))
await c.execute(
"select id, username, password, created_at, discord_user_id,"
"is_banned, banned_at, banned_by_dc_id, ban_reason from users where username = ?",
(username,))

row = await c.fetchone()
if row is None:
Expand All @@ -136,21 +153,30 @@ async def authenticate_user(self, username: str, password: bytes | None, auto_cr
password=row[2],
created_at=row[3],
discord_user_id=row[4],
is_banned=bool(row[5]),
banned_at=datetime.fromtimestamp(row[6], tz=timezone.utc) if row[6] else None,
banned_by_dc_id=row[7],
ban_reason=row[8],
)

if not db_user.is_guest and verify_password and not password_verify(password, db_user.password):
return None

return db_user

async def change_user_password(self, user_id: str, password: bytes | None):
async def change_user_password(self, user_id: str, password: bytes | None) -> bool:
"""
Returns:
bool: True if successfully changed password
"""
c = await self.conn.cursor()
await c.execute(
"update users set password = ? where id = ?", (
password_hash(password) if password else None,
user_id
))
await self.conn.commit()
return bool(c.rowcount)

async def claim_member(self, user_id: str, password: bytes | None, discord_user_id: str):
c = await self.conn.cursor()
Expand Down Expand Up @@ -719,6 +745,50 @@ async def add_duel_match(self, tournament_id: str, duel_idx: int, match_id: str)
await self.conn.commit()
return bool(c.rowcount)

async def ban_user(self, user_id: str, banned_by_dc_id: str, ban_reason: str) -> bool:
"""
Returns:
bool: True if succesfully banned
"""

c = await self.conn.cursor()
await c.execute(
"update users"
" set is_banned = ?,"
" banned_at = ?,"
" banned_by_dc_id = ?,"
" ban_reason = ?"
" where id = ? and is_banned is null",
(
True,
int(datetime.now(timezone.utc).timestamp()),
banned_by_dc_id,
ban_reason,
user_id,
)
)
await self.conn.commit()
return bool(c.rowcount)

async def unban_user(self, user_id: str) -> bool:
"""
Returns:
bool: True if succesfully unbanned
"""

c = await self.conn.cursor()
await c.execute(
"update users"
" set is_banned = null,"
" banned_at = null,"
" banned_by_dc_id = null,"
" ban_reason = null"
" where id = ? and is_banned is not null",
(user_id, )
)
await self.conn.commit()
return bool(c.rowcount)


async def create_connector(config) -> DbConnector:
data_dir = os.path.abspath(config.data_dir.get())
Expand Down
21 changes: 21 additions & 0 deletions server/src/QRServer/db/migrations.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ async def execute_migrations(c, config: Config, max_version=None):
_migration_upgrade_to_v6,
_migration_upgrade_to_v7,
_migration_upgrade_to_v8,
_migration_upgrade_to_v9,
]

for i in range(max_version if max_version and max_version <= len(migrations) else len(migrations)):
Expand Down Expand Up @@ -255,3 +256,23 @@ async def _migration_upgrade_to_v8(c, _config):
)

await _set_version(c, 8)


async def _migration_upgrade_to_v9(c, config):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be better to store bans in a separate table and join?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe, can do, will do

await c.execute(
"alter table users"
" add column is_banned integer"
)
await c.execute(
"alter table users"
" add column banned_at integer"
)
await c.execute(
"alter table users"
" add column banned_by_dc_id varchar"
)
await c.execute(
"alter table users"
" add column ban_reason varchar"
)
await _set_version(c, 9)
4 changes: 4 additions & 0 deletions server/src/QRServer/db/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ class DbUser:
password: str
created_at: str
discord_user_id: str
is_banned: bool
banned_at: datetime | None
banned_by_dc_id: str | None
ban_reason: str | None

@property
def is_guest(self):
Expand Down
Loading