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
2 changes: 1 addition & 1 deletion api_schemas/user_schemas.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import TYPE_CHECKING, Annotated, Literal
from pydantic import StringConstraints
from fastapi_users_pelicanq import schemas as fastapi_users_schemas
from fastapi_users import schemas as fastapi_users_schemas
from api_schemas.post_schemas import PostRead
from helpers.constants import MAX_FIRST_NAME_LEN, MAX_LAST_NAME_LEN
from api_schemas.base_schema import BaseSchema
Expand Down
2 changes: 1 addition & 1 deletion db_models/user_model.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from typing import TYPE_CHECKING, Callable, Optional
from fastapi_users_pelicanq.db import SQLAlchemyBaseUserTable
from fastapi_users.db import SQLAlchemyBaseUserTable
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

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

SQLAlchemyBaseUserTable is imported from fastapi_users.db, but with the dependency split (fastapi-users-db-sqlalchemy is pinned in requirements) the SQLAlchemy base table class is usually provided by fastapi_users_db_sqlalchemy. If fastapi_users doesn’t re-export this symbol, this will break at import time; consider importing from fastapi_users_db_sqlalchemy for compatibility with the pinned packages.

Suggested change
from fastapi_users.db import SQLAlchemyBaseUserTable
from fastapi_users_db_sqlalchemy import SQLAlchemyBaseUserTable

Copilot uses AI. Check for mistakes.
from sqlalchemy import String, JSON
from sqlalchemy.orm import Mapped, relationship, mapped_column
from db_models.candidate_model import Candidate_DB
Expand Down
10 changes: 5 additions & 5 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ dnspython==2.8.0
email-validator==2.1.0.post1
fakeredis==2.31.0
fastapi==0.115.0
fastapi-users-db-sqlalchemy-pelicanq==6.0.6
fastapi-users-pelicanq==13.0.4
fastapi-users-db-sqlalchemy==7.0.0
fastapi-users==15.0.5
google-api-core==2.25.1
google-api-python-client==2.177.0
google-auth==2.40.3
Expand Down Expand Up @@ -45,19 +45,19 @@ pluggy==1.6.0
proto-plus==1.26.1
protobuf==6.33.5
psycopg==3.1.17
pwdlib==0.2.0
pwdlib==0.3.0
pyasn1==0.6.3
pyasn1_modules==0.4.2
pycparser==2.21
pydantic==2.9.2
pydantic-extra-types==2.5.0
pydantic_core==2.23.4
PyJWT==2.8.0
PyJWT==2.12.1
pyparsing==3.2.3
pytest==7.4.4
python-dateutil==2.9.0.post0
python-dotenv==1.2.1
python-multipart==0.0.9
python-multipart==0.0.22
pytz==2025.2
PyYAML==6.0.1
redis==7.4.0
Expand Down
2 changes: 1 addition & 1 deletion routes/auth_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from api_schemas.user_schemas import UserCreate, UserRead
from helpers.rate_limit import rate_limit
from fastapi import APIRouter
from fastapi_users_pelicanq.schemas import BaseUserUpdate
from fastapi_users.schemas import BaseUserUpdate
from user.custom_auth_router import get_auth_router, get_update_account_router
from user.user_stuff import USERS, auth_backend, refresh_backend

Expand Down
16 changes: 16 additions & 0 deletions routes/permission_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from database import DB_dependency, get_db
from db_models.permission_model import Permission_DB
from db_models.post_model import Post_DB
from db_models.user_model import User_DB
from api_schemas.permission_schemas import (
PermissionCreate,
PermissionRead,
Expand All @@ -31,6 +32,21 @@ def get_all_permissions(db: Annotated[Session, Depends(get_db)]):
return res


@permission_router.get("/me", response_model=list[tuple[str, str]] | None)
def get_my_permissions(member: Annotated[User_DB | None, Permission.check_member()]):
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

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

Permission.check_member() appears to return False when the requester is anonymous or not a member, but this parameter is annotated as User_DB | None. This works with if not member, but the type hint is misleading (and can confuse editors/mypy). Consider returning None from the dependency instead of False, or widening the annotation here to include bool.

Suggested change
def get_my_permissions(member: Annotated[User_DB | None, Permission.check_member()]):
def get_my_permissions(member: Annotated[User_DB | bool | None, Permission.check_member()]):

Copilot uses AI. Check for mistakes.
if not member:
return None
seen: set[tuple[str, str]] = set()
result: list[tuple[str, str]] = []
for post in member.posts:
for perm in post.post_permissions:
key: tuple[str, str] = (perm.permission.action, perm.permission.target)
if key not in seen:
seen.add(key)
result.append(key)
return result


# Create a new permission which later can be assigned to posts
@permission_router.post("/", response_model=PermissionRead, dependencies=[Permission.require("manage", "Permission")])
def create_permission(perm_data: PermissionCreate, db: Annotated[Session, Depends(get_db)]):
Expand Down
2 changes: 1 addition & 1 deletion routes/user_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
UpdateUserPosts,
)
from user.user_stuff import USERS
from fastapi_users_pelicanq.manager import BaseUserManager
from fastapi_users.manager import BaseUserManager
from helpers.image_checker import validate_image
from helpers.rate_limit import rate_limit
from helpers.types import ALLOWED_EXT, ALLOWED_IMG_SIZES, ALLOWED_IMG_TYPES, ASSETS_BASE_PATH
Expand Down
94 changes: 90 additions & 4 deletions tests/basic_fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,8 @@ def registered_users(client, user1_data, user2_data):


@pytest.fixture
def admin_post(db_session):
"""Create and return an admin post."""

def admin_council(db_session):
"""Create and return a council for the admin user."""
council = Council_DB(
name_sv="AdminCouncilSV",
description_sv="Svensk beskrivning för admins",
Expand All @@ -50,12 +49,18 @@ def admin_post(db_session):
)
db_session.add(council)
db_session.commit()
return council


@pytest.fixture
def admin_post(db_session, admin_council):
"""Create and return an admin post."""
post = Post_DB(
name_sv="AdminPostSV",
name_en="AdminPost",
description_en="AdminDescriptionEn",
description_sv="AdminDescriptionSv",
council_id=council.id,
council_id=admin_council.id,
elected_user_recommended_limit=1,
elected_user_max_limit=2,
elected_at_semester="HT",
Expand Down Expand Up @@ -107,6 +112,87 @@ def admin_post(db_session):
return post


@pytest.fixture
def super_user_post(db_session, admin_council):
"""Create and return a post which has only the "super" - "User" permission."""

post = Post_DB(
name_sv="SuperUserPostSV",
name_en="SuperUserPostEN",
description_en="SuperUserDescriptionEn",
description_sv="SuperUserDescriptionSv",
council_id=admin_council.id,
elected_user_recommended_limit=1,
elected_user_max_limit=2,
elected_at_semester="HT",
elected_by="Guild",
)
db_session.add(post)
db_session.commit()

permissions = [
Permission_DB(action="super", target="User"),
]
post.permissions.extend(permissions)
db_session.commit()

return post


@pytest.fixture
def manage_user_post(db_session, admin_council):
"""Create and return a post which has only the "manage" - "User" permission."""

post = Post_DB(
name_sv="ManageUserPostSV",
name_en="ManageUserPostEN",
description_en="ManageUserDescriptionEn",
description_sv="ManageUserDescriptionSv",
council_id=admin_council.id,
elected_user_recommended_limit=1,
elected_user_max_limit=2,
elected_at_semester="HT",
elected_by="Guild",
)
db_session.add(post)
db_session.commit()

permissions = [
Permission_DB(action="manage", target="User"),
]
post.permissions.extend(permissions)
db_session.commit()

return post


@pytest.fixture
def view_user_post(db_session, admin_council):
"""Create and return a post which has only the "view" - "User" permission."""

post = Post_DB(
name_sv="ViewUserPostSV",
name_en="ViewUserPostEN",
description_en="ViewUserDescriptionEn",
description_sv="ViewUserDescriptionSv",
council_id=admin_council.id,
elected_user_recommended_limit=1,
elected_user_max_limit=2,
elected_at_semester="HT",
elected_by="Guild",
)
db_session.add(post)
db_session.commit()

permissions = [
Permission_DB(action="view", target="User"),
]
post.permissions.extend(permissions)
db_session.commit()

return post


@pytest.fixture
def admin_user(client, db_session, admin_post):
"""Create and return a full admin user with the admin post and permissions."""
Expand Down
Loading
Loading