adapt CanAccessLookup to api key auth

This commit is contained in:
imperosol 2025-05-20 21:04:49 +02:00
parent 867878e5c0
commit 851db41665
10 changed files with 75 additions and 9 deletions

View File

@ -8,7 +8,7 @@ from ninja_extra.schemas import PaginatedResponseSchema
from apikey.auth import ApiKeyAuth from apikey.auth import ApiKeyAuth
from club.models import Club from club.models import Club
from club.schemas import ClubSchema from club.schemas import ClubSchema, SimpleClubSchema
from core.auth.api_permissions import CanAccessLookup, HasPerm from core.auth.api_permissions import CanAccessLookup, HasPerm
@ -16,8 +16,10 @@ from core.auth.api_permissions import CanAccessLookup, HasPerm
class ClubController(ControllerBase): class ClubController(ControllerBase):
@route.get( @route.get(
"/search", "/search",
response=PaginatedResponseSchema[ClubSchema], response=PaginatedResponseSchema[SimpleClubSchema],
auth=[SessionAuth(), ApiKeyAuth()],
permissions=[CanAccessLookup], permissions=[CanAccessLookup],
url_name="search_club",
) )
@paginate(PageNumberPaginationExtra, page_size=50) @paginate(PageNumberPaginationExtra, page_size=50)
def search_club(self, search: Annotated[str, MinLen(1)]): def search_club(self, search: Annotated[str, MinLen(1)]):
@ -28,6 +30,7 @@ class ClubController(ControllerBase):
response=ClubSchema, response=ClubSchema,
auth=[SessionAuth(), ApiKeyAuth()], auth=[SessionAuth(), ApiKeyAuth()],
permissions=[HasPerm("club.view_club")], permissions=[HasPerm("club.view_club")],
url_name="fetch_club",
) )
def fetch_club(self, club_id: int): def fetch_club(self, club_id: int):
return self.get_object_or_exception( return self.get_object_or_exception(

View File

@ -1,16 +1,21 @@
import pytest import pytest
from django.test import Client
from django.urls import reverse
from model_bakery import baker from model_bakery import baker
from ninja_extra.testing import TestClient
from pytest_django.asserts import assertNumQueries from pytest_django.asserts import assertNumQueries
from club.api import ClubController
from club.models import Club, Membership from club.models import Club, Membership
from core.baker_recipes import subscriber_user
@pytest.mark.django_db @pytest.mark.django_db
def test_fetch_club(): def test_fetch_club(client: Client):
club = baker.make(Club) club = baker.make(Club)
baker.make(Membership, club=club, _quantity=10, _bulk_create=True) baker.make(Membership, club=club, _quantity=10, _bulk_create=True)
with assertNumQueries(3): user = subscriber_user.make()
res = TestClient(ClubController).get(f"/{club.id}") client.force_login(user)
with assertNumQueries(7):
# - 4 queries for authentication
# - 3 queries for the actual data
res = client.get(reverse("api:fetch_club", kwargs={"club_id": club.id}))
assert res.status_code == 200 assert res.status_code == 200

View File

@ -5,11 +5,13 @@ from django.conf import settings
from django.db.models import F from django.db.models import F
from django.http import HttpResponse from django.http import HttpResponse
from ninja import File, Query from ninja import File, Query
from ninja.security import SessionAuth
from ninja_extra import ControllerBase, api_controller, paginate, route from ninja_extra import ControllerBase, api_controller, paginate, route
from ninja_extra.exceptions import PermissionDenied from ninja_extra.exceptions import PermissionDenied
from ninja_extra.pagination import PageNumberPaginationExtra from ninja_extra.pagination import PageNumberPaginationExtra
from ninja_extra.schemas import PaginatedResponseSchema from ninja_extra.schemas import PaginatedResponseSchema
from apikey.auth import ApiKeyAuth
from club.models import Mailing from club.models import Mailing
from core.auth.api_permissions import CanAccessLookup, CanView, HasPerm from core.auth.api_permissions import CanAccessLookup, CanView, HasPerm
from core.models import Group, QuickUploadImage, SithFile, User from core.models import Group, QuickUploadImage, SithFile, User
@ -90,6 +92,7 @@ class SithFileController(ControllerBase):
@route.get( @route.get(
"/search", "/search",
response=PaginatedResponseSchema[SithFileSchema], response=PaginatedResponseSchema[SithFileSchema],
auth=[SessionAuth(), ApiKeyAuth()],
permissions=[CanAccessLookup], permissions=[CanAccessLookup],
) )
@paginate(PageNumberPaginationExtra, page_size=50) @paginate(PageNumberPaginationExtra, page_size=50)
@ -102,6 +105,7 @@ class GroupController(ControllerBase):
@route.get( @route.get(
"/search", "/search",
response=PaginatedResponseSchema[GroupSchema], response=PaginatedResponseSchema[GroupSchema],
auth=[SessionAuth(), ApiKeyAuth()],
permissions=[CanAccessLookup], permissions=[CanAccessLookup],
) )
@paginate(PageNumberPaginationExtra, page_size=50) @paginate(PageNumberPaginationExtra, page_size=50)

View File

@ -189,4 +189,4 @@ class IsLoggedInCounter(BasePermission):
return Counter.objects.filter(token=token).exists() return Counter.objects.filter(token=token).exists()
CanAccessLookup = IsOldSubscriber | IsRoot | IsLoggedInCounter CanAccessLookup = IsLoggedInCounter | HasPerm("core.access_lookup")

View File

@ -805,6 +805,8 @@ class Command(BaseCommand):
"add_peoplepicturerelation", "add_peoplepicturerelation",
"add_page", "add_page",
"add_quickuploadimage", "add_quickuploadimage",
"view_club",
"access_lookup",
] ]
) )
) )

View File

@ -0,0 +1,28 @@
# Generated by Django 5.2 on 2025-05-20 17:50
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("core", "0045_quickuploadimage")]
operations = [
migrations.CreateModel(
name="GlobalPermissionRights",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
],
options={
"permissions": [("access_lookup", "Can access any lookup in the sith")],
"managed": False,
"default_permissions": [],
},
),
]

View File

@ -754,6 +754,23 @@ class UserBan(models.Model):
return f"Ban of user {self.user.id}" return f"Ban of user {self.user.id}"
class GlobalPermissionRights(models.Model):
"""Little hack to have permissions not linked to a specific db table."""
class Meta:
# No database table creation or deletion
# operations will be performed for this model.
managed = False
# disable "add", "change", "delete" and "view" default permissions
default_permissions = []
permissions = [("access_lookup", "Can access any lookup in the sith")]
def __str__(self):
return self.__class__.__name__
class Preferences(models.Model): class Preferences(models.Model):
user = models.OneToOneField( user = models.OneToOneField(
User, related_name="_preferences", on_delete=models.CASCADE User, related_name="_preferences", on_delete=models.CASCADE

View File

@ -16,10 +16,12 @@ from django.conf import settings
from django.db.models import F from django.db.models import F
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from ninja import Query from ninja import Query
from ninja.security import SessionAuth
from ninja_extra import ControllerBase, api_controller, paginate, route from ninja_extra import ControllerBase, api_controller, paginate, route
from ninja_extra.pagination import PageNumberPaginationExtra from ninja_extra.pagination import PageNumberPaginationExtra
from ninja_extra.schemas import PaginatedResponseSchema from ninja_extra.schemas import PaginatedResponseSchema
from apikey.auth import ApiKeyAuth
from core.auth.api_permissions import CanAccessLookup, CanView, IsInGroup, IsRoot from core.auth.api_permissions import CanAccessLookup, CanView, IsInGroup, IsRoot
from counter.models import Counter, Product, ProductType from counter.models import Counter, Product, ProductType
from counter.schemas import ( from counter.schemas import (
@ -62,6 +64,7 @@ class CounterController(ControllerBase):
@route.get( @route.get(
"/search", "/search",
response=PaginatedResponseSchema[SimplifiedCounterSchema], response=PaginatedResponseSchema[SimplifiedCounterSchema],
auth=[SessionAuth(), ApiKeyAuth()],
permissions=[CanAccessLookup], permissions=[CanAccessLookup],
) )
@paginate(PageNumberPaginationExtra, page_size=50) @paginate(PageNumberPaginationExtra, page_size=50)
@ -74,6 +77,7 @@ class ProductController(ControllerBase):
@route.get( @route.get(
"/search", "/search",
response=PaginatedResponseSchema[SimpleProductSchema], response=PaginatedResponseSchema[SimpleProductSchema],
auth=[SessionAuth(), ApiKeyAuth()],
permissions=[CanAccessLookup], permissions=[CanAccessLookup],
) )
@paginate(PageNumberPaginationExtra, page_size=50) @paginate(PageNumberPaginationExtra, page_size=50)

View File

@ -68,7 +68,7 @@ class TestUVSearch(TestCase):
def test_permissions(self): def test_permissions(self):
# Test with anonymous user # Test with anonymous user
response = self.client.get(self.url) response = self.client.get(self.url)
assert response.status_code == 403 assert response.status_code == 401
# Test with not subscribed user # Test with not subscribed user
self.client.force_login(baker.make(User)) self.client.force_login(baker.make(User))

View File

@ -5,6 +5,7 @@ from django.core.exceptions import ValidationError
from django.db.models import F from django.db.models import F
from django.urls import reverse from django.urls import reverse
from ninja import Body, File, Query from ninja import Body, File, Query
from ninja.security import SessionAuth
from ninja_extra import ControllerBase, api_controller, paginate, route from ninja_extra import ControllerBase, api_controller, paginate, route
from ninja_extra.exceptions import NotFound, PermissionDenied from ninja_extra.exceptions import NotFound, PermissionDenied
from ninja_extra.pagination import PageNumberPaginationExtra from ninja_extra.pagination import PageNumberPaginationExtra
@ -12,6 +13,7 @@ from ninja_extra.permissions import IsAuthenticated
from ninja_extra.schemas import PaginatedResponseSchema from ninja_extra.schemas import PaginatedResponseSchema
from pydantic import NonNegativeInt from pydantic import NonNegativeInt
from apikey.auth import ApiKeyAuth
from core.auth.api_permissions import ( from core.auth.api_permissions import (
CanAccessLookup, CanAccessLookup,
CanEdit, CanEdit,
@ -53,6 +55,7 @@ class AlbumController(ControllerBase):
@route.get( @route.get(
"/autocomplete-search", "/autocomplete-search",
response=PaginatedResponseSchema[AlbumAutocompleteSchema], response=PaginatedResponseSchema[AlbumAutocompleteSchema],
auth=[SessionAuth(), ApiKeyAuth()],
permissions=[CanAccessLookup], permissions=[CanAccessLookup],
) )
@paginate(PageNumberPaginationExtra, page_size=50) @paginate(PageNumberPaginationExtra, page_size=50)