Skip to content

Commit

Permalink
fix(backend): pretty errors for unique constraints
Browse files Browse the repository at this point in the history
  • Loading branch information
mikonse committed Jan 2, 2024
1 parent e57c925 commit 350f419
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 58 deletions.
90 changes: 48 additions & 42 deletions abrechnung/application/groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@
class GroupService(Service):
@with_db_transaction
async def create_group(
self,
*,
conn: Connection,
user: User,
name: str,
description: str,
currency_symbol: str,
add_user_account_on_join: bool,
terms: str,
self,
*,
conn: Connection,
user: User,
name: str,
description: str,
currency_symbol: str,
add_user_account_on_join: bool,
terms: str,
) -> int:
if user.is_guest_user:
raise PermissionError(f"guest users are not allowed to create group new groups")
Expand Down Expand Up @@ -70,15 +70,15 @@ async def create_group(

@with_db_transaction
async def create_invite(
self,
*,
conn: Connection,
user: User,
group_id: int,
description: str,
single_use: bool,
join_as_editor: bool,
valid_until: datetime,
self,
*,
conn: Connection,
user: User,
group_id: int,
description: str,
single_use: bool,
join_as_editor: bool,
valid_until: datetime,
) -> int:
if user.is_guest_user:
raise PermissionError(f"guest users are not allowed to create group invites")
Expand All @@ -98,12 +98,12 @@ async def create_invite(

@with_db_transaction
async def delete_invite(
self,
*,
conn: Connection,
user: User,
group_id: int,
invite_id: int,
self,
*,
conn: Connection,
user: User,
group_id: int,
invite_id: int,
):
await check_group_permissions(conn=conn, group_id=group_id, user=user, can_write=True)
deleted_id = await conn.fetchval(
Expand Down Expand Up @@ -155,6 +155,12 @@ async def join_group(self, *, conn: Connection, user: User, invite_token: str) -
if not group:
raise PermissionError(f"Invalid invite token")

user_is_already_member = await conn.fetchval(
"select exists (select user_id from group_membership where user_id = $1 and group_id = $2)", user.id,
invite["group_id"])
if user_is_already_member:
raise InvalidCommand(f"User is already a member of this group")

await conn.execute(
"insert into group_membership (user_id, group_id, invited_by, can_write, is_owner) "
"values ($1, $2, $3, $4, false)",
Expand Down Expand Up @@ -203,16 +209,16 @@ async def get_group(self, *, conn: Connection, user: User, group_id: int) -> Gro

@with_db_transaction
async def update_group(
self,
*,
conn: Connection,
user: User,
group_id: int,
name: str,
description: str,
currency_symbol: str,
add_user_account_on_join: bool,
terms: str,
self,
*,
conn: Connection,
user: User,
group_id: int,
name: str,
description: str,
currency_symbol: str,
add_user_account_on_join: bool,
terms: str,
):
await check_group_permissions(conn=conn, group_id=group_id, user=user, is_owner=True)
await conn.execute(
Expand All @@ -229,14 +235,14 @@ async def update_group(

@with_db_transaction
async def update_member_permissions(
self,
*,
conn: Connection,
user: User,
group_id: int,
member_id: int,
can_write: bool,
is_owner: bool,
self,
*,
conn: Connection,
user: User,
group_id: int,
member_id: int,
can_write: bool,
is_owner: bool,
):
if user.id == member_id:
raise InvalidCommand(f"group members cannot modify their own privileges")
Expand Down
1 change: 0 additions & 1 deletion abrechnung/application/transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
from abrechnung.core.errors import InvalidCommand, NotFoundError
from abrechnung.core.service import Service
from abrechnung.domain.transactions import (
ALLOWED_FILETYPES,
NewFile,
NewTransaction,
NewTransactionPosition,
Expand Down
36 changes: 21 additions & 15 deletions abrechnung/application/users.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from datetime import datetime, timezone
from typing import Optional

import asyncpg
from asyncpg.pool import Pool
from email_validator import EmailNotValidError, validate_email
from jose import JWTError, jwt
Expand Down Expand Up @@ -31,6 +30,20 @@ class TokenMetadata(BaseModel):
session_id: int


async def _check_user_exists(*, conn: Connection, username: str, email: str):
user_exists = await conn.fetchrow(
"select "
"exists(select from usr where username = $1) as username_exists, "
"exists(select from usr where email = $2) as email_exists",
username,
email,
)
if user_exists["username_exists"]:
raise InvalidCommand("A user with this username already exists")
if user_exists["email_exists"]:
raise InvalidCommand("A user with this email already exists")


class UserService(Service):
def __init__(
self,
Expand Down Expand Up @@ -96,17 +109,6 @@ async def _verify_user_password(self, user_id: int, password: str) -> bool:

return self._check_password(password, user["hashed_password"])

@with_db_transaction
async def is_session_token_valid(self, *, conn: Connection, token: str) -> Optional[tuple[int, int]]:
"""returns the session id"""
row = await conn.fetchrow(
"select user_id, id from session where token = $1 and valid_until is null or valid_until > now()",
token,
)
if row:
await conn.execute("update session set last_seen = now() where token = $1", token)

return row

@with_db_transaction
async def login_user(
Expand Down Expand Up @@ -154,9 +156,10 @@ async def logout_user(self, *, conn: Connection, user: User, session_id: int):

@with_db_transaction
async def demo_register_user(self, *, conn: Connection, username: str, email: str, password: str) -> int:
await _check_user_exists(conn=conn, username=username, email=email)
hashed_password = self._hash_password(password)
user_id = await conn.fetchval(
"insert into usr (username, email, hashed_password, pending) " "values ($1, $2, $3, false) returning id",
"insert into usr (username, email, hashed_password, pending) values ($1, $2, $3, false) returning id",
username,
email,
hashed_password,
Expand All @@ -166,7 +169,8 @@ async def demo_register_user(self, *, conn: Connection, username: str, email: st

return user_id

def _validate_email_address(self, email: str) -> str:
@staticmethod
def _validate_email_address(email: str) -> str:
try:
valid = validate_email(email)
email = valid.email
Expand Down Expand Up @@ -200,14 +204,16 @@ async def register_user(
if not self.enable_registration:
raise PermissionError(f"User registrations are disabled on this server")

await _check_user_exists(conn=conn, username=username, email=email)

email = self._validate_email_address(email)

is_guest_user = False
has_valid_email = self._validate_email_domain(email)

if invite_token is not None and self.allow_guest_users and not has_valid_email:
invite = await conn.fetchval(
"select id " "from group_invite where token = $1 and valid_until > now()",
"select id from group_invite where token = $1 and valid_until > now()",
invite_token,
)
if invite is None:
Expand Down

0 comments on commit 350f419

Please sign in to comment.