- CertificateVerifyView: GET /certificates/verify/{serial}/ — AllowAny
- Re-computes SHA-256 from stored PDF and compares to archived hash
- Returns {valid, serial_number, course_title, issued_at, hash} — no PII
- Returns 404 for non-completed or non-existent certificates
- 7 integration tests: hash match, tampered hash, missing file, PII exclusion, 404 cases
Co-Authored-By: Paperclip <noreply@paperclip.ing>
59 lines
1.9 KiB
Python
59 lines
1.9 KiB
Python
from django.shortcuts import get_object_or_404
|
|
from rest_framework.permissions import AllowAny, IsAuthenticated
|
|
from rest_framework.response import Response
|
|
from rest_framework.views import APIView
|
|
|
|
from .models import Certificate, CertificateStatus
|
|
from .renderer import compute_pdf_hash
|
|
from .serializers import CertificateSerializer
|
|
|
|
|
|
class MyCertificatesView(APIView):
|
|
permission_classes = [IsAuthenticated]
|
|
|
|
def get(self, request):
|
|
certs = Certificate.objects.filter(user=request.user).select_related("course")
|
|
return Response(CertificateSerializer(certs, many=True).data)
|
|
|
|
|
|
class CertificateDetailView(APIView):
|
|
permission_classes = [IsAuthenticated]
|
|
|
|
def get(self, request, cert_id):
|
|
cert = get_object_or_404(Certificate, pk=cert_id, user=request.user)
|
|
return Response(CertificateSerializer(cert).data)
|
|
|
|
|
|
class CertificateVerifyView(APIView):
|
|
"""
|
|
Public endpoint for QR/hash-based certificate verification.
|
|
Returns minimal info only (no PII beyond course name and issue date).
|
|
Also validates hash integrity against the stored PDF.
|
|
"""
|
|
|
|
permission_classes = [AllowAny]
|
|
|
|
def get(self, request, serial_number):
|
|
cert = get_object_or_404(
|
|
Certificate,
|
|
serial_number=serial_number,
|
|
status=CertificateStatus.COMPLETED,
|
|
)
|
|
|
|
# Verify hash integrity (re-compute from stored file)
|
|
hash_valid = False
|
|
if cert.pdf_path and cert.pdf_hash:
|
|
try:
|
|
actual_hash = compute_pdf_hash(cert.pdf_path)
|
|
hash_valid = actual_hash == cert.pdf_hash
|
|
except (OSError, FileNotFoundError):
|
|
hash_valid = False
|
|
|
|
return Response({
|
|
"valid": hash_valid,
|
|
"serial_number": cert.serial_number,
|
|
"course_title": cert.course.title,
|
|
"issued_at": cert.issued_at,
|
|
"hash": cert.pdf_hash,
|
|
})
|