feat: implement API security measures phase 1

This commit is contained in:
Marko (Hermes Implementer)
2026-05-27 00:24:09 +00:00
parent a0b3ae7e34
commit c2fc7bc15f
21 changed files with 1050 additions and 44 deletions
+29 -7
View File
@@ -1,7 +1,7 @@
from django.db.models import Count, Q
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated
from rest_framework.request import Request
from rest_framework.response import Response
@@ -14,16 +14,30 @@ from .serializers import (
class JobApplicationViewSet(viewsets.ModelViewSet):
queryset = JobApplication.objects.all().prefetch_related("updates")
serializer_class = JobApplicationSerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
"""Filter by user for non-admin users; admins see all."""
user = self.request.user
qs = JobApplication.objects.all().prefetch_related("updates")
if not user.is_admin:
qs = qs.filter(user=user)
return qs
class JobUpdateViewSet(viewsets.ModelViewSet):
queryset = JobUpdate.objects.select_related("job_application").all()
serializer_class = JobUpdateSerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
"""Filter by user via job_application for non-admin users."""
user = self.request.user
qs = JobUpdate.objects.select_related("job_application").all()
if not user.is_admin:
qs = qs.filter(job_application__user=user)
return qs
@action(detail=False, methods=["get"])
def latest(self, request: Request) -> Response:
"""Return the 3 most recent job updates with job info and metrics."""
@@ -38,16 +52,24 @@ class JobUpdateViewSet(viewsets.ModelViewSet):
@action(detail=False, methods=["get"])
def metrics(self, request: Request) -> Response:
"""Return dashboard metrics: total apps, status breakdown, interview/offer counts."""
total = JobApplication.objects.count()
qs = self.get_queryset()
total = qs.values("job_application").distinct().count()
# Get status counts from the distinct job applications the user owns
app_qs = JobApplication.objects.all()
if not request.user.is_admin:
app_qs = app_qs.filter(user=request.user)
total = app_qs.count()
status_counts = dict(
JobApplication.objects.values("status")
app_qs.values("status")
.annotate(count=Count("id"))
.values_list("status", "count")
)
interview_count = JobApplication.objects.filter(
interview_count = app_qs.filter(
Q(status="INTERVIEW") | Q(status="OFFER")
).count()
offer_count = JobApplication.objects.filter(status="OFFER").count()
offer_count = app_qs.filter(status="OFFER").count()
rejection_rate = (
round(
status_counts.get("REJECTED", 0) / total * 100, 1