adds api keys for api usage

apikeys
Laurent 2 months ago
parent 08f78e7de4
commit 08ada3d771
  1. 0
      api/__init__.py
  2. 21
      api/admin.py
  3. 7
      api/apps.py
  4. 24
      api/authentication.py
  5. 36
      api/migrations/0001_initial.py
  6. 0
      api/migrations/__init__.py
  7. 23
      api/models.py
  8. 4
      api/views.py
  9. 2
      padelclub_backend/settings.py
  10. 1
      requirements.txt

@ -0,0 +1,21 @@
from django.contrib import admin
from rest_framework_api_key.admin import APIKeyModelAdmin
from rest_framework_api_key.models import APIKey as DefaultAPIKey
from .models import APIKey
# Unregister the default APIKey admin
admin.site.unregister(DefaultAPIKey)
@admin.register(APIKey)
class APIKeyAdmin(APIKeyModelAdmin):
list_display = [*APIKeyModelAdmin.list_display, "user"]
list_filter = [*APIKeyModelAdmin.list_filter, "user"]
search_fields = [*APIKeyModelAdmin.search_fields, "user__username", "user__email"]
def get_form(self, request, obj=None, **kwargs):
form = super().get_form(request, obj, **kwargs)
# Make user field required
if 'user' in form.base_fields:
form.base_fields['user'].required = True
return form

@ -0,0 +1,7 @@
from django.apps import AppConfig
class ApiConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'api'
verbose_name = 'API'

@ -0,0 +1,24 @@
from rest_framework_api_key.permissions import BaseHasAPIKey
from .models import APIKey
class HasAPIKey(BaseHasAPIKey):
model = APIKey
def has_permission(self, request, view):
# First check if we have a valid API key
has_api_key = super().has_permission(request, view)
if has_api_key:
# Get the API key from the request
key = self.get_key(request)
if key:
try:
api_key = APIKey.objects.get_from_key(key)
# Set the request.user to the user associated with the API key
request.user = api_key.user
return True
except APIKey.DoesNotExist:
pass
return False

@ -0,0 +1,36 @@
# Generated by Django 5.1 on 2025-09-17 07:49
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='APIKey',
fields=[
('id', models.CharField(editable=False, max_length=150, primary_key=True, serialize=False, unique=True)),
('prefix', models.CharField(editable=False, max_length=8, unique=True)),
('hashed_key', models.CharField(editable=False, max_length=150)),
('created', models.DateTimeField(auto_now_add=True, db_index=True)),
('name', models.CharField(default=None, help_text='A free-form name for the API key. Need not be unique. 50 characters max.', max_length=50)),
('revoked', models.BooleanField(blank=True, default=False, help_text='If the API key is revoked, clients cannot use it anymore. (This cannot be undone.)')),
('expiry_date', models.DateTimeField(blank=True, help_text='Once API key expires, clients cannot use it anymore.', null=True, verbose_name='Expires')),
('user', models.ForeignKey(help_text='The user this API key belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='api_keys', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'API Key',
'verbose_name_plural': 'API Keys',
'ordering': ('-created',),
'abstract': False,
},
),
]

@ -0,0 +1,23 @@
from django.db import models
from rest_framework_api_key.models import AbstractAPIKey
from tournaments.models import CustomUser
class APIKey(AbstractAPIKey):
"""
API Key model linked to a specific user.
This allows filtering API access based on the user associated with the API key.
"""
user = models.ForeignKey(
CustomUser,
on_delete=models.CASCADE,
related_name='api_keys',
help_text='The user this API key belongs to'
)
class Meta(AbstractAPIKey.Meta):
verbose_name = "API Key"
verbose_name_plural = "API Keys"
def __str__(self):
return f"API Key for {self.user.username}"

@ -4,6 +4,7 @@ from rest_framework.decorators import api_view, permission_classes
from rest_framework import status from rest_framework import status
from rest_framework.exceptions import MethodNotAllowed from rest_framework.exceptions import MethodNotAllowed
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import IsAuthenticated
from .authentication import HasAPIKey
from django.conf import settings from django.conf import settings
from django.http import Http404, HttpResponse, JsonResponse from django.http import Http404, HttpResponse, JsonResponse
@ -76,10 +77,11 @@ class ClubViewSet(SoftDeleteViewSet):
class TournamentSummaryViewSet(SoftDeleteViewSet): class TournamentSummaryViewSet(SoftDeleteViewSet):
queryset = Tournament.objects.all() queryset = Tournament.objects.all()
serializer_class = TournamentSummarySerializer serializer_class = TournamentSummarySerializer
permission_classes = [HasAPIKey]
def get_queryset(self): def get_queryset(self):
if self.request.user.is_anonymous: if self.request.user.is_anonymous:
return [] return Tournament.objects.none()
return self.queryset.filter( return self.queryset.filter(
Q(event__creator=self.request.user) | Q(related_user=self.request.user) Q(event__creator=self.request.user) | Q(related_user=self.request.user)

@ -37,6 +37,7 @@ INSTALLED_APPS = [
'tournaments', 'tournaments',
'shop', 'shop',
'biz', 'biz',
'api',
'django.contrib.admin', 'django.contrib.admin',
'django.contrib.auth', 'django.contrib.auth',
'django.contrib.contenttypes', 'django.contrib.contenttypes',
@ -50,6 +51,7 @@ INSTALLED_APPS = [
'channels_redis', 'channels_redis',
'django_filters', 'django_filters',
'background_task', 'background_task',
'rest_framework_api_key',
] ]

@ -19,3 +19,4 @@ stripe==11.6.0
django-background-tasks==1.2.8 django-background-tasks==1.2.8
Pillow==10.2.0 Pillow==10.2.0
playwright==1.40.0 playwright==1.40.0
djangorestframework-api-key==3.1.0

Loading…
Cancel
Save